La théorie
Les différents composants et concepts en jeu
Avant d’entrer plus dans les détails dans la construction de notre extension, il est nécessaire de lister et de positionner les différents composants de Firefox à considérer. Cette synthèse nous permettra également de déterminer quels sont les concepts à connaître et les compétences techniques requises pour développer une extension.
Le moteur Gecko
Gecko est le moteur d’affichage de Firefox. Il implémente notamment les standards suivants : X/HTML, CSS, DOM, JavaScript, XML, XSL, XPath, RDF et SVG. Il est également responsable de l’affichage des pages XUL (XML-based User interface Language) qui servent à décrire l’IHM de Firefox et de ses extensions (le langage XUL est abordé en détail plus loin).Composants XPCOM
XPCOM (Cross-Platform Component Object Model) est le modèle de composants de Mozilla, similaire à CORBA ou Microsoft COM. Ce modèle permet de développer des composants dans différents langages (C++, C, JavaScript, Python et d’autres) et de les faire communiquer. La plupart des fonctionnalités du moteur Gecko sont visibles sous forme de composants XPCOM.La couche d’interface graphique
Langage XUL
XUL (XML User Interface Language), qui se prononce "zoul", est un langage de description d’interfaces graphiques, basé sur le standard XML. Il s’utilise conjointement avec CSS, RDF, DOM et JavaScript. Il permet de décrire et de positionner en XML des composants graphiques ou widgets (fenêtres, boutons, listes, menus, zones d’édition, etc.), de leur associer des évènements, des styles et des sources de données, ainsi que de les modifier via des scripts. Les développeurs habitués au DHTML n’auront pas de problème à s’adapter à XUL.Bindings XBL
Firefox supporte également le langage XBL (XML Bindings Language) qui permet de lier aux éléments d’un document XML, des comportements, des interfaces ou des modèles de contenu. Couplé avec XUL, il est utilisé pour étendre et personnaliser les widgets.Code JavaScript
Mozilla Firefox embarque le moteur SpiderMonkey capable d’interpréter et d’exécuter des instructions JavaScript. Bien qu’elles puissent elles-mêmes contenir des composants XPCOM, les extensions sont généralement codées en langage JavaScript. Elles utilisent la technologie XPConnect (Cross Platform Connect) pour manipuler les composants XPCOM de Firefox et accéder aux fonctionnalités du navigateur. Le code JavaScript fait donc le lien entre l’interface graphique décrite en XUL et les composants XPCOM de Gecko.Chrome et la sécurité
Les fichiers XUL, lorsqu’ils sont accédés via le protocole HTTP, sont soumis aux mêmes restrictions de sécurité que les pages HTML. De façon à lever ces restrictions, le moteur Gecko gère un référentiel dans lequel peuvent être enregistrés les fichiers XUL. Ces fichiers sont alors accessibles via un protocole spécifique et des URL du type chrome://<paquetage>/<partie>/<fichier.xul> (par exemple tapez chrome://browser/content/browser.xul dans la barre d’adresses de Firefox ;). Du point de vue du développement d’extensions, le terme de "chrome" regroupe à la fois le référentiel et le protocole d’accès aux fichiers XUL.Concept d’overlay
Firefox utilise la notion d’ "overlay" pour partager des éléments entre plusieurs pages XUL, mais aussi pour surcharger et étendre un fichier XUL sans pour autant modifier celui-ci. C’est cette méthode qui est utilisée pour insérer les extensions dans l’IHM de Firefox (menu Outils, menu contextuel, etc.) sans pour autant impacter le navigateur.Paquet XPI
Les extensions sont distribuées et installées sous forme de paquets XPI (Cross-Platform Install). Cela se prononce "zippy". Il s’agit d’une archive Zip regroupant tous les fichiers constituant l’extension.
chrome: répertoire contenant les fichiers propres à l’extension elle-même (le contenu de ce répertoire peut également être compressé dans une archive JAR) ;content: fichiers XUL et JavaScript associés ;locale: contient un répertoire par langue supportée par l’extension ;skin: regroupe les feuilles de style CSS ;
components: éventuels composants XPCOM ;defaults: fichiers liés au profil utilisateur ;preferences: fichiers de préférences, en JavaScript, exécutés au lancement de l’extension ;
chrome.manifest: fichier d’enregistrement de l’extension dans le référentiel chrome de Firefox ;install.rdf: fichier contenant les informations nécessaires au gestionnaire d’extensions de Firefox.
Résumé : compétences nécessaires
On a vu que Mozilla Firefox utilise de nombreuses technologies et plusieurs langages de programmation. De quoi avoir le vertige lorsque l’on désire réaliser une extension. Le tableau ci-dessous résume le niveau de connaissance qu’il est nécessaire d’avoir pour commencer à développer une extension.
La pratique
Nous allons illustrer la partie théorique précédente en créant une extension et en détaillant chaque étape du processus. L’extension que nous prévoyons d’implémenter est fonctionnellement très limitée, puisqu’elle se borne à afficher une boîte de dialogue affichant un texte d’information à l’utilisateur. L’objectif de cet exemple est donc, vous l’aurez bien compris, uniquement pédagogique. Dans cet exemple simple, nous nous limiterons à des pages XUL simples, associées à des scripts JavaScript et n’implémenterons ni de composants XBL, ni de composants XPCOM, ni de modèles de contenu RDF. L’arborescence constituant notre extension sera de la forme suivante :
IHM : fichier XUL
Dans le développement de notre première extension, nous allons procéder par itérations : cela nous permettra d’expliquer les différentes étapes et d’introduire les concepts au fur et à mesure. L’extension sera composée d’une unique page XUL contenant un texte et un bouton. L’appui sur le bouton déclenchera l’apparition d’une fenêtre popup. Commençons par décrire l’IHM de notre extension. Page XUL :<?xml version=”1.0” encoding=”UTF-8”?>
<?xml-stylesheet href=”chrome://global/skin/” type=”text/css”?>
<window id=”page” title=”Begin”
xmlns=”http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul”
width=”200px” height=”100px”>
<vbox>
<label value=”There is no data”/>
<button label=”...”/>
</vbox>
</window>
Examinons plus en détail le contenu de la page XUL :
- ligne
<?xml version="1.0"?>: désigne qu’il s’agit d’un fichier XML ; - ligne
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>: import d’une feuille de style par défaut ; - élément
<window/>: déclaration d’une fenêtre, en précisant son identifiant, son titre et ses dimensions ; - l’attribut
xmlns: déclaration de l’espace de nommage propre au langage XUL ; - élément
<vbox/>: boîte qui permet le placement vertical des éléments qu’elle contient ; - élément
<label/>: champ texte ; - élément
<button/>: bouton (auquel pour le moment aucune action n’a été attachée).

