Développer des modules pour B2G OS

Les modules sont un concept bien connu dans le monde des navigateurs web, et ce concept a été ajouté à Firefox OS. Un module Firefox OS unique peut étendre les fonctionalités d'une ou plusieurs applications, voire toutes les applications, en incluant les applications système. Cet article est un guide pour la création de votre propre module Firefox OS, avec des conseils, des astuces, et des informations à prendre en compte.

Note: Les modules Firefox OS utilisent le système d'extensions WebExtensionns, qui est largement basé sur les extensions de Chrome/Blink, donnant de nombreux avantages sur la façon dont les modules sont créés en terme d'interopérabilité et de fonctionalités. Pour en savoir plus, gardez un œil sur notre documentation grandissante sur WebExtensions.

Important : Les modules ne sont disponibles que sur Firefox OS 2.5+, et vous aurez besoin d'un nouveau build installé sur votre téléphone pour être sûr que vous avez un support pour le débogage d'extensions dans WebIDE, etc. Soyez sûr d'avoir mis à jour avec le dernier build (2015-27-08 ou plus) sur votre téléphone développeur avant de commencer à développer des modules Firefox OS.

Développer des modules

Les modules sont des paquets composés de Javascipt, CSS, et d'autres assets. Cependant, ils ne sont pas autonomes. À la place, le manifeste du module inclut des fonctionnalités spéciales qui définissent les applications auxquelles appliquer le module. Quand les applications sont lancées sur un appareil Firefox OS qui a un module installé, le module est injecté dans l'application qui concorde avec le modèle spécifié dans le champ du fichier manifest.json.

Les modules Firefox OS utilisent les mêmes syntaxe et structure dans leur code que la nouvelle école de modules Firefox utilisant l'API WebExtensions, qui est basée sur le modèle d'extensions de Chrome.

Un exemple simple

Pour comprendre les bases des modules Firefox OS, nous allons présenter un simple exemple qui ajoute une bannière a l'application système, sur laquelle on peut cliquer pour la cacher.

firefox os screenshot showing add-on banner

C'est vraiment basique et trivial, mais cela vous donnera ce qu'il faut pour commencer. Vous pouvez regarder le code de l'exemple sur Github, et installer le module sur votre appareil Firefox OS en clonant le dépôt localement, puis en utilisant WebIDE (voir la section Testing your add-on using WebIDE.) Vous pouvez distribuer le module en utilisant le Marketplace Firefox.

Sachez qu'un module Firefox OS peut faire bien plus que ce qui est listé ici. La documentation de WebExtensions comportera plus d'informations au fil du temps.

L'anatomie d'un module Firefox OS

Dans cette partie, nous allons parcourir le contenu du dépôt du module d'exemple, en expliquant chaque élément. La structure des dossiers ressemble à ça :

  • simple-addon/
    • manifest.json
    • update.webapp
    • css/
      • style.css
    • js/
      • index.js
    • icons/
      • 128.png
    • extension.zip

manifest.json

Remarquez qu'il y a deux fichiers de manifeste dans notre dossier d'exemple de module. Le premier, manifest.json, suit la structure de manifeste Chrome, et est placé dans l'archive extensions.zip avec le CSS, le Javascript et l'icône compris dans le module. Il peut contenir une large palette d'instructions (voir Chrome Manifest File Format), mais pour le moment nous allons nous concentrer sur une petite partie :

{
  "manifest_version": 1,
  "name": "Add-on banner",
  "description": "Firefox OS add-on example",
  "version": "1.0",
  "author": "Chris Mills",
  "content_scripts": [{
    "matches": ["app://system.gaiamobile.org/index.html"],
    "css": ["css/style.css"],
    "js": ["js/index.js"]
  }],
  "icons": {
    "128": "/icons/128.png"
  }
}

La plupart de ces champs sont assez explicites, mais nous allons voir les derniers.

