Click here to Skip to main content
Click here to Skip to main content

Working on an NPAPI-browser plugin

By , 8 Jul 2010
 

Screenshot.png

Introduction

A browser plug-in has the same rights as a window application on the PC. Plug-ins empower websites with rich features like as graphics, or extended computation, or system access. But it also opens the doors for criminals. So everyone has to be aware which browser plug-in gets installed in their systems. As some Google guys wrote, NPAPI is a real hammer.

This sample is based on a sample npruntime of the Gecko SDK. So all credits go there; I have only extended in the way I want to show the power and share my knowledge; my additions to the original files are all marked with kk (I hope so).

Background

The power of NPAPI is that it can work in Mozilla Firefox, Google Chrome, and Apple Safari, and is also cross platform. But that doesn't come for free; there are some installation steps to do. My plug-in now only works in Firefox on Windows. To compile the code, you need the Gecko SDK.

Using the sample

You need to copy the DLL in the plug-ins directory of Mozilla Firefox and than open Codeproject.html in the project with Firefox.

Using the code

The code is, as mentioned, based on the npruntime sample, and extended in some ways. The skeleton is setting up and initializing a plug-in which can accessed from websites.

To get a plug-in running, you have to deal with at least three languages: HTML, JavaScript, and C++.

To include a plug-in in a website, you only need this HMTL-code:

<embed type="application/npcodeproject" width=800 height=200 id="plugin">

To use the plug-in, the plug-in must be carefully installed on a PC that includes all the required stuff like DLLs. So the best thing to do is to link statically. Mozilla Firefox installs plug-ins in its "plugins" directory.

To access the plug-in from JavaScript, you need a reference to the instance of the plug-in. If it doesn't work, then something has went wrong. Dealing with that case is really a pain.

var PLUGIN = document.getElementById('plugin');

Writing the plug-in is done in C++. At first, you need to understand the startup of the plug-in: getting function pointers from the Netscape Plug-in API to work. Callback communications are done via these functions.

NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs)
{
  if(pFuncs == NULL)
    return NPERR_INVALID_FUNCTABLE_ERROR;

  if(pFuncs->size < sizeof(NPPluginFuncs))
    return NPERR_INVALID_FUNCTABLE_ERROR;

  pFuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
  pFuncs->newp          = NPP_New;
  pFuncs->destroy       = NPP_Destroy;
  pFuncs->setwindow     = NPP_SetWindow;
  pFuncs->newstream     = NPP_NewStream;
  pFuncs->destroystream = NPP_DestroyStream;
  pFuncs->asfile        = NPP_StreamAsFile;
  pFuncs->writeready    = NPP_WriteReady;
  pFuncs->write         = NPP_Write;
  pFuncs->print         = NPP_Print;
  pFuncs->event         = NPP_HandleEvent;
  pFuncs->urlnotify     = NPP_URLNotify;
  pFuncs->getvalue      = NPP_GetValue;
  pFuncs->setvalue      = NPP_SetValue;
  pFuncs->javaClass     = NULL;

  return NPERR_NO_ERROR;
}

