Introduction

Permettre à un utilisateur de contrôler un plugin utilisant NPAPI avec Javascript apporte beaucoup de changements. Nous allons remodeler notre Makefile pour compiler plusieurs fichiers sources (.CPP), ajouter une classe proxy entre notre plugin et le navigateur et gérer la conversion C++ vers Javascript et Javascript vers C++.

Modification au fichier Makefile

Le fichier Makefile doit bâtir la bibliothèque monplugin.so et les fichiers intermédiaires. L'introduction d'une nouvelle classe (NPNavigatorProxy) en est la principale cause. Les changements sont apportés au cible à $(pluginname).so. $(pluginname).so sera la liaison de $(pluginname).o et de NPNavigator.o. Lors de la compilation des fichiers intermédiaires (.o), l'option -c (compilation seulement) sera envoyée.

PKGCONFIG=/usr/bin/pkg-config
CPP=g++
pluginname=monplugin
npnproxy=NPNavigatorProxy

# directory
USERPLUGINDIR=~/.mozilla/plugins

CPPFLAGS+=-fno-rtti -fno-exceptions
LINKERFLAGS=-Wl,-z,defs -shared
MOZILLA_PLUGIN_CFLAGS=`$(PKGCONFIG) --cflags mozilla-plugin`
MOZILLA_PLUGIN_LIBS=`$(PKGCONFIG) --libs mozilla-plugin`
GTK_CFLAGS=`$(PKGCONFIG) --cflags gtk+-2.0`
GTK_LIBS=`$(PKGCONFIG) --libs gtk+-2.0`

.PHONY: build clean

build: $(pluginname).so

clean:
  -rm $(pluginname).so $(pluginname).o $(npnproxy).o

install:
  cp $(pluginname).so $(USERPLUGINDIR)/

$(npnproxy).o: $(npnproxy).cpp
  $(CPP) -c -o $@ $(MOZILLA_PLUGIN_CFLAGS) $(CPPFLAGS) $^

$(pluginname).o: $(pluginname).cpp
  $(CPP) -c -o $@ $(MOZILLA_PLUGIN_CFLAGS) $(GTK_CFLAGS) $(CPPFLAGS) $^

$(pluginname).so: $(pluginname).o $(npnproxy).o
  $(CPP) -o $@ $(LINKERFLAGS) $^ $(MOZILLA_PLUGIN_LIBS) $(GTK_LIBS)

Classe NPNavigatorProxy

La classe NPNavigatorProxy est initialisée dans la fonction NP_Initialize et joue le rôle d'intermédiaire entre le plugin et le navigateur. On pourrait utiliser les fonction NPN_... définit dans le fichier npapi.h mais NPNavigatorProxy fournit une interface à la C++.

Voici la déclaration de la classe NPNavigatorProxy

/**
 * This class allow instance of plugin to communicate with the navigator
 * it should be initiated with a init(NPNetscapeFuncs* aNPNFuncs)
 */
class NPNavigatorProxy {
  public:
    NPNavigatorProxy();
    virtual ~NPNavigatorProxy();

    /** initialize internal structure */
    void init(NPNetscapeFuncs *aNPNFuncs);

    // external implementation

    // get/post url
    NPError getURLNotify(NPP instance, const char *url, const char *target, void* notifyData);
    NPError getURL(NPP instance, const char *url, const char *target);
    NPError postURLNotify(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file, void* notifyData);
    NPError postURL(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file);

    // stream
    NPError requestRead(NPStream* stream, NPByteRange* rangeList);
    NPError newStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream);
    int32_t writeToStream(NPP instance, NPStream *stream, int32_t len, void *buffer);
    NPError destroyStream(NPP instance, NPStream* stream, NPError reason);

    // memory
    void* allocMem(uint32 size);
    void freeMem(void* ptr);
    uint32_t flushMem(uint32_t size);

    // action
    void reloadPlugins(NPBool reloadPages);
    void setStatus(NPP instance, const char *message);

    // information
    void getVersion(int* plugin_major, int* plugin_minor, int* netscape_major, int* netscape_minor);
    const char* getUserAgent(NPP instance);