Tout d'abord, le champ content_scripts désigne le code qui sera injecté dans les applications sur lesquelles le module est appliqué, en fournissant le chemin des fichiers CSS et Javascript dans les champs css et js. Le champ matches contient un modèle qui spécifie dans quelles applications le code sera injecté. Ce modèle peut prendre différentes formes (voir Chrome Match Patterns), mais pour l'instant nous spécifions simplement app://system.gaiamobile.org/index.html, ce qui affecte uniquement l'application système. Vous pourriez l'appliquer à toutes les applications en utilisant app://*/*.

Note : Vous pouvez désigner plusieurs scripts et feuilles de styles en incluant simplement plusieurs éléments dans les tableaux, par exemple "css": ["css/style.css", "css/more.css"].

Note : Firefox OS ne supporte pas le mot-clé de Chrome <all_urls>.

En bas du manifeste nous avons inclus le champ icons, voir la section suivante pour plus d'infos à ce sujet.

update.webapp

Note : Vous n'avez pas besoin du manifeste .webapp si vous voulez soumettre un module au Marketplace Firefox, simplement du fichier .zip

Le manifeste update.webapp est un manifeste de style Firefox OS, ce qui est sensiblement équivalent à un mini manifeste d'application empaquetée (voir Auto-publication d'applications empaquetées.)

Notre update.webapp ressemblera à ça :

{
  "name" : "Add-on banner",
  "description": "Firefox OS add-on example",
  "developer": { "name": "Chris Mills" },
  "package_path": "extension.zip",
  "icons": {
    "128": "/icons/128.png"
  }
}

Encore une fois, ceci est assez explicite.

Le champ le plus important ici est probablement package_path, qui référence l'archive qui contient l'extension.

Vous remarquerez que le champ icons est inclus ici de la même façon que dans manifest.json, update.webapp est le seul endroit où vous avez besoin des informations d'icônes pour le moment, mais nous vous recommendons de les inclure dans les deux au cas où cela deviendrait nécessaire. Le champ icons dirige vers l'icône du module pour être utilisé dans l'application Paramètres de Gaia.

Inclure une icône

Vous devez inclure au moins une icône et la référencer dans votre manifeste, autrement le manifeste ne sera pas validé. Voir la section de référence des icônes de Manifeste pour plus d'informations. 

CSS

Important : A cause du bug 1179536, injecter des feuilles de styles ailleurs que dans l'application système est impossible, cela fonctionnera tout de même pour ce tutoriel car le module n'affecte que l'application système, mais pour ajouter des règles de style à une autre application ou page web vous aurez besoin de modifier le style en utilisant JavaScript.

Il n'y a rien de spécial à propos du CSS dans cet exemple. La seule chose à garder à l'esprit est de faire attention aux noms de classe et sélecteurs de votre module pour ne pas entrer en conflit avec le CSS de/des application(s).

Par exemple, nous avons enveloppé notre bannière d'exemple dans une <div> avec la classe fxos-banner. Mais vous pourriez tout aussi bien utiliser votre propre code pour votre nom de classe.

JavaScript

Encore une fois, le ficher JavaScript qui fait fonctionner le module ne contient aucune fonctionnalité spéciale (voir le code source JavaScript sur Github). Il est injecté dans l'applicaction avec le CSS spécifié dans le fichier manifest.json.

Note : Le code du module est injecté à chaque fois qu'une application qui est référencée par le modèle du manifest.json est lancé. Il est aussi injecté quand les modules sont activés. Quand le module est injecté par le lancement de l'application, tous les fichiers du module sont injectés dans l'application avant que quoi que ce soit dans l'app ne soit initialisé, en incluant le DOM. C'est au développeur du module de gérer les différents cas de lancement (injection immédiate contre injection au lancement), il y a plus d'informations plus bas.

Plusieurs autres points sont abordés plus bas.

L'objet window

Les modules ne partagent qu'une représentation du contenu de window. En conséquence, tout ce qui est enregistré dans l'objet window par un module est indisponible dans le code de l'application. Cependant, tout ce qui est dans l'objet window par le code de l'application est disponible aux modules. Le DOM est accessible comme d'habitude.

