À propos des extensions

Dans le monde de Gecko (Firefox, Thunderbird, Iceweasel, Icedove, Xulrunner, etc), une extension permet d'ajouter des fonctionnalités et de modifier une application XUL. Firefox 3.0 utilise la technologie XUL pour décrire son interface graphique. La technologie XUL est un ensemble de balise XML permettant de décrire une application. Une application se compose de fenêtres, de dialogue, de barres d'outils, de menu, de boutons, etc... Puisque Firefox est décrit en XUL, les extensions seront aussi en XUL.

Une extension pour une application XUL a accès à l'interface graphique et le comportement interne de l'application. L'extension peut changer les couleurs de chaque élément visuel, déplacer leur position, les enlever ou s'immiscer entre eux. L'extension a accès aux pages web consultés, au code interne de l'application et même au système de fichiers. Il faut donc avoir une grande confiance dans une extension avant de l'installer sur notre ordinateur.

Une extension XUL est distribuée dans un fichier .xpi (on prononce "zippi" et non "x - pé - i"). Un fichier .xpi est un fichier archive de type "zip" avec une extension "xpi" tout simplement. À l'intérieur du fichier .xpi se retrouve un document XML qui décrit l'extension et un fichier archive du code. Le fichier archive est un fichier .jar, une autre archive compressé à l'aide de l'algorithme "zip". Lorsqu'une application XUL (comme firefox) reçoit l'ordre d'installer un fichier .xpi, il extrait le contenu du fichier .xpi, lit la description de l'extension pour l'installer et copie dans un répertoire le code de l'extension, le fichier .jar. À la réouverture de l'application XUL, au redémarrage de Firefox, le logiciel lira le code de l'extension et l'ajoutera à son propre code.

Structure de base

Les différents type de fichier qu'on peut retrouver dans une extension sont:

  • Des fichiers de description et d'installation (chrome.manifest, install.rdf)
  • Des descriptions d'interface XUL (chrome/content/)
  • Du code javascript
  • Des feuilles de styles CSS pour modifier l'affichage de l'interface XUL (aussi appelé "thème" ou "skin")
  • Des fichiers de traduction des étiquettes textes (chrome/locale)
  • Des icônes et des images
  • Des composants Javascript et C++
  • Des plugins C/C++
  • Des préférences prédéfinies pour l'usager (about:config)

Pour une extensions très simple, nous n'avons pas besoin de tous ces types de fichiers. Nous allons donc créé une structure minimale pour une extension qui fera peu de chose : lister dans un dialogue la liste des URI des onglets ouverts.

1ere étape: trouver un nom au paquet à notre extension.

Le nom du paquet doit être un nom court, en minuscule de préférence, sans caractères spéciaux, sans espaces ou accent et simple à retenir. Dans ce tutoriel, j'utiliserai "listuri". Les fichiers seront stockés dans un répertoire "listuri". Ensuite, deux fichiers d'installation seront requis: install.rdf et chrome.manifest. Pour l'instant des fichiers vides suffiront. Ensuite, il faut un répertoire pour stocker le coeur de l'extension : l'interface graphique XUL. L'interface sera placée dans un répertoire content/chrome. Enfin, un fichier listuri.xul permettra d'ajouter un élément graphique à l'intérieur du navigateur.

2e étape: générer la structure de base de notre extension.

export extname=listuri
mkdir $extname
touch $extname/install.rdf $extname/chrome.manifest
mkdir -p $extname/chrome/content
touch $extname/chrome/content/$extname.xul

L'hiérarchie des fichiers correspond à cet arbre:

