Introduction

Ce tutoriel permet de créer un composant XPCOM et de l'intégrer à un projet xul à l'aide de xulrunner 1.9 sous Debian sid. Ce document est basé sur le livre Creating XPCOM Components sur MDC (écrit il y a bien longtemps). J'ai essayé d'apporter quelques modifications aux pages du wiki pour corriger certaines erreurs.

J'aimerais remercié les auteurs du livre, bsmedberg sur #xulrunner, paul et gavin sur #xulfr d'irc.mozilla.org pour m'avoir aidé à créer ce composant.

Je commence tout d'abord en parlant de l'environnement de développement et de compilation, ensuite, l'interface idl sera créé ainsi que les fichiers .h et .xpt.

L'environnement de travail

Pour compiler et tester ce composant, j'utilise ces outils et ces bibliothèques:

  • xulrunner-1.9 (package debian xulrunner-1.9)
  • xulrunner-devel-1.9 (package debian xulrunner-dev)
  • build-essential (make, perl, ...), gcc 4.3.1, pkg-config 0.22
  • uuid-runtime

Voici l'emplacement des différents fichiers qui seront utilisés:

Outils et fichiers xulrunner (libxul)

  • /usr/lib/pkgconfig/libxul-unstable.pc : Liste des chemins d'accès au format pkg-config.
  • /usr/include/xulrunner-1.9/unstable et /usr/include/xulrunner-1.9/stable : fichiers d'entêtes C++ (.h)
  • /usr/local/lib/xulrunner-devel-1.9/lib : bibliothèque XPCOM et XUL
  • /usr/lib/xulrunner-1.9/xpidl : générateur de fichiers d'entête C++ (.h) et de typelib xpt
  • /usr/share/idl/xulrunner-1.9/unstable : interfaces idl de xulrunner
  • /usr/bin/xulrunner-1.9 : lanceur d'application XUL

XPCOM

  • ~/project/monxpcom/ : code source du composant XPCOM (monxpcom)
  • ~/project/monxpcom/Makefile : Script de compilation
  • ~/project/monxpcom/iMonxpcom.idl : Interface du composant
  • ~/project/monxpcom/iMonxpcom.h : Entête C++ généré à l'aide du fichier iMonxpcom.idl et de la commande xpidl -m header
  • ~/project/monxpcom/iMonxpcom.xpt : typelib généré à l'aide du fichier iMonxpcom.xpt et de la commande xpidl -m typelib
  • ~/project/monxpcom/monxpcom.cpp : Code source du composant
  • ~/project/monxpcom/monxpcom.so : Bibliothèque final du composant (à copier dans ~/project/monappxul/components/)

Application XUL

  • ~/project/monappxul/ : application xul (on peut le générer à l'aide de mon script générateur d'application xul)
  • ~/project/monappxul/components/ : emplacement final des fichiers .xpt et .so
  • ~/.vendor/appname/repertoire.default/compreg.dat : fichier d'enregistrements personnel des composants objets XPCOM (.so)
  • ~/.vendor/appname/repertoire.default/xpti.dat : fichier d'enregistrements personnel des interfaces de composants XPCOM (.xpt)

Fichier de compilation Makefile

Pour nous aider à développer et compiler le programme, rien de mieux qu'un fichier Makefile. 5 commandes seront utilisées: make, make header, make typelib, make install, make clean

  • make : compile le fichier .cpp et génère le fichier objet .so
  • make header : génère l'entête C++ .h avec l'interface idl
  • make typelib : génère le fichier .xpt avec l'interface idl
  • make install : copie les fichiers .so et .xpt dans le répertoire components/ de l'application xul
  • make clean : supprime le fichier objet .so
# user variables
XULAPP_DIR=~/project/monappxul
SOURCES=monxpcom.cpp
OUTPUT=monxpcom.so
INTERFACE_NAME=iMonxpcom

# programs
XPIDL=/usr/lib/xulrunner-1.9/xpidl
PKGCONFIG=/usr/bin/pkg-config
CPP=g++

# flags
CPPFLAGS+=-fno-rtti -fno-exceptions -shared -Wl,-z,defs
XPCOM_CFLAGS=`$(PGKCONFIG) --cflags libxul-unstable`
XPCOM_LIBS=`$(PGKCONFIG) --libs libxul-unstable`
XPCOM_IDL=-I `$(PGKCONFIG) --variable=idldir libxul-unstable`/unstable

.PHONY: build header typelib install clean