Injection DOM

Vous pouvez utiliser les APIs JavaScript pour manipuler le DOM de l'application.

Injecter du code au bon moment

Vous devez être prudent pour gérer correctement les cas où un module est injecté dans une application après qu'elle ait été chargée. Un tel scénario peut se produire quand une application est déjà lancée et qu'un module qui la concerne est activé. Dans ce cas, un gestionnaire window.onload ne fonctionnera pas parce que l'événement DOMContentLoaded s'est déjà produit.

Pour l'instant, il n'existe pas de bonne solution à ce problème. En attendant, nous conseillons de vérifier si le DOM a été chargé ou non avant de définir un callback DOMContentLoaded. Ce modèle est utilisé dans la démo :

// En cas d'injection dans une appli déjà lancée au moment où
// l'appli a été activée, l'initialiser simplement.
if (document.documentElement) {
  initialize();
}

// Sinon, nous devons attendre que le DOM soit prêt avant de
// lancer l'initialisation car les modules sont en général (toujours ?)
// injectés *avant* que `document.documentElement` ne soit défini.
else {
  window.addEventListener('DOMContentLoaded', initialize);
}

function initialize() {
  // ...
}

Empêcher les injections multiples

Enfin, pour empêcher un module d'être injecté à plusieurs reprises dans une instance unique d'application, nous devons vérifier si votre module est déjà présent ou non, de cette manière :

function initialize() {
  if (document.querySelector('.fxos-banner')) {
    // Déjà injectée, annulation.
    return;
  } else {
    var body = document.querySelector('body');
    var fxosBanner = document.createElement('div');
    fxosBanner.classList.add('fxos-banner');
    var bannerText = document.createElement('p');
    var closeBtn = document.createElement('button');

    fxosBanner.appendChild(bannerText);
    fxosBanner.appendChild(closeBtn);
    body.appendChild(fxosBanner);

    closeBtn.textContent = 'X';
    bannerText.textContent = 'Waouh, vous avez une extension installée !';

    closeBtn.onclick = function() {
      fxosBanner.parentNode.removeChild(fxosBanner);
    }
  }
}

Ici donc, nous faisons usage de if (document.querySelector('.fxos-banner')) pour vérifier si la bannière d'exemple existe déjà. Dans l'affirmative, nous quittons la fonction. Si ce n'est pas le cas, la méthode querySelector() renvoie alors null, et nous exécutons le bloc de code qui crée la bannière.

Fonctions de gestion des applis dans les modules

Toutes les fonctions Apps et Mgmt se comportent avec les modules de la même façon qu'avec les applications. Notez cependant qu'elles ne sont disponibles pour les modules que lorsque ces derniers sont injectés dans une application certifiée possédant la permission webapps-manager spécifiée dans le manifeste.

En plus de ces fonctions, un callback onenabledstatechange est exposé aux modules qui sont activés et désactivés. Comme cet événement est déclenché pour tous les modules, il vous faut vérifier lequel d'entre-eux a été activé/désactivé avant de procéder à toute initialisation ou nettoyage.

navigator.mozApps.mgmt.addEventListener('enabledstatechange', function(event) {
  var app = event.application;
  if (app.manifestURL === 'https://origin.of.manifest/manifest.webapp') {
    var wasEnabled = app.enabled;
    // faire quelque chose de cette information
  }
});

Important :  En raison du bogue 1214155, il n'est pas possible d'ajouter le gestionnaire d'événement enabled state via navigator.mozApps.mgmt.onenabledstatechange = function() {...} : vous devez utiliser la méthode addEventListener comme mentionné ci-dessus.

extension.zip

Note : Le fichier extension.zip a été laissé dans le dépôt de démo principalement dans un but d'illustration ; la façon dont fonctionne le système est ainsi claire. En réalité, vous n'avez pas besoin d'inclure le zip dans votre répertoire car WebIDE va le générer à votre place lorsque vous installerez le module.

