Building a firefox plugin – part three

August 4, 2009 201 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.

Previous posts

The purpose of this post is to cover the basics of providing an interface by which javascript can interface with an NPAPI plugin in a cross platform manner.  The primary focus of this post will be on npruntime, which is recommended by the Mozilla development team (or at least seems to be from the meager and confusing documentation available from Mozilla).  More importantly, of course, it is the framework that I recommend, which is all-important, since I’m the one writing this post. =]

Previous posts

If you are new to NPAPI plugins, you may want to go back and read part one and part two of this series, which cover the basic architecture and lifecycle of an NPAPI plugin.

What is npruntime?

I’m so glad you ask.  This confused the daylights out of me when I first got into building browser plugins; there are references to XPCOM and references to LiveConnect and references to NPRuntime, which I never did quite get straightened out until I ran into a Wikipedia article on NPAPI.  I recommend that you read it if you want further background information.

Beyond the simple question of “what is recommended?” is the consideration of practical needs.  For me, the primary considerations that led me to decide that NPRuntimeis the only mechanism that should be used on any new plugin are:

  • NPRuntime allows dynamic interfaces — more on this, and why it is useful, later
  • NPRuntime is the plugin scripting standard supported by Mac Plugins, Safari (even on windows), Chrome, and Opera.  My goal is always to make things as cross-platform as possible.
  • Firefox 3.6 drops support for XPCOM plugins (e.g. xpt file scripting interfaces aren’t recognized)

Getting Started

Throughout this post, I will be referring to Scriptable Objects a lot.  A Scriptable Object is simply an object that can be “scripted”, or accessed and used by javascript.  In firefox, a Scriptable Object is always a NPObject:

struct NPObject {
  NPClass *_class;
  uint32_t referenceCount;
  /*
   * Additional space may be allocated here by types of NPObjects
   */
};

As you can see, a NPObject is nothing more than a reference counted object with a pointer to a NPClass.  As of the latest (as of this writing) Gecko SDK, 1.9, NPClass is defined as follows:

struct NPClass
{
  uint32_t structVersion; // Version of the structure (to determine which features may be missing)
  NPAllocateFunctionPtr allocate; // Called to create a new instance of this object
  NPDeallocateFunctionPtr deallocate; // Called to free the instance of this object
  NPInvalidateFunctionPtr invalidate; // Called on live objects that belong to a plugin instance that is being destroyed. This call is always followed by a call to the deallocate function, or free(). Any attempt to use an invalidated object will result in undefined behavior.
  NPHasMethodFunctionPtr hasMethod; // Called to query if a method exists on the NPObject
  NPInvokeFunctionPtr invoke; // Used to invoke a method on the NPObject
  NPInvokeDefaultFunctionPtr invokeDefault; // Used to invoke the NPObject as a function
  NPHasPropertyFunctionPtr hasProperty; // Called to query if a property exists on the NPObject
  NPGetPropertyFunctionPtr getProperty; // Called to get the value of a property on the NPObject
  NPSetPropertyFunctionPtr setProperty; // Called to set the value of a property on the NPObject
  NPRemovePropertyFunctionPtr removeProperty; // Called to remove/delete a property on the NPObject
  NPEnumerationFunctionPtr enumerate; // Used to get a list of properties and methods that exist on the NPObject (new in FF 3, Gecko SDK 1.9)
  NPConstructFunctionPtr construct; // Used when the new keyword is called in javascript to create a new instance of the NPObject (new in FF 3, Gecko SDK 1.9)
};

Making it Object Oriented

The Gecko SDK is intentionally written to use structs instead of objects; this allows it to work across many platforms, with many different types of compilers, and never worry about binary compatibility.  If you haven’t figured it out yet, the NPClass structure contains a list of function pointers that should be called by the browser (or anyone else) to talk to the NPObject.  These should never be called explicitly, but rather they should be called through the NPN_ functions that I briefly mentioned towards the end of part one.  When one wishes to create a new instance of an NPObject, you call the NPN_CreateObject function and give it the class that should be used.

Fortunately, since all the browser cares about is the NPClass, its function pointers, and the reference count, any class that inherits from NPObject can easily satisfy that requirement; that means that it is not difficult to make a smart NPObject; simply create static methods for each of the pointers in the NPClass struct, dereference the NPObject* that they pass in, and call a member function.

Simple class definition and implementation:

#include "npapi.h"
#include "npupp.h"

