7.5 Interfaces XPCOM

Écrit par Neil Deakin , mise à jour par les contributeurs à MDC .
Traduit par Maximilien (24/07/2004), mise à jour par Alain B. (04/04/2007) .
Page originale : http://developer.mozilla.org/en/docs/XUL_Tutorial/XPCOM_Interfaces

Attention : Ce tutoriel est ancien et n'est pas mis à jour. Bien que beaucoup d'informations soient encore valables pour les dernières versions de gecko, beaucoup sont aussi obsolètes. Il est préférable d'aller consulter cette page sur la version française de ce tutoriel sur developer.mozilla.org.

Dans cette section, nous allons faire une brève présentation de XPCOM ("Modèle de composants objets multi plate-forme"), qui est le système d'objets utilisé par Mozilla.

Appel des objets natifs

En utilisant XUL, nous pouvons construire des interfaces utilisateurs complexes. En y joignant des scripts, on peut modifier l'interface et réaliser des actions. Cependant, il y a un certain nombre de choses qui ne peuvent pas être réalisées directement en javascript. Par exemple, si nous voulons créer une application gérant des courriels, nous avons besoin d'écrire des scripts permettant de se connecter au serveur de courriels, afin de les retirer ou d'en envoyer. Le langage Javascript ne permet pas de faire ce genre de choses.

Le seul moyen pour le faire est d'écrire du code natif implémentant ces fonctionnalités avancées. Nous avons aussi besoin d'un moyen pour pouvoir appeler ce code natif aisément à partir de nos scripts. Mozilla fournit une telle possibilité en utilisant XPCOM.

Mozilla fournit déjà plusieurs composants et interfaces XPCOM. Donc, dans la plupart des cas, il sera inutile d'écrire votre propre code natif. Après avoir lu cette section, vous pourrez rechercher des interfaces en utilisant la référence XPCOM de XULPlanet.

À propos d'XPCOM

Mozilla est construit à partir d'une multitude de composants, où chacun d'eux réalise une tâche précise. Par exemple, il y a un composant pour chaque menu, bouton et élément. Ces composants sont construits à partir de plusieurs définitions appelées interfaces.

Une interface dans Mozilla est une définition d'un ensemble de fonctions que peuvent implémenter des composants. Les composants sont ce qui permet au code de Mozilla de réaliser des traitements. Chaque composant implémente les fonctions conforme à une interface. Un composant peut implémenter plusieurs interfaces. Et plusieurs composants peuvent implémenter la même interface.

Prenons l'exemple d'un composant de fichier. Une interface sera créée décrivant les propriétés et les fonctions que l'on veut pouvoir appliquer sur un fichier. Les propriétés seront le nom du fichier, sa date de dernière modification ou sa taille. Les fonctions permettront d'effacer, de déplacer ou de copier le fichier.

L'interface "Fichier" décrit uniquement les caractéristiques du fichier, elle ne les implémente pas. L'implémentation est laissé au composant. Celui-ci contiendra le code qui permettra de récupérer le nom du fichier, sa date, sa taille. Il contiendra également le code pour le copier ou le renommer.

Nous n'avons pas à s'intéresser sur la manière dont l'implémentation est faite par le composant, du moment qu'il respecte l'interface correctement. Bien sûr, nous aurons une implémentation différente pour chaque plate-forme. Entre les versions Macintosh et Windows, les composants de fichier seront très différents. Cependant ils implémentent la même interface et par conséquent on peut accéder au composant en utilisant les fonctions de cette interface.

Dans Mozilla, les interfaces sont préfixées par nsI ou mozI ainsi elles sont facilement reconnaissables. Par exemple, nsIAddressBook est l'interface qui interagit avec le carnet d'adresses, nsISound est celle utilisée pour écouter des fichiers et nsILocalFile pour manipuler des fichiers. Pour plus de détails, consultez les interfaces de Mozilla.

Typiquement, les composants XPCOM sont implémentés nativement, ce qui signifie qu'ils font des choses que le langage Javascript ne peut pas réaliser. Par contre, on peut les appeler à partir de scripts. C'est ce que l'on va voir maintenant. Nous pouvons appeler n'importe laquelle des fonctions fournies par le composant telles que décrites par les interfaces qu'il implémente. Par exemple, si vous avez un composant à votre disposition, vous vérifiez alors s'il implémente l'interface nsISound, et si c'est le cas, vous pouvez jouer un son grâce lui.

Le processus d'appel de composants XPCOM à partir d'un script se nomme XPConnect : une couche qui traduit les objets du script en objets natifs.

Création d'objets XPCOM

L'appel d'un composant XPCOM se fait en trois étapes :

  1. Récupérer un composant.
  2. Récupérer la partie du composant qui implémente l'interface que l'on veut utiliser.
  3. Appeler la fonction que l'on a besoin.