L'archive extension.zip contient le code de l'extension et est référencée dans le champ update.webapp package_path — C'est ainsi que Gecko trouve le code à installer. Archivés à l'intérieur, vous trouverez :

  • css/
    • style.css
  • js/
    • index.js
  • icons/
    • 128.png
  • manifest.json

Le fichier manifest.json se trouve donc à l'intérieur de l'archive et sert à faire référence aux fichiers à injecter et à spécifier quelles sont les applications à affecter.

Tester votre module avec WebIDE

L'outil WebIDE de Mozilla est disponible par défaut dans Firefox pour ordinateur. Pour s'en servir afin d'installer des modules sur votre téléphone, suivez les étapes énumérées ci-dessous :

  1. Assurez-vous d'avoir installé Firefox 43 ou plus récent (il s'agissait de Nightly au moment de la rédaction de cet article). Les modules ne sont pas pris en charge par WebIDE avant cette version.
  2. Ouvrez votre navigateur puis l'outil WebIDE (cliquez sur le bouton WebIDE ou allez dans le menu Outils > Développement web > WebIDE.)
  3. Vérifiez que le débogage distant est activé sur votre téléphone (Paramètres > Développeur > Définissez "Débogage via USB " sur "ADB et outils de développement".)
  4. Branchez votre téléphone sur votre ordinateur avec un câble USB. Vérifiez que vous n'avez pas branché d'autres téléphones en même temps.
  5. Dans l'interface de WebIDE, choisissez l'option Sélectionner l'environnement puis sélectionnez votre téléphone, celui-ci devant être listé dans la catégorie Périphériques USB.
  6. À ce stade, votre téléphone devrait afficher une demande Autoriser la connexion de débogage USB ?. Choisissez Autoriser.
  7. Sélectionnez l'option Ouvrir une application puis choisissez Ouvrir une application empaquetée...
  8. Dans le sélecteur de fichiers qui apparaît, naviguez jusqu'au répertoire qui contient votre fichier de manifeste update.webapp et cliquez sur Ouvrir.
  9. En supposant qu'il n'y ait aucun avertissement ni erreur, vous pouvez installer votre module sur votre appareil en cliquant sur le bouton "Lecture" situé au centre (Installer et lancer.)
  10. Pour voir le module en action, activez-le en choisissant Paramètres > Modules > Module exemple > basculez la case à cocher en haut.

Déboguer les modules

Il faut noter qu'à cause du bug 1185464 il n'est pas possible pour l'instant de déboguer les modules avec WebIDE.

Paramètres du module

Vous pouvez contrôler les modules de votre téléphone en allant dans Paramètres > Modules ; vous trouverez à cet endroit une liste de vos modules installés et vous pourrez voir plus d'informations sur chacun d'eux en tapant sur l'entrée correspondante.

firefox os screenshot showing a list of installed add-ons in the settings appinformation screen for an individual addon, with a list of apps this add-on affects, and controls to disable and delete the add-on

Activer/désactiver et supprimer des modules

Par défaut, les modules sont activés après leur installation s'ils sont installés depuis le Marketplace Firefox. Par contre, lorsqu'ils sont installés via WebIDE, ils sont désactivés par défaut.

Vous avez la possibilité d'activer/désactiver des modules manuellement par l'intermédiaire de la case à cocher qui se trouve en haut de la page individuelle du module (accessible dans Paramètres > Modules), ou par programmation en utilisant la fonction navigator.mozApps.mgmt.setEnabled() (voir cet exemple d'utilisation de setEnabled() sur Github.)

Vous pouvez supprimer totalement un module en tapant le bouton Supprimer qui se trouve sur les pages individuelles des applications.

Permissions

Les modules héritent toutes leurs permissions depuis leur application hôte. Demander des permissions dans le manifeste du module (voir update.webapp) n'aura aucun effet et n'exposera au module aucune API qui n'est pas disponible dans l'application hôte.

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : jwhitlock, xdelatour, foxstorm, Ilphrin
 Dernière mise à jour par : jwhitlock,