# main command (build is default)
build: $(OUTPUT)
header: $(INTERFACE_NAME).h
typelib: $(INTERFACE_NAME).xpt
install: $(OUTPUT) $(INTERFACE_NAME).xpt
	cp $(OUTPUT) $(XULAPP_DIR)/components/
	cp $(INTERFACE_NAME).xpt $(XULAPP_DIR)/components/

clean:
	-rm $(OUTPUT)
	-rm $(INTERFACE_NAME).xpt

# WARNING: LIBS should be after sources files ($^) else it will not compile!
$(OUTPUT): $(SOURCES)
	$(CPP) -o $@ $(XPCOM_CFLAGS) $(CPPFLAGS) $^ $(XPCOM_LIBS)

$(INTERFACE_NAME).h : $(INTERFACE_NAME).idl
	$(XPIDL) -m header $(XPCOM_IDL) -e $@ $^

$(INTERFACE_NAME).xpt : $(INTERFACE_NAME).idl
	$(XPIDL) -m typelib $(XPCOM_IDL) -e $@ $^

L'interface .idl

Après avoir un outil pour la compilation, il faudrait savoir ce que l'on veut compiler. Dans le livre dont s'inspire se tutoriel, la classe créée permet de filtrer les sites Web accéder avec une liste blanche (white-list). Elle utilise des évènements du navigateur, des évènements de xul, des énumérateurs (enumerator), des chaînes de caractères, des variables internes, etc.

De plus, la classe utilise des interfaces instables et des interfaces gelées (frozen) de l'API XPCOM.

La classe implémente une interface désignée pour l'utilisation avec javascript. Le nom choisi de l'interface est iMonxpcom et implémente l'interface nsISupports, la classe de base de toutes les interfaces dans le monde XPCOM. nsISupports est une interface définie dans la liste des fichiers idl de xulrunner.

Un composant XPCOM a besoin de ces propriétés:

  • Un identifiant unique pour l'interface du composant (un CID 128-bits ou encore un uuid). Cet identifiant peut être généré aléatoirement à l'aide du programme /usr/bin/uuidgen (package uuid-runtime sur Debian Sid). Un identifiant unique ressemble à ceci: 2ce26fc3-5117-4aaa-90a0-efb377e2dd49
  • Un nom d'interface (pour le projet: iMonxpcom)
  • Un autre identifiant unique pour le composants (un CID de 128 bits)
  • Un nom de classe (pour le projet: Monxpcom)
  • Une description (pour le projet: "Monxpcom est mon premier xpcom")
  • Un identifiant de contrat (contract id). Il s'agit d'une chaîne de caractère unique comme le CID mais beaucoup plus simple à retenir pour un être humain. Elle ressemble à une URL ou une adresse de courriel. Le format est le suivant: @nomdedomaine/repertoire/identifiant;option . (pour le projet: @progysm.com/xpcom/monxpcom).

Les fonctions et attributs définies par notre interfaces sont: lock(), unlock(), addSite(url), removeSite(url) et sites (un enumérateur/itérateur).

Voici le code du fichier iMonxpcom.idl. N'oublier pas de générer un nouveau uuid à l'aide de la commande /usr/bin/uuidgen

#include "nsISupports.idl"
interface nsISimpleEnumerator;
[scriptable, uuid(2ce26fc3-5117-4aaa-90a0-efb377e2dd49)]
interface iMonxpcom : nsISupports
{
  void lock();
  void unlock();

  void addSite(in string url);
  void removeSite(in string url);
  attribute nsISimpleEnumerator sites;
};

Explication:

ligne #1: on inclut la déclaration de l'interface parente nsISupports (voir le répertoire des idl de xulrunner)
ligne #2: on déclare l'utilisation d'une interface tierce propre à XPCOM: nsISimpleEnumerator
ligne #3: on indique qu'on peut utiliser l'interface en javascript (scriptable) et on indique le CID généré par uuidgen.
ligne #4: on déclare l'interface iMonxpcom héritant de l'interface parente nsISupports.
ligne #5 et plus: on définit les méthodes et attributs de la classe.
                  note: in = argument d'entrée, 
                        out = argument de sortie pouvant être modifié par la fonction

Après avoir écrit le code dans notre fichier iMonxpcom.idl, on peut générer le fichier .h et .xpt. La commande xpidl permettra de valider les erreurs de syntaxes. Après l'exécution des deux commandes, le fichier iMonxpcom.h (texte) et iMonxpcom.xpt (binaire) devrait être présent.