Une fois que les deux premières étapes sont réalisées, nous pouvons effectuer la dernière autant de fois que nécessaire. Prenons le cas du renommage d'un fichier. La première étape est de récupérer le composant "fichier". Puis on interroge ledit composant pour récupérer la portion qui implémente l'interface nsILocalFile. Enfin, on appelle les fonctions fournies par l'interface. Cette interface est utilisée pour représenter un unique fichier.

Nous avons vu que les noms d'interfaces commencent toujours par nsI ou mozI. Par contre, la désignation des composants utilise la syntaxe URI. Mozilla stocke une liste de tous les composants disponibles dans son propre registre. Un utilisateur peut installer de nouveaux composants si besoin est. Ce mécanisme fonctionne comme les plugins.

Mozilla fournit un composant "fichier" c'est-à-dire implémentant nsILocalFile. Ce composant est désigné par la chaîne @mozilla.org/file/local;1. Cette chaîne est appelée un contractID. La syntaxe d'un contractID est :

@<internetdomain>/module[/submodule[...]];<version>[?<name>=<value>[&<name>=<value>[...]]]

D'autres composants peuvent être appelés de la même manière.

Le contractID du composant sert à obtenir le composant. Voici en Javascript le code correspondant :

var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();

Le composant "fichier" est récupéré et stocké dans la variable aFile. Dans l'exemple, Components fait référence à un objet global fournissant les fonctions relatives à certains composants. Ici la classe d'un composant est récupérée en utilisant la propriété classes. Cette propriété est un tableau de tous les composants disponibles. Pour obtenir un composant différent, il suffit de remplacer l'URI par celui du composant voulu. Finalement, une instance est créée avec la fonction createInstance().

Vous devez vérifier que la valeur de retour de createInstance() est différente de null, valeur qui indiquerait que le composant n'existe pas.

Pour l'instant, nous avons seulement une référence sur le composant "fichier". Pour appeler ses fonctions, nous avons besoin de récupérer une de ces interfaces, dans notre cas nsILocalFile. Une seconde ligne est ajoutée à notre code comme suit :

 var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();
 if (aFile)   aFile.QueryInterface(Components.interfaces.nsILocalFile);

La fonction QueryInterface() est fournie par tous les composants, elle permet d'obtenir une interface précise du composant. Elle prend un seul paramètre, le nom de l'interface souhaitée. La propriété interfaces de Components contient une liste de toutes les interfaces des composants. Ici on utilise l'interface nsILocalFile que l'on passe en paramètre à QueryInterface(). Ainsi aFile fera référence à la partie du composant qui implémente l'interface nsILocalFile.

Ces deux lignes de Javascript peuvent être utilisées pour obtenir n'importe quelle interface de n'importe quel composant. Il suffit de remplacer le nom du composant et le nom de l'interface que vous voulez utiliser. Vous pouvez bien sûr choisir n'importe quel nom pour la variable. Par exemple si vous voulez utiliser l'interface pour le son, notre code pourrait être comme suit :

var sound = Components.classes["@mozilla.org/sound;1"].createInstance();
if (sound) sound.QueryInterface(Components.interfaces.nsILocalFile);

Les interfaces XPCOM peuvent hériter d'autres interfaces. L'interface héritière possède ses propres fonctions mais aussi toutes celles des interfaces parentes. Ainsi toute interface hérite de l'interface principale nsISupports qui fournit la fonction QueryInterface(). Comme tout composant doit implémenter nsISupports, la fonction QueryInterface() est disponible sur tous les composants.

Plusieurs composants peuvent implémenter la même interface. Typiquement ce sont des sous-classes de l'original mais pas nécessairement. N'importe quel composant peut implémenter les fonctionnalités de nsILocalFile. De plus, un composant peut implémenter plusieurs interfaces. C'est pour ces raisons que l'on doit procéder en deux étapes pour appeler les fonctions d'une interface.

Cependant, il existe un raccourci pour réduire ces deux étapes en une seule ligne de code :

 var aLocalFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);

Ce code effectue la même action qu'avec les deux lignes, mais en une seule ligne. Il élimine le besoin de créer une instance et ensuite de l'interroger pour obtenir une interface précise, en deux étapes séparées.

Un appel à QueryInterface() sur un objet qui ne fournit pas l'interface demandée lance une exception. Si vous n'êtes pas sûr que le composant supporte une interface, vous pouvez utiliser l'opérateur instanceof comme suit :

var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();
if (aFile instanceof Components.interfaces.nsILocalFile){
  // faire quelque chose si il s'agit d'une instance du bon type
}

L'opérateur instanceof renvoie true si aFile implémente l'interface nsILocalFile, et il effectue également l'appel de la méthode QueryInterface, ce qui fournit par la suite un objet nsILocalFile aFile valide.

Appel des fonctions de l'interface

