Building a firefox plugin – part one

March 1, 2009 104 Comments by Richard

Note: For a better way to create a Browser Plugin (that will work on all browsers, not just NPAPI), check out the FireBreath project.

Getting help: For a better place to ask NPAPI-related questions, go to StackOverflow and make sure to use the “npapi” and/or “firebreath” tags.

Introduction

I have now been researching and working on a cross-platform browser plugin for several months.  By far my greatest frustration throughout this process has been the significant lack of documentation on the subject.  So, with the creation of this site, I wish to create a small series that will hopefully provide assistance to other poor developers who are trying to break into this strangely secretive field =]

I’m not going to be able to cover everything in one post (for time reasons if nothing else).  How fast I get the rest of the posts done may depend in large part on whether or not anyone seems to be reading them and what requests are made in the comments =]

Plugin architecture

In these articles I plan to focus on the basic requirements for creating an NPAPI (Netscape Plugin) style plugin, which is used by most (if not all) plugin-supporting open browsers, including: Apple Internet Plugins on Mac (Firefox, Safari, probably others); Firefox (and all other gecko-based browsers), Opera, Safari, and Chrome on windows, and at least firefox on linux.  I haven’t yet implemented a plugin on linux, but most of the same principles should apply.

Because of the minor differences between platforms, I will first cover the architecture as it is used in Windows, and then in a later post I will cover the differences between windows and other platforms, such as Mac and Linux.

Plugin API vs Scripting API

When I first began developing browser plugins, I failed to understand the difference between the scripting API and the plugin API.  They are closely related, of course, but they serve different purposes.

The Scripting API is used to provide methods callable from javascript, while the Browser API provides the interface for hosting the plugin itself in the browser.

NPAPI

You can find the Mozilla documentation for plugins here:

https://developer.mozilla.org/en/Gecko_Plugin_API_Reference

For history of the NPAPI (Netscape Plugin API), see the Wikipedia page: http://en.wikipedia.org/wiki/NPAPI

What makes up an NPAPI browser plugin?

A NPAPI browser plugin is, at it’s core, simply a DLL with a few specific entry points.  Each of these entry points is only called once by the browser for all instances of your plugin on a given page.  They are listed here in the order they should be called:

  • NP_GetEntryPoints – Called immediately after the plugin is loaded and is used by the browser (no longer just netscape) to get pointers to all of the API functions that the browser might need to call.
  • NP_Initialize – Provides global initialization of your plugin.
  • NP_Shutdown – Provides global deinitialization of your plug-in.

It is important to note that since these entrypoints are specific to NPAPI, there is no reason you can’t have a NPAPI plugin inside a DLL that also provides other services (like, for example, an ActiveX plugin).

Since these three entrypoints provide the core of the NPAPI architecture, it is worth our time to look at them a little more closely.

NP_GetEntryPoints

NPError WINAPI NP_GetEntryPoints(NPPluginFuncs* pFuncs)

This is undoubtedly the most important of the three to understand.  Because of this, it shocks me that the only actual useful documentation I have found for this method so far is found in an old, but still mostly accurate web-book written who-knows-when by Zan Oliphant.

While not as straightforward as the other two entrypoint functions, NP_GetEntryPoints is nonetheless relatively straightforward.  As you can see from the above function prototype, NP_GetEntryPoints takes a pointer to the NPPluginFuncs structure:

typedef struct _NPPluginFuncs {
    uint16 size;
    uint16 version;
    NPP_NewUPP newp;
    NPP_DestroyUPP destroy;
    NPP_SetWindowUPP setwindow;
    NPP_NewStreamUPP newstream;
    NPP_DestroyStreamUPP destroystream;
    NPP_StreamAsFileUPP asfile;
    NPP_WriteReadyUPP writeready;
    NPP_WriteUPP write;
    NPP_PrintUPP print;
    NPP_HandleEventUPP event;
    NPP_URLNotifyUPP urlnotify;
    JRIGlobalRef javaClass;
    NPP_GetValueUPP getvalue;
    NPP_SetValueUPP setvalue;
} NPPluginFuncs;

