Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / Javascript
Article

Working on an NPAPI-browser plugin

Rate me:
Please Sign up or sign in to vote.
2.33/5 (3 votes)
8 Jul 2010CPOL3 min read 116.3K   8.3K   30   35
Extending the NPAPI-runtime to a Firefox plugin.

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:

HTML
<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.

JavaScript
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.

C++
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.

C++
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.

C++
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)


Written By
Software Developer
Germany Germany
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.

Comments and Discussions

 
QuestionSorry,it doesn't work ! Pin
winart27-Feb-15 22:41
winart27-Feb-15 22:41 
QuestionI can not find npfunctions.h file Pin
Member 1006688328-Jun-14 21:48
Member 1006688328-Jun-14 21:48 
AnswerRe: I can not find npfunctions.h file Pin
KarstenK29-Jun-14 6:27
mveKarstenK29-Jun-14 6:27 
QuestionWorking on an NPAPI-browser plugin for IE Pin
gawadepd21-May-14 4:35
gawadepd21-May-14 4:35 
AnswerRe: Working on an NPAPI-browser plugin for IE Pin
KarstenK21-May-14 4:41
mveKarstenK21-May-14 4:41 
Questionthe demo dll can work at chrome, but rebuild the dll from your source code, the chrome alert "Could not load the plugin" Pin
maryNet14-Jun-13 0:14
maryNet14-Jun-13 0:14 
AnswerRe: the demo dll can work at chrome, but rebuild the dll from your source code, the chrome alert "Could not load the plugin" Pin
KarstenK8-Jul-13 20:46
mveKarstenK8-Jul-13 20:46 
Questionis this code working? Pin
JayShin15-Jan-12 14:25
JayShin15-Jan-12 14:25 
AnswerRe: is this code working? Pin
KarstenK15-Jan-12 21:21
mveKarstenK15-Jan-12 21:21 
QuestionDeploying the plug-in Pin
L4pp17-Dec-11 22:07
L4pp17-Dec-11 22:07 
AnswerRe: Deploying the plug-in Pin
KarstenK7-Dec-11 22:41
mveKarstenK7-Dec-11 22:41 
GeneralRe: Deploying the plug-in Pin
L4pp17-Dec-11 22:54
L4pp17-Dec-11 22:54 
GeneralRe: Deploying the plug-in Pin
KarstenK15-Jan-12 21:21
mveKarstenK15-Jan-12 21:21 
You better contacts these people.
Press F1 for help or google it.
Greetings from Germany

QuestionPluginWinProc can receive WM_IME_* message ? Pin
chengdu111317-Nov-11 20:37
chengdu111317-Nov-11 20:37 
AnswerRe: PluginWinProc can receive WM_IME_* message ? Pin
KarstenK17-Nov-11 22:13
mveKarstenK17-Nov-11 22:13 
GeneralRe: PluginWinProc can receive WM_IME_* message ? Pin
chengdu111320-Nov-11 16:19
chengdu111320-Nov-11 16:19 
GeneralRe: PluginWinProc can receive WM_IME_* message ? Pin
KarstenK20-Nov-11 20:47
mveKarstenK20-Nov-11 20:47 
QuestionChrome doesn't support functions with parameters? Pin
dkjfsoman30-Oct-11 20:27
dkjfsoman30-Oct-11 20:27 
AnswerRe: Chrome doesn't support functions with parameters? Pin
Dan Ochiana2-Nov-11 0:42
Dan Ochiana2-Nov-11 0:42 
GeneralCrash on Firefox 4.01 Pin
swa00423-May-11 6:07
swa00423-May-11 6:07 
GeneralRe: Crash on Firefox 4.01 Pin
KarstenK23-May-11 21:04
mveKarstenK23-May-11 21:04 
GeneralRe: Crash on Firefox 4.01 Pin
swa0042-Jun-11 4:13
swa0042-Jun-11 4:13 
GeneralRe: Crash on Firefox 4.01 Pin
KarstenK5-Jun-11 20:59
mveKarstenK5-Jun-11 20:59 
GeneralRe: Crash on Firefox 4.01 Pin
Murasoli Selvan K26-Feb-13 21:01
Murasoli Selvan K26-Feb-13 21:01 
GeneralRe: Crash on Firefox 4.01 Pin
KarstenK26-Feb-13 21:09
mveKarstenK26-Feb-13 21:09 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.