Maintenant que nous avons un objet qui fait référence à un composant avec l'interface nsILocalFile, nous pouvons appeler les fonctions de celle-ci à travers l'objet. La liste suivante montre quelques propriétés et méthodes de l'interface nsILocalFile.

initWithPath
Cette méthode est utilisée pour initialiser le chemin et le nom du fichier pour l'interface nsILocalFile. Le premier paramètre doit être le chemin du fichier, comme par exemple /usr/local/mozilla.
leafName
Le nom du fichier sans son chemin complet.
fileSize
La taille du fichier.
isDirectory()
Renvoie true si nsILocalFile représente un répertoire.
remove(recursif)
Efface un fichier. Si le paramètre recursif est true, le répertoire et tous ses fichiers et sous-répertoires sont effacés.
copyTo ( repertoire, nouveauNom )
Copie un fichier dans un autre répertoire, et optionnellement renomme le fichier. La variable repertoire doit être un objet nsILocalFile représentant le répertoire où l'on veut copier le fichier.
moveTo ( repertoire, nouveauNom )
Déplace le fichier dans un autre répertoire ou le renomme. La variable repertoire doit être un objet nsILocalFile représentant le répertoire où l'on va mettre le fichier.

Pour effacer un fichier, on doit d'abord l'assigner à un objet nsILocalFile. Nous appelons la méthode initWithPath() pour définir le fichier en question, en indiquant juste le chemin de celui-ci. Puis nous appelons la fonction remove() avec le paramètre recursif à false. Voici le code correspondant :

var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();
if (aFile instanceof Components.interfaces.nsILocalFile){
  aFile.initWithPath("/mozilla/testfile.txt");
  aFile.remove(false);
}

Ce code prend le fichier /mozilla/testfile.txt et l'efface. Essayez cet exemple en ajoutant le code à un gestionnaire d'évènements. Vous devez changer le nom du fichier pour qu'il corresponde à un fichier existant que vous voulez effacer sur votre poste local.

Dans la liste ci-dessus, nous avons vu deux fonctions copyTo() et moveTo(). Ces fonctions sont utilisées pour respectivement copier et déplacer des fichiers.

Notez que ces fonctions ne prennent pas en paramètre une chaîne de caractères pour désigner un répertoire mais un objet nsILocalFile. Cela signifie que nous devons récupérer les deux composants "fichier".

L'exemple suivant montre comment copier un fichier :

function copyFile(sourcefile,destdir)
{
  // récupérer un composant pour le fichier à copier
  var aFile = Components.classes["@mozilla.org/file/local;1"]
    .createInstance(Components.interfaces.nsILocalFile);
  if (!aFile) return false;

  // récupérer un composant pour le répertoire où la copie va s'effectuer.
  var aDir = Components.classes["@mozilla.org/file/local;1"]
    .createInstance(Components.interfaces.nsILocalFile);
  if (!aDir) return false;

  // ensuite, on initialise les chemins
  aFile.initWithPath(sourcefile);
  aDir.initWithPath(destdir);

  // Au final, on copie le fichier sans le renommer
  aFile.copyTo(aDir,null);
}

copyFile("/mozilla/testfile.txt","/etc");

Les services XPCOM

Certains composants XPCOM spéciaux sont appelés services. Vous ne pouvez pas créer plusieurs instances d'un service parce qu'il doit être unique. Les services fournissent des fonctions manipulant des données globales ou effectuent des opérations sur d'autres objets. Au lieu d'utiliser createInstance(), vous devez appeler getService() pour récupérer une référence sur le composant de type "service". À part ça, les services ne diffèrent pas des autres composants.

Un exemple de service fournit par Mozilla est le service pour les marque-pages. Il vous permet d'ajouter un marque-page à la liste courante des marque-pages de l'utilisateur. Voici un exemple :

var bmarks = Components.classes["@mozilla.org/browser/bookmarks-service;1"].getService();
bmarks.QueryInterface(Components.interfaces.nsIBookmarksService);
bmarks.addBookmarkImmediately("http://www.mozilla.org","Mozilla",0,null);

Tout d'abord, le composant @mozilla.org/browser/bookmarks-service;1 est récupéré et son service est placé dans la variable bmarks. Nous utilisons QueryInterface() pour récupérer l'interface nsIBookmarksService. La fonction addBookmarkImmediately() fournie par cette interface peut être utilisée pour ajouter des marque-pages. Les deux premiers paramètres de cette fonction sont l'URL et le titre du marque-page. Le troisième paramètre est le type de marque-page qui doit normalement être 0, et le dernier paramètre est l'encodage des caractères du document correspondant au marque-page, qui peut être nul.


Dans la section suivante, nous verrons quelques-unes des interfaces que l'on peut utiliser, fournies par Mozilla.


Mozilla® est une marque déposée de la fondation Mozilla.
Mozilla.org™, Firefox™, Thunderbird™, Mozilla Suite™ et XUL™ sont des marques de la fondation Mozilla.