#ifdef OJI
    JRIEnv* getJavaEnv(void);
    jref getJavaPeer(NPP instance);
#endif
    NPError getValue(NPP instance, NPNVariable variable, void *value);
    NPError setValue(NPP instance, NPPVariable variable, void *value);

    // draw
    void invalidateRect(NPP instance, NPRect *invalidRect);
    void invalidateRegion(NPP instance, NPRegion invalidRegion);
    void forceRedraw(NPP instance);

    // NPVariant
    NPIdentifier getStringIdentifier(const NPUTF8 *name);
    void getStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers);
    NPIdentifier getStringIdentifier(int32_t intid);
    bool identifierIsString(NPIdentifier identifier);
    NPUTF8 *getUTF8FromIdentifier(NPIdentifier identifier);
    int32_t getIntFromIdentifier(NPIdentifier identifier);
    void releaseVariantValue(NPVariant *variant);

    // NPObject
    NPObject *createObject(NPP npp, NPClass *aClass);
    NPObject *retainObject(NPObject *obj);
    void releaseObject(NPObject *obj);
    bool invoke(NPP npp, NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);
    bool invokeDefault(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
    bool evaluate(NPP npp, NPObject* obj, NPString *script, NPVariant *result);
    bool getProperty(NPP npp, NPObject* obj, NPIdentifier propertyName, NPVariant *result);
    bool setProperty(NPP npp, NPObject* obj, NPIdentifier propertyName, const NPVariant *value);
    bool removeProperty(NPP npp, NPObject* obj, NPIdentifier propertyName);
    bool enumerate(NPP npp, NPObject *obj, NPIdentifier **identifier, uint32_t *count);
    bool construct(NPP npp, NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
    bool hasProperty(NPP npp, NPObject* obj, NPIdentifier propertyName);
    bool hasMethod(NPP npp, NPObject* obj, NPIdentifier methodName);
    void setException(NPObject* obj, const NPUTF8 *message);

  private:
    /** 0/1 that specify if init() was called */
    bool mInit;
    /** NPNNetscapeFuncs structure */
    NPNetscapeFuncs NPNFuncs;
};

Le code source se retrouve à l'adresse: /test/monplugin/NPNavigator.cpp et /test/monplugin/NPNavigator.h

Initialisation de NPNavigatorProxy

La classe NPNavigatorProxy est déclarée globalement et est initialisée dans la fonction NP_Initialize avec les pointeurs des fonctions du navigateur.

#include "NPNavigatoryProxy.h"

// ...

NPNavigatorProxy gNavigatoryProxy;

// ...

NPError
NP_Initialize(NPNetscapeFuncs* aNPNFuncs,
              NPPluginFuncs* aNPPFuncs)
{
  // ...
  gNavigatorProxy.init(aNPNFuncs);
  // ...
}

Communication Javascript vers le plugin

Lorsque le code Javascript veut obtenir une fonction ou une propriété du plugin, il le demande via la fonction NP_GetValue() une instance NPP du plugin, une variable NPPVpluginScriptableNPObject et un paramètre de retour NPObject *. Le plugin doit donc construire et retourner un NPObject *, nsnull ou une erreur. Voici le code à ajouter dans la fonction NP_GetValue().

NPError
NP_GetValue(void *instance,
            NPPVariable aVariable,
            void *aValue)
{
  // ...
    case NPPVpluginScriptableNPObject:
      if (instance != NULL) {
         MonPlugin *monplugin = reinterpret_cast<MonPlugin *> (((NPP )instance)->pdata);
         if (monplugin) {
           err = monplugin->getScriptableNPObject(aValue);
         } else {
           err = NPERR_INVALID_PLUGIN_ERROR;
         }   
      }   
    break;
  // ...
}

Ce code appelle la méthode getScriptableNPObject avec le pointeur void * aValue. Nous devons donc définir cette méthode dans la classe MonPlugin. La méthode devra construire un objet NPObject.

class MonPlugin {
  public:
  //...
  NPError getScriptableNPObject(void *aValue);
  //...
};

Enrichir l'interface Javascript de la balise object ou embed

NPObject et NPClass