class MyScriptableNPObject : public NPObject
{
protected:
    // Class member functions that do the real work
    void Deallocate();
    void Invalidate();
    bool HasMethod(NPIdentifier name);
    bool Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
    bool InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result);
    bool HasProperty(NPIdentifier name);
    bool GetProperty(NPIdentifier name, NPVariant *result);
    bool SetProperty(NPIdentifier name, const NPVariant *value);
    bool RemoveProperty(NPIdentifier name);
    bool Enumerate(NPIdentifier **identifier, uint32_t *count);
    bool Construct(const NPVariant *args, uint32_t argCount, NPVariant *result);
public:
    // This is the method used to create the NPObject
    // This method should not be called explicitly
    // Instead, use NPN_CreateObject
    static NPObject* Allocate(NPP npp, NPClass *aClass) {
        return (NPObject *)new MyScriptableNPObject(npp);
    }

    /////////////////////////////
    // Static NPObject methods //
    /////////////////////////////
    static void _Deallocate(NPObject *npobj);
    static void _Invalidate(NPObject *npobj);
    static bool _HasMethod(NPObject *npobj, NPIdentifier name);
    static bool _Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
    static bool _InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
    static bool _HasProperty(NPObject * npobj, NPIdentifier name);
    static bool _GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result);
    static bool _SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value);
    static bool _RemoveProperty(NPObject *npobj, NPIdentifier name);
    static bool _Enumerate(NPObject *npobj, NPIdentifier **identifier, uint32_t *count);
    static bool _Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);

    static NPClass _npclass;

protected:
    NPP m_Instance;
};

Due to code ownership issues, I’m not able to give you the actual code that I’m using, but if there are any compiler errors I’m sure they’ll be simple to straighten out.  The implementation for these static members will be pretty straightforward:

// static
void MyScriptableNPObject::_Invalidate(NPObject *obj) {
    ((MyScriptableNPObject*)obj)->Invalidate();
}
void MyScriptableNPObject::Invalidate() {
    // Invalidate the control however you wish
}

// static
void MyScriptableNPObject::_Deallocate(NPObject *obj) {
    ((MyScriptableNPObject*)obj)->Deallocate();
    delete ((MyScriptableNPObject*)obj);
}
void MyScriptableNPObject::Deallocate() {
    // Do any cleanup needed
}

For brevity, I’m not going to include all of the methods here; I will go into more detail on them later.  For now, suffice it to say that after you have implemented the rest of these functions, all that is left is to define the NPClass itself:

NPClass MyScriptableNPObject::_npclass = {
    NP_CLASS_STRUCT_VERSION,
    MyScriptableNPObject::Allocate,
    MyScriptableNPObject::_Deallocate,
    MyScriptableNPObject::_Invalidate,
    MyScriptableNPObject::_HasMethod,
    MyScriptableNPObject::_Invoke,
    MyScriptableNPObject::_InvokeDefault,
    MyScriptableNPObject::_HasProperty,
    MyScriptableNPObject::_GetProperty,
    MyScriptableNPObject::_SetProperty,
    MyScriptableNPObject::_RemoveProperty,
    MyScriptableNPObject::_Enumerate,
    MyScriptableNPObject::_Construct
};

Creating the NPObject

Once you have your custom NPObject created and your NPClass defined, we can cover the basics of the NPObject lifecycle.  Creating an NPObject is pretty straightforward:

NPObject *newObj = NPN_CreateObject(m_Instance, &MyScriptableNPObject::_npclass);
NPN_RetainObject(newObj);

As you can see, all that is needed is the NPP instance handler (see part two) and a pointer to the class.  Since the class is very specific to a specific NPObject, I like to create a static function to instantiate the object, like so:

static MyScriptableNPObject* NewObject(NPP npp) {
    MyScriptableNPObject* newObj = (MyScriptableNPObject*)NPN_CreateObject(npp, &_npclass);
    return newObj;
}

This is completely voluntary, of course, but it also allows you some additional control of the creation process; for example, you may want to pass in additional parameters and have them set on the object as soon as it is created.

NPObject lifecycle

As you might guess, NPN_RetainObject increments the reference count on a given NPObject.  Similarly, NPN_ReleaseObject decrements the reference count.  When the reference count hits zero, the object will be destroyed by calling the specified Deallocate function, just like the Allocate function is called by NPN_CreateObject to create the object.

When code in one DLL allocates something and it is deallocated by a different DLL, heap corruption can (and often does) occur.  For this reason, all things that belong to you will ultimately get created and destroyed in your code.  All things that belong to the browser will get created and freed in browser code.  This way everyone stays happy =]  Make sure you keep track of your Retains and Releases; just like any reference counting, this can cause you a lot of hard to track problems.

