Building a firefox plugin – part two

Posted on May 26th, 2009 by Richard | Categories: Browser Plugin Development, NPAPI | Tags: , , ,

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

Recap

Last time, I talked about the fundamentals of implementing a NPAPI plugin.  Today, I’m going to go into a little more detail on how to use the strange callback architecture that firefox exposes.

The NPAPI uses a rather unique (in my experience) methodology for providing an interface between the plugin and browser, but it does have some rather significant advantages.  Instead of providing binaries to be linked against, the browser simply expects you to implement the entry point NP_Initialize in your dll that the browser will call and tell you where the methods that you will be using are located.  In order for the browser to interface with you, it calls NP_GetEntryPoints with a pointer to a structure for your plugin to fill out with function pointers that the browser will be calling on your plugin.

This removes the need for a large number of include and library files in your project.  In fact, in practice I found that I only actually needed 12 include files from the Gecko SDK:

  • jni.h
  • jni_md.h
  • jri.h
  • jritypes.h
  • jri_md.h
  • npapi.h
  • npruntime.h
  • nptypes.h
  • npupp.h
  • obsolete/protypes.h
  • prcpucfg.h
  • prtypes.h

Basic Lifecycle of a plugin:

Jean-Lou Dupont has an excelent visual diagram of this on his blog, which you can find here: http://jldupont.blogspot.com/2009/11/notes-on-npapi-based-plugins.html

The lifecycle of a plugin is actually pretty simple, once you figure it out.  The initial entrypoints NP_Initialize and NP_GetEntryPoints could, according to the documentation, get called in any order; however, in practice, NP_GetEntryPoints seems to get called first on Windows.  With that in mind, here is the order of calls for basic windowed plugin initialization on Windows:

  1. NP_GetEntryPoints – Plugin fills out a function table with the addresses of NPP_NewNPP_DestroyNPP_SetWindow, etc
  2. NP_Initialize – Plugin stores a copy of a function table with the addresses of NPN_CreateObject, NPN_MemAlloc, etc
  3. NPP_New – Plugin creates a new instance of itself and initializes it
  4. *NPP_SetWindow – This is called multiple times for each instance — each time the instance’s window is created, resized, or otherwise changed
  5. NPP_GetValue (Variable = NPPVpluginScriptableNPObject) – Plugin creates a scriptable NPObject and returns a pointer to it (after calling NPN_RetainObject on it)
  6. — Standard Plugin Activity —
  7. NPP_Destroy – Plugin instance is destroyed
  8. NP_Shutdown – All remaining plugin resources are destroyed

It is worth our time to investigate a few of these calls in more detail.

NPError NPP_New(NPMIMEType pluginType, NPP npp, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)

NPP_New is called once for each instance of the plugin.  Note the difference in terminology: There is one plugin, and there are multiple instances of that plugin.  NP_Initialize is only called once for the plugin – that is, the entire memory space.
From the Gecko SDK source:
1
2
3
4
5
6
7
8
9
10
/*
 *  NPP is a plug-in's opaque instance handle
 */
typedef struct _NPP
{
  void*<span>	</span>pdata;      /* plug-in private data */
  void*<span>	</span>ndata;      /* netscape private data */
} NPP_t;
 
typedef NPP_t*  NPP;

This structure is normally seen as NPP and serves as the handle of a plugin instance. As you can see, there is private data for the plugin and private data for the browser.  When NPP_New is called, you should:

  1. Create some sort of data structure to uniquely identify this instance of the plugin
  2. Assign a pointer to that data structure to npp->pdata

What I like to do is to create a PluginInstance class and use that as my “pdata”.  Then my NPP_New function looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Called by the browser to create a new instance of the plugin
 
