I. Introduction

Dans ce tutoriel, nous allons utiliser la fonctionnalité Override Pages qui permet de remplacer une page de Google Chrome. Nous verrons également les bases de l'utilisation de l'API chrome.bookmarks permettant la gestion des favoris.

I-A. Connaissances nécessaires

La compréhension de ce tutoriel nécessite de connaitre un minimum des langages HTML, CSS ainsi qu'une bonne notion en Javascript. Si vous n'avez jamais créé d'extension pour Google Chrome, allez lire le tutoriel d'introductionTutoriel d'introduction aux extensions Google Chrome.

II. Override Pages

Actuellement, cette fonctionnalité ne peut être utilisée que sur la page de nouvel onglet. C'est pourquoi les extensions décritent par la suite se focaliseront sur cette page. Si vous cliquez sur le '+' à droite des onglets, cette page s'affichera :

La fenêtre Nouvel Onglet

Cette page vous affiche les sites les plus visités récemment. C'est bien mais, personnellement, je n'ai pas besoin que l'on me rappelle les sites pour lesquels j'ai déjà sûrement un onglet ouvert. Pour changer cela, nous allons devoir développer un plugin.

III. Un site par défaut à l'ouverture d'un nouvel onglet.

Pour notre premier exemple, nous allons changer cette page par un site de notre choix. Pour l'exemple nous allons prendre Google.

III-A. Le fichier manifest.json

Nous allons dans un premier temps indiquer à Google Chrome les informations sur notre extension.

 
Sélectionnez

        {
          "name": "New Tab Bookmarks",
          "version": "1.0",
          "chrome_url_overrides": 
          {
            "newtab": "newtab.html"
          }
        }
        

Ici, la seule nouveauté se trouve être le paramètre chrome_url_overrides. La variable newtab permet de définir le fichier à ouvrir, ici newtab.html. Celui-ci fait obligatoirement partie de l'extension. En effet, il est impossible d'indiquer une URL externe directement.

III-B. Le fichier newtab.html

Comme son extension l'indique, ce fichier sera écrit en HTML. Pour charger une autre page, nous allons utiliser le javascript :

 
Sélectionnez

        <html>
          <head>
          <script>
          document.location.href="http://www.google.fr";
          </script>
          </head>
        </html>
        

La page sera donc chargée puis directement redirigée vers Google.

IV. Gestion des favoris, l'API chrome.bookmarks

Pour gérer les favoris, Google Chrome propose une API chrome.bookmarks. Elle permet de faire toutes les manipulations possibles "facilement".

IV-A. Présentation

Une structure en arbre a été utilisée pour gérer les favoris. Pour ceux qui ne connaissent pas ce type de structure, on peut la comparer avec vos répertoires. Voici un schéma qui pourra vous aider à comprendre :

Arbre

Ici, nous pouvons voir en jaune les répertoire et en bleu les liens. Le nœud principal ne peut être atteint. Le répertoire "Barre de favoris" possède l'identifiant 1, "Autres favoris" le 2.

IV-B. L'objet BookmarkTreeNode

Cet objet est utilisé dans quasiment toutes les fonctions de l'API. C'est pourquoi il est nécessaire de connaitre un minimum de ses attributs :

  • id : Identifiant de l'élément.
  • parentId : Identifiant du nœud parent. (facultatif)
  • index : Ordre des éléments. (facultatif)
  • url : URL. (facultatif)
  • title : Nom affiché.
  • dateAdded : Date d'ajout du favori. (facultatif)
  • dateGroupModified : Date de la dernière modification au sein du groupe. (facultatif)
  • children : Tableau contenant les nœuds enfants. (facultatif)

IV-D. Un premier exemple

La fonction chrome.bookmarks.create permet d'ajouter un favori. Pour l'exemple, nous allons l'ajouter à la barre de favoris ayant pour identifiant 1. Elle prend en paramètre un objet de type bookmark dont voici les attributs :

  • parentId : Identifiant du nœud parent.
  • index : Position. (facultatif)
  • title : Titre affiché. (facultatif)
  • url : URL. (facultatif)

Elle prend également une fonction callback en paramètre. Celle-ci sera étudiée par la suite. Voici maintenant comment appeler cette fonction :

 
Sélectionnez

     chrome.bookmarks.create({"parentId": "1", "titre": "Développez.com", "url": "http://www.developpez.com"});
          

Si vous essayez cette ligne de code, un favori pointant vers Développez.com s'ajoutera à votre barre d'outils.

V. Liste des favoris

V-A. Présentation

Le deuxième exemple de ce tutoriel sera de modifier la page Nouvel onglet par votre liste des favoris affichée sous forme de liste à plusieurs niveaux.

fav

V-B. Le fichier manifest.json

Ce sera le même que dans l'exemple précédent sauf l'ajout d'une ligne donnant la permission de gérer les favoris avec l'extension :

 
Sélectionnez

          {
            "name": "New Tab Bookmarks",
            "version": "1.0",
            "chrome_url_overrides": 
            {
              "newtab": "newtab.html"
            },
            "permissions": ["bookmarks"]
          }
          