Another important thing to keep in mind about the lifecycle of a NPObject is that you don’t know exactly when it will go away; the browser will release it whenever it chooses to do so.  Therefore, you need to be able to handle things gracefully even if things are destroyed in a different order than you expect.

Giving the correct NPObject to the browser

Now that you know what an NPObject is and rougly what it does, you need to know how the browser finds out about it.  If you’ll refer back to part two and look at the plugin lifecycle, you’ll see on entry #5 that “Plugin creates a scriptable NPObject and returns a pointer to it (after calling NPN_RetainObject on it.)”  Simply put, at that point in the initialization sequence (or somewhere in there; the spec is ambiguous about the order), the Browser will call GetValue on the plugin and ask for a NPObject.  If our plugin supports NPObjects, it simply needs to create one, call NPN_RetainObject on it, and return it as a void pointer.  *NOTE: Make sure that you typecast it as a NPObject* before assigning it to the void pointer; I made that mistake once and spent a week trying to figure out why the wrong functions were getting called.

Here is an example implementation of GetValue:

NPError PluginInstance::NpapiGetValue(NPPVariable variable, void *value)
{
   NPError rv = NPERR_NO_ERROR;
   switch(variable)
   {
      case NPPVpluginNameString:
          value = *((char **)value) = STRINGS_PRODUCTNAME;
          break;
      case NPPVpluginDescriptionString:    // Plugin description
          *((char **)value) = STRINGS_FILEDESCRIPTION;
          break;
      case NPPVpluginScriptableNPObject:// Scriptable plugin interface (for accessing from javascript)
          *(NPObject **)value = this->getScriptableObject();
          break;
      case NPPVpluginWindowBool:
          *((PRBool *)value) = this->isWindowed;
          break;
      default:
          rv = NPERR_GENERIC_ERROR;
  }
  return rv;
}

NPObject *PluginInstance::getScriptableObject()
{
   NPObject *retval;
   if (this->npobj != NULL)
      retval = (NPObject *)this->npobj;
   else
      retval = (NPObject *)MyScriptableNPObject::NewObject(this->npp);

   NPN_RetainObject(retval);

   return retval;
}

HasMethod and HasProperty

On to the meat of the article: Say you have a javascript function like so:

var objTag = document.getElementById("myObjectTag");
alert(objTag.doSomeCoolFunction);

You may think that this looks a little strange, since “doSomeCoolFunction” sounds like a function call, but it is clearly accessed like a parameter here.  In Javascript, this is legal even if that is a function, because it can be used to determine if a function exists without calling it.  Therefore, you cannot have a method and a property on the same object with the same name.

When this call is made, the browser will call two functions on the NPObject:

  1. HasMethod(NPIdentifier name);
  2. HasProperty(NPIdentifier name);

The purpose of these two calls is to ascertain whether or not a property or method exists on the NPObject (as you might have guessed).  So, what is with this NPIdentifier thing?  Well, a NPIdentifier can map to either an integer or a string.  a NPIdentifier can be converted to a string (if it is a string identifier) with NPN_UTF8FromIdentifier.  To find out if it is a string identifier, use NPN_IdentifierIsString.  If it is an integer, you can use NPN_IntFromIdentifier.

Now, the purpose of integer identifiers may not be immediately apparent to everyone; it wasn’t to me at first.  Once I figured it out, however, I was thrilled.  If you are familiar with javascript, you are doubtlessly familiar with the concept that everything in javascript is an object; arrays are actually just objects with integer keys.  So, to allow array-style access, simply handle integer identifiers!

Once you have converted the identifier into something you can deal with, decide if your object has that property or method (I’ll let you figure out which goes in which function) and return true or false.

Oh, and after you get a string wtih NPN_UTF8FromIdentifier, don’t forget to release the memory with NPN_MemFree().

NPVariant

Before I go any further, I need to spend a brief paragraph or two on the NPVariant structure.

typedef struct _NPVariant {
  NPVariantType type;
  union {
    bool boolValue;
    int32_t intValue;
    double_t doubleValue;
    NPString stringValue;
    NPObject *objectValue;
  } value;
} NPVariant;

NPVariant is the datatype used by the browser to pass variables into and out of NPObjects.  From the MDC docs, the following types are valid:

JavaScript type NPVariantType
undefined NPVariantType_Void
null NPVariantType_Null
boolean NPVariantType_Bool
number NPVariantType_Int32 or NPVariantType_Double
string NPVariantType_String
All other types NPVariantType_Object

