Building a firefox plugin – part one

Posted on March 1st, 2009 by Richard | Tags: , , , , ,

Note: if you haven’t already, please read up on FireBreath, the open source cross-platform plugin framework, and consider contributing.

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

1
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

1
2
3
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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

  • Guybrush
  • Thanks; I have updated the link on the post.
  • Raja
    Hi,

    I am trying to build a LInux plugin. Can you please let me know what all points I should keep in mind and things to do ?

    Thanks,
  • LOL. Well, I have not as of yet built a complete, functional linux plugin, so I'm not sure I can fully answer that question; also, to share what I do know would require another full post, which I do plan to do, but have been waiting to learn more about the specifics first.

    If you are just getting started, I recommend you try out FireBreath; when you run into problems, post to the firebreath-dev list and we'll help you find solutions to them that can be integrated into FireBreath so that the next person doesn't have to solve the same problem. It's not complete, but it's a good place to start.

    http://firebreath.googlecode.com
    see also later posts on this blog about FireBreath.
  • Navaid
    I created a minimal plugin dll with a dllmain and the 3 functions you mention in this article (blank implementations) and placed it in the firefox plugin folder ( I can see that the 3 functions have been exported). However, I can see both from Firefox and VS debugger that this DLL is not picked up by Firefox. I expected that it would at least attempt to load the dll (and then may be reject it). However it didn't. What might be missing?

    edit: Apologies, it does get picked. Firfox loads the DLL only when required not at startup. (You may delete this comment if required).
  • I strongly recommend against putting plugins in the plugins/ directory; install them in the registry instead. The reason for this is that it is considered extremely bad practice to clutter up the program directory of another program; also, npapi plugins can work on other browsers too if you install them right. Firefox 3.6 stopped honoring 3rd party additions to the components directory, and they were unclear as to whether or not this included the plugins dir; perhaps that will come later.

    https://developer.mozilla.org/devnews/index.php...

    Glad you found your problem!
  • soluzione_ltda
    Hello , I´m Daniel from Brazil and after 20 days with a problem I hope you can help-me.
    Well, I´m running Ubuntu 9.10 and Firefox 3.5 and started to research how to make a scriptable plugin. I found the Npruntime as solution and followed all the steps and finally got a build (libnprt.so).
    The problem is: I can run the test.html fine only on the Firefox that I build, but when I try to run the test in the Firefox that came in ubuntu it don´t work.
    The "Ubuntu Firefox" can see the plugin in 'about:plugins' like "my firefox" do but I don´t have response calling the functions from the built in one.
    I´ve tried to check any dependencies and made some links that appears to be missing on /usr/lib but I could not solve the problem.
    I could check that the difference is in the Firefox that I build because the plugin works from any location, I can link plugin in the ~/Desktop and still work on the firefox that I built, so it don´t depends of any lib in the original place, it´s something that the Firefox that I build have and the built in Ubuntu don´t, but I could not found what is.
    If you can help-me, I just don´t know what more can I do, I´m totally lost !!

    Thanks in advance.
    Daniel.
  • snehalp
    hi,
    I liked your tutorial very much. I have a query on activeX support in mozilla. I want to know whether I can launch activeX control using npapi during initialising of np plugin. If not how can I extend np api to support activeX in mozilla. Please provide me nay reference for this or any suggestion.

    Thanks in advance
  • gf
    Yes, there is nothing that prevents you from using ActiveX controls in NPAPI plugins. But if you want to write a browser plugin that works in IE as well as in Firefox etc. - thats exactly what we're providing a framework for with FireBreath: https://code.google.com/p/firebreath/
  • Well, technically this isn't totally true. You can actually embed an activeX control inside an NPAPI plugin; there are a few different projects out there for doing this. For example, a quick google search turns up: http://code.google.com/p/ff-activex-host/

    However, in my experience, this tends to be more error prone than anything else; it's just kinda a pain to maintain. Still, if you already have the activex control and are totally ambivalent towards cross-platform compatibility, it's an option. As Georg mentioned, FireBreath is a much more flexible option, though it would require porting your code to the framework. After you've done so, however, porting it to other platforms is easier and it'll work as an NPAPI plugin or an ActiveX control
  • black spider
    ok. thanks for your help.
  • black spider
    i had used the npruntime. is it ok? sorry i'm new to this plugin so i'm kind of mind bubbling but a little i understand the code. but i hardly dont know how to add this even simple like function.
  • Start with the npruntime sample from the mozilla source code. Create an NPObject. Implement the NPP_Invoke method. It'll take some research on your own.

    Check out the firebreath project to see some examples, but it's a little more complicated than your question requires. By the end of the year, there should be a release candidate of Firebreath for windows only.
  • black spider
    Pls. can u help me. I want to know how to add a simple function in plugin like for example a function the has a param int and will just return its value like:
    int retval(int i)
    {
    return i;
    }

    i want to know how this retval function in plugin can be called in javascript. I would really appreciate help.
  • You need to create a NPObject and implement Invoke for a method, GetProperty and SetProperty for a property.

    There are details on how all that works in part three: http://colonelpanic.net/2009/08/building-a-fire...
  • Try pulling down the Firebreath source and building it; that has a basic functional NPRuntime scripting plugin up and working (though it's not well documented yet).

    http://firebreath.googlecode.com
  • Guybrush
    I am trying to learn how to build Firefox plugins and want to share my experience to run the sample npruntime plugin


    Follow the guide at: https://developer.mozilla.org/en/Compiling_The_...

    NB: As for Gecko SDK use Gecko 1.9 (Firefox 3.0) as the Gecko 1.9.1 (Firefox 3.5) gave several errors which I couldnt Solve

    When you compile several Depreciated warnings will show up replace them with the suggestions that the VS will give you but some memcpy wont be replaceable by memcpy_s cause of number of parameters so I included _CRT_SECURE_NO_WARNINGS with the preprocessor

    also rename #include "afxres.h" to #include "windows.h" in nprt.rc as it will not be found and should work
  • Guybrush
    Hi,

    I am not sure about my previous post how to setup the development enviroment. Disabling warnings isn't a great idea because it might produce unexpected behaviors and errors. So I kindly ask for a description to setup VS2008 with Gecko 1.9.1 (Firefox 3.5) and maybe a "hello world" plugin in complaince to the latest API.

    I can't understand how updates and changes are done to the APIs and descriptions/samples stay put this will not help new developers start experimenting with it.

    I tried to follow this but doesn't work with latest Gecko.
    https://developer.mozilla.org/en/Compiling_The_...

    Regards
  • lucka
    Hi, I really liked your tutorial and thought you might be able to help. I have an NPAPI plugin that works in firefox, but does not work in google chrome - more specifically calling plugin.myPluginMethod() from javascript fails because plugin.myPluginMethod is undefined.
    Do you have any idea what might have gone wrong?
  • Without additional information about what you're trying to do and what technologies you are using, I would guess that you're still trying to use XPCOM and an .idl file to define your interface. This won't work in Chrome, and reportedly it won't work in Firefox starting with 3.6
  • pineapple_monkey
    I'm using NPRUNTIME for my plugin and it works perfectly fine in Firefox. But I'm experiencing the same problem with Chrome as lucka.
    "Uncaught TypeError: Object #<an HTMLEmbedElement> has no method 'SmartCardListReaders'". I´ll return with more info after debugging :)
  • rymeister
    I'm having this exact same problem. Were you ever able to find a solution?
  • sara
    hi

    I am again writing you. I am running npruntime sample in ubuntu. I call a function in plugin.cpp. The function is declared in external library. While comipling plugin sample I want to link that external library so that function can be called. I am having linking problems.Please help me
  • It sounds like your problem has nothing directly to do with npapi or npruntime, but rather that you simply aren't linking your project correctly.

    When you compile, make sure you specify the library that you need to link against; it won't find it automatically, you have to specifiy the .so or .a file that contains showtime()
  • sara
    hi, Your work on plugin is great. I am very new to plugin. I am sorry for any stupid question. i am confused that how plugin interact with application. i am developing a plugin for minisip application. how plugin will interact with that application...sorry if i am asking question at wrong place...but i need help...please
  • I'm not sure if I understand what you mean by "how does the plugin interact with the application". It depends on what you are trying to do;

    Think of a plugin as an application that lives in a browser and interacts with the web page as a DOM element. Before developing a plugin, you should be familiar already with javascript and DHTML.

    Beyond that, the plugin will essentially live in a window most of the time (I haven't actually made a windowless plugin yet, so I can't speak to the details of that version), and you can communicate with it using javascript calls (see the "part three" post that is linked to at the bottom of this article)
  • sara
    thanks a lot...I try to explain to you. I have minisip.exe file from minisip.org website. Now I want to make plugin for minisip. Do I need to bring all functions of minisip application in plugin? I mean is minisip.exe is used separately by plugin or the whole minisip is embedded inside plugin functions. I am sorry if I am confusing you.
    regards

    sara
  • minisip.exe would not work just by itself; it probably is possible for a plugin to interact with other processes, though you could run into some problems if you wanted to make an IE compatible ActiveX control that way, and it would require modifying both sides to be aware of the communication.

    Much better would be to either build a minisip library (static or DLL) that you could link to and then write a plugin wrapper around it. The plugin itself is the only process that would be running, and your minisip library could run inside it. Be a little careful what APIs you expose to the web page so that it doesn't become a security risk.
  • sara
    thanks a lot
  • Marius
    Great post. Most relevant info I've found so far on the subject!
    Thanks a bunch!
  • Juri
    Great post, by far the most adequate on this topic. Thanks.
  • cygnl7
    Good stuff! Thanks for pulling this information together.

    More detail on what the differences are (especially in the initialization) on the three OSes would be useful. For example, what do you mean by a "main" function for MacOS?
  • I think part 4 or 5 will probably have more of that detail; I haven't actually finished the Mac and Linux ports of my implementation (though I know in theory what needs to be done), so I want to wait until I've completely worked through all the cross-platform issues before I blog about them. =]
blog comments powered by Disqus