V-C. Le fichier newtab.html

Celui-ci sera plus compliqué que pour les autres tutoriels. Le parcours d'un arbre se fait en général avec la récursivité. Pour faire simple, une fonction récursive est une fonction qui va s'appeler elle-même. Ainsi nous écrirons une fonction qui aura pour but de parcourir un répertoire (en partant du nœud donné). Puis lorsqu'elle tombera sur un répertoire, elle se rappellera elle-même pour parcourir ce répertoire, etc...

V-C-1. Les fonctions nécessaires

Nous aurons besoin de deux fonctions pour réaliser cette extension dont voici les prototypes :

  • chrome.bookmarks.getTree(function callback) : Permet l'accès à l'arbre complet.
  • chrome.bookmarks.getChildren(string id, function callback) : Donne la liste des enfants sous forme de tableau.

De nombreuses fonctionnalités des APIs de Google Chrome utilisent les fonctions callback. En français, nous les appelons les fonctions de rappel. Elles sont passées en paramètre à une autre fonction qui pourra alors les appeler par la suite. Ici, elles nous permettent d'accéder aux données de cette façon :

 
Sélectionnez

          chrome.bookmarks.getTree(function (tree)
          {
            // Dans cette partie, nous avons accès au données de l'arbre de favoris
            
            // Affichage de l'identifiant du premier élément
            console.log(tree[0].id);
          })
            

La fonction console.log(string) permet d'afficher du texte dans la console de debuggage. Souvent très pratique.

V-C-2. La fonction init

La première étape va être de créer la fonction d'initialisation :

 
Sélectionnez

              function init()
              {
                // Récupération de l'arbre des favoris
                chrome.bookmarks.getTree(function (tree)
                {
                  // On commence du nœud principal
                  chrome.bookmarks.getChildren(tree[0].id, callback_show);
                })
              }
            

Dans un premier temps nous récupérons l'arbre des favoris puis nous lançons son parcours. La fonction callback est désignée par la variable callback_show. Cette fonction sera par l'évènement "onload". Voici le code HTML :

 
Sélectionnez

              <body onload="init()">
                <h1>Vos favoris</h1>
                <div id="level0">
                </div>
              </body>
            

Le conteneur div a l'identifiant level0 qui sera utilisé par la fonction callback pour insérer la liste de base.

V-C-3. La fonction callback

Nous allons déjà commencer par une version pseudo-algorithmique de cette fonction pour faciliter la compréhension :

 
Sélectionnez

        FONCTION affichage_arbre(arbre)
        {
          Création de la liste
          
          BOUCLE sur les enfants de l'arbre
          {
            Création d'un élément de la liste
            
            SI le nœud est un url ALORS
            {
              Ecriture des données dans l'élément
              Insertion de l'élément à la liste
            }
            SINON
            {
              Création d'un élément de la liste
              Ecriture du titre du répertoire
              Insertion de l'élément à la liste
              Lancer la fonction affichage_arbre(arbre.enfant)
            }
          }
          
          Insertion de la liste dans celle de niveau supérieur
        }
            

Voici maintenant la fonction principale de l'extension. Elle pourra paraitre complexe pour certains mais il est surtout important de se focaliser sur l'API Google Chrome :

 
Sélectionnez

        var callback_show = function show_tree(tree)
        {
          // Création d'une nouvelle liste
          var list = document.createElement("ul");
          // Parcours de l'arbre
          for (var i = 0; i < tree.length; ++i)
          {
            // Création d'un nouvel élément
            var item = document.createElement("li");
            // Test si c'est un lien
            if (tree[i].url != null)
            {
              // Création du lien
              var alink = document.createElement("a");
              var alink_text = document.createTextNode(tree[i].title);
              alink.href = tree[i].url;
              alink.appendChild(alink_text);
              // Ajoute le lien à l'élément de la liste
              item.appendChild(alink);
              // Ajoute l'élément à la liste
              list.appendChild(item);
            }
            // Si ce n'est pas un lien, c'est un répertoire
            else
            {
              // Création de l'élément pour la sous-liste
              var sublist = document.createElement("li");
              var sublist_title = document.createTextNode(tree[i].title);
              // L'identifiant de la sous-liste sera l'identifiant du nœud
              sublist.id = "level"+tree[i].id;
              sublist.appendChild(sublist_title);
              // Ajoute la sous-liste à la liste
              list.appendChild(sublist);
              // Parcours du répertoire par appel récursif
              chrome.bookmarks.getChildren(tree[i].id, callback_show);
            }
          }
          // Ajoute la liste à la liste de niveau supérieure (identifiant égale à l'identifiant du nœud parent)
          var parent = document.getElementById("level"+tree[0].parentId);
          parent.appendChild(list);
        }
            

L'utilisation du DOM ne facilite pas la lecture du code, mais il y a un autre point plus important. En javascript, lorsqu'une fonction callback est appelée, cet appel est "stocké" tant que la fonction appelante n'a pas fini son traitement. Une fois ce traitement fini, elle sera éxécutée et pourra elle-mêmes en rajouter. Voici un exemple, sur l'arbre vu précédemment à la section IV-A, d'exécution de l'algorithme effectué avec un interpréteur Javascript :

 
Sélectionnez

		  Barre de favoris
		  Autres favoris
		  - Moteurs de recherche
		  - Développez.com
		  -- Google
		  -- Bing
		  -- Yahoo
          	