Since it’s getting late and I’ve already put too much in this post (I’m over 2000 words), I’ll leave it as an excercise to the reader to read up on the macros and such for using this structure.

Properties

At this point, you should be getting a pretty good idea of how everything works.  Getting and setting properties is fairly straightforward:

    bool GetProperty(NPIdentifier name, NPVariant *result);
    bool SetProperty(NPIdentifier name, const NPVariant *value);

To get a property, simply check the name to see what you’re going to be doing and store the value to return in *result.  If the operation succeeds, you should return true.  If not, false.

To set a property, again check the name to see which property you’re working with and save the value of the NPVariant parameter however you wish.  Again, return true if success, false if failure.

Methods

Methods aren’t much more difficult.  The only real difference is that now you have parameters to worry about.  No problem!  Parameters are passed in as an array of NPVariants:

    bool Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
    bool InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result);

The difference between Invoke and InvokeDefault is that InvokeDefault does not ever have an identifier.  If you call “objTag.funcName()” Invoke gets called; if you call “objTag()”, then InvokeDefault gets called.

As with properties, store the return value in the pointer given in the last argument and handle the other arguments however you wish.

Wrapping it up

Alright, I know I haven’t covered everything, but this is already too long.  If you have specific questions, ask them in the comments and I’ll do my best to answer; the comments may determine the direction of my next post.

Since dealing with the NPIdentifiers can get to be such a pain, I recommend using a map or hashtable to store structures with function pointers to map a string name to functions to handle it; that way you don’t end up with giant if/then or case statements, and everything ends up being much cleaner.

Another idea for the adventuresome: abstract the actual logic (properties and methods) into another object that can be wrapped with your NPObject class, and then make another wrapper class that implements IDispatch so that your scriptable objects are cross platform (IDispatch objects are scriptable on Internet Explorer).

Good luck!

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!

48 Comments

  1. wwwhack
    9 years ago

    By far the most helpful tutorial I’ve come across for the NPAPI. Thank you!!

  2. wwwhack
    9 years ago

    By far the most helpful tutorial I’ve come across for the NPAPI. Thank you!!

  3. coder
    9 years ago

    Hey richard, truly amazing work. I really found everything useful.

    Question though, I’m a little confused as to how I use this plugin. I’ve made a simple plugin that runs in the browser but it does nothing, just draws the frame and all.

    Now, if I have an API with native draw calls and such that is utilized by a javascript code I write, how can my plugin get access to this javascript through the browser. I assume it’s passed in as some html text or something? Basically I want to make a native plugin that’ll run my code across many platforms. I’m just wondering how my plugin would implement these certain functions (such as interpreting certain draw calls) in conjunction with the browser.

    I know it’s kind of specific but I’m pretty confused and there is VERY little documentation on this type of thing…

  4. coder
    9 years ago

    Hey richard, truly amazing work. I really found everything useful.

    Question though, I’m a little confused as to how I use this plugin. I’ve made a simple plugin that runs in the browser but it does nothing, just draws the frame and all.

    Now, if I have an API with native draw calls and such that is utilized by a javascript code I write, how can my plugin get access to this javascript through the browser. I assume it’s passed in as some html text or something? Basically I want to make a native plugin that’ll run my code across many platforms. I’m just wondering how my plugin would implement these certain functions (such as interpreting certain draw calls) in conjunction with the browser.

    I know it’s kind of specific but I’m pretty confused and there is VERY little documentation on this type of thing…

  5. taxilian
    9 years ago

    coder:

    I’ll do another post soon with more detail on this; I was running out of time =] Here is a brief rundown, though.

    Say you want to call from Javascript:

    pluginObj.drawLine(x1, y1, x2, y2);

    Your NPObject will have Invoke called on it:
    bool Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);

    When you call NPN_UTF8FromIdentifier on “name” it will return the C string “drawLine”. argCount will be 4, and args will be a pointer to a 4 element array of NPVariant objects.

    Double check the type with NPVARIANT_IS_DOUBLE() and NPVARIANT_IS_INT32() on each element (in my experience most often javascript gives you doubles), and then convert the values to normal ints or doubles like so:

    int x1 = NPVARIANT_TO_INT32(args[0]);
    or
    double x1 = NPVARIANT_TO_DOUBLE(args[0]);

    Strings are a little trickier, but my preferred method is to convert it to an STL string:

    std::string str(args[0].stringValue.UTF8Characters, args[0].stringValue, UTF8Length);

    for more information, read up on NPVariants at https://developer.mozilla.org/en/NPVariant and NPStrings at https://developer.mozilla.org/en/NPString

  6. Richard
    9 years ago

    coder:

    I’ll do another post soon with more detail on this; I was running out of time =] Here is a brief rundown, though.

    Say you want to call from Javascript:

    pluginObj.drawLine(x1, y1, x2, y2);

    Your NPObject will have Invoke called on it:
    bool Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);

    When you call NPN_UTF8FromIdentifier on “name” it will return the C string “drawLine”. argCount will be 4, and args will be a pointer to a 4 element array of NPVariant objects.

    Double check the type with NPVARIANT_IS_DOUBLE() and NPVARIANT_IS_INT32() on each element (in my experience most often javascript gives you doubles), and then convert the values to normal ints or doubles like so:

    int x1 = NPVARIANT_TO_INT32(args[0]);
    or
    double x1 = NPVARIANT_TO_DOUBLE(args[0]);

    Strings are a little trickier, but my preferred method is to convert it to an STL string:

    std::string str(args[0].stringValue.UTF8Characters, args[0].stringValue, UTF8Length);

    for more information, read up on NPVariants at https://developer.mozilla.org/en/NPVariant and NPStrings at https://developer.mozilla.org/en/NPString

  7. coder
    9 years ago

    Thanks a lot man! You’re the best. I’m still baffled that there is so LITTLE documentation on this. I googled expecting thousands of results and got NOTHING. Thanks and can’t wait for the next parts..but of course, take your time!

  8. coder
    9 years ago

    Thanks a lot man! You’re the best. I’m still baffled that there is so LITTLE documentation on this. I googled expecting thousands of results and got NOTHING. Thanks and can’t wait for the next parts..but of course, take your time!

  9. taxilian
    9 years ago

    Yeah, the same thing baffled me when I started looking into this almost a year ago; it’s the main reason that we decided to set up this blog site; it gives us a place to document things that are nearly impossible to find otherwise.

    Glad I could help =] It’s a lot more gratifying to spend a few hours writing a post when you know that it is being used.

  10. Richard
    9 years ago

    Yeah, the same thing baffled me when I started looking into this almost a year ago; it’s the main reason that we decided to set up this blog site; it gives us a place to document things that are nearly impossible to find otherwise.

    Glad I could help =] It’s a lot more gratifying to spend a few hours writing a post when you know that it is being used.

  11. coder
    9 years ago

    Hey Richard, I’m sorry to bother you again but I’m still kind of confused as to how to use the JS. The pluginObj I assume is created within the JS using the MyScriptableObject which is also a JS? So then, do I put Invoke method in the plugin because that makes sense to me since we want the JS to tell the plugin to do something, like drawing a line.

  12. coder
    9 years ago

    Hey Richard, I’m sorry to bother you again but I’m still kind of confused as to how to use the JS. The pluginObj I assume is created within the JS using the MyScriptableObject which is also a JS? So then, do I put Invoke method in the plugin because that makes sense to me since we want the JS to tell the plugin to do something, like drawing a line.

  13. taxilian
    9 years ago

    If I wanted to not be bothered, I wouldn’t have written the article.

    pluginObj that I refer to is the DOM element of the plugin. So, if you have:

    <object id=”myPlugin” type=”…”></object>

    then

    var pluginObj = document.getElementById(“myPlugin”);

    pluginObj.drawLine(x1, y1, x2, y2);

    that will result in Invoke being called on the default NPObject that you returned when the browser called NPP_GetValue. Does that help?

  14. Richard
    9 years ago

    If I wanted to not be bothered, I wouldn’t have written the article.

    pluginObj that I refer to is the DOM element of the plugin. So, if you have:

    <object id=”myPlugin” type=”…”></object>

    then

    var pluginObj = document.getElementById(“myPlugin”);

    pluginObj.drawLine(x1, y1, x2, y2);

    that will result in Invoke being called on the default NPObject that you returned when the browser called NPP_GetValue. Does that help?

  15. coder
    9 years ago

    Ok, so I define my ID within the javascript (or the plugin?)

    Then within the JS I define my pluginObj the way you said and then I can use it to call something like a DrawLine. Then of course the invoke is called within the plugin and then I can do something there? Is that correct?

    I’m not sure where the scriptable object you provided comes in then. I’m really sorry for my potentially terrible question, just my first time doing then and I’m pretty confused. Really appreciate all your help.

  16. coder
    9 years ago

    Ok, so I define my ID within the javascript (or the plugin?)

    Then within the JS I define my pluginObj the way you said and then I can use it to call something like a DrawLine. Then of course the invoke is called within the plugin and then I can do something there? Is that correct?

    I’m not sure where the scriptable object you provided comes in then. I’m really sorry for my potentially terrible question, just my first time doing then and I’m pretty confused. Really appreciate all your help.

  17. Richard
    9 years ago

    The ID is defined in the html; remember that to the web page, your plugin is just an <object> tag. To access that from javascript, you access it the same way you would access anything else in the DOM. document.getElementById simply returns a reference to the DOM element with the specified ID; in this case, it’s your plugin.

    for more information: http://www.w3schools.com/Dhtml/default.asp

    Whenever you try to access a property or method of that DOM element, the browser recognizes that it is your plugin. During the plugin initialization (or at the latest, when you first try to access it from script), the browser calls NPP_GetValue (see section “Giving the correct NPObject to the browser” above) to get the NPObject.

    Then, every time you try to call a method on that DOM element, Invoke gets called. Whenever you access a property or method, Invoke, GetProperty, or SetProperty gets called on the NPObject instance that you returned.

  18. taxilian
    9 years ago

    The ID is defined in the html; remember that to the web page, your plugin is just an <object> tag. To access that from javascript, you access it the same way you would access anything else in the DOM. document.getElementById simply returns a reference to the DOM element with the specified ID; in this case, it’s your plugin.

    for more information: http://www.w3schools.com/Dhtml/default.asp

    Whenever you try to access a property or method of that DOM element, the browser recognizes that it is your plugin. During the plugin initialization (or at the latest, when you first try to access it from script), the browser calls NPP_GetValue (see section “Giving the correct NPObject to the browser” above) to get the NPObject.

    Then, every time you try to call a method on that DOM element, Invoke gets called. Whenever you access a property or method, Invoke, GetProperty, or SetProperty gets called on the NPObject instance that you returned.

  19. coder
    9 years ago

    Thanks Richard, it’s slowly getting more clear thanks to you. I’ll see if I can get a simple line to draw.

  20. coder
    9 years ago

    Thanks Richard, it’s slowly getting more clear thanks to you. I’ll see if I can get a simple line to draw.

  21. Sleepy
    9 years ago

    Thanks for this impressive tutorial.
    It is now very “easy” to call plugin methods from JS.
    But do you know how to call a JS method from the plugin ?

    My question actually deals with events.
    In my HTML page, I have for example :

    alert(“MyEvent has been called”);

    This kind of event script works in IE and in Firefox. (I managed to make it work in Firefox with nsXPConnect).
    I would like to find a way to send the MyEvent event from the plugin code through NPRuntime.

    If you know a way to do it…

    Thanks.
    Sleepy

  22. Sleepy
    9 years ago

    Thanks for this impressive tutorial.
    It is now very “easy” to call plugin methods from JS.
    But do you know how to call a JS method from the plugin ?

    My question actually deals with events.
    In my HTML page, I have for example :

    alert(“MyEvent has been called”);

    This kind of event script works in IE and in Firefox. (I managed to make it work in Firefox with nsXPConnect).
    I would like to find a way to send the MyEvent event from the plugin code through NPRuntime.

    If you know a way to do it…

    Thanks.
    Sleepy

  23. coder
    9 years ago

    So I’m wondering what the -&gt means in your code? That makes no sense to me. I assume it’s actually meant to be an arrow?

  24. coder
    9 years ago

    So I’m wondering what the -&gt means in your code? That makes no sense to me. I assume it’s actually meant to be an arrow?

  25. taxilian
    9 years ago

    coder :So I’m wondering what the -&gt means in your code? That makes no sense to me. I assume it’s actually meant to be an arrow?

    Good catch. Thanks. I hope I have fixed them all now; the blog engine was doing some translations that I didn’t expect.

  26. Richard
    9 years ago

    coder :

    So I’m wondering what the -&gt means in your code? That makes no sense to me. I assume it’s actually meant to be an arrow?

    Good catch. Thanks. I hope I have fixed them all now; the blog engine was doing some translations that I didn’t expect.

  27. coder
    9 years ago

    Thought I should let you know, you fixed the arrow issue in part three only, the other parts still have the weird -&gt.

    Also, what do you mean when you say STRINGS_PRODUCTNAME and STRINGS_FILEDESCRIPTION in GetValue? Those things aren’t defined or anything, I assume we fill it in ourselves?

    But man I can feel it, getting closer, slowly understanding it more!

  28. coder
    9 years ago

    Thought I should let you know, you fixed the arrow issue in part three only, the other parts still have the weird -&gt.

    Also, what do you mean when you say STRINGS_PRODUCTNAME and STRINGS_FILEDESCRIPTION in GetValue? Those things aren’t defined or anything, I assume we fill it in ourselves?

    But man I can feel it, getting closer, slowly understanding it more!

  29. coder
    9 years ago

    Hey richard I’m very confused as to how I can get access to the NPN browser functions? I included npruntime and in NP_Initialize I did browser = browserfuns where browser is NetScapeFuncs. I look around a lot on google and it seems this is all I have to do but but I try and call NPN_CreateObject or NPN_RetainObject I get a symbol not found in my xcode proj.

    Man so frustrating when there is no information, no matter where I look. lol I think I’ve exhausted like every possible search term on google.

  30. coder
    9 years ago

    Hey richard I’m very confused as to how I can get access to the NPN browser functions? I included npruntime and in NP_Initialize I did browser = browserfuns where browser is NetScapeFuncs. I look around a lot on google and it seems this is all I have to do but but I try and call NPN_CreateObject or NPN_RetainObject I get a symbol not found in my xcode proj.

    Man so frustrating when there is no information, no matter where I look. lol I think I’ve exhausted like every possible search term on google.

  31. coder
    9 years ago

    Hey figured it out man. I didn’t know you had to implement the NPN functions yourself.

  32. coder
    9 years ago

    Hey figured it out man. I didn’t know you had to implement the NPN functions yourself.

  33. padolph
    9 years ago

    Richard:
    Thanks for this. Best NPAPI tutorial around! I’ve been struggling for a few weeks with making my plugin and your writeups have answered a number of my lingering questions.

    One thing I learned that I don’t think you covered yet, is how to handle a plugin that has its own thread. The MDC documentation states clearly that the NPN_ functions are not thread-safe and need to be called in the browser context. It turns out that they added NPN_PluginThreadAsyncCall to NPAPI for just this purpose, but much of the documentation out there does not mention it.

    Coder:
    Take a look at the DiamondX plugin as a starting point example. I found this to be more useful than the Mozilla examples. It should answer all your questions about NPN_ and NPP functions, etc. Does not cover the scripting (npruntime) stuff though.

  34. padolph
    9 years ago

    Richard:
    Thanks for this. Best NPAPI tutorial around! I’ve been struggling for a few weeks with making my plugin and your writeups have answered a number of my lingering questions.

    One thing I learned that I don’t think you covered yet, is how to handle a plugin that has its own thread. The MDC documentation states clearly that the NPN_ functions are not thread-safe and need to be called in the browser context. It turns out that they added NPN_PluginThreadAsyncCall to NPAPI for just this purpose, but much of the documentation out there does not mention it.

    Coder:
    Take a look at the DiamondX plugin as a starting point example. I found this to be more useful than the Mozilla examples. It should answer all your questions about NPN_ and NPP functions, etc. Does not cover the scripting (npruntime) stuff though.

  35. taxilian
    9 years ago

    Good point, padolph; I’ve actually been meaning to add a section about that — and hopefully still will, but I’m limited on time. My main concern with NPN_PluginThreadAsyncCall is that it only works with firefox 3+, and I use my plugins on older browsers as well; however, I’ve made my own implementation of PluginThreadAsyncCall on Windows, and as soon as I have an equivilent that will work on Mac and Linux I’ll do a post explaining how to do that. Basically, I’m using the built-in implementation if there, if not I use the newer one.

    I’ll probably need to do a full post on backwards compatibility.

  36. Richard
    9 years ago

    Good point, padolph; I’ve actually been meaning to add a section about that — and hopefully still will, but I’m limited on time. My main concern with NPN_PluginThreadAsyncCall is that it only works with firefox 3+, and I use my plugins on older browsers as well; however, I’ve made my own implementation of PluginThreadAsyncCall on Windows, and as soon as I have an equivilent that will work on Mac and Linux I’ll do a post explaining how to do that. Basically, I’m using the built-in implementation if there, if not I use the newer one.

    I’ll probably need to do a full post on backwards compatibility.

  37. padolph
    9 years ago

    Wow – if you figured out how to make your own PluginThreadAsyncCall I would love to see how! I share your concern about NPN_PluginThreadAsyncCall being available in all browsers.

    The only way I figured I could do this was to either modify the browser code, or install some javascript via NPN_Evalute to poll the plugin. Neither approach seems very viable.

    I’m coding exclusively for linux at the moment, but if you are ready (and have time) to post your Windows stuff for this don’t let me stop you. :-)

  38. padolph
    9 years ago

    Wow – if you figured out how to make your own PluginThreadAsyncCall I would love to see how! I share your concern about NPN_PluginThreadAsyncCall being available in all browsers.

    The only way I figured I could do this was to either modify the browser code, or install some javascript via NPN_Evalute to poll the plugin. Neither approach seems very viable.

    I’m coding exclusively for linux at the moment, but if you are ready (and have time) to post your Windows stuff for this don’t let me stop you. :-)

  39. taxilian
    9 years ago

    I don’t have code that I can post yet — like I said, when we get it all ironed out, I’ll do a post on it, but here is the gist:

    On windows, post a message back to the main window and then in the message loop on the main thread catch it and fire the event. Pass the event data as the LPARAM (or rather, a pointer to a struct containing the needed data)

    On Mac, you can use a critical-section protected queue to store the event data structure in, and then on the NULL event that is fired something like 50 times a second just check the queue and fire the event when needed.

    On Linux, I haven’t worked it all out yet; perhaps there is some sort of cross-thread scheduling mechanism that linux has that we can use. I don’t know. :-/

  40. Richard
    9 years ago

    I don’t have code that I can post yet — like I said, when we get it all ironed out, I’ll do a post on it, but here is the gist:

    On windows, post a message back to the main window and then in the message loop on the main thread catch it and fire the event. Pass the event data as the LPARAM (or rather, a pointer to a struct containing the needed data)

    On Mac, you can use a critical-section protected queue to store the event data structure in, and then on the NULL event that is fired something like 50 times a second just check the queue and fire the event when needed.

    On Linux, I haven’t worked it all out yet; perhaps there is some sort of cross-thread scheduling mechanism that linux has that we can use. I don’t know. :-/

  41. Georg
    9 years ago

    I am still about 2 weeks away from continuing on our plugin, but from quickly looking over the 3 posts here i am thankful for the quick and clear introduction.
    I'll have to get back for the Scriptable Objects later ;)

  42. Georg
    9 years ago

    I am still about 2 weeks away from continuing on our plugin, but from quickly looking over the 3 posts here i am thankful for the quick and clear introduction.
    I'll have to get back for the Scriptable Objects later ;)

  43. taxilian
    9 years ago

    Sleepy: There certainly is a way to do this. Somehow your comment fell off the board system; I don’t know why, but I apologize.

    I’ll cover this in detail in another post (yeah, that list is getting long, definitely time to write another and make it smaller), but here is the gist:

    implement two functions on your NPObject: addEventListener and removeEventListener

    When they call addEventListener, the parameter that is a function gets passed in as an NPObject *; NPN_RetainObject this and store it in a multimap or similar with the name of the event as the key.

    When they call removeEventListener, search through and see if you have a saved pointer to the passed-in npobject; if so, NPN_ReleaseObject it and delete your saved pointer.

    When you need to fire an event, marshall the arguments you want as an array of NPObjects and loop through all of the saved npobjects for that event and call NPN_InvokeDefault on each of them.

    *IMPORTANT*: you can only call javascript functions from the main thread of your plugin. If you have multiple threads, you need to call NPN_InvokeDefault from the one that the plugin started on. (See earlier comments for more information)

  44. taxilian
    9 years ago

    Sleepy: There certainly is a way to do this. Somehow your comment fell off the board system; I don’t know why, but I apologize.

    I’ll cover this in detail in another post (yeah, that list is getting long, definitely time to write another and make it smaller), but here is the gist:

    implement two functions on your NPObject: addEventListener and removeEventListener

    When they call addEventListener, the parameter that is a function gets passed in as an NPObject *; NPN_RetainObject this and store it in a multimap or similar with the name of the event as the key.

    When they call removeEventListener, search through and see if you have a saved pointer to the passed-in npobject; if so, NPN_ReleaseObject it and delete your saved pointer.

    When you need to fire an event, marshall the arguments you want as an array of NPObjects and loop through all of the saved npobjects for that event and call NPN_InvokeDefault on each of them.

    *IMPORTANT*: you can only call javascript functions from the main thread of your plugin. If you have multiple threads, you need to call NPN_InvokeDefault from the one that the plugin started on. (See earlier comments for more information)

  45. jiyingjun
    9 years ago

    wonderful…thanks for your post,it's helpful to me

  46. jiyingjun
    9 years ago

    expecting for your next post

  47. jiyingjun
    9 years ago

    wonderful…thanks for your post,it's helpful to me

  48. jiyingjun
    9 years ago

    expecting for your next post

2 Trackbacks

  1. […] 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 […]

  2. […] Building a firefox plugin – part three Leave a comment | Trackback […]

Post a Comment

Your email is never published or shared. Required fields are marked *