make header
make typelib

On pourrait installer directement le fichier iMonxpcom.xpt dans le répertoire components/ de l'application XUL et notre interface serait présente en javascript dans l'objet Components.interfaces.iMonxpcom.

Il est très instructif de regarder tout de suite le fichier d'entête C++ iMonxpcom.h.

  • La ligne #2 interface "nsISimpleEnumerator" a été modifié en class nsISimpleEnumerator;
  • Les macros IMONXPCOM_IID_STR et IMONXPCOM_IID définissent le CID de l'interface. La macro IMONXPCOM_IID coupe en 11 morceaux le CID. Le CID de la classe sera coupé de la même façon.
  • Si vous aviez bien utilisé xpidl de xulrunner-1.9, vous devriez avoir la ligne NS_DECLARE_STATIC_IID_ACCESSOR(IMONXPCOM_IID). Si vous avez NS_DEFINE_STATIC_IID_ACCESSOR(IMONXPCOM_IID), noter bien le DEFINE et non pas DECLARE, vous venez d'utiliser la commande xpidl de xulrunner-1.8. Vous devez regénérer le .h et le .xpt avec la nouvelle version de xpidl, sinon votre composant ne fonctionnera pas!
  • La macro NS_DECL_IMONXPCOM permet d'écrire les déclarations des fonctions d'iMonxpcom dans la classe Monxpcom.
  • À la fin du fichier .h, se trouve un gabarit pour implémenter la classe monxpcom.cpp. Utiliser le et copier-le dans le fichier monxpcom.cpp. Remplacer _MYCLASS_ par Monxpcom dans le fichier monxpcom.cpp. Avec l'éditeur vim, on utilise la commande :%s/_MYCLASS_/Monxpcom/g . Il ne faut pas copier le #ifdef 0 et le #endif.

Le fichier source monxpcom.cpp

Le générateur xpidl nous donne un grand coup de main pour créer la classe Monxpcom du fichier monxpcom.cpp. Je vous recommande de l'utiliser pour éviter les erreurs. Il faut à tout prix remplacer _MYCLASS_ par Monxpcom.

Les entêtes

Dans le gabarit, on retrouve le commentaire /* Header file */. Après ce commentaire, on doit déclarer toutes les classes et entêtes C++ (.h) utilisées. À ce stade du projet, les classes et interfaces suivantes sont utilisés: iMonxpcom, nsISupports, nsISimpleEnumerator. On rajoute donc les entêtes suivantes au début du fichier.

#include "iMonxpcom.h" // include nsISupports too
#include "nsISimpleEnumerator.h"

Avec ce simple ajout, on peut déjà compiler et lier notre classe à l'aide de la commande "make". Le fichier monxpcom.cpp devrait ressembler à mon fichier version 001. Sur mon ordinateur, le fichier monxpcom.so fait 9409 octets et on peut le tester à l'aide de la commande suivante: LD_LIBRARY_PATH=/usr/lib/xulrunner-1.9 ldd -r monxpcom.so. Une liste de bibliothèques devraient s'affichée. Il ne devrait pas y avoir d'éléments ou de symboles non trouvés (not found).

Il ne serait pas pertinent à ce moment-ci d'utiliser notre classe dans du code javascript (projet xulrunner) puisqu'elle n'a pas été enregistré dans la liste des composants XPCOM. On ne la retrouve donc pas dans Components.classes['@progysm.com/xpcom/monxpcom'] .

L'enregistrement de notre classe dans le registre XPCOM

La prochaine étape (version 002) est d'enregistrer notre composant XPCOM dans la liste des composants XPCOM de XUL. Deux fonctions doivent être implémentées pour dire à XUL que notre XPCOM existe. Une fonction Registration et Unregistration. De plus, des macros permettrons d'ajouter la déclaration rapide de notre classe. Pour enregistrer notre classe, nous avons besoin d'un module qui gère une usine (factory) et construit notre classe.

  • NS_GENERIC_FACTORY_CONSTRUCTOR(nomClasse) : permet de générer une usine "factory" pour construire notre classe.
  • static NS_METHOD nomClasseRegistration(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *registryLocation, const char *componentType, const nsModuleComponentInfo *info) : fonction d'enregistrement de la classe
  • static NS_METHOD nomClasseUnregistration(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *registryLocation, const nsModuleComponentInfo *info) : fonction pour enlever l'enregistrement de la classe
  • static const nsModuleComponentInfo components[] = { {} }; : variable qui stock des informations sur les classes de notre module.
  • NS_IMPL_NSGETMODULE(NomModule, components) : génère le module qui lance l'enregistrement et la construction de la classe.

