You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



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

  1. Introduction
  2. New in Mozilla code
  3. New in plugin code
  4. JavaScript code
  5. Building and installing the plugin
  6. What else to read
  7. 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?

  1. A unique interface id should be obtained. Windows command uuidgen should be sufficient.
  2. An Interface Definition (.idl) file describing the plugin scriptable interface should be added to the project (see example 1).
  3. 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).
  4. 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.)

  1. Compile nsITestPlugin.idl with the idl compiler. This will generate nsITestPlugin.h and nsITestPlugin.xpt files.
  2. Put nsITestPlugin.xpt to the Components folder.
  3. Build nptestplugin.dll with nsITestPlugin.h included for compiling scriptable instance class implementaion.
  4. Put nptestplugin.dll to the Plugins folder.

Related sources

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);
}