Scripting Old Style Plugins in Mozilla
This article was written on August 31, 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
Introduction
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.
What's in the Mozilla code?
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
What's in the plugin code?
- 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>
How to build and install
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 generatensITestPlugin.h
andnsITestPlugin.xpt
files. - Put
nsITestPlugin.xpt
to the Components folder. - Build
nptestplugin.dll
withnsITestPlugin.h
included for compiling scriptable instance class implementaion. - Put
nptestplugin.dll
to the Plugins folder.
Related sources
- The full sample plugin code can be found in the Mozilla source tree under modules/plugin/samples/4x-scriptable
- IBM Developer Works has published a good article on XPCOM.
Examples
Example 1. Sample .idl
file
#include "nsISupports.idl" [scriptable, uuid(bedb0778-2ee0-11d5-9cf8-0060b0fbd8ac)] interface nsITestPlugin : nsISupports { void nativeMethod(); };
Example 2. Scriptable instance class
#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
#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) { nsITestPlugin *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 NS_IF_RELEASE(instance->pdata); }