UPP in each of these type names stands for “Universal Proc Pointer”, which is essentially just a function pointer that the Gecko SDK uses in conjunction with a CallUniversalProc macro for all of its function pointer needs.  For more information, grep the Gecko SDK.

These function pointers basically tell the browser how to interact with your plugin.  Notice the naming convention here: NPP_* for all plugin functions.  There are also NPN_* functions that we will see later, and these are functions that the plugin can call on the browser.  They will be given to us in the NP_Initialize call.

So, the primary purpose of the NP_GetEntryPoints function is to give the browser pointers to all of the functions that it needs to call when creating or interacting with your plugin.

Here is a quick overview of the NPP plugin functions that your plugin must provide (and give addresses to when NP_GetEntryPoints is called).  This table is copied from Zan Oliphant’s book and updated to reflect the current practices and Gecko SDK 1.8:

API Name Description
NPP_New Creates a new instance of a plug-in.
NPP_Destroy Deletes an instance of a plug-in.
NPP_SetWindow Tells the plug-in when a window is created, moved, sized, or destroyed.
NPP_NewStream Notifies a plug-in instance of a new data stream.
NPP_DestroyStream Tells the plug-in that a stream is about to be closed or destroyed.
NPP_StreamAsFile Provides a local file name for the data from a stream.
NPP_WriteReady Determines whether a plug-in is ready for data (and the maximum number of bytes it is prepared to accept).
NPP_Write Called to write/deliver data to a plug-in.  The docs note that this might be better named “NPP_DataArrived”.
NPP_Print Requests a platform-specific print operation for an embedded or full-screen plug-in.
NPP_HandleEvent Event handler, currently only used by Windowed plugins on Mac OS; windowless plugins on all platforms use this.
NPP_URLNotify Notifies the completion of a URL request.
NPP_GetJavaClass Deprecated / No longer used. Set to NULL
NPP_GetValue Called to query the plugin for information (also used to get an instance of a NPObject/Scriptable Plugin)
NPP_SetValue This call is used to inform plugins of variable information controlled by the browser.

We will talk more about the specifics of how each of these functions works later.

NP_Initialize

NPError WINAPI NP_Initialize(NPNetscapeFuncs *aNPNFuncs) // Windows
// -or-
NPError NP_Initialize(NPNetscapeFuncs *aNPNFuncs, NPPluginFuncs *aNPPFuncs) // Linux

As noted in the documentation, NP_Initialize provides global initialization for a plug-in.  The API reference is a little confusing in that it claims that NP_Initialize is actually the first function called by the browser.  The reason for this is that on linux, there aparently is no NP_GetEntryPoints call; instead, the NPPluginFuncs struct is passed into the NP_Initialize function to be filled out.

Since this isn’t confusing enough, on Mac they have replaced both of these functions with a single “main” function that not only gets passed both function pointer structures (one with the browser functions and one to be filled out with plugin functions) but also is given a shutdown function pointer to be filled out with the address to the NP_Shutdown function.

Because of these discrepancies, I recommend that you write a separate function for filling out the NPPluginFuncs structure so that it can be called from any of the various init functions.

The NPNetscapeFuncs structure that is passed in is the complement to the NPPluginFuncs structure that we have discussed previously.  As might be guessed from the name, the NPNetscapeFuncs structure contains pointers to browser functions that can be called by the plugin.  There are a lot more of these, and we will discuss more about how to use them next time.

For now, take a look at the structure to get a general idea of what is there.  I’ve added comments on some of the more common function calls.