$extname/
|-- chrome.manifest
|-- chrome
|   `-- content
|       `-- $extname.xul
`-- install.rdf

Fichier d'installation

Il existe deux fichiers d'installations pour notre extension. Une description de l'extension (install.rdf) et une description de la structure des fichiers (chrome.manifest).

Le fichier install.rdf peut être généré à l'aide d'un générateur web. Il suffit d'inscrire chaque champ et de générer un identifiant unique pour notre extension. L'identifiant unique sera utilisé pour un stocker notre extension dans le répertoire personnel de l'utilisateur. Pour mon extension "listuri", mon fichier install.rdf ressemble à ceci:

3e étape: générer le fichier d'installation (install.rdf). Les champs que vous devriez remplir sont: Titre de l'extension, Nom du paquet, GUID de l'extension (note: le guid peut aussi être une adresse courriel), créateur. Ensuite cliquer sur "Générer install.rdf".

<?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>{28f38eed-0ac6-6ae4-b545-da3e4288b9c3}</em:id>
		<em:version>0.0.1</em:version>
		<em:type>2</em:type>

		<!-- Target Application this extension can install into, 
	     with minimum and maximum supported versions. -->
		<em:targetApplication>
			<Description>
				<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
				<em:minVersion>1.5</em:minVersion>
				<em:maxVersion>3.0.*</em:maxVersion>
			</Description>
		</em:targetApplication>


		<em:name>Liste URI</em:name>
		<em:description>Mon extension affiche la liste des URL ouverts</em:description>
		<em:creator>Yan Morin</em:creator>
		<em:homepageURL>http://yansanmo.no-ip.org/a/fxcreerext30</em:homepageURL>
	</Description>
</RDF>

Note: j'ai supprimé le bloc <em:file> puisqu'il n'est plus nécessaire dans la version 3.0. Le fichier chrome.manifest sera utilisé.

Puisque l'extension ne comporte pas de traduction ou de feuilles de style CSS, le fichier contiendra seulement deux lignes. La première ligne contient le nom du paquet de l'extension "listuri" et spécifie que les fichiers dans le répertoire chrome/content aura l'adresse interne "chrome://listuri/content/nomdufichier.ext". Ensuite, la deuxième ligne permettra de modifier le fichier du navigateur pour ajouter une option dans le menu.

4e étape: Générer le fichier chrome.manifest.

content     listuri    chrome/content/
overlay chrome://browser/content/browser.xul chrome://listuri/content/listuri.xul

Note: voici quelques URI chrome pour les applications XUL:

Firefox, fenêtre principale              : chrome://browser/content/browser.xul
Thunderbird, fenêtre principale          : chrome://messenger/content/messager.xul
Thunderbird, fenêtre d'éditionde message : chrome://messenger/content/messengercompose/messengercompose.xul

Si Firefox parlait en français lors de l'ouverture de ce fichier, il dirait:
Pour chaque fichier à l'adresse suivante: chrome://listuri/content/, je vais les rechercher dans le répertoire chrome/content/ de l'extension.
Je permets au fichier chrome://listuri/content/listuri.xul de surcharger (modifier) le fichier chrome://browser/content/browser.xul .

Interface graphique

Pour notre interface graphique, nous allons écrire du XUL pour ajouter un item dans le menu "Outils". Pour ce faire, il faut étudier le code qui affiche le menu "Outils". Il s'agit du code dans le fichier chrome://browser/content/browser.xul. Le code du menu "Outils" ressemble à ceci (à quelques détails près):

            <menu id="tools-menu" label="&toolsMenu.label;" accesskey="&toolsMenu.accesskey;">
              <menupopup id="menu_ToolsPopup">
              <menuitem label="&search.label;" accesskey="&search.accesskey;" 
                        key="key_search" command="Tools:Search"/>
              <menuseparator id="browserToolsSeparator"/>
              <menuitem id="menu_openDownloads" label="&downloads.label;"
                        accesskey="&downloads.accesskey;"
                        key="key_openDownloads" command="Tools:Downloads"/>
              <menuitem id="menu_openAddons" label="&addons.label;"
                        accesskey="&addons.accesskey;" command="Tools:Addons"/>
              <menuseparator id="devToolsSeparator"/>
              <menuitem id="javascriptConsole" 
                        label="&errorConsoleCmd.label;" accesskey="&errorConsoleCmd.accesskey;" 
                        key="key_errorConsole" oncommand="toJavaScriptConsole();"/>
              <menuitem accesskey="&pageInfoCmd.accesskey;" label="&pageInfoCmd.label;"   
                        key="key_viewInfo" command="View:PageInfo"/>
              <menuseparator id="sanitizeSeparator"/>
              <menuitem id="sanitizeItem"
                        accesskey="&clearPrivateDataCmd.accesskey;"
                        label="&clearPrivateDataCmd.label;"
                        key="key_sanitize" command="Tools:Sanitize"/>
              </menupopup>
            </menu>

Le plus important dans ce code est l'identifiant (attribut id) de l'élément menupopup. Nous allons utiliser l'id pour ajouter une nouvelle option. Il suffit de recréer une balise menupopup avec le même attribut "id" et d'y ajouter un item (menuitem). L'item a une étiquette, le texte affiché, et un évènement javascript lorsqu'on le sélectionne qui affiche un message. Ce code devrait être dans le fichier listuri/chrome/content/listuri.xul .

5e étape: Écrire le fichier chrome/content/listuri.xul.

<?xml version="1.0"?>
<overlay id="listuri" 
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <menupopup id="menu_ToolsPopup">
  <menuitem label="List URI" oncommand="alert('list uri')"/>
 </menupopup>
</overlay>

Tester immédiatement

Firefox nous permet de tester immédiatement ce code dans un navigateur. Il suffit de copier notre répertoire dans le répertoire "extension" de notre profil usager. On peut aussi créé un lien symbolique. Le répertoire du profil usager de Firefox a un nom qui change pour chaque extension. Dans mon cas précis, voici mon environnement de travail:

  • Le répertoire de mon extension est: /home/ymorin/project/listuri
  • Le répertoire des extensions de mon profil usager d'Iceweasel (firefox) est: /home/ymorin/.mozilla/firefox/7hn8o4ok.default/extensions/

Dans mon répertoire des extensions, je vais créé un lien symbolique vers mon répertoire d'extension (ln -s source destination). La destination est l'id de notre extension, inscrit dans le fichier install.rdf.

ln -s /home/ymorin/project/listuri '/home/ymorin/.mozilla/firefox/7hn8o4ok.default/extensions/{28f38eed-0ac6-6ae4-b545-da3e4288b9c3}'

Normalement, après un démarrage de l'application XUL, l'extension devrait être installé et l'option dans le menu outils devrait s'afficher.

Code javascript

Améliorons maintenant notre extension pour afficher la liste des urls en cours. Il faut tout d'abord étudier le fichier de Firefox pour connaître quel objet contient le navigateur par onglet. Il s'agit d'un élément tabbrowser.

      <tabbrowser id="content" disablehistory="true"
                  flex="1" contenttooltip="aHTMLTooltip"
                  contentcontextmenu="contentAreaContextMenu"
                  onnewtab="BrowserOpenTab();"
                  autocompletepopup="PopupAutoComplete"
                  ondragdrop="nsDragAndDrop.drop(event, contentAreaDNDObserver);"
                  onclick="return contentAreaClick(event, false);"/>

Selon la documentation qu'on peut retrouver sur developer.mozilla.org, un objet tabbrowser contient une propriétés "browsers" qui est une liste de noeuds "browser". Un élément browser contient une propriété "currentURI" de type nsIURI qui a sont tour, contient une propriété "spec" qui contient l'adresse complète.

Le code sera donc:

function listuri_getTabBrowserUri() {
  var tabbrowser = document.getElementById('content');
  var list = '';
  if (tabbrowser) {
    for(var i = 0; i < tabbrowser.browsers.length; i++) { 
       var uri = tabbrowser.browsers[i].currentURI.spec;
       list += uri + "\n"; 
    } 
  }
  return list;
}

Il ne reste qu'à modifié listuri.xul pour ajouter ce code javascript.

<?xml version="1.0"?>
<overlay id="listuri" 
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <script type="text/javascript"><![CDATA[
function listuri_getTabBrowserUri() {
  var tabbrowser = document.getElementById('content');
  var list = '';
  if (tabbrowser) {
    for(var i = 0; i < tabbrowser.browsers.length; i++) { 
       var uri = tabbrowser.browsers[i].currentURI.spec;
       list += uri + "\n"; 
    } 
  }
  return list;
}
 ]]></script>

 <menupopup id="menu_ToolsPopup">
  <menuitem label="List URI" oncommand="alert(listuri_getTabBrowserUri())"/>
 </menupopup>
</overlay>

Archiver et installer

Pour distribuer notre extension, il faut modifier notre chrome.manifest et compressé deux fois notre extension.

  1. Dans le répertoire listuri, il faut compressé le répertoire "chrome". La commande est zip -r listuri.jar chrome.
  2. Ensuite, il faut modifier le fichier chrome.manifest pour utiliser des répertoires à l'intérieur du fichier listuri.jar. Il faut seulement modifier les chemins relatifs.
    content listuri jar:listuri.jar!/chrome/content/
  3. Finalement, il faut compresser l'ensemble des fichiers dans le fichier listuri.xpi. Il ne faut pas compresser le répertoire du paquet, mais les fichiers à l'intérieur sans le répertoire chrome.
    zip listuri.xpi chrome.manifest install.rdf listuri.jar

Ensuite, on peut installer le .xpi avec un gliser-déposer (drag'n'drop) du fichier dans la fenêtre "Outils -> Modules complémentaires".