Code : JavaScript et XPCOM
Attachons une action à l’évènement "clic sur le bouton". Pour ce faire, nous utilisons l’attribut<button label="..." oncommand="show();"/>Il reste à coder la fonction
function show() {
var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
prompts.alert(null, "End", "There is only XUL");
}
Chaque composant XPCOM est enregistré dans le référentiel de Firefox avec une URI, celle du service prompt est @mozilla.org/embedcomp/prompt-service;1. Chaque composant implémente également une ou plusieurs interfaces.
On indique ici, via <?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="page" title="Begin"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
width="200px" height="100px">
<script type="application/x-javascript" src="monextension.js"></script>
<vbox>
<label value="There is no data"/>
<button label="..." oncommand="show();"/>
</vbox>
</window>
Un clic sur le bouton déclenchera l’affichage de la boîte de dialogue suivante :

Overlay
Nous allons maintenant insérer notre extension dans le menu Outils de Firefox. Pour ce faire, nous devons utiliser la notion d’overlay présentée dans la partie théorique de cet article. Il s’agit en fait d’une page XUL qui va étendre la page XUL principale de Firefox (chrome://browser/content/browser.xul). Il nous fait repérer le menu Outils dans la page browser.xul. Pour ce faire, il existe plusieurs possibilités :- ouvrir la page dans un éditeur de texte (la page est incluse dans l’archive
$FIREFOX_INSTALL_DIR/chrome/browser.jar) ; - ouvrir la page dans Firefox, puis l’analyser avec l’inspecteur DOM (activé lors de l’installation avancée de Firefox et lancé via le menu Outils/Inspecteur DOM) ;
- ou enfin ouvrir la page dans Firefox et l’analyser avec l’extension Firebug (http://www.getfirebug.com).

<?xml version=”1.0” encoding=”UTF-8”?>
<overlay id=”monoverlay”
xmlns=”http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul”>
<menupopup id=”menu_ToolsPopup”>
<menuitem id=”monextension_menu”
label=”Mon extension”
oncommand=”window.openDialog(‘chrome://monextension/content/monextension.xul’, ‘Mon extension’, ‘_blank’, ‘chrome,dialog=no’);”/>
</menupopup>
</overlay>
- Nous utilisons l’élément
<overlay/>prévu à cet effet. - Nous indiquons que l’on veut ajouter quelque chose dans le menu Outils, en reprenant l’élément que l’on a trouvé via le DOM inspector :
<menupopup id="menu_ToolsPopup"> - Dans cet élément, nous insérons un nouvel élément <menuitem/>.
- L’élément de menu
<menuitem/>est ajouté avec les attributs suivants : id: identifiant unique de l’élément de menu.label: titre de l’élément tel qu’il apparaîtra dans le menu.oncommand: action à déclencher lorsque l’élément de menu est sélectionné. Ici, le code JavaScript lance l’ouverture de la page XUL de notre extension (monextension.xul) dans une fenêtre séparée, avec le titre‘Mon extension’. Nous reviendrons plus loin sur l’URL passée en paramètre de la fonctionwindow.openDialog().

Packaging
Reste désormais à créer le paquet XPI en accompagnant nos deux pages XUL des fichiers suivants :chrome.manifest
Déclaration des composants de l’extension dans le référentiel chrome de Gecko :content monextension chrome/content/ overlay chrome://browser/content/browser.xul chrome://monextension/content/overlay.xul
- La directive
contentenregistre l’emplacement à utiliser lors de la résolution de l’URI chrome://monextension/content/. Nos deux fichiers XUL doivent donc se trouver dans le répertoirechrome/content/. Ainsi, l’URI chrome://monextension/content/monextension.xul, présente dans le fichier d’overlay plus haut, pointe désormais sur la page XUL de notre extension. - La directive
overlayenregistre notre fichier d’overlay comme devant s’appliquer à la page principale browser.xul de Firefox.
install.rdf
Indications d’installation à destination du gestionnaire d’extensions de Firefox : <?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>monextension@test.org</em:id>
<em:version>1.0</em:version>
<em:type>2</em:type>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>1.5</em:minVersion>
<em:maxVersion>2.0.0.*</em:maxVersion>
</Description>
</em:targetApplication>
<em:name>Mon extension</em:name>
<em:description>Ma première extension Firefox</em:description>
</Description>
</RDF>
- La balise
<em:id/>définit de manière unique l’identifiant de l’extension. Depuis Firefox 1.5, cet identifiant peut être de la forme nom_extension@organisation.tld. - La balise
<em:version/>définit la version de l’extension. - La balise
<em:type/>précise, via la valeur 2, qu’il s’agit d’une extension. - La balise
<em:targetApplication/>précise dans quelle application l’extension peut être installée :<em:id/>: identifiant unique de l’application concernée (ici Firefox avec l’identifiant{ec8030f7-c20a-464f-9b0e-13a3a9e97384}) ;<em:minVersion/>et<em:maxVersion/>: versions minimale et maximale de Firefox supportées par l’extension. Notez qu’il est recommandé que la version maximale soit celle d’une version existante, et non une version majeure future, pour éviter que l’utilisateur puisse l’installer sur des versions de Firefox pour lesquelles l’extension n’a pas été testée.
- La balise
<em:name/>définit le nom de l’extension telle qu’elle apparaîtra dans le gestionnaire d’extensions de Firefox. - La balise
<em:description/>précise une courte description de l’extension pour le gestionnaire d’extensions de Firefox.
Archive XPI
Nous avons donc la hiérarchie suivante :



Localisation
Améliorons maintenant notre extension en y ajoutant la gestion du multi-langage. La plate-forme Mozilla propose un mécanisme spécifique pour gérer ceci : il faut séparer les chaînes de caractères des pages XUL, les informations de localisation sont alors stockées dans l’arborescencecontent monextension chrome/content/ overlay chrome://browser/content/browser.xul chrome://monextension/content/overlay.xul locale monextension en-US chrome/locale/en-US/ locale monextension fr-FR chrome/locale/fr-FR/
Localisation de l’IHM
Les chaînes comprises dans le code XUL sont externalisées dans des fichiers DTD sous la forme<?xml version=”1.0” encoding=”UTF-8”?>
<?xml-stylesheet href=”chrome://global/skin/” type=”text/css”?>
<!DOCTYPE window SYSTEM “chrome://monextension/locale/monextension.dtd”>
<window id=”page” title=”&window.title;”
xmlns=”http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul”
width=”200px” height=”100px”>
<script type=”application/x-javascript” src=”monextension.js”></script>
<vbox>
<label value=”&label.value;”/>
<button label=”...” oncommand=”show();”/>
</vbox>
</window>
chrome/monextension.xul
Vous remarquerez que dans l’URI chrome de la DTD, on n’indique pas la langue. Quand il s’agit d’une URI chrome de type "locale", Gecko fait automatiquement le "mapping" vers le fichier de la langue activée dans Firefox par l’utilisateur.<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE overlay SYSTEM "chrome://monextension/locale/overlay.dtd">
<overlay id="monoverlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<menupopup id="menu_ToolsPopup">
<menuitem id="monextension_menu"
label="&menuitem.label;"
oncommand="window.openDialog(‘chrome://monextension/content/monextension.xul’, ‘&dialog.title;’, ‘_blank’, ‘chrome,dialog=no’);"/>
</menupopup>
</overlay>
chrome/overlay.xul
<!ENTITY window.title "Début"> <!ENTITY label.value “Il n’y a pas de Data”>
chrome/locale/fr-FR/monextension.dtd
<!ENTITY menuitem.label "Mon extension"> <!ENTITY dialog.title "Mon extension">
chrome/locale/fr-FR/overlay.dtd
<!ENTITY window.title "Begin"> <!ENTITY label.value "There is no Data">
chrome/locale/en-US/monextension.dtd
<!ENTITY menuitem.label “My extention”> <!ENTITY dialog.title “My extention”>
chrome/locale/en-US/overlay.dtd
Localisation des messages JavaScript
Les messages inclus dans les scripts sont eux aussi externalisés dans un fichier- insérer dans le fichier XUL concerné une ligne du type
<stringbundle id="properties" src="chrome://chemin/locale/fichier.properties/">; - dans le code JavaScript, récupérer l’élément
<stringbundle/> et utiliser sa méthodegetString()pour récupérer la valeur d’une chaîne :
var strbundle=document.getElementById("properties");
var value=strbundle.getString("key");
Dans notre cas, il nous faut donc créer un fichier title=Fin text=Il n’y a que XUL
chrome/locale/fr-FR/monextension.properties
title=End text=There is only XUL
chrome/locale/en-US/monextension.properties
Puis insérer un élément<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://monextension/locale/monextension.dtd">
<window id="page" title="&window.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
width="200px" height="100px">
<script type="application/x-javascript" src="monextension.js"></script>
<stringbundle id="properties" src="chrome://monextension/locale/monextension.properties"/>
<vbox>
<label value="&label.value;"/>
<button label="..." oncommand="show();"/>
</vbox>
</window>
Et enfin modifier le scriptfunction show() {
var strbundle = document.getElementById("properties");
var prompts = Components.classes[“@mozilla.org/embedcomp/prompt-service;1”]
.getService(Components.interfaces.nsIPromptService);
prompts.alert(null, strbundle.getString("title"), strbundle.getString("text"));
}
L’arborescence de notre extension est désormais la suivante :


Infrastructure de mise à jour
Nous terminerons cet article en illustrant une fonctionnalité très intéressante offerte par la plate-forme Mozilla : la gestion des mises à jour de notre extension. En effet, la manipulation manuelle réalisée plus haut dans le gestionnaire d’extensions de Firefox est fastidieuse : supprimer l’ancienne version, puis installer la nouvelle nécessite de redémarrer deux fois Firefox. Il est possible de préciser à Firefox, via le fichier<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>monextension@test.org</em:id>
<em:version>1.0</em:version>
<em:type>2</em:type>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>1.5</em:minVersion>
<em:maxVersion>2.*</em:maxVersion>
</Description>
</em:targetApplication>
<em:name>Mon extension</em:name>
<em:description>Ma première extension Firefox</em:description>
<em:updateURL>http://www.test.org/monextension/monextension-update.rdf</em:updateURL>
</Description>
</RDF>
install.rdf
Dans notre exemple, le manifeste de mises à jour sera stocké à l’adresse : http://www.test.org/monextension/monextension-update.rdf Ce fichier, en RDF, liste les différentes mises à jour disponibles : <?xml version="1.0"?>
<r:RDF xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/2004/em-rdf#">
<r:Description about="urn:mozilla:extension:monextension@test.org">
<updates>
<r:Seq>
<r:li>
<r:Description>
<version>1.1</version>
<targetApplication>
<r:Description>
<id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</id>
<minVersion>1.5</minVersion>
<maxVersion>2.0.0.</maxVersion>
<updateLink>http://www.test.org/monextension/monextension-1.1.xpi</updateLink>
</r:Description>
</targetApplication>
</r:Description>
</r:li>
</r:Seq>
</updates>
<version>1.1</version>
<updateLink>http://www.test.org/monextension/monextension-1.1.xpi</updateLink>
</r:Description>
</r:RDF>
On remarquera que deux espaces de noms différents sont utilisés (- La première balise
<r:Description/>précise l’extension concernée par la (ou les) mise(s) à jour. On retrouve donc dans l’attributaboutl’identifiant unique de notre extension :monextension@test.org. - Les balises
<r:Seq/>et<r:li/>permettent de déclarer une liste de mises à jour. Ici nous n’en déclarons qu’une seule. - La balise
<version/>précise le numéro de version de la mise à jour. - La balise
<targetApplication/>détaille la version de Firefox pour laquelle la mise à jour est destinée (on y retrouve donc les balises<id/>,<minVersion/>et<maxVersion/>déjà utilisées dans le fichier d’installationinstall.rdfde notre extension. - Enfin, la balise
<updateLink>contient le lien vers l’archive XPI de la nouvelle version de l’extension.
AddType text/xml rdfqui peut être intégrée à la configuration d’Apache ou stockée dans un fichier

- http://xulfr.org/xulplanet/xultu/ : le tutoriel XUL traduit en français sur l’excellent site http://xulfr.org.
- http://developer.mozilla.org/fr/docs/Extensions : la section française du site Mozilla Developper Center réservée au développement d’extensions.
- http://www.xulplanet.com/references/xpcomref/ : la référence des composants et des interfaces XPCOM de Mozilla.
- Un certain nombre d’outils sont disponibles pour faciliter le développement d’extensions. On retiendra par exemple :
- L’inspecteur DOM, à activer lors de l’installation avancée de Firefox, que nous avons vu plus haut.
- L’extension " Firebug " (http://www.getfirebug.com) qui propose de nombreuses fonctionnalités dont le débogage de code JavaScript.
- L’extension " CSView " (http://xulfr.org/outils/) qui permet de lister tous les composants et interfaces XPCOM disponibles.





Donnez votre avis
Vous devez avoir ouvert une session pour écrire un commentaire.