NPError NPP_New(NPMIMEType pluginType,
            NPP npp,
            uint16 mode,
            int16 argc,
            char* argn[],
            char* argv[],
            NPSavedData* saved)
{
    if (npp == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;
 
    NPError rv = NPERR_NO_ERROR;
    PluginInstance* pPluginObj = new PluginInstance(npp);
 
    if (pPluginObj == NULL)
        return NPERR_OUT_OF_MEMORY_ERROR;
 
    npp-&gt;pdata = pPluginObj;
    return pPluginObj-&gt;NpapiNew(pluginType, mode, argc, argn, argv, saved);
}

In this way I can have a PluginInstance class that handles the entire lifecycle of a given instance of a plugin, and I simply create stub static functions to dereference the pdata pointer and call the correct function.

NPP_SetWindow (NPP npp, NPWindow* pNPWindow)

For many, this will be where the real fun starts — this function is called to tell the plugin which window they are in.  From the Gecko SDK (npapi.h):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _NPWindow
{
  void* window;  /* Platform specific window handle */
                 /* OS/2: x - Position of bottom left corner  */
                 /* OS/2: y - relative to visible netscape window */
  int32 x;       /* Position of top left corner relative */
  int32 y;       /* to a netscape page.					*/
  uint32 width;  /* Maximum window size */
  uint32 height;
  NPRect clipRect; /* Clipping rectangle in port coordinates */
                   /* Used by MAC only.			  */
  void * ws_info; /* Platform-dependent additonal data, linux specific */
  NPWindowType type; /* Is this a window or a drawable? */
} NPWindow;

A pointer to this structure is passed in with each call.  On windows, the “void* window” will dereference to an HWND. On other platforms, it will likewise be dereferenced as an appropriate type.

Notice that again NPP npp is the first parameter.  This will be the case on all NPP functions except NPP_New, where the mimetype is also passed in.  Since we created a PluginInstance object and assigned it to npp->pdata in NPP_New, we need to create a small stub function to forward our NPP_New to a method on that object, like so:

1
2
3
4
5
6
7
8
9
10
11
// Called by browser whenever the window is changed, including to set up or destroy
NPErrorNPP_SetWindow (NPP npp, NPWindow* pNPWindow)
{
    if (npp == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;
    else if (npp-&gt;pdata == NULL)
        return NPERR_GENERIC_ERROR;
 
    PluginInstance *inst = (PluginInstance *)npp-&gt;pdata;
    return inst-&gt;NpapiSetWindow(pNPWindow);
}

On windows, when SetWindow is called we need to save the HWND and subclass the window so that we can get our own window event proc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NPError PluginInstance::NpapiSetWindow (NPWindow* pNPWindow)
{
    NPError rv = NPERR_NO_ERROR;
 
    if(pNPWindow == NULL)
        return NPERR_GENERIC_ERROR;
 
    // window just created; in initWindow, set initialized to true
    if(!this-&gt;initialized) {
        if(!this-&gt;initWindow(pNPWindow)) {
            return NPERR_MODULE_LOAD_FAILED_ERROR;
        }
    }
 
    // Window was already created; just pass on the updates
    this-&gt;updateWindow(pNPWindow);
 
    return rv;
}

With these functions, we get notified in one function when the window is first set, and another is called each time an update is made.

NPP_Destroy (NPP npp, NPSavedData** save)

When the Browser is ready to shut down a given plugin instance (the user leaves the page or the object tag is removed from the DOM), the browser calls NPP_Destroy.  This call is responsible to free any memory being used by that plugin instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Called by browser to destroy an instance of the plugin
NPError NPP_Destroy (NPP npp, NPSavedData** save)
{
    if (npp == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;
    else if (npp-&gt;pdata == NULL)
        return NPERR_GENERIC_ERROR;
 
    PluginInstance *inst = (PluginInstance *)npp-&gt;pdata;
    NPError rv = inst-&gt;NpapiDestroy(save);
 
    delete getPluginObject(npp);
    npp-&gt;pdata = NULL;
 
    return rv;
}

Next Time

That’s all the time I have today.  Next time I will discuss NPObjects and how they are used to provide an interface by which Javascript can interact with your plugin.

Building a firefox plugin – part one

Building a firefox plugin – part two

Building a firefox plugin – part three

  1. Richard
    July 2nd, 2009 at 07:51
    Reply | Quote | #1

    You can find other mostly linux-specific information on this at http://inferno-firefox.blogspot.com/

  2. September 1st, 2009 at 10:19
    Reply | Quote | #2

    Hi;

    I work on a audio/video player Firefox plugin on Windows, i have some questions about that.

    1) Should i use DirectShow API or MCI ? in DirectShow i found codecs so i can make a player that support lots of formats but in MCI, supporting formats are too few

    2) How can i embed the playing screen on the browser? I mean that I played the file on browser but in another opened window. I want it to make in browser like quicktime or flash or windows media player firefox plugin.

    Thank you

  3. taxilian
    September 1st, 2009 at 11:19
    Reply | Quote | #3

    Well, I'm having a hard time understanding exactly what you're trying to do. Most of it, however, is personal preference.

    You can get a reference to your HWND from NPP_SetWindow. If you haven't already, download the Mozilla source tree and look at the plugin examples in it.

    Once you have your hwnd, you can draw in it however you wish. (This is when using it as a windowed plugin). It will not participate in the DOM z-order unless you make it windowless, but windowless plugins don't draw nearly as quickly and don't work as well for video.

    Personally, I have used it with DX7 and DX9, but I know of people who have drawn using directshow as well. I've never used MCI, but I imagine that you could use it if you want.

    As to embedding it in the browser, just use an object tag; it'll create the window for you and give you the handle in SetWindow.

  4. September 1st, 2009 at 16:19
    Reply | Quote | #4

    Hi;

    I work on a audio/video player Firefox plugin on Windows, i have some questions about that.

    1) Should i use DirectShow API or MCI ? in DirectShow i found codecs so i can make a player that support lots of formats but in MCI, supporting formats are too few

    2) How can i embed the playing screen on the browser? I mean that I played the file on browser but in another opened window. I want it to make in browser like quicktime or flash or windows media player firefox plugin.

    Thank you

  5. taxilian
    September 1st, 2009 at 17:19
    Reply | Quote | #5

    Well, I'm having a hard time understanding exactly what you're trying to do. Most of it, however, is personal preference.

    You can get a reference to your HWND from NPP_SetWindow. If you haven't already, download the Mozilla source tree and look at the plugin examples in it.

    Once you have your hwnd, you can draw in it however you wish. (This is when using it as a windowed plugin). It will not participate in the DOM z-order unless you make it windowless, but windowless plugins don't draw nearly as quickly and don't work as well for video.

    Personally, I have used it with DX7 and DX9, but I know of people who have drawn using directshow as well. I've never used MCI, but I imagine that you could use it if you want.

    As to embedding it in the browser, just use an object tag; it'll create the window for you and give you the handle in SetWindow.

  6. September 2nd, 2009 at 02:22
    Reply | Quote | #6

    Sorry for misunderstood.

    I want to play video on browser, my basic thing is this. Yes i did what did u advice but it doesn't work. I am giving some of my function here:

    Setwindow func:

    NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
    {
    if(instance == NULL)
    return NPERR_INVALID_INSTANCE_ERROR;

    NPError rv = NPERR_NO_ERROR;

    if(pNPWindow == NULL)
    return NPERR_GENERIC_ERROR;

    CPlugin * pPlugin = (CPlugin *)instance->pdata;

    if(pPlugin == NULL)
    return NPERR_GENERIC_ERROR;

    // window just created
    if(!pPlugin->isInitialized() && (pNPWindow->window != NULL)) {
    if(!pPlugin->init(pNPWindow)) {
    delete pPlugin;
    pPlugin = NULL;
    return NPERR_MODULE_LOAD_FAILED_ERROR;
    }
    }

    // window goes away
    if((pNPWindow->window == NULL) && pPlugin->isInitialized())
    return NPERR_NO_ERROR;

    // window resized
    if(pPlugin->isInitialized() && (pNPWindow->window != NULL))
    return NPERR_NO_ERROR;

    // this should not happen, nothing to do
    if((pNPWindow->window == NULL) && !pPlugin->isInitialized())
    return NPERR_NO_ERROR;

    return rv;
    }

    CallBack Function
    —————–
    static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    //DirectShow

    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent *pEvent = NULL;

    // Initialize the COM library.
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
    printf(“ERROR – Could not initialize COM library”);
    return false;
    }

    // Create the filter graph manager and query for interfaces.
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr))
    {
    printf(“ERROR – Could not create the Filter Graph Manager.”);
    return false;
    }

    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
    hr = pGraph->RenderFile(L”http://www.fakedomain.com/test.m4a”, NULL);

    if (SUCCEEDED(hr))
    {
    // Run the graph.
    hr = pControl->Run();
    if (SUCCEEDED(hr))
    {
    // Wait for completion.
    long evCode;
    pEvent->WaitForCompletion(INFINITE, &evCode);

    // Note: Do not use INFINITE in a real application, because it
    // can block indefinitely.
    }
    }
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();

    switch (msg) {
    case WM_PAINT:
    {
    // draw a frame and display the string
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    RECT rc;
    GetClientRect(hWnd, &rc);
    FrameRect(hdc, &rc, GetStockBrush(BLACK_BRUSH));
    CPlugin * p = (CPlugin *)GetWindowLong(hWnd, GWL_USERDATA);
    if(p) {
    if (p->m_String[0] == 0) {
    strcpy(“foo”, p->m_String);
    }

    DrawText(hdc, p->m_String, strlen(p->m_String), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    }
    EndPaint(hWnd, &ps);
    }
    break;
    default:
    break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);

    Thank you

  7. September 2nd, 2009 at 02:23
    Reply | Quote | #7

    Sorry for misunderstood.

    I want to play video on browser, my basic thing is this. Yes i did what did u advice but it doesn't work. I am giving some of my function here:

    Setwindow func:

    NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
    {
    if(instance == NULL)
    return NPERR_INVALID_INSTANCE_ERROR;

    NPError rv = NPERR_NO_ERROR;

    if(pNPWindow == NULL)
    return NPERR_GENERIC_ERROR;

    CPlugin * pPlugin = (CPlugin *)instance->pdata;

    if(pPlugin == NULL)
    return NPERR_GENERIC_ERROR;

    // window just created
    if(!pPlugin->isInitialized() && (pNPWindow->window != NULL)) {
    if(!pPlugin->init(pNPWindow)) {
    delete pPlugin;
    pPlugin = NULL;
    return NPERR_MODULE_LOAD_FAILED_ERROR;
    }
    }

    // window goes away
    if((pNPWindow->window == NULL) && pPlugin->isInitialized())
    return NPERR_NO_ERROR;

    // window resized
    if(pPlugin->isInitialized() && (pNPWindow->window != NULL))
    return NPERR_NO_ERROR;

    // this should not happen, nothing to do
    if((pNPWindow->window == NULL) && !pPlugin->isInitialized())
    return NPERR_NO_ERROR;

    return rv;
    }

    CallBack Function
    —————–
    static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    //DirectShow

    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent *pEvent = NULL;

    // Initialize the COM library.
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
    printf(“ERROR – Could not initialize COM library”);
    return false;
    }

    // Create the filter graph manager and query for interfaces.
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr))
    {
    printf(“ERROR – Could not create the Filter Graph Manager.”);
    return false;
    }

    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
    hr = pGraph->RenderFile(L”http://www.fakedomain.com/test.m4a”, NULL);

    if (SUCCEEDED(hr))
    {
    // Run the graph.
    hr = pControl->Run();
    if (SUCCEEDED(hr))
    {
    // Wait for completion.
    long evCode;
    pEvent->WaitForCompletion(INFINITE, &evCode);

    // Note: Do not use INFINITE in a real application, because it
    // can block indefinitely.
    }
    }
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();

    switch (msg) {
    case WM_PAINT:
    {
    // draw a frame and display the string
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    RECT rc;
    GetClientRect(hWnd, &rc);
    FrameRect(hdc, &rc, GetStockBrush(BLACK_BRUSH));
    CPlugin * p = (CPlugin *)GetWindowLong(hWnd, GWL_USERDATA);
    if(p) {
    if (p->m_String[0] == 0) {
    strcpy(“foo”, p->m_String);
    }

    DrawText(hdc, p->m_String, strlen(p->m_String), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    }
    EndPaint(hWnd, &ps);
    }
    break;
    default:
    break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);

    Thank you

  8. September 2nd, 2009 at 08:22
    Reply | Quote | #8

    Sorry for misunderstood.

    I want to play video on browser, my basic thing is this. Yes i did what did u advice but it doesn't work. I am giving some of my function here:

    Setwindow func:

    NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
    {
    // ….
    // window just created
    if(!pPlugin->isInitialized() && (pNPWindow->window != NULL)) {
    if(!pPlugin->init(pNPWindow)) {
    delete pPlugin;
    pPlugin = NULL;
    return NPERR_MODULE_LOAD_FAILED_ERROR;
    }
    }

    // ….
    }

    CallBack Function
    —————–
    static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    //DirectShow

    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent *pEvent = NULL;

    // Initialize the COM library.
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
    printf(“ERROR – Could not initialize COM library”);
    return false;
    }

    // Create the filter graph manager and query for interfaces.
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr))
    {
    printf(“ERROR – Could not create the Filter Graph Manager.”);
    return false;
    }

    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
    hr = pGraph->RenderFile(L”http://www.fakedomain.com/test.m4a”, NULL);

    if (SUCCEEDED(hr))
    {
    // Run the graph.
    hr = pControl->Run();
    if (SUCCEEDED(hr))
    {
    // Wait for completion.
    long evCode;
    pEvent->WaitForCompletion(INFINITE, &evCode);

    // Note: Do not use INFINITE in a real application, because it
    // can block indefinitely.
    }
    }
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();

    switch (msg) {
    case WM_PAINT:
    {
    // draw a frame and display the string
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    RECT rc;
    GetClientRect(hWnd, &rc);
    FrameRect(hdc, &rc, GetStockBrush(BLACK_BRUSH));
    CPlugin * p = (CPlugin *)GetWindowLong(hWnd, GWL_USERDATA);
    if(p) {
    if (p->m_String[0] == 0) {
    strcpy(“foo”, p->m_String);
    }

    DrawText(hdc, p->m_String, strlen(p->m_String), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    }
    EndPaint(hWnd, &ps);
    }
    break;
    default:
    break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);

    Thank you

  9. taxilian
    September 2nd, 2009 at 22:35
    Reply | Quote | #9

    Well, the first *major* problem I'm seeing is that PluginWinProc gets called about a billion times a second — every time a message is sent to the window, such as a keypress, a mouse movement or click, a refresh message, or any number of others, that function gets called. Therefore, you most certainly should not be using it to initialize your directshow graph.

    I recommend studying up on what a winproc is, and then do some debugging. With that, I will give you some advice: “It doesn't work” is never useful. It doesn't give me any information to work with. I need to know what doesn't work; I need to know what you have tried and what tests you have done. I am willing — happy even — to help you with your problem, but I better see that you are doing your part as well, and that is how everyone else feels too.

    Set some breakpoints; add some logging messages. Find out what is happening, and what isn't. For instance, does your PluginWinProc even get called? If not, you probably missed the part in the example where it subclasses the window (that part of the code wasn't shown here). Are the return values from your COM calls S_OK?

    Do some digging on your own; when you know what is and isn't running, then come back and ask specific, preferably short, questions to get help with those pieces that you don't understand. Also, please try to avoid posting long code fragments in comments =] Once you have done all that you can do, I will be happy to continue to help you; I just need more than “Yes i did what did u advice but it doesn't work.”

    =]

  10. taxilian
    September 3rd, 2009 at 04:35

    Well, the first *major* problem I'm seeing is that PluginWinProc gets called about a billion times a second — every time a message is sent to the window, such as a keypress, a mouse movement or click, a refresh message, or any number of others, that function gets called. Therefore, you most certainly should not be using it to initialize your directshow graph.

    I recommend studying up on what a winproc is, and then do some debugging. With that, I will give you some advice: “It doesn't work” is never useful. It doesn't give me any information to work with. I need to know what doesn't work; I need to know what you have tried and what tests you have done. I am willing — happy even — to help you with your problem, but I better see that you are doing your part as well, and that is how everyone else feels too.

    Set some breakpoints; add some logging messages. Find out what is happening, and what isn't. For instance, does your PluginWinProc even get called? If not, you probably missed the part in the example where it subclasses the window (that part of the code wasn't shown here). Are the return values from your COM calls S_OK?

    Do some digging on your own; when you know what is and isn't running, then come back and ask specific, preferably short, questions to get help with those pieces that you don't understand. Also, please try to avoid posting long code fragments in comments =] Once you have done all that you can do, I will be happy to continue to help you; I just need more than “Yes i did what did u advice but it doesn't work.”

    =]

  11. Nicolas.
    September 11th, 2009 at 08:36

    Hi,

    Until now I didn't restrict my Firefox plugin to GeckoAPI, but since I want to be compliant with Chrome, Safari and Opera, I'm working and porting my code to GeckoAPI.
    But I meets a lot of difficulties.

    For example, I didn't find a way to do GET http request with custom headers. (The POST request with custom headers is horrible since it passes custom headers in the POST content…)

    Besides, since HTTP request are asynchronous, is there a way to create a message loop to create a pseudo synchronous request ?

    Thanks for any advice.
    Nicolas.

  12. taxilian
    September 11th, 2009 at 11:38

    Unfortunately, I do not know of a way to do what you're trying to. You can, of course, use NPN_GetUrl and NPN_PostUrl to make page requests, but they are not (as far as I can tell) designed to allow for custom HTTP headers. You could either use query string paramters, or you could write your own TCP handlers (as we have done to handle cases like these and others).

  13. Nicolas.
    September 11th, 2009 at 14:36

    Hi,

    Until now I didn't restrict my Firefox plugin to GeckoAPI, but since I want to be compliant with Chrome, Safari and Opera, I'm working and porting my code to GeckoAPI.
    But I meets a lot of difficulties.

    For example, I didn't find a way to do GET http request with custom headers. (The POST request with custom headers is horrible since it passes custom headers in the POST content…)

    Besides, since HTTP request are asynchronous, is there a way to create a message loop to create a pseudo synchronous request ?

    Thanks for any advice.
    Nicolas.

  14. taxilian
    September 11th, 2009 at 17:38

    Unfortunately, I do not know of a way to do what you're trying to. You can, of course, use NPN_GetUrl and NPN_PostUrl to make page requests, but they are not (as far as I can tell) designed to allow for custom HTTP headers. You could either use query string paramters, or you could write your own TCP handlers (as we have done to handle cases like these and others).

  15. taxilian
    September 17th, 2009 at 22:04

    I would particularly like to encourage all of you who have commented to consider helping out with FireBreath, the open source plugin framework project that I am starting. Even if you don't have a lot of time, a little time here and there can go a long ways.

    http://colonelpanic.net/2009/09/call-for-plugin...

  16. taxilian
    September 18th, 2009 at 04:04

    I would particularly like to encourage all of you who have commented to consider helping out with FireBreath, the open source plugin framework project that I am starting. Even if you don't have a lot of time, a little time here and there can go a long ways.

    http://colonelpanic.net/2009/09/call-for-plugin...

  17. November 8th, 2009 at 19:36

    This plug in programming talk is not quite Mandarin Chinese stuff to me, but it is only slightly more distinct. I have a severe traumatic brain injury and forgot most languages.

    How difficult will it be to create a plug in that offers a click-protest option?
    Suppose I search for something and only land on a parked page that is a Google Inc. fraud? A page where the site is ONLY running ads under license from Google Inc. or another search provider? The user of the plug-in could click the protest button when reaching a parked/cybersquatted page and open and close every page linked on the site without even seeing the sites that run the ads?

    Google might call a click-protest a click-fraud? We will soon address that in my current lawsuit. The protest button would also store the website address for reporting to a repository of such sites as well as providing a customized parked/cybersquatted fraudulent page error screen. I am creating a website where sites that are listed will be listed to send our protest-click visitors. My lawsuit will attempt to make Google's fraudulent policy of licensing parked domains for a profit no longer fiscally sound. I am extremely angry at Google for having a fraudulent business policy of licensing parked/cybersquatted domains also called by Google Inc. as AdSense for Domains.

    I used to run a web reservation site and was well versed in programming. It seems that I now can only ask for anyone's help. I could still write a script to do it in Perl5 but one server IP is too easy to counteract.

    Help. I consider any aid a lien on my lawsuit settlement of $75/hr.

  18. November 9th, 2009 at 02:36

    This plugin programming talk is not quite Mandarin Chinese stuff to me, but it is only slightly more distinct. I have a severe traumatic brain injury and forgot most languages.

    How difficult will it be to create a plugin that offers a click-protest option?
    Suppose I search for something and only land on a parked page that is a Google Inc. fraud? A page where the site is ONLY running ads under license from Google Inc or another search provider? The user of the plugin could click the protest button when reaching a parked/cybersquatted page and open and close every page linked on the site without even seeing the sites that ran the ads?

    Google might call a click-protest a click-fraud? We will soon address that in my current lawsuit. The protest button would also store the website address for reporting to a repository of such sites as well as providing a customized parked/cybersquatted fraudulent page error screen. I am creating a website where sites that are reported/discovered will be listed to send our protest-click visitors. My lawsuit will attempt to make Google's fraudulent policy of licensing parked domains for a profit no longer fiscally sound. I am extremely angry at Google for having a fraudulent business policy of licensing parked/cybersquatted domains. This fraud perpetrating policy is called by Google Inc. as AdSense for Domains.

    I used to run a web reservation site and was well versed in programming. It seems that I now can only ask for anyone's help. I could still write a script to do it in Perl5 but one server IP is too easy to counteract.

    Help. I consider any aid a lien on my lawsuit settlement of $75/hr.

  19. taxilian
    November 9th, 2009 at 08:07

    I don't believe that what you want to do can be done with a plugin; I think you need an extension, which will be browser specific (i.e. firefox extensions only work on firefox, etc)

  20. taxilian
    November 9th, 2009 at 15:07

    I don't believe that what you want to do can be done with a plugin; I think you need an extension, which will be browser specific (i.e. firefox extensions only work on firefox, etc)

  21. taxilian
    November 9th, 2009 at 15:07

    I don't believe that what you want to do can be done with a plugin; I think you need an extension, which will be browser specific (i.e. firefox extensions only work on firefox, etc)

  22. March 4th, 2010 at 10:30

    gosh i didn't expect it to be this complicated…

  23. March 4th, 2010 at 10:30

    gosh i didn't expect it to be this complicated…

  24. taxilian
    March 4th, 2010 at 15:14

    That would be why I finally started FireBreath (http://firebreath.googlecode.com). Even with FireBreath it's more complicated than I'd like, but it's a whole lot easier than it is without….

  25. taxilian
    March 4th, 2010 at 15:14

    That would be why I finally started FireBreath (http://firebreath.googlecode.com). Even with FireBreath it's more complicated than I'd like, but it's a whole lot easier than it is without….

  26. Ryan Schipper
    March 17th, 2010 at 11:24

    Hi there,

    I was involved with the development of a windowless Firefox (and IE ActiveX Control as it turns out) plugin during 2007. I continue to maintain the software, although the primary developers have both left the project.

    My experience of plugin lifecycle in NPAPI doesn't exactly match with your description: “quite simple”. At the time, we found that the following actions resulted in slightly different lifecycles (in Firefox 2 at least):
    - refreshing the page
    - navigating from one page containing an active plugin instance to the same URI
    - navigating from one page containing an active plugin instance to a distinct URI that contained an embed of the same plugin
    - opening a window to a distinct URL which embed'd the plugin

    The details are a bit hazy, but do my statements match with your experience?

  27. taxilian
    March 17th, 2010 at 16:22

    Well, yes and no. The lifecycle of a plugin instance is quite simple — but simple becomes more complicated when you realize that there can be multiple instances at one time, and that there is no guarantee of what order they will load or unload in relationship to each other.

    In my experience, for example, if you refresh the page, instance 2 (on the new page) will start up before instance 1 goes away. Sometimes a long time before. Also, you never quite know when the scriptable objects (NPObjects) will be destroyed, since those are reference counted. It gets even more interesting when you add and remove the object tag from the page without refreshing.

    However, the lifecycle itself is still quite simple, just the concurrency of multiple plugin instances makes it a little weird. This can be solved by avoiding the use of static and global variables, etc. Does that meet with your experience? If not, would you care to share some examples?

    Also, if you'd be interested in helping us add windowless support to FireBreath, that's one piece where I don't have much experience, and that would greatly benefit the project.

  28. Nikolai Danylchyk
    April 8th, 2010 at 22:59

    Hello everyone.
    I have a slight problem with my development.
    My plug-in works fine, I'm able to render objects with my ported OpenGL engine without much problems. The thing is.. if I hit F5 to refresh, it won't render anything. If I reload page (click on url and hit enter again) works fine again. I'm not sure but I guess I'm destructing all the instances correctly. By the way, is it possible to create several instances in different tabs and is there any documentation about tab handling?
    Thanks in advance.

  29. taxilian
    April 9th, 2010 at 06:09

    Привет. Most likely this is due to an oddity of the plugin lifecycle; when you refresh the page, it actually loads the next plugin before unloading the previous one.

    Another possibility is that you may be using global variables somewhere; if so, keep in mind that these are global to every instance of the plugin in the browser. If you try to open your plugin in two different firefox windows and experience problems, this is likely the issue. You don't need to do anything special to handle tabs, but you do need to remember to avoid using global variables at all costs!

    I recommend using FireBreath as a simple way to abstract a lot of the complexity involved.

  30. Nikolai Danylchyk
    April 9th, 2010 at 09:21

    Спасибо!

  31. April 15th, 2010 at 18:26

    Very helpful. Thanks

  32. Thekumar
    August 12th, 2010 at 20:55

    Thanks for this post! I just got thrown right in the middle of a porting project for a plugin, and I had no NPAPI experience, and by far this is the most clear-cut explanation of what’s going on with our code that I can find on the web. Thanks again!!!

blog comments powered by Disqus