Sauf que nous voulons faire un affichage de ce type :

 
Sélectionnez

		  Barre de favoris
		  - Moteurs de recherche
		  -- Google
		  -- Bing
		  -- Yahoo
		  - Développez.com
		  Autres favoris
          	

Les fonctions callback s'exécutant à la fin du traitement d'une itération, il va être nécessaire de faire attention lors de l'insertion des données dans l'arbre. Une façon simple de faire va être d'utiliser les identifiants des nœuds de Google Chrome pour les réutiliser dans notre arbre au sein des listes.

V-C-4. Fichier complet

 
Sélectionnez

            <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
          
            <script>
          
            var callback_show = function show_tree(tree)
            {
              // Création d'une nouvelle liste
              var list = document.createElement("ul");
              // Parcours de l'arbre
              for (var i = 0; i < tree.length; ++i)
              {
                // Création d'un nouvel élément
                var item = document.createElement("li");
                // Test si c'est un lien
                if (tree[i].url != null)
                {
                  // Création du lien
                  var alink = document.createElement("a");
                  var alink_text = document.createTextNode(tree[i].title);
                  alink.href = tree[i].url;
                  alink.appendChild(alink_text);
                  // Ajoute le lien à l'élément de la liste
                  item.appendChild(alink);
                  // Ajoute l'élément à la liste
                  list.appendChild(item);
                }
                // Si ce n'est pas un lien, c'est un répertoire
                else
                {
                  // Création de l'élément pour la sous-liste
                  var sublist = document.createElement("li");
                  var sublist_title = document.createTextNode(tree[i].title);
                  // L'identifiant de la sous-liste sera l'identifiant du nœud
                  sublist.id = "level"+tree[i].id;
                  sublist.appendChild(sublist_title);
                  // Ajoute la sous-liste à la liste
                  list.appendChild(sublist);
                  // Parcours du répertoire par appel récursif
                  chrome.bookmarks.getChildren(tree[i].id, callback_show);
                }
              }
              // Ajoute la liste à la liste de niveau supérieure (identifiant égale à l'identifiant du nœud parent)
              var parent = document.getElementById("level"+tree[0].parentId);
              parent.appendChild(list);
            }
          
            function init()
            {
              // Récupération de l'arbre des favoris
              chrome.bookmarks.getTree(function (tree)
              {
                // On commence du nœud principal
                chrome.bookmarks.getChildren(tree[0].id, callback_show);
              })
            }
            </script>
            </head>
            <body onload="init()">
              <h1>Vos favoris</h1>
              <div id="level0">
              </div>
            </body>
          </html>
          

V-C-5. Un peu de CSS

Une pointe de CSS rendra la chose plus agréable surtout avec Webkit. Voici un code CSS que vous pourriez ajouter :

 
Sélectionnez

              h1
              {
                background-color: #9292ff;
                text-align: center;
                width: 300px;
                margin-left: auto;
                margin-right: auto;
                -webkit-border-radius: 25px;
              }
              
              a
              {
                text-decoration: none;
                color: black;
                background-color: #c0c0ff;
                -webkit-border-radius: 10px;
                padding-left: 5px;
                width: 500px;
                display: block;
                margin-bottom: 3px;
              }
              
              a:hover
              {
                background-color: #e36d6d;
                margin-left: 30px;
                -webkit-transition: margin-left 0.5s;
              }
              
              ul, li
              {
                list-style: none;
                padding-left: 10px;
              }
              
              .directory
              {
                background-color: #4ef685;
                -webkit-border-radius: 10px;
                padding-left: 5px;
                width: 500px;
                display: block;
                font-weight: bold;
              }
            

Une légère modification dans le code javascript :

 
Sélectionnez

                    // Création de l'élément pour la sous-liste
                    var sublist = document.createElement("li");
                    var sublist_div = document.createElement("div");
                    var sublist_title = document.createTextNode(tree[i].title);
                    // L'identifiant de la sous-liste sera l'identifiant du nœud
                    sublist.id = "level"+tree[i].id;
                    sublist_div.className = "directory";
                    sublist_div.appendChild(sublist_title);
                    sublist.appendChild(sublist_div);
                    // Ajoute la sous-liste à la liste
                    list.appendChild(sublist);
            

C'est tout de même plus joli :

Avec css

VI. Conclusion

Ce tutoriel était bien plus compliqué que tous les autres à cause de l'intégration de différents concepts. Je vous conseille de faire différents essais pour mieux comprendre l'utilisation de l'API de gestion des favoris pour être plus à l'aise par la suite. N'hésitez pas à me transmettre vos suggestions pour enrichir et améliorer ce document, notamment si vous trouvez des explications peu claires, des passages de code non optimisés, ou toute autre remarque. Elles seront les bienvenues.

Tous mes remerciements à gorgonite et jacques_jean pour la relecture de ce tutoriel.