florian@instantbird.orgEn collaboration avec :
http://xulfr.org/
3 étapes :
Exemple simple (maowIExemple.idl) :
#include "nsISupports.idl"
[scriptable, uuid(95e5dd33-6ce1-4e03-8d55-62dc2839c9b3)]
interface maowIExemple: nsISupports {
readonly attribute AUTF8String message;
float divise(in long dividende,
in long diviseur);
};
On utilise l'outil xpidl, faisant partie du SDK de Mozilla.
2 utilisations du fichier .idl :
${SDK}/bin/xpidl -m typelib -w -I${SDK}/idl maowIExemple.idlmaowIExemple.xpt.${SDK}/bin/xpidl -m header -w -I${SDK}/idl maowIExemple.idlmaowIExemple.h.Résultat :
...
class NS_SCRIPTABLE maowIExemple : public nsISupports {
public:
...
/* readonly attribute AUTF8String message; */
NS_SCRIPTABLE NS_IMETHOD GetMessage(nsACString & aMessage) = 0;
/* float divise (in long dividende, in long diviseur); */
NS_SCRIPTABLE NS_IMETHOD Divise(PRInt32 dividende,
PRInt32 diviseur,
float *_retval) = 0;
};
...
Correspondance des types :
| IDL | C++ |
|---|---|
| void | void |
| boolean | PRBool |
| octet | PRUint8 |
| short | PRInt16 |
| long | PRInt32 |
| long long | PRInt64 |
| unsigned short | PRUint16 |
| unsigned long | PRUint32 |
| unsigned long long | PRUint64 |
| float | float |
| double | double |
Correspondance des types :
| IDL | C++ |
|---|---|
| char | char |
| wchar | PRUnichar |
| ACString | nsACString |
| AUTF8String | nsACString |
| AString | nsAString |
Et aussi... autres interfaces, constantes, types natifs, ... (voir documentation).
Utilisation du code généré à partir de l'interface :
/* Use the code below as a template for the
implementation class for this interface. */
/* Header file */
class _MYCLASS_ : public maowIExemple
{
public:
NS_DECL_ISUPPORTS
NS_DECL_MAOWIEXEMPLE
_MYCLASS_();
private:
~_MYCLASS_();
protected:
/* additional members */
};
/* Implementation file */
NS_IMPL_ISUPPORTS1(_MYCLASS_, maowIExemple)
_MYCLASS_::_MYCLASS_()
{
/* member initializers and constructor code */
}
_MYCLASS_::~_MYCLASS_() { /* destructor code */ }
...
/* float divise (in long dividende, in long diviseur); */
NS_IMETHODIMP _MYCLASS_::Divise(PRInt32 dividende,
PRInt32 diviseur,
float *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* End of implementation class template. */
maowExemple.h et maowExemple.cpp._MYCLASS_ par maowExemple.return NS_ERROR_NOT_IMPLEMENTED; doit être remplacé.
/* float divise (in long dividende, in long diviseur); */
NS_IMETHODIMP maowExemple::Divise(PRInt32 dividende,
PRInt32 diviseur,
float *_retval)
{
if (!diviseur)
return NS_ERROR_INVALID_ARG;
*_retval = (float)dividende / diviseur;
return NS_OK;
}
Observations :
nsresult comme type de retour._retval.
/* float divise (in long dividende, in long diviseur); */
NS_IMETHODIMP maowExemple::Divise(PRInt32 dividende,
PRInt32 diviseur,
float *_retval)
{
if (!diviseur)
return NS_ERROR_INVALID_ARG;
*_retval = (float)dividende / diviseur;
return NS_OK;
}
function divise(dividende, diviseur) {
if (!diviseur)
throw "Division par 0.";
return dividende / diviseur;
}
Il faut :
nsIModule.NSGetModule.Les deux se font à l'aide de macros, il suffit de définir quelques paramètres.
Dans maowExemple.h, on ajoute :
// f4b9733f-e851-431d-a1f7-fdcc305d0f4e
#define MAOW_EXEMPLE_CID \
{ 0xf4b9733f, 0xe851, 0x431d, \
{ 0xa1, 0xf7, 0xfd, 0xcc, 0x30, 0x5d, 0x0f, 0x4e } \
}
#define MAOW_EXEMPLE_CONTRACTID \
"@instantbird.org/maow;1"
On crée un fichier maowModule.cpp qui implémente le module :
#include "nsIGenericFactory.h"
#include "maowExemple.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(maowExemple)
static const nsModuleComponentInfo composants[] =
{
{
"MAOW Exemple", // Description
MAOW_EXEMPLE_CID, // Component id
MAOW_EXEMPLE_CONTRACTID, // Contract id
maowExempleConstructor // Constructeur
}
};
NS_IMPL_NSGETMODULE(maowModule, composants)
Nous avons donc maintenant les fichiers suivants :
maowIExemple.idl : l'interface (XPIDL)maowIExemple.h : l'interface C++ (Généré)maowIExemple.xpt : l'interface binaire (Généré)maowExemple.h : l'en-tête C++ de l'implémentationmaowExemple.cpp : implémentation C++maowModule.cpp : module (pour l'enregistrement XPCOM).On compile nos 2 fichiers .cpp pour former une bibliothèque dynamique que l'on place avec l'interface binaire dans le dossier components de l'application (ou extension).
Components permet au code JavaScript privilégié d'accéder à XPCOM.Components.interfaces.maowIExempleComponents.classes["@instantbird.org/maow;1"]
var resultat =
Components.classes["@instantbird.org/maow;1"]
.createInstance(Components.interfaces.maowIExemple)
.divise(5, 3);
alert(resultat);
Plusieurs possibilités pour utiliser une autre bibliothèque :
Si le composant XPCOM binaire dépend d'autres bibliothèques (non liées statiquement), il faut qu'elles soient chargées en même temps que le composant XPCOM lui-même.
Toutes les dépendances du composant sont placées dans le même dossier que l'exécutable de l'application.
Ça marche tout seul !
Les bibliothèques sont dans un autre dossier, il va falloir les charger manuellement...
Exemple complet sur developer.mozilla.org
La méthode :
componentscomponents, on place un 'faux composant' (stub) qui s'occupe du chargement.Le stub implémente uniquement la fonction NSGetModule. Son rôle est de :
En partant du dossier où se trouve le stub, puis en accédant au dossier parent, et à un sous dossier libraries/ par exemple.
nsCOMPtr<nsIFile> library;
... (FIXME)
library->SetNativeLeafName("libmaowlib.so");
PRLibrary *lib;
rv = library->Load(&lib);
NSGetModule :
nsGetModuleProc getmoduleproc = (nsGetModuleProc) PR_FindFunctionSymbol(lib, NS_GET_MODULE_SYMBOL);
return getmoduleproc(aCompMgr, aLocation, aResult);
L'enregistrement XPCOM du composant a échoué, comment trouver le problème ?
NSPR_LOG_MODULES=nsNativeModuleLoader:5 ./firefox
strace :
./firefox -d strace -g 2>&1 |fgrep .so
Plus de détails...
Une partie du contenu de ces slides est directement inspiré des articles Creating Custom Firefox Extensions with the Mozilla Build System de Matthew Gertner et Using Dependent Libraries In Extension Components de Benjamin Smedberg.