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