Les entêtes C++ utilisées seront (après nos #include):

  • #include "nsIGenericFactory.h" // macros NS_GENERIC_FACTORY_CONSTRUCTOR, NS_IMPL_NSGETMODULE

Il faut générer le CID de la classe avec uuidgen puisqu'il est utilisé dans la macro NS_IMPL_NSGETMODULE. Le CID doit être divisé en 11 morceaux comme dans le fichier iMonxpcom.h. Les macros sont:

#define MONXPCOM_DESCRIPTION "Monxpcom est mon premier xpcom"
#define MONXPCOM_CID_STR "03b57938-57f6-4dcc-8bde-d69d8c4b572e"
#define MONXPCOM_CID \
  {0x03b57938, 0x57f6, 0x4dcc, \
    { 0x8b, 0xde, 0xd6, 0x9d, 0x8c, 0x4b, 0x57, 0x2e }}

#define MONXPCOM_CONTRACTID "@progysm.com/xpcom/monxpcom"

Voici la déclarations des macros et de la variable components pour notre classe à la fin du fichier monxpcom.cpp:

NS_GENERIC_FACTORY_CONSTRUCTOR(Monxpcom)

static NS_METHOD MonxpcomRegistration(nsIComponentManager *aCompMgr,~
                                      nsIFile *aPath,~
                                      const char *registryLocation,~
                                      const char *componentType,~
                                      const nsModuleComponentInfo *info)
{
    return NS_OK;
}
static NS_METHOD MonxpcomUnregistration(nsIComponentManager *aCompMgr,~
                                        nsIFile *aPath,~
                                        const char *registryLocation,~
                                        const nsModuleComponentInfo *info)
{
    return NS_OK;
}

static const nsModuleComponentInfo components[] =~
{
  {
    MONXPCOM_DESCRIPTION,
    MONXPCOM_CID,
    MONXPCOM_CONTRACTID,
    MonxpcomConstructor,
    MonxpcomRegistration,
    MonxpcomUnregistration
  }
};

NS_IMPL_NSGETMODULE(Monxpcom, components)

On peut remarquer que la définition de la fonction MonxpcomConstructor n'apparait pas dans notre fichier. Elle est créé par la macro NS_GENERIC_FACTORY_CONSTRUCTOR.

Tester avec xulrunner

Et voilà, on peut déjà tester notre classe et notre interface avec xulrunner. Il suffit de recompiler le fichier monxpcom.cpp (make), créer le répertoire Components dans notre application xul et de copier les fichiers avec make install.

Pour bien tester notre application xul, on peut insérer dans le fichier chrome/content/main.xul une alerte de débogage dans notre fenêtre principale.

<window id="main" title="monappxul" width="300" height="300" 
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
        onload="alert('class: ' + Components.classes['@progysm.com/xpcom/monxpcom'] + '\ninterface: ' + Components.interfaces.iMonxpcom);">
</window>

On peut lancer l'application avec la commande xulrunner application.ini -jsconsole .

Après le fermeture de l'application les fichiers du profil xpti.dat et compreg.dat contiendront des informations à propos de la classe et de l'interface.

Ajout des fonctionnalités

L'attribut mLocked

Les fonctions lock() et unlock() vont modifier une variable booléenne (à deux états). On doit spécifier un état de départ. La donnée de départ sera dans la fonction du constructeur Monxpcom(). Le type d'une variable booléenne avec Mozilla est PRBool. Vrai = PR_TRUE, Faux = PR_FALSE. Il y aura 4 ajouts.

  • Ajout de l'attribut mLocked dans la définition de la classe Monxpcom(). L'attribut sera privé et dans la section private.
  • Ajout de l'initialisation de la variable à "faux" dans le constructeur.
  • Ajout du code de la fonction lock(). Puisque la fonction retourne un code de résultat Mozilla, le retour deviendra NS_OK.
  • Ajout du code de la fonction unlock(). Puisque la fonction retourne un code de résultat Mozilla, le retour deviendra NS_OK.

Voici une vue compressée des changements:

class Monxpcom : public iMonxpcom
{
...
private:
  PRBool mLocked;
...
};

Monxpcom::Monxpcom()
{
  /* member initializers and constructor code */
  mLocked = PR_FALSE;
}

...
/* void lock (); */
NS_IMETHODIMP Monxpcom::Lock()
{
    mLocked = PR_TRUE;
    return NS_OK;
}

/* void unlock (); */
NS_IMETHODIMP Monxpcom::Unlock()
{
    mLocked = PR_FALSE;
    return NS_OK;
}

L'attribut mRootURLNode

Puisque la classe contient une liste blanche (white-list) d'URL, il faut garder cette liste dans un attribut de la classe. Une liste chaînée, à la C, sera utilisé dans notre cas. L'allocation et la libération de mémoire se fera avec malloc et free. On pourrait aussi utilisé un dictionnaire, une table de hachage ou encore new/delete.

Une liste chaînée simple est définie par un contenu (data) et un pointeur vers le noeud précédent ou vers le suivant. Ensuite, un pointeur racine nous permet de parcourir la liste à partir d'une adresse mémoire. Il faut donc définir avant la définition de la classe, une structure pour un noeud de la liste et ensuite un attribut de la classe Monxpcom pour le pointeur racine. On peut aussi ajouter une petite méthode privée pour effacer correctement cette liste. Les ajouts sont:

  • Ajout de l'entête C stdlib.h pour malloc/free.
  • Ajout de la structure urlNode pour chaque noeud de la liste.
  • Ajout de la définition du pointeur racine.
  • Ajout de la définition d'une fonction permettant d'effacer la liste.
  • Ajout de l'initialisation du pointeur racine.
  • Ajout du code pour effacer la liste (destructeur et nouvelle méthode).
  • Ajout du code pour ajouter un élément (AddSite).
  • Ajout du code pour effacer un élément (RemoveSite).
#include <stdlib.h> // for malloc, free

struct urlNode {
  char* urlString;
  urlNode* next;
};

class Monxpcom : public iMonxpcom
{
  private:
    ...
    urlNode* mRootURLNode;
    NS_IMETHOD clearSiteList();
};

Monxpcom::Monxpcom()
{
  /* member initializers and constructor code */
  mLocked = PR_FALSE;
  mRootURLNode = nsnull;
}

Monxpcom::~Monxpcom()
{
  /* destructor code */
  clearSiteList();
}


NS_IMETHODIMP Monxpcom::clearSiteList()
{
  urlNode* node = mRootURLNode;
  urlNode* next = nsnull;
  while(node)
  {
    free(node->urlString);
    next = node->next;
    free(node);
    node = next;
  }
  return NS_OK;
}

/* void addSite (in string url); */
NS_IMETHODIMP Monxpcom::AddSite(const char *url)
{
  urlNode* node = (urlNode*) malloc(sizeof(urlNode));
  node->urlString = strdup(url);
  node->next = mRootURLNode;
  mRootURLNode = node;
  return NS_OK;
}

/* void removeSite (in string url); */
NS_IMETHODIMP Monxpcom::RemoveSite(const char *url)
{
  urlNode* node = mRootURLNode;
  urlNode* prev = nsnull;

  while(node)
  {
    if (strcmp(node->urlString, url) == 0)
    {
      free(node->urlString);
      if (prev)
        prev->next = node->next;
      free(node);
      return NS_OK;
    }
    prev = node;
    node = node->next;
  }
  return NS_ERROR_FAILURE;
}

On peut voir le résultat de ces changements dans ma version 3.

Ajout de la liste sites avec nsIEnumerator

Jusqu'à maintenant, il n'y a pas eu beaucoup d'utilisation de classes fournies par Mozilla dans notre code. Pour implémenter setSites() et getSites() toutefois, on doit convertir des chaînes de caractères (char*) en chaînes accessibles à partir de Javascript et retourner des énumérateurs.

setSites() utilisera un objet nsIEnumerator pour ajouter à notre liste des nouveaux URL. Un objet nsIEnumerator contient une liste d'objet implémentant l'interface "nsISupports". Il faut donc convertir les nsISupports en nsISupportsCString (des Strings en javascript). Ensuite, on peut obtenir un objet nsEmbedCString pour enfin convertir le tout en char*. En résumé:

- nsIEnumerator contient plusieurs nsISupportsCString en tant que nsISupports
- nsISupportCString contient un objet nsCString (ou encore un nsEmbedCString).
- nsIEmbedCString peut convertir sa chaîne en char*.

Le code sera:

NS_IMETHODIMP Monxpcom::SetSites(nsISimpleEnumerator * aSites)
{
  PRBool more = PR_TRUE;
  while(more)
  {
    nsCOMPtr<nsISupports> supports;
    aSites->GetNext(getter_AddRefs(supports));

    nsCOMPtr<nsISupportsCString> supportsString = do_QueryInterface(supports);

    if (supportsString)
    {
      nsEmbedCString url;
      supportsString->GetData(url);
      AddSite(url.get());
    }

    aSites->HasMoreElements(&more);
  }
  return NS_OK;
}

Les déclarations nécessaires sont:

  • #include "nsISupportsPrimitives.h" // for nsISupportsCString
  • #include "nsEmbedString.h" // for nsEmbedCString
  • #include "nsCOMPtr.h" // for nsCOMPtr

Pour la fonction GetSites(), il suffit de retourner un objet qui implémente nsIEnumerator en utilisant notre pointeur racine comme point de départ. Puisque notre structure urlNode est propre à notre composant, il faut développer une classe urlNodeEnumerator qui hérite de nsIEnumerator. La nouvelle classe doit implémenter getNext et hasMoreElements. Le code de la fonction GetSites() crée un énumérateur et affecte la variable de retour à notre nouveau énumérateur. Il est important de noter que lorsqu'on envoit un élément vers du code externe (javascript), il faut absolument ajouter une référence pour que la mémoire soit libérée en temps voulu. On ne peut pas faire de "delete" puisque nous ne savons pas quand le code externe n'aura plus besoin de la variable. Voici le code de la fonction GetSites():

NS_IMETHODIMP Monxpcom::GetSites(nsISimpleEnumerator * *aSites)
{
  urlNodeEnumerator* enumerator = new urlNodeEnumerator(mRootURLNode);
  if (!enumerator)
    return NS_ERROR_OUT_OF_MEMORY;
  NS_ADDREF(*aSites = enumerator);

  return NS_OK;
}

Et voici le code pour notre classe urlNodeEnumerator (qui hérite de nsISimpleEnumerator, nsIEnumerator et nsISupports. Cette classe doit être définie avant la fonction GetSites() et après la déclaration d'urlNode. On peut par exemple placer le code de cette classe après la structure urlNode.

class urlNodeEnumerator : public nsISimpleEnumerator
{
  public:
    NS_DECL_ISUPPORTS
    NS_DECL_NSISIMPLEENUMERATOR

    urlNodeEnumerator(urlNode* node) {
      mNode = node;
    }
    virtual ~urlNodeEnumerator(void) {}

  protected:
    urlNode* mNode;
    nsCOMPtr<nsIComponentManager> mCompMgr;
};

NS_IMPL_ISUPPORTS1(urlNodeEnumerator, nsISimpleEnumerator);

NS_IMETHODIMP
urlNodeEnumerator::HasMoreElements(PRBool* aResult)
{
  if (!aResult)
    return NS_ERROR_NULL_POINTER;

  if (!mNode) {
    *aResult = PR_FALSE;
    return NS_OK;
  }
  *aResult = PR_TRUE;
  return NS_OK;
}

static NS_DEFINE_CID(kSupportsCStringCID, NS_SUPPORTS_CSTRING_CID);

NS_IMETHODIMP
urlNodeEnumerator::GetNext(nsISupports** aResult)
{
  if (!aResult)
    return NS_ERROR_NULL_POINTER;
  *aResult = nsnull;
  if (!mNode)
    return NS_ERROR_FAILURE;
  if (!mCompMgr)
  {
    NS_GetComponentManager(getter_AddRefs(mCompMgr));
    if(!mCompMgr)
      return NS_ERROR_UNEXPECTED;
  }

  nsISupportsCString* stringSupports;
  mCompMgr->CreateInstance(kSupportsCStringCID,
                           nsnull,
                           NS_GET_IID(nsISupportsCString),
                           (void**)&stringSupports);
  if(!stringSupports)
    return NS_ERROR_UNEXPECTED;

  nsEmbedCString str(mNode->urlString);
  stringSupports->SetData(str);

  *aResult = stringSupports; // addref'ed above
  mNode = mNode->next;
  return NS_OK;
}

Ce code a besoin de l'entête C++ supplémentaire suivante:

  • #include "nsIComponentManager.h" // for nsIComponentManager

Tous ces changements se retrouve dans la version 3.

Et encore plus...

Ensuite, on peut rajouter des triggers, les interfaces nsIObserver, nsIContentPolicy. Tout est expliquer sur developer.mozilla.org.