Écrit par Neil Deakin.
Traduit par Maximilien (24/07/2004).
Page originale :
http://www.xulplanet.com/tutorials/xultu/xpcom.html
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.
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 ê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 est construit à partir d'une multitude de composants, 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.
On n'a pas à s'intéresser à 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 plateforme. Les versions Macintosh ou Windows pour gérer les fichiers seront très différentes. 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 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.
Typiquement, les composants XPCOM sont implémentés nativement, ce qui signifie qu'ils font des choses que
le langage Javascript ne peut 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,
tel qu'il est décrit par les interfaces qu'il implémente.
Par exemple, vous avez un composant à votre disposition, vous vérifiez alors s'il implémente l'interface
nsISound
, et si c'est la cas, vous pouvez jouer un son grâce lui.
Le processus d'appel de composant XPCOM à partir de script se nomme XPConnect : une couche qui traduit les objets du script en objets natifs.
L'appel de composant XPCOM se fait en trois étapes :
Une fois que l'on a effectué les deux premières étapes, on peut 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. 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. Cela fonctionne comme les plugins.
Mozilla fournit un composant "fichier" c'est à dire implémentant nsILocalFile
.
Ce composant est désigné par l'URI @mozilla.org/file/local;1.
Le schéma URI component:
permet de spécifier un composant. D'autres composants peuvent être
désignés de manière similaire.
L'URI du composant peut être utilisé pour récupérer 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
.
Components
dans l'exemple fait
référence à un objet fournissant des fonctions sur les composants.
Ici on récupère la classe d'un composant 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,
dans le cas contraire cela indique que le composant n'existe pas.
Pour l'instant, nous n'avons qu'une référence sur le composant "fichier". Pour appeler des 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 particulière du composant. Elle prend un seul paramètre : le nom de l'interface que
l'on veut. 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
fait référence à la partie du
composant qui implémente 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 l'on veut utiliser. On peut bien sûr choisir n'importe quel nom pour la variable. Par exemple si l'on veut utiliser l'interface pour le son, notre code peut ê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
.
Et 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);
Cela élimine le besoin de créer une instance et ensuite de l'interroger pour 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 de manière similaire à la méthode
QueryInterface
, rend l'objet aFile compatible avec l'interface nsILocalFile
.
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 suivant montre quelques propriétés et méthodes de l'interface nsILocalFile
.
initWithPath
leafName
fileSize
isDirectory()
nsILocalFile
représente un répertoire.remove(recursif)
recursif
est true, le répertoire et
tous ses fichiers et sous-répertoires sont effacés.
copyTo ( repertoire, nouveauNom )
repertoire
doit être un objet nsILocalFile
représentant
le répertoire où l'on veut copier le fichier .
moveTo ( repertoire, nouveauNom )
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 assigner le fichier à un objet nsILocalFile
.
Nous appelons la méthode initWithPath
pour indiquer le fichier
en question, en indiquant juste le chemin de celui-ci.
Puis on appelle 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 sur votre poste local et que vous voulez effacer.
Dans la liste du dessus, nous avons vu deux fonctions copyTo
et
moveTo
. Ces fonctions sont utilisées pour respectivement
copier et déplacer des fichiers.
Notez qu'elles ne prennent pas en paramètre une chaîne de caractères pour désigner un répertoire
mais un objet nsILocalFile
. Cela veut dire que l'on doit récupérer 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");
Il y a des composants spéciaux qu'on appelle services.
On ne peut 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
, on appelle 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
fournis 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.