NPError OSCALL
NP_Initialize(NPNetscapeFuncs* pFuncs
#ifdef XP_UNIX
              , NPPluginFuncs* pluginFuncs
#endif
              )
{
  if(pFuncs == NULL)
    return NPERR_INVALID_FUNCTABLE_ERROR;

  if(HIBYTE(pFuncs->version) > NP_VERSION_MAJOR)
    return NPERR_INCOMPATIBLE_VERSION_ERROR;

  if(pFuncs->size < sizeof(NPNetscapeFuncs))
    return NPERR_INVALID_FUNCTABLE_ERROR;

  NPNFuncs.size                    = pFuncs->size;
  NPNFuncs.version                 = pFuncs->version;
  NPNFuncs.geturlnotify            = pFuncs->geturlnotify;
  NPNFuncs.geturl                  = pFuncs->geturl;
  NPNFuncs.posturlnotify           = pFuncs->posturlnotify;
  NPNFuncs.posturl                 = pFuncs->posturl;
  NPNFuncs.requestread             = pFuncs->requestread;
  NPNFuncs.newstream               = pFuncs->newstream;
  NPNFuncs.write                   = pFuncs->write;
  NPNFuncs.destroystream           = pFuncs->destroystream;
  NPNFuncs.status                  = pFuncs->status;
  NPNFuncs.uagent                  = pFuncs->uagent;
  NPNFuncs.memalloc                = pFuncs->memalloc;
  NPNFuncs.memfree                 = pFuncs->memfree;
  NPNFuncs.memflush                = pFuncs->memflush;
  NPNFuncs.reloadplugins           = pFuncs->reloadplugins;
  NPNFuncs.getJavaEnv              = NULL;
  NPNFuncs.getJavaPeer             = NULL;
  NPNFuncs.getvalue                = pFuncs->getvalue;
  NPNFuncs.setvalue                = pFuncs->setvalue;
  NPNFuncs.invalidaterect          = pFuncs->invalidaterect;
  NPNFuncs.invalidateregion        = pFuncs->invalidateregion;
  NPNFuncs.forceredraw             = pFuncs->forceredraw;
  NPNFuncs.getstringidentifier     = pFuncs->getstringidentifier;
  NPNFuncs.getstringidentifiers    = pFuncs->getstringidentifiers;
  NPNFuncs.getintidentifier        = pFuncs->getintidentifier;
  NPNFuncs.identifierisstring      = pFuncs->identifierisstring;
  NPNFuncs.utf8fromidentifier      = pFuncs->utf8fromidentifier;
  NPNFuncs.intfromidentifier       = pFuncs->intfromidentifier;
  NPNFuncs.createobject            = pFuncs->createobject;
  NPNFuncs.retainobject            = pFuncs->retainobject;
  NPNFuncs.releaseobject           = pFuncs->releaseobject;
  NPNFuncs.invoke                  = pFuncs->invoke;
  NPNFuncs.invokeDefault           = pFuncs->invokeDefault;
  NPNFuncs.evaluate                = pFuncs->evaluate;
  NPNFuncs.getproperty             = pFuncs->getproperty;
  NPNFuncs.setproperty             = pFuncs->setproperty;
  NPNFuncs.removeproperty          = pFuncs->removeproperty;
  NPNFuncs.hasproperty             = pFuncs->hasproperty;
  NPNFuncs.hasmethod               = pFuncs->hasmethod;
  NPNFuncs.releasevariantvalue     = pFuncs->releasevariantvalue;
  NPNFuncs.setexception            = pFuncs->setexception;
  
  //end of snippet

If these functions get called, the plug-in runs, and the special implementation does stuff like getting and setting properties. But before you can do that, check if the "Has" callback is implemented.

if( !strcmp( "Name", pProp ) )
{
    //allocating Memory for the string with invocation of Browser-API
    char *p = (char*) NPN_MemAlloc( m_pszName );
    STRINGZ_TO_NPVARIANT( p, *result);
    return true;
}

The interesting point here is that allocating memory with the NPAPI is needed to create the result strings. It took me some time to figure that out. The handy macro STRINGZ_TO_NPVARIANT helps in the output.

The real functionality is done via the Invoke interface. As seen, it is possible to call my "Add" function with strings and numbers mixed and also a varying count of arguments. But that is done in the tuning of the interface.

bool ScriptablePluginObject::Invoke(NPIdentifier name, 
     const NPVariant *args, uint32_t argCount, NPVariant *result)
{
    //kk
    char *pFunc = NPN_UTF8FromIdentifier(name);

    if( !strcmp( "Add", pFunc ) )
    {
        int sum = 0;

        for( unsigned int i = 0; i < argCount; i++ )
        {
            if( args[i].type == NPVariantType_Int32 )
            {
                sum += args[i].value.intValue;
            }
            else if( args[i].type == NPVariantType_String )
            {
                CNPString s(args[i].value.stringValue);
                sum += atoi( s );
            }
            else return false;//an error happenend

        }

Points of interest

It is really interesting to see how the plug-ins work under the hood. Sometimes it gets mysterious when it doesn't work. I am not happy that it doesn't work with Google Chrome and Apple Safari, but I do not have the time to figure it out now.

Internet Explorer doesn't support the NPAPI, so you will need ActiveX for it. But this should be considered in the architecture of the code.

If you write a plug-in, don't forget to set the accurate MIMEType in NPP_GetMIMEDescription() and the rc file, or you will have a headache for hours. If you can't compile the code because a lot of Windows types are unknown, include the Windows headers first!!!

The output DLL has to be named in the "np*.dll" format to be a Firefox plug-in, and goes to to the plugins directory of Firefox: "C:\Program Files\Mozilla Firefox\plugins".

Some interesting links

History

  • 08/07/2010: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

KarstenK
Software Developer
Germany Germany
Member
I am living in germany and now living from programming for some Years. In my spare time I like sports as jogging, playing football (soccer) and basketball.
 
We must take care for our planet, because we and our family has no other. And everybody has to do something for it.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionis this code working?memberJayShin15 Jan '12 - 14:25 
AnswerRe: is this code working?memberKarstenK15 Jan '12 - 21:21 
QuestionDeploying the plug-inmemberL4pp17 Dec '11 - 22:07 
AnswerRe: Deploying the plug-inmemberKarstenK7 Dec '11 - 22:41 
GeneralRe: Deploying the plug-inmemberL4pp17 Dec '11 - 22:54 
GeneralRe: Deploying the plug-inmemberKarstenK15 Jan '12 - 21:21 
QuestionPluginWinProc can receive WM_IME_* message ?memberchengdu111317 Nov '11 - 20:37 
AnswerRe: PluginWinProc can receive WM_IME_* message ?memberKarstenK17 Nov '11 - 22:13 
GeneralRe: PluginWinProc can receive WM_IME_* message ?memberchengdu111320 Nov '11 - 16:19 
GeneralRe: PluginWinProc can receive WM_IME_* message ?memberKarstenK20 Nov '11 - 20:47 
QuestionChrome doesn't support functions with parameters?memberdkjfsoman30 Oct '11 - 20:27 
AnswerRe: Chrome doesn't support functions with parameters?memberdan_mihai_ro2 Nov '11 - 0:42 
GeneralCrash on Firefox 4.01memberswa00423 May '11 - 6:07 
GeneralRe: Crash on Firefox 4.01memberKarstenK23 May '11 - 21:04 
GeneralRe: Crash on Firefox 4.01memberswa0042 Jun '11 - 4:13 
GeneralRe: Crash on Firefox 4.01memberKarstenK5 Jun '11 - 20:59 
GeneralRe: Crash on Firefox 4.01memberMurasoli Selvan K26 Feb '13 - 21:01 
GeneralRe: Crash on Firefox 4.01memberKarstenK26 Feb '13 - 21:09 
QuestionA problemmemberneufeng20109 Mar '11 - 1:04 
AnswerRe: A problemmemberKarstenK9 Mar '11 - 1:16 
GeneralMy vote of 1memberMember 740954118 Oct '10 - 23:14 
GeneralNo useful informationmembersharp prospector3 Sep '10 - 5:33 
Generalchrome or safari supportmemberrathnagiri27 Jul '10 - 1:35 
GeneralRe: chrome or safari supportmemberKarstenK27 Jul '10 - 2:01 
GeneralRe: chrome or safari supportmemberVanuan4 Oct '10 - 1:29 
GeneralFireBreathmembertaxilian13 Jul '10 - 18:42 
GeneralRe: FireBreathmemberKarstenK13 Jul '10 - 20:45 
GeneralRe: FireBreathmemberRichard Bateman20 Feb '11 - 18:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 8 Jul 2010
Article Copyright 2010 by KarstenK
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid