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!

49 Comments

  1. taxilian
    8 years ago

    As explained in part one: http://colonelpanic.net/2009/03/building-a-fire

    NPP_ plugin entry points are the functions that the browser calls on the plugin. NPN_ netscape entry points are the entry points that the plugin calls when it needs to talk to the browser.

    Think of it as namespacing; NPP_ functions are the ones you have to implement. NPN_ functions are the ones that mozilla, webkit, or opera implement that you call when you need to do something.

  2. Navaid
    8 years ago

    Don't know if this has been covered somewhere, but can one use the Invoke function to pass BYREF parameters. I wish to be able to return values from the Javascript functions (other than the return val). If so how.

  3. taxilian
    8 years ago

    Well, it depends on what you’re trying to do. when you pass a primitive in (string, double, int32, boolean) it is always by value, but when you pass in an NPObject (such as a javascript object or array or something generated by the plugin) it is always by reference.

    You have to be a bit careful with that, but if you pass in a javascript array and then change it from inside your plugin, those changes will be reflected in the object you passed in.

  4. taxilian
    8 years ago

    Well, it depends on what you're trying to do. when you pass a primitive in (string, double, int32, boolean) it is always by value, but when you pass in an NPObject (such as a javascript object or array or something generated by the plugin) it is always by reference.

    You have to be a bit careful with that, but if you pass in a javascript array and then change it from inside your plugin, those changes will be reflected in the object you passed in.

  5. Navaid
    8 years ago

    Let's say I have a function in Javascript (such as an event sink function) which I call from the plugin using Invoke. I am able to do that. Now I want that function to return, say, an int and a string. I would like to be able to pass variables through invoke which would be updated by the Javascript and used (these would be similar to the [out] or [in, out] parameters of COM).

  6. taxilian
    8 years ago

    Unfortunately, Javascript itself doesn't support passing ints or strings by reference, and thus neither does a plugin.

    The best you can do is pass an NPObject that acts like a string or an int.

  7. taxilian
    8 years ago

    Like all NPN_ functions, you must provide it yourself; the code for it is in the NPNetscapeFuncs structure that the browser gives you. There are no linker dependencies for NPAPI; no symbols that you link in at all. Just structures and function pointers.

    here is FireBreath’s implementation: http://code.google.com/p/firebreath/source/browse/src/NpapiPlugin/NpapiBrowserHost.cpp#409

  8. Stephan
    8 years ago

    Hi,

    Like many other, my question is about NPN_PluginThreadAsyncCall. Where is the code for that function ? I am using gecko sdk 1.9.2 testing in Mozilla 3.6.3.
    (https://developer.mozilla.org/en/Gecko_SDK)
    I tried to link various lib files contained in gecko sdk, still unresolved symbol.

    Also tried to reconstruct the function by looking at the gecko sdk source. With no success. Do you know whether it is possible to inherit from nsIRunnable like this

    class nsRunnable : public nsIRunnable
    {
    NS_DECL_ISUPPORTS
    NS_DECL_NSIRUNNABLE
    };
    NS_IMPL_ISUPPORTS1(nsRunnable, nsIRunnable)

    implementing Run() function.
    Finaly using NS_DispatchToMainThread for dispatching.
    I tried, but NS_GetMainThread within NS_DispatchToMainThread returns failure

    Thanks

  9. taxilian
    8 years ago

    Like all NPN_ functions, you must provide it yourself; the code for it is in the NPNetscapeFuncs structure that the browser gives you. There are no linker dependencies for NPAPI; no symbols that you link in at all. Just structures and function pointers.

    here is FireBreath's implementation: http://code.google.com/p/firebreath/source/brow

  10. taxilian
    8 years ago

    Like all NPN_ functions, you must provide it yourself; the code for it is in the NPNetscapeFuncs structure that the browser gives you. There are no linker dependencies for NPAPI; no symbols that you link in at all. Just structures and function pointers.

    here is FireBreath's implementation: http://code.google.com/p/firebreath/source/brow

  11. Stephan
    8 years ago

    Thank you, this really helped to enlighten some points for me. Works now.

    @everybody who uses the sdk-samples for learning and has the same problem too.
    dont forget to put :
    NPNFuncs.pluginthreadasynccall= pFuncs->pluginthreadasynccall;
    into NP_Initialize
    I remember someone was wondering why the function crashs, may be this was the reason ;-)

  12. khetan
    8 years ago

    Hi,

    Hey can we invoke only the functions in my scripableplugin class? Can we invoke the functions in my plugin class ..

    can i write this

    var objTag = document.getElementById(“myObjectTag”);
    alert(objTag.pluginfunction);

    and i can implement the below invoke call with the help of plugin instance (npp)

    like

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

    npp.pluginfunction();

    }

    My concern is can we invoke the plugin class functions through javascript or we can invoke only the functions in scriptableplugin class?

  13. taxilian
    8 years ago

    From javascript, you can only call functions on the NPObject. The NPObject can then call methods on your plugin class, however.

  14. khetan
    8 years ago

    Hi,

    please guide me for the following scenario?

    If i want to expose my platform native class to the javascript, then can i use plugin instedof LUT Table ?

    If yes can you please tell me how?

    Will the class i need to expose to javascript will be the plugin class or scriptable plugin class ?

    My example might be wrong buti hope u understand what i want to tell?

  15. taxilian
    8 years ago

    The only type of class you can expose to javascript is an NPObject (scriptable plugin, you could say) class, however that class can proxy calls to your “plugin class”.

    I highly recommend you look at FireBreath (http://firebreath.googlecode.com), as it would make a lot of that easier to understand. If you look in the mailing list archives, there are several posts explaining how to do what you are describing with FireBreath.

  16. saxena
    8 years ago

    hi,

    Can we add or remove the parameter of the function NPN_Createobject?

  17. Georg
    8 years ago

    If you mean passing additional parameters to NPN_CreateObject() – no can do. The signature of that function is defined by the NPAPI. You can however pass additional parameters to your creation function, e.g. NewObject() above, and then call some initialization function on the object.

  18. prasanth
    8 years ago

    I am writing an NPAPI plugin on MAC OS. This plugin actually downloads a file and prints the document that is downloaded. On MAC OS, brwser calls NPP_StreamAsFile to give me the file and after that NPP_DestroyStream. After that, it calls NPP_URLNotify and on completion of this, it goes on continuously calling NPP_HandleEvent.

    I think because of that, when I click on Download button on my web page continuously, my application crashes.

    Any expertise opinion/help on this would be really helpful.
    I tried but not go further on this.

    You can reach me on [email protected]

  19. Adam Cobb
    8 years ago

    Hi,

    Thanks for your great tutorial, really helped me. Just having an issue with creating the NPObject at the moment.

    I had the whole plugin working previously as a very simple implementation with lots of global and static variables, i've now followed your tutorials and converted it to an object based design so I can instantiate multiple instances safely etc.

    However, since I have done that, when creating an instance of the plugin it gets as far as the MyScriptableNPObject::NewObject() method, where it attempts to call createobject() but never returns and the browser crashes.

    I have defined an Allocate() method as you described for my NPObject and the NPClass seems to be defined correctly as far as I can tell – I haven't defined all the methods as you have in your example, but I assume that wouldn't be a problem? Some are just NULL.

    Any help would be greatly appreciated,

    Thanks.

  20. taxilian
    8 years ago

    Well, I haven't experimented with it to be certain, but my guess would be that your crash is caused by leaving some of the NPClass pointers as NULL. Keep in mind that the browser will use those pointers to call the functions; it may be that the browser checks for NULL values before calling them, but I'm not certain about that.

    I normally just implement all of the NPClass methods and then return false on methods like NPN_Construct that I haven't implemented.

  21. Adam Cobb
    8 years ago

    Thanks for the reply.

    Unfortunately this had no effect, i've written stubs for all the methods and updated the class struct as suggested but the browser still crashes when i'm attempting the call to createobject().

    I am even logging the first line in my Allocate() method but it is definitely not getting that far.

    Thanks again.

  22. taxilian
    8 years ago

    Well, if it's still crashed, you've probably got a bad pointer in there somewhere. What I would do is get a copy of mozilla and build from source, then you can step through the create process. I realize that sounds like a pain, but it's not as hard as it sounds, and then you can probably find where it is crashing.

  23. Adam Cobb
    8 years ago

    Thanks. Yeah, looks like it might be my npnfuncs struct! I just checked and it appears to be null, no idea why as i'm definitely storing it on initialization!

  24. SRB
    8 years ago

    Hi,
    Firstly, thanks for posting such a nice piece of information, cleared a lot of things for me.

    I have a question about DOM 0 event handlers. I have an <iframe> created by my plugin. I created another NPObject and set it to the onload property of the iframe (using NPN_SetProperty). I implemented the InvokeDefault() & Invoke() apis on that NPObject. But none of them get invoked. Could you please give some pointers on this?

    Thanks

  25. bikash
    8 years ago

    Hi,

    I have a basic question.

    Is it necessary to have a plugin class for a scriptable plugin class ? i mean can we directly create npobject instance and bind it to java script with name.

    I mean can’t we simply write

    NPObject *pNpObject = NULL;
    pNpObject = _NPN_CreateObject(0, npclass);

    Then we can bind this npobject to the javascript with a name say”obj” and directly
    invoke the object method like “obj.methodname()”.

    Please guide me in it.

  26. taxilian
    8 years ago

    An NPObject does not have any direct connection to any c++ object. The only thing that matters to it is what function pointers are in the NPClass structure. Keep in mind that the only way to get an NPObject to the browser is to return it from the NPP_GetValue call or to return it from a NPObject that the browser already has (such as one that was returned by NPP_GetValue). However, you can have as many NPObjects as you want in any given plugin.

  27. Glauco
    8 years ago

    Hi,

    I would like to fire an event that passes an object that contains some members and an array so that in my event handler I can do things like:
    function(e) { alert(e.my_var); alert(e.my_array[0]); }

    Do I need to create a class that mimics this object or can I create this object directly on the Javascript from the c++? I’ve tried to follow the second approach by first using NPN_GetValue to get the window object and then calling NPN_Invoke with “Object” to create the object, but then when I tried to add an “Array” to this object NPN_Invoke returned false.

    Am I on the wrong path?

    Thanks

  28. taxilian
    8 years ago

    The process you describe calling window.Object() to get an NPObject and then using SetProperty to set my_var on that, then using window.Array() to get an NPObject array and then using SetProperty to add elements to that, etc works just fine. FireBreath uses this methodology to return objects and arrays to javascript.

  29. taxilian
    8 years ago

    The process you describe calling window.Object() to get an NPObject and then using SetProperty to set my_var on that, then using window.Array() to get an NPObject array and then using SetProperty to add elements to that, etc works just fine. FireBreath uses this methodology to return objects and arrays to javascript.

  30. Glauco
    8 years ago

    Thank you taxilian! That is going to work beautifully :-)
    Now, I just need to figure out how to do it in COM.

    – Glauco

  31. taxilian
    8 years ago

    In COM you do it the same way. Of course, you could save yourself a heck of a lot of time and just use FireBreath, where I’ve already solved that problem in a ridiculously easy to use way.

    Or, if you’re determined to do it yourself, you can look at the FireBreath code for an example: to set a property on an array (which will be an IDispatchEx object): http://code.google.com/p/firebreath/source/browse/src/ActiveXPlugin/IDispatchAPI.cpp

    look at http://code.google.com/p/firebreath/source/browse/src/ActiveXPlugin/FBControl.h for an example of getting a reference to the window / document.

  32. Glauco
    8 years ago

    Yes – FireBreath is pretty cool and I’ve been checking the code a lot and it’s definitely a really nice framework. But, this is my first experience with browser plug-ins and I think to fully appreciate the framework, I need to go through the pains at least once :-)

    Thanks to your help and this tutorial, I got almost everything figure out however, I have one more issue. When, in the JavaScript, I put alert(e.array[0]) I see a coma (,) – is this garbage? Only if I do alert(e.array[1]) then I actually see what I pushed from the c++ code.
    I’m sure it’s something silly but I can’t see it. I’ve pasted below the main steps in the code where I fill the array. Do you see anything wrong?

    // To create the array
    arrayObj->GetDispID(SysAllocString(CA2W(“Array”)),
    fdexNameEnsure | fdexNameCaseSensitive | 0x10000000, &dispId);

    arrayObj->InvokeEx(dispId, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &result, NULL, NULL);

    // Push elements to array
    arrayObj->GetDispID(SysAllocString(CA2W(“push”)),
    fdexNameEnsure | fdexNameCaseSensitive | 0x10000000, &dispId);

    VARIANT arg, result;
    DISPPARAMS params = {0};
    VariantInit(&arg);
    VariantInit(&result);
    arg.vt = VT_I2;
    arg.iVal = 4; // first element of the array

    params.rgvarg = &arg;
    params.rgdispidNamedArgs = 0;
    params.cArgs = 1;
    params.cNamedArgs = 0;

    hr = arrayObj->InvokeEx(dispId, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &result, NULL, NULL);

  33. taxilian
    8 years ago

    Hard to say for sure, but if your code is a direct quote, you have a problem. You could be getting the dispid from the window object, then invoking Array on the window object. You should then be getting the result and getting the dispid of push from the array object.

    Firebreath does this and it works exactly as you would expect; if you have more than 1 item on the array when only calling push once, something is wrong.

  34. Glauco
    8 years ago

    Yes, I’m careful to not mix these variables when calling the various methods.

    Something strange that I’ve noticed is that,when I create the array, if I also add into the variant the first element of the array (so, the variant has two arguments: arg[0].vt = VT_DISPATH, arg[0].pdispVal = NULL; arg[1].vt = VT_BSTR, arg[1].bstrValue = “test”), then e.array[0] is equal to something…..only to “t” :-(

    Does this ring any bell to you?

  35. taxilian
    8 years ago

    not particularly; I haven’t ever seen the problem you describe. I can’t reproduce it using the FireBreath code, so I’m not sure what would be going on. If you want you could email me the code and I could look at it, but it might be a few days before I could find time to look into it; I’m in the process of wrapping up one project and will be starting another one next week.

  36. Mehdi
    8 years ago

    Hi Richard,

    Thanks to your post. It was very useful.
    I have a basic question that I couldn’t find anywhere.
    I would like to access HTML page’s DOM and change them in c++ such as getElementById(id).innerText=”Text”;
    In IE we can access this with IHTMLElement::put_InnerText(…) but for NPAPI I couldn’t find that. How could we do that?

    Thanks

  37. mohit
    8 years ago

    this is the only helpful thing for npruntime… I am done with converting my xpcom plugin to npruntime… things seem to be working…what are the kind of tests required to verify that everything is being handled properly??.. and working as expected.. ??
    i have 100+ methods ..so creating JS code to verify each of them and interlinked scenarios might not be the ideal way…plzzz let me know if there is something i can do..
    thanks in advance :)

  38. Mohitgoel Com
    8 years ago

    this is the only helpful thing for npruntime… I am quite new to all this but somehow done with converting my xpcom plugin to npruntime… things seem to be working…what are the kind of tests required to verify that everything is being handled properly??.. and working as expected.. ??
    i have 100+ methods ..so creating JS code to verify each of them and interlinked scenarios might not be the ideal way…plzzz let me know if there is something i can do..
    thanks in advance :)

  39. taxilian
    8 years ago

    So somehow I missed your comment and thus didn’t respond. I’m verry sorry about that. In NPAPI you call NPN_GetValue to get the NPObject for the window and then you can get the document property, call getElementById(id), and finally set the innerHTML property to whatever you want — all using NPN_GetProperty, NPN_SetProperty, and NPN_Invoke.

  40. taxilian
    8 years ago

    Well, if you use a reliable and consistent method for implementing your NPObjects it should be fine; short of creating test cases for each method and/or property i don’t know how to better verify that.

    If you were converting to npruntime anyway you should have switched to FireBreath — it would simplify your code greatly and give you a well-tested foundation on which your code could be built.

  41. aaajeetee
    8 years ago

    Thanks so much for these blogs/tutorials. With these pages I am able to create my own firefox (and other browsers) plugin and I understand what I’m doing (in stead of modifying examples).

    By far the most helpful pages available on the web about NP plugins! Thanks!

  42. Arj Sja
    8 years ago

    Hi Taxilian,
    very nice & useful article…..I read it fully.
    I would like to know if we can instanciate a firefox plugin in a native win32 environment
    (it is actually within another firefox plugin which is kind of a wrapper or it could be a standalone application).Basically, I would like to create a window in the win32 environment & supply the window handle to the plugin, so that it can get rendered within that window.

    So, can you guide me toward the approch I should take to achive this. If FireBreath can solve this, can you also suggest a few pointers to start with.
    Thanks,
    AJ

  43. taxilian
    8 years ago

    You could, but it would be a fair bit of work. I’ve done it before, and the NPAPIHost project in FireBreath was intended to eventually facilitate this and has the beginnings of what would be needed, but I don’t have a current need for it so I haven’t continued the project. It would basically just require you to implement your own NPAPI host that would load the DLL and provide the calls in the way that a plugin would expect them.

  44. Arj Sja
    8 years ago

    Thanks a lot for the quick response. Yeah..I looked into the NPAPIHost which really has the beginnings.
    before that, I still have to scratch my head on a few details before starting with anything .I’ll get back to you once I make some progress.
    Thanks,
    AJ

  45. Shyamal2006
    8 years ago

    Hey Richard,
    Very concise and well written document :)
    Thank you so much.

    I have a kind of a basic question.
    Can my plugin have functions like fread() and fwrite(), access a file on my computer, read it and send the data back to the NPobject on the browser?

    So in effect, can i make a file reader plugin with JS on the browser and an NPAPI plugin?

  46. taxilian
    8 years ago

    yes, you can.  That said, be *very* *very* *very* careful (like, seriously, REALLY careful) if you’re going to do this.  Make sure you put very tight controls on this. Consider that if you write a plugin that can access the filesystem, any webpage can load that plugin and use it potentially without your knowledge.  Be certain that the APIs you expose can’t do anything in the wrong hands.  Even if you filter it by domain, keep in mind the potential for DNS spoofing and XSS hacks.

    Really, though, there are only a few reasons why someone would actually want to write a Browser Plugin; I mean, seriously, if you can do it in any other way you should. However, if you need high performance graphics, access to a lower level network API than the browser exposes, filesystem access, or some sort of hardware integration, there simply aren’t other options, and that is why plugins exist and aren’t going away (despite the hype you may read some places). Bear in mind that since you are running in the browser process you are limited to the filesystem permissions of that process.

    Also be sure to check out FireBreath; it’s a far easier way to write plugins and has workarounds and bugfixes for a lot of different issues.

  47. Anonymous
    8 years ago

    Thanks. My plugin is ready now. Currently ( for testing ) I am manually embeding tag to invoke plugin’s method. How can I invoke it from an extension so that I can collect text of the displayed page say on click of a button ?

  48. Ahmad
    8 years ago

    I have a pluggin that works on every FF version other than 7.0.01 .. NPP_GetValue() is called twice and then NPP_Destroy() is called. Any idea why??NPP_Destroy() is called. Any idea why??

  49. Anonymous
    8 years ago

    thank you for this tutorial,

    NPAPI is what can help me to create an API to communicate a webkit browser
    with   application C / C + + external.

    thank you

One Trackback

  1. […] in Visual Studio Working on an NPAPI-browser plugin Building a firefox plugin – part one part 2 part 3 Writing an NPAPI plugin for Mac OS […]

Post a Comment

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