typedef struct _NPNetscapeFuncs {
    uint16 size;
    uint16 version; // Newer versions may have additional fields added to the end
    NPN_GetURLUPP geturl; // Make a GET request for a URL either to the window or another stream
    NPN_PostURLUPP posturl; // Make a POST request for a URL either to the window or another stream
    NPN_RequestReadUPP requestread;
    NPN_NewStreamUPP newstream;
    NPN_WriteUPP write;
    NPN_DestroyStreamUPP destroystream;
    NPN_StatusUPP status;
    NPN_UserAgentUPP uagent;
    NPN_MemAllocUPP memalloc; // Allocates memory from the browser's memory space
    NPN_MemFreeUPP memfree; // Frees memory from the browser's memory space
    NPN_MemFlushUPP memflush;
    NPN_ReloadPluginsUPP reloadplugins;
    NPN_GetJavaEnvUPP getJavaEnv;
    NPN_GetJavaPeerUPP getJavaPeer;
    NPN_GetURLNotifyUPP geturlnotify; // Async call to get a URL
    NPN_PostURLNotifyUPP posturlnotify; // Async call to post a URL
    NPN_GetValueUPP getvalue; // Get information from the browser
    NPN_SetValueUPP setvalue; // Set information about the plugin that the browser controls
    NPN_InvalidateRectUPP invalidaterect;
    NPN_InvalidateRegionUPP invalidateregion;
    NPN_ForceRedrawUPP forceredraw;
    NPN_GetStringIdentifierUPP getstringidentifier; // Get a NPIdentifier for a given string
    NPN_GetStringIdentifiersUPP getstringidentifiers;
    NPN_GetIntIdentifierUPP getintidentifier;
    NPN_IdentifierIsStringUPP identifierisstring;
    NPN_UTF8FromIdentifierUPP utf8fromidentifier; // Get a string from a NPIdentifier
    NPN_IntFromIdentifierUPP intfromidentifier;
    NPN_CreateObjectUPP createobject; // Create an instance of a NPObject
    NPN_RetainObjectUPP retainobject; // Increment the reference count of a NPObject
    NPN_ReleaseObjectUPP releaseobject; // Decrement the reference count of a NPObject
    NPN_InvokeUPP invoke; // Invoke a method on a NPObject
    NPN_InvokeDefaultUPP invokeDefault; // Invoke the default method on a NPObject
    NPN_EvaluateUPP evaluate; // Evaluate javascript in the scope of a NPObject
    NPN_GetPropertyUPP getproperty; // Get a property on a NPObject
    NPN_SetPropertyUPP setproperty; // Set a property on a NPObject
    NPN_RemovePropertyUPP removeproperty; // Remove a property from a NPObject
    NPN_HasPropertyUPP hasproperty; // Returns true if the given NPObject has the given property
    NPN_HasMethodUPP hasmethod; // Returns true if the given NPObject has the given Method
    NPN_ReleaseVariantValueUPP releasevariantvalue; // Release a MNVariant (free memory)
    NPN_SetExceptionUPP setexception;
    NPN_PushPopupsEnabledStateUPP pushpopupsenabledstate;
    NPN_PopPopupsEnabledStateUPP poppopupsenabledstate;
} NPNetscapeFuncs;

In addition to saving the function pointers given so that browser calls can be made, any memory that is to be shared by all instances of your browser plugin should be initialized here.

NP_Shutdown

This is the simplest of the three entrypoints.  Free any shared memory and release any shared resources.  This is called when the browser has already destroyed all instances of your plugin (by calling NPP_Destroy) and does not expect to create any more in the near future.

Next Time

Next time we will go into greater detail on implementing the NPP functions and also cover some of the most commonly used NPN functions.

Building a firefox plugin – part one

Building a firefox plugin – part two

Building a firefox plugin – part three

Building a firefox plugin – part four

Getting more help

Update May 14, 2011: Many people have been asking questions in the comments; while I don’t mind that, it would probably be more useful if you ask your question on the FireBreath forums. There is a forum there for those just using NPAPI as well!