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. Waleri
    9 years ago

    Has anyone tried to call a JS function in the HTML from the plugin on Opera?
    NPNetscapeFuncs::invoke() works *only* if I call it from a JS method in my plugin, when it is called from the browser.

    NPNetscapeFuncs::pluginthreadasynccall never worked for me on Opera either.

    Everything works fine on FireFox, Chrome and Safari (all on Windows)

  2. taxilian
    9 years ago

    What that tells me is that you are trying to call NPN_Invoke from the wrong thread; this will not work. The reason you can call it from a function call that results when JS calls something on your plugin is that at that point you end up on the main plugin thread.

    Usually, the HWND's winproc is on the correct thread; the way that I have handled this in the past is to emulate pluginthreadasynccall by posting a message to that window with a pointer to a structure in which I stored the function pointer and the opaque pointer, which was then dereferenced and called inside the winproc.

    (See earlier comments for a more complete discussion of this topic). Some browsers (Opera, aparently, and Firefox 2.x and earlier) do not implement gecko SDK 1.9 and as such don't have a pluginthreadasynccall.

  3. Waleri
    9 years ago

    Has anyone tried to call a JS function in the HTML from the plugin on Opera?
    NPNetscapeFuncs::invoke() works *only* if I call it from a JS method in my plugin, when it is called from the browser.

    NPNetscapeFuncs::pluginthreadasynccall never worked for me on Opera either.

    Everything works fine on FireFox, Chrome and Safari (all on Windows)

  4. Waleri
    9 years ago

    pluginthreadasynccall *is* defined in opera. Unfortunatley, this API is of type void so I do not know whether it si called successfully or not. Yes, I am aware, that I can't call the browser from any other thread, that's why I use pluginthreadasynccall in a first place. However, even if I implement my own queueing and I call invoke from the main thread, the result is false in this case and nothing happens. OK, I haven't tried posting a message to a window but I used SetTimer() to handle the message queue which also called my timerproc within the plugin thread (I checked the thread ID in a debugger, it is the correct one)

  5. Waleri
    9 years ago

    PS
    As I said, everthing works fine in FireFox, Chrome and Safari, all on windows. Only Opera causes the trouble…

  6. Waleri
    9 years ago

    PS
    As I said, everthing works fine in FireFox, Chrome and Safari, all on windows. Only Opera causes the trouble…

  7. taxilian
    9 years ago

    What that tells me is that you are trying to call NPN_Invoke from the wrong thread; this will not work. The reason you can call it from a function call that results when JS calls something on your plugin is that at that point you end up on the main plugin thread.

    Usually, the HWND's winproc is on the correct thread; the way that I have handled this in the past is to emulate pluginthreadasynccall by posting a message to that window with a pointer to a structure in which I stored the function pointer and the opaque pointer, which was then dereferenced and called inside the winproc.

    (See earlier comments for a more complete discussion of this topic). Some browsers (Opera, aparently, and Firefox 2.x and earlier) do not implement gecko SDK 1.9 and as such don't have a pluginthreadasynccall.

  8. taxilian
    9 years ago

    Did you check the thread ID when you are calling invoke from a javascript-invoked function to see which it is?

  9. Waleri
    9 years ago

    pluginthreadasynccall *is* defined in opera. Unfortunatley, this API is of type void so I do not know whether it si called successfully or not. Yes, I am aware, that I can't call the browser from any other thread, that's why I use pluginthreadasynccall in a first place. However, even if I implement my own queueing and I call invoke from the main thread, the result is false in this case and nothing happens. OK, I haven't tried posting a message to a window but I used SetTimer() to handle the message queue which also called my timerproc within the plugin thread (I checked the thread ID in a debugger, it is the correct one)

    PS
    As I said, everthing works fine in FireFox, Chrome and Safari, all on windows. Only Opera causes the trouble…

  10. taxilian
    9 years ago

    Did you check the thread ID when you are calling invoke from a javascript-invoked function to see which it is?

  11. Waleri
    9 years ago

    Yes, I did, it is the same – the main thread…
    In any case, pluginthreadasynccall should work (and it actually does) from any thread, but not on Opera :(.

  12. Waleri
    9 years ago

    Yes, I did, it is the same – the main thread…
    In any case, pluginthreadasynccall should work (and it actually does) from any thread, but not on Opera :(.

  13. Sangeeth K
    9 years ago

    Hey,

    judging by the comments .. it seems you have put up quite an impressive article…
    But frankly speaking it is too much for a beginner like me…
    Have you had a chance to go through this link for extension development .. http://people.mozilla.com/~myk/bootcamp/

    I'm sure many others like me are looking for a bootcamp kind of a session for plugin development. I also know that there are samples in mozilla code .. only thing is I'm not able to put the pieces together.
    Considering the fact that you quite a good experience and knowledge in this area .. can u put up a step by step sort of thing for a “Hello World” kind of plugin.
    Also it would be great if you could provide me some pointers on how to get booted.

    Thanks a lot!!!

  14. Georg
    9 years ago

    Thanks for the information on the work-around for PluginThreadAsyncCall – if you have time to put that into an article it would be greatly appreciated.

    On another matter: while my plugin loads & works mostly fine on firefox as well as opera on windows, it doesn't do anything at all in safari .
    Profiling with dependency walker shows that only NP_Initialize(), NP_GetEntryPoints() and NP_Shutdown are called from webkit.dll.
    Any thoughts on that?

  15. Sangeeth K
    9 years ago

    Hey,

    judging by the comments .. it seems you have put up quite an impressive article…
    But frankly speaking it is too much for a beginner like me…
    Have you had a chance to go through this link for extension development .. http://people.mozilla.com/~myk/bootcamp/

    I'm sure many others like me are looking for a bootcamp kind of a session for plugin development. I also know that there are samples in mozilla code .. only thing is I'm not able to put the pieces together.
    Considering the fact that you quite a good experience and knowledge in this area .. can u put up a step by step sort of thing for a “Hello World” kind of plugin.
    Also it would be great if you could provide me some pointers on how to get booted.

    Thanks a lot!!!

  16. Georg
    9 years ago

    Thanks for the information on the work-around for PluginThreadAsyncCall – if you have time to put that into an article it would be greatly appreciated.

    On another matter: while my plugin loads & works mostly fine on firefox as well as opera on windows, it doesn't do anything at all in safari .
    Profiling with dependency walker shows that only NP_Initialize(), NP_GetEntryPoints() and NP_Shutdown are called from webkit.dll.
    Any thoughts on that?

  17. taxilian
    9 years ago

    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

  18. taxilian
    9 years ago

    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

  19. Charles Dietrich
    9 years ago

    I suggest not writing NPAPI plugins from scratch. Check out Nixysa (http://code.google.com/p/nixysa/, Apache 2.0 license):

    “Nixysa is a framework written in Python to automatically generate glue code for NPAPI plugins (plugins for browsers such as Google Chrome or Firefox), letting you easily expose C++ classes to Javascript from a simple IDL representation.

    Nixysa was originally conceived for the needs of O3D, but is flexible enough to support a wide range of use cases. “

    Nota bene: Python is just used for C++ code generation; Nixysa is not a way to make Python plugins.

    Nixysa is a mature project, except for documentation.

    I'm using it for what is essentially a bluetooth terminal plugin.

  20. Waleri
    9 years ago

    I think the idea is to have both npapi/activex plugin framework

  21. taxilian
    9 years ago

    Rather than posting a reponse twice, I'll link to the response I posted on the other comments thread:

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

  22. Charles Dietrich
    9 years ago

    I suggest not writing NPAPI plugins from scratch. Check out Nixysa (http://code.google.com/p/nixysa/, Apache 2.0 license):

    “Nixysa is a framework written in Python to automatically generate glue code for NPAPI plugins (plugins for browsers such as Google Chrome or Firefox), letting you easily expose C++ classes to Javascript from a simple IDL representation.

    Nixysa was originally conceived for the needs of O3D, but is flexible enough to support a wide range of use cases. “

    Nota bene: Python is just used for C++ code generation; Nixysa is not a way to make Python plugins.

    Nixysa is a mature project, except for documentation.

    I'm using it for what is essentially a bluetooth terminal plugin.

  23. Waleri
    9 years ago

    I think the idea is to have both npapi/activex plugin framework

  24. taxilian
    9 years ago

    Rather than posting a reponse twice, I'll link to the response I posted on the other comments thread:

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

  25. anmorf
    9 years ago

    Hey taxilian thanks for your good tutorial :-), it helped me a lot.
    I have a problem with the alert popup window: is there a way to change the title of the alert popup window which is by default always [JavaScript Application]? I would like to change this title to my own title like in normal popup dialogs.

  26. anmorf
    9 years ago

    Hey taxilian thanks for your good tutorial :-), it helped me a lot.
    I have a problem with the alert popup window: is there a way to change the title of the alert popup window which is by default always [JavaScript Application]? I would like to change this title to my own title like in normal popup dialogs.

  27. chopper83
    9 years ago

    @Sangeeth K
    i used the npruntime example from the mozilla cvs
    mozillamodulespluginsamplesnpruntime
    and compiled it with help of https://developer.mozilla.org/en/Compiling_The_
    you need to download the geko sdk from https://developer.mozilla.org/En/Gecko_SDK but some of the *.h files, the ones you need are mentioned in part 2 of this blog, don't work and npupp are vanished totally (i think replaced and cleaned up in npfunctions.h)

    so i used the one in mozillamodulespluginbasepublic

    Now to my question ..
    i compiled this example, and it works partially. The calls to the html page from the plugin works NPN_Evaluate() test and also calls from js to attributes pluginobj.bar.

    But the function calls and set attribute don't work …
    Any idea what could be the mistake ?

    Also i searched for mozilla plugin chats on #addons #amo #extdev but they seemed to be the wrong address, do you know any chat forum or something else to ask questions?

  28. gf
    9 years ago

    Have you tried to confirm via logging if Invoke() is called as expected?

  29. chopper83
    9 years ago

    @Sangeeth K
    i used the npruntime example from the mozilla cvs
    mozillamodulespluginsamplesnpruntime
    and compiled it with help of https://developer.mozilla.org/en/Compiling_The_
    you need to download the geko sdk from https://developer.mozilla.org/En/Gecko_SDK but some of the *.h files, the ones you need are mentioned in part 2 of this blog, don't work and npupp are vanished totally (i think replaced and cleaned up in npfunctions.h)

    so i used the one in mozillamodulespluginbasepublic

    Now to my question ..
    i compiled this example, and it works partially. The calls to the html page from the plugin works NPN_Evaluate() test and also calls from js to attributes pluginobj.bar.

    But the function calls and set attribute don't work …
    Any idea what could be the mistake ?

    Also i searched for mozilla plugin chats on #addons #amo #extdev but they seemed to be the wrong address, do you know any chat forum or something else to ask questions?

  30. gf
    9 years ago

    Have you tried to confirm via logging if Invoke() is called as expected?

  31. chopper83
    9 years ago

    small update

    i compiled it on my windows 7 desktop machine too and there everything works finde.
    And if i compile it in release mode it also works on other machines.
    (i use full version of Visual Studio because in the express version a header file was missing)

  32. taxilian
    9 years ago

    Short answer: no.

    However, you could implement your own messagebox routine, but I would be really careful doing things like that in a plugin; remember that anything you do on the main thread that is blocking will block the browser from doing anything; this is generally considered bad practice. Nonetheless, you have a reference to an HWND, and you can do with it as you want.

  33. taxilian
    9 years ago

    Without more information, it's hard to say for sure; the one thing that I would submit, though, is that it's better not to use NPN_Evaluate when you don't have to; better to get the DOM Window with NPN_GetValue and call things with GetProperty and Invoke.

    I'd check your NPClass and make sure the function pointers are working. You could also get a debug copy of Mozilla building and then you could step into the NPN_ functions and see what is happening there. Chances are good you have a function pointer set up wrong or a pointer corrupted.

  34. chopper83
    9 years ago

    small update

    i compiled it on my windows 7 desktop machine too and there everything works finde.
    And if i compile it in release mode it also works on other machines.
    (i use full version of Visual Studio because in the express version a header file was missing)

  35. chopper83
    9 years ago

    small update

    i compiled it on my windows 7 desktop machine too and there everything works finde.
    And if i compile it in release mode it also works on other machines.
    (i use full version of Visual Studio because in the express version a header file was missing)

  36. taxilian
    9 years ago

    Short answer: no.

    However, you could implement your own messagebox routine, but I would be really careful doing things like that in a plugin; remember that anything you do on the main thread that is blocking will block the browser from doing anything; this is generally considered bad practice. Nonetheless, you have a reference to an HWND, and you can do with it as you want.

  37. taxilian
    9 years ago

    Short answer: no.

    However, you could implement your own messagebox routine, but I would be really careful doing things like that in a plugin; remember that anything you do on the main thread that is blocking will block the browser from doing anything; this is generally considered bad practice. Nonetheless, you have a reference to an HWND, and you can do with it as you want.

  38. taxilian
    9 years ago

    Without more information, it's hard to say for sure; the one thing that I would submit, though, is that it's better not to use NPN_Evaluate when you don't have to; better to get the DOM Window with NPN_GetValue and call things with GetProperty and Invoke.

    I'd check your NPClass and make sure the function pointers are working. You could also get a debug copy of Mozilla building and then you could step into the NPN_ functions and see what is happening there. Chances are good you have a function pointer set up wrong or a pointer corrupted.

  39. taxilian
    9 years ago

    Without more information, it's hard to say for sure; the one thing that I would submit, though, is that it's better not to use NPN_Evaluate when you don't have to; better to get the DOM Window with NPN_GetValue and call things with GetProperty and Invoke.

    I'd check your NPClass and make sure the function pointers are working. You could also get a debug copy of Mozilla building and then you could step into the NPN_ functions and see what is happening there. Chances are good you have a function pointer set up wrong or a pointer corrupted.

  40. Andy Idsinga
    9 years ago

    Hey – Excellent series on NPAPI/NPRUNTIME plugins! You really helped me through a bind late last night when I was trying to get my plugin to expose a simple string property to my javascript code. The browser kept hanging, my keyboard got a dent in it in the shape of my head etc etc ;)

    Here is one thing I still don't understand. In this snippet of code from my plugin, who is required to NPN_Free() the memory I NPN_MemAlloc() – the browser? or me at some future time?
    bool
    MyPlugin::GetProperty(NPIdentifier name, NPVariant *result)
    {
    snip snip…
    NPUTF8* retstr = (NPUTF8*)NPN_MemAlloc(m_myStrLen);
    memcpy(retstr,m_myStr,m_myStrLen);
    result->type = NPVariantType_String;
    result->value.stringValue.utf8characters = retstr;
    result->value.stringValue.utf8length = strlen(retstr);

  41. Andy Idsinga
    9 years ago

    Hey – Excellent series on NPAPI/NPRUNTIME plugins! You really helped me through a bind late last night when I was trying to get my plugin to expose a simple string property to my javascript code. The browser kept hanging, my keyboard got a dent in it in the shape of my head etc etc ;)

    Here is one thing I still don't understand. In this snippet of code from my plugin, who is required to NPN_Free() the memory I NPN_MemAlloc() – the browser? or me at some future time?
    bool
    MyPlugin::GetProperty(NPIdentifier name, NPVariant *result)
    {
    snip snip…
    NPUTF8* retstr = (NPUTF8*)NPN_MemAlloc(m_myStrLen);
    memcpy(retstr,m_myStr,m_myStrLen);
    result->type = NPVariantType_String;
    result->value.stringValue.utf8characters = retstr;
    result->value.stringValue.utf8length = strlen(retstr);

  42. taxilian
    9 years ago

    First of all, you should probably read through my post on memory management: http://colonelpanic.net/2009/12/memory-manageme….

    But, to answer your question, any memory you pass back to the browser as a result of a function call the browser will release. By the same token, any memory the browser passes back to you (such as strings returned from UTF8FromIdentifier) you must release yourself. Also if you get any properties or invoke any methods on browser objects you should call ReleaseVariantValue on the values it returns, and it will call that on values you return.

  43. taxilian
    9 years ago

    First of all, you should probably read through my post on memory management: http://colonelpanic.net/2009/12/memory-manageme….

    But, to answer your question, any memory you pass back to the browser as a result of a function call the browser will release. By the same token, any memory the browser passes back to you (such as strings returned from UTF8FromIdentifier) you must release yourself. Also if you get any properties or invoke any methods on browser objects you should call ReleaseVariantValue on the values it returns, and it will call that on values you return.

  44. Andy Idsinga
    9 years ago

    Hey thanks so much for the quick response!
    I goofed up a little an posted this question in the wrong place – I read that article last night – that was the one that got things working for me …but I needed to clarify what I through you were saying at the very end :)
    Cheers!

  45. Andy Idsinga
    9 years ago

    Hey thanks so much for the quick response!
    I goofed up a little an posted this question in the wrong place – I read that article last night – that was the one that got things working for me …but I needed to clarify what I through you were saying at the very end :)
    Cheers!

  46. Kishore
    9 years ago

    Hi taxilan,
    I have requirement to create multiple instances of an NPObject and use them in JS.
    let us say i have created an NPObject with NPN_CreateObject(0, functionpointer)
    Named it as MyObject
    I need to create multiple instances of MyObject.
    var obj1 = new MyObject();
    var obj2 = new MyObject();
    Could you please give sample implementation of NPConstructFunctionPtr construct function ?
    you mentioned in your post that new object needs to be created in construct function. For that i need example

  47. Kishore
    9 years ago

    Hi taxilan,
    I have requirement to create multiple instances of an NPObject and use them in JS.
    let us say i have created an NPObject with NPN_CreateObject(0, functionpointer)
    Named it as MyObject
    I need to create multiple instances of MyObject.
    var obj1 = new MyObject();
    var obj2 = new MyObject();
    Could you please give sample implementation of NPConstructFunctionPtr construct function ?
    you mentioned in your post that new object needs to be created in construct function. For that i need example

  48. taxilian
    9 years ago

    Creating multiple instances of an NPObject is no harder than creating a single instance. Check out the NPJavascriptObject class in FireBreath for an example: http://code.google.com/p/firebreath/source/brow

    Pay particularly close attention to the NewObject function at the top; it's a static function that creates the object and returns it. The NPClass instance (NPJavascriptObjectClass) is referenced is the NPN_CreateObject call.

    I also feel compelled to mention that you could just use FireBreath for your project and it would all a) just work, and b) work on IE as well =]

    Linux and Mac versions are close.

  49. taxilian
    9 years ago

    Creating multiple instances of an NPObject is no harder than creating a single instance. Check out the NPJavascriptObject class in FireBreath for an example: http://code.google.com/p/firebreath/source/brow

    Pay particularly close attention to the NewObject function at the top; it's a static function that creates the object and returns it. The NPClass instance (NPJavascriptObjectClass) is referenced is the NPN_CreateObject call.

    I also feel compelled to mention that you could just use FireBreath for your project and it would all a) just work, and b) work on IE as well =]

    Linux and Mac versions are close.

One Trackback

  1. By Building a firefox plugin « HeejinDev on September 8, 2009 at 6:43 pm

    […] Building a firefox plugin – part one Building a firefox plugin – part two Building a firefox plugin – part three […]

Post a Comment

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