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).
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 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.
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)
if(pFuncs->size < sizeof(NPPluginFuncs))
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;
, NPPluginFuncs* pluginFuncs
if(pFuncs == NULL)
if(HIBYTE(pFuncs->version) > NP_VERSION_MAJOR)
if(pFuncs->size < sizeof(NPNetscapeFuncs))
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;
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 ) )
char *p = (char*) NPN_MemAlloc( m_pszName );
STRINGZ_TO_NPVARIANT( p, *result);
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)
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 )
sum += atoi( s );
else return false;
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
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
- 08/07/2010: Initial release.