Scripting Plugins in Mozilla
October 30, 2001
This article applies to Mozilla versions 0.9.2 and higher and Netscape versions 6.1 and higher. It does not cover Netscape 6 and 6.01
Introduction
New in Mozilla code
New in plugin code
JavaScript code
Building and installing the plugin
What else to read
Examples
Plugins that used to take advantage of being scriptable via LiveConnect in 4.x Netscape browsers lost this possibility in the new world. The main reason for this is that there is no guarantee of Java compatibility on a binary level due to the jri/jni switch. The new Mozilla XPCOM architecture allows XPCOM components be scriptable via a different mechanism called XPConnect. We leverage some of these ideas to help you make your Netscape Communicator 4.x plugins exposed to JavaScript in Mozilla based browsers.
In order to make it still possible to script plugins, some changes have been made to the Mozilla code. The changes allow to make existing 4.x plugins scriptable with only minor modifications in their code. The present document describes the steps of what should be done to the plugin code to turn it scriptable again.
A couple of lines have been added to the DOM code asking a
plugin to return a scriptable iid and a pointer to a scriptable
instance object. The old Plugin API call NPP_GetValue
is used to retrieve this information from the plugin. So the
plugin project should be aware of two new additions to
NPPVariable
enumeration type which are now defined in
npapi.h as
NPPVpluginScriptableInstance = 10, NPPVpluginScriptableIID = 11
and two analogous additions to nsPluginInstanceVariable type in nsplugindefs.h as
nsPluginInstanceVariable_ScriptableInstance = 10, nsPluginInstanceVariable_ScriptableIID = 11
- A unique interface id should be obtained. Windows command
uuidgen
should be sufficient. - An Interface Definition (
.idl
) file describing the plugin scriptable interface should be added to the project (see example 1). - A Scriptable instance object should be implemented in the plugin. This class will contain native methods callable from JavaScript. This class should also inherit from nsIClassInfo and implement its methods to be able to request all necessary privileges from the Mozilla security manager (see example 2).
- Two new cases for the above mentioned new variables
should be added to the plugin implementation of
NPP_GetValue
(see example 3).
How to call plugin native methods
The following HTML code will do the job:
<embed type="application/plugin-mimetype"> <script language="javascript"> var embed = document.embeds[0]; embed.nativeMethod(); </script>
Having the built Mozilla tree is probably not necessary, but building the plugin with a scriptable instance interface will require Mozilla headers and the XPCOM compatible idl compiler -- xpidl.exe. MS DevStudio MIDL must not be used. (Let's assume 'TestPlugin' as a plugin name-place holder.)
- Compile nsITestPlugin.idl with the idl compiler. This will generate nsITestPlugin.h and nsITestPlugin.xpt files.
- Put nsITestPlugin.xpt to the Components folder.
- Build nptestplugin.dll with nsITestPlugin.h included for compiling scriptable instance class implementaion.
- Put nptestplugin.dll to the Plugins folder.
- The full sample plugin code can be found in the Mozilla source tree under modules/plugin/samples/4x-scriptable
- Some other plugins samples can be found in the Plugin SDK: modules/plugin/tools/sdk
- IBM Developer Works has published a good article on XPCOM.
Example 1. Sample .idl file
(this code is for illustration purposes, for full samples please
refer to Related sources section)
#include "nsISupports.idl" [scriptable, uuid(bedb0778-2ee0-11d5-9cf8-0060b0fbd8ac)] interface nsITestPlugin : nsISupports { void nativeMethod(); };
Example 2. Scriptable instance class
(this code is for illustration purposes, for full samples please refer
to Related sources section)
#include "nsITestPlugin.h" #include "nsIClassInfo.h" // We must implement nsIClassInfo because it signals the // Mozilla Security Manager to allow calls from JavaScript. // helper class to implement all necessary nsIClassInfo method stubs // and to set flags used by the security system class nsClassInfoMixin : public nsIClassInfo { // These flags are used by the DOM and security systems to signal that // JavaScript callers are allowed to call this object's scritable methods. NS_IMETHOD GetFlags(PRUint32 *aFlags) {*aFlags = nsIClassInfo::PLUGIN_OBJECT | nsIClassInfo::DOM_OBJECT; return NS_OK;} NS_IMETHOD GetImplementationLanguage(PRUint32 *aImplementationLanguage) {*aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS; return NS_OK;} // The rest of the methods can safely return error codes... NS_IMETHOD GetInterfaces(PRUint32 *count, nsIID * **array) {return NS_ERROR_NOT_IMPLEMENTED;} NS_IMETHOD GetHelperForLanguage(PRUint32 language, nsISupports **_retval) {return NS_ERROR_NOT_IMPLEMENTED;} NS_IMETHOD GetContractID(char * *aContractID) {return NS_ERROR_NOT_IMPLEMENTED;} NS_IMETHOD GetClassDescription(char * *aClassDescription) {return NS_ERROR_NOT_IMPLEMENTED;} NS_IMETHOD GetClassID(nsCID * *aClassID) {return NS_ERROR_NOT_IMPLEMENTED;} NS_IMETHOD GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) {return NS_ERROR_NOT_IMPLEMENTED;} }; class nsScriptablePeer : public nsITestPlugin, public nsClassInfoMixin { public: nsScriptablePeer(); ~nsScriptablePeer(); NS_DECL_ISUPPORTS NS_DECL_NSITESTPLUGIN }; nsScriptablePeer::nsScriptablePeer() { NS_INIT_ISUPPORTS(); } nsScriptablePeer::~nsScriptablePeer() { } // Notice that we expose our claim to implement nsIClassInfo. NS_IMPL_ISUPPORTS2(nsScriptablePeer, nsITestPlugin, nsIClassInfo) // the following method will be callable from JavaScript NS_IMETHODIMP nsScriptablePeer::NativeMethod() { return NS_OK; }
Example 3. NPP_GetValue implementation and
possible scenario of scriptable object life cycle
(this code is for illustration purposes, for full samples please
refer to Related sources section)
#include "nsITestPlugin.h" NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) { if(instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; // just prime instance->pdata with null for the purpose of this example // it will be assigned to the scriptable interface later to keep its // association with the specific plugin instance instance->pdata = NULL; return rv; } NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) { if(instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; NPError rv = NPERR_NO_ERROR; static nsIID scriptableIID = NS_ITESTPLUGIN_IID; if (variable == NPPVpluginScriptableInstance) { // nsITestPlugin interface object should be associated with the plugin // instance itself. For the purpose of this example to keep things simple // we just assign it to instance->pdata after we create it. nsITestPlugin *scriptablePeer = (nsITestPlugin *)instance->pdata; // see if this is the first time and we haven't created it yet if (!scriptablePeer) { scriptablePeer = new nsScriptablePeer(); if (scriptablePeer) NS_ADDREF(scriptablePeer); // addref for ourself, // don't forget to release on // shutdown to trigger its destruction } // add reference for the caller requesting the object NS_ADDREF(scriptablePeer); *(nsISupports **)value = scriptablePeer; } else if (variable == NPPVpluginScriptableIID) { nsIID* ptr = (nsIID *)NPN_MemAlloc(sizeof(nsIID)); *ptr = scriptableIID; *(nsIID **)value = ptr; } return rv; } NPError NPP_Destroy (NPP instance, NPSavedData** save) { if(instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; // release the scriptable object // Note that even though we release it here it does not necesserely // mean that its refcount will go to zero and it will be deleted. // XPCOM code in the browser may still hold more outstanding // references to this object and even execute calls to it at some // later time, when other plugin objects no longer exist. So in real // implementations some measures of precaution must be taken here to // prevent crash such as notifying the scriptable object that there // may be some dangling pointers and it is not safe to use them any more NS_IF_RELEASE(instance->pdata); }