Maîtriser le Développement d'Extensions de Navigateur : De l'Idée à la Publication
Maîtriser le Développement d'Extensions de Navigateur : De l'Idée à la Publication

Gestion des Données et APIs Avancées : Utiliser le Stockage Local et les Permissions

Introduction à la Persistance des Données dans les Extensions de Navigateur

Bienvenue dans cette leçon dédiée à un aspect fondamental du développement d'extensions de navigateur : la gestion des données et l'utilisation des permissions. Au-delà de l'interface utilisateur et de la logique de base, une extension performante et utile nécessite souvent de stocker des informations, qu'il s'agisse des préférences de l'utilisateur, de données mises en cache, ou d'états d'application.

Imaginez une extension qui gère des tâches : si elle perd toutes les tâches ajoutées par l'utilisateur à chaque fermeture du navigateur, elle devient inutile. C'est là qu'intervient le stockage local. De plus, pour accéder à ces données ou interagir avec des fonctionnalités spécifiques du navigateur (comme modifier des onglets ou accéder à des API système), votre extension aura besoin de permissions explicites, qui sont la clé de voûte de la sécurité et du contrôle dans l'écosystème des extensions.

Dans cette leçon, nous allons explorer les différentes options de stockage offertes aux extensions, comprendre comment les permissions fonctionnent et comment les déclarer correctement, et enfin, voir comment les utiliser de manière efficace et sécurisée.

Pourquoi Gérer les Données dans une Extension ?

La persistance des données est cruciale pour plusieurs raisons :

  • Personnalisation : Stocker les préférences de l'utilisateur (thème sombre, langue, réglages spécifiques) pour une expérience cohérente.
  • Performance : Mettre en cache des données coûteuses à récupérer (API externes, résultats de calculs complexes) pour réduire la latence et la consommation de ressources.
  • État de l'application : Sauvegarder l'état actuel de l'extension (éléments ouverts, progression, données en cours de saisie) pour le restaurer à la prochaine utilisation.
  • Fonctionnalités avancées : Enregistrer des historiques, des signets spécifiques à l'extension, ou d'autres informations propres à la logique métier.

Options de Stockage Local pour les Extensions

Les extensions de navigateur disposent de plusieurs mécanismes pour stocker des données localement. Le choix de l'API dépendra de la nature des données, de leur volume, de la nécessité de synchronisation et des performances attendues.

L'API chrome.storage (ou browser.storage)

C'est l'API recommandée pour le stockage de données persistantes dans les extensions. Elle offre une solution asynchrone, optimisée pour les extensions, et est plus sécurisée que d'autres méthodes. Elle est disponible pour tous les contextes de votre extension (scripts d'arrière-plan, pop-up, pages d'options, scripts de contenu via un message).

L'API storage se décline en plusieurs zones :

  • chrome.storage.local :

    • Description : Stocke les données localement sur la machine de l'utilisateur. Ces données ne sont pas synchronisées entre différents navigateurs ou profils.
    • Capacité : Généralement, jusqu'à 5 MB, mais peut être étendue avec la permission unlimitedStorage.
    • Cas d'usage : Préférences non critiques, données mises en cache spécifiques à la machine, états d'application volumineux.
    • Avantages : Asynchrone, plus fiable et sécurisé que localStorage, accessible depuis tous les contextes de l'extension.
    • Inconvénients : Non synchronisé.
  • chrome.storage.sync :

    • Description : Stocke les données localement et les synchronise automatiquement via le compte utilisateur du navigateur (par exemple, un compte Google pour Chrome).
    • Capacité : Plus limitée, généralement autour de 100 KB par élément, avec une limite totale de 8 MB. Il y a aussi des limites de débit d'écriture.
    • Cas d'usage : Préférences utilisateur importantes qui doivent persister à travers différentes machines, petits réglages globaux.
    • Avantages : Synchronisation transparente entre les appareils, résilience aux pertes de données locales.
    • Inconvénients : Capacités et débits limités, coût en bande passante.
  • chrome.storage.managed (moins courant pour les développeurs d'extensions classiques) :

    • Description : Permet aux administrateurs de domaine de définir des stratégies pour l'extension, fournissant des données en lecture seule.

Permissions Requises pour chrome.storage

Pour utiliser chrome.storage, vous devez déclarer la permission storage dans votre fichier manifest.json. Si vous avez besoin de plus de 5 MB de stockage local, vous devrez également ajouter la permission unlimitedStorage.

{
  "name": "Mon Extension",
  "version": "1.0",
  "manifest_version": 3,
  "permissions": [
    "storage",
    "unlimitedStorage" 
  ],
  "background": {
    "service_worker": "background.js"
  }
}

Utilisation de chrome.storage.local

Voici un exemple simple pour sauvegarder et récupérer des données :

// background.js ou popup.js

// Fonction pour sauvegarder des données
async function saveSettings(settings) {
  try {
    await chrome.storage.local.set({ userSettings: settings });
    console.log('Paramètres sauvegardés avec succès !');
  } catch (error) {
    console.error('Erreur lors de la sauvegarde des paramètres :', error);
  }
}

// Fonction pour récupérer des données
async function loadSettings() {
  try {
    const data = await chrome.storage.local.get('userSettings');
    if (data.userSettings) {
      console.log('Paramètres récupérés :', data.userSettings);
      return data.userSettings;
    } else {
      console.log('Aucun paramètre trouvé.');
      return null;
    }
  } catch (error) {
    console.error('Erreur lors de la récupération des paramètres :', error);
    return null;
  }
}

// Exemple d'utilisation
const myNewSettings = {
  theme: 'dark',
  notificationsEnabled: true,
  lastVisited: new Date().toISOString()
};

saveSettings(myNewSettings);

// Après un certain temps ou événement
loadSettings().then(settings => {
  if (settings) {
    console.log("Thème actuel :", settings.theme);
  }
});

// Écouter les changements de stockage
chrome.storage.onChanged.addListener((changes, namespace) => {
  if (namespace === 'local' && changes.userSettings) {
    console.log('Les paramètres locaux ont changé :', changes.userSettings.newValue);
  }
});

// Supprimer un élément
async function removeSetting(key) {
  try {
    await chrome.storage.local.remove(key);
    console.log(`Clé "${key}" supprimée.`);
  } catch (error) {
    console.error(`Erreur lors de la suppression de la clé "${key}" :`, error);
  }
}

// removeSetting('userSettings'); // Désactivez pour tester la suppression

// Vider tout le stockage local de l'extension
async function clearAllLocalData() {
  try {
    await chrome.storage.local.clear();
    console.log('Tout le stockage local de l\'extension a été vidé.');
  } catch (error) {
    console.error('Erreur lors du vidage du stockage local :', error);
  }
}

// clearAllLocalData(); // Désactivez pour tester le vidage complet

Explication du code : Ce bloc de code JavaScript montre comment interagir avec chrome.storage.local.

  • saveSettings utilise chrome.storage.local.set() pour stocker un objet sous la clé userSettings.
  • loadSettings utilise chrome.storage.local.get() pour récupérer l'objet associé à userSettings.
  • chrome.storage.onChanged.addListener est un écouteur d'événements qui se déclenche chaque fois qu'une donnée dans le stockage de l'extension est modifiée. C'est très utile pour mettre à jour l'UI ou la logique de l'extension en temps réel.
  • removeSetting montre comment supprimer une entrée spécifique avec chrome.storage.local.remove().
  • clearAllLocalData illustre le vidage complet du stockage local de votre extension avec chrome.storage.local.clear(). Nous utilisons async/await pour gérer les opérations asynchrones de manière plus lisible.

L'API localStorage (Web Storage API)

L'API localStorage est une fonctionnalité du navigateur standard, non spécifique aux extensions. Elle permet de stocker des paires clé-valeur sous forme de chaînes de caractères.

  • Description : Stocke des données de manière persistante pour une origine donnée (domaine, protocole, port).
  • Capacité : Environ 5-10 MB par origine.
  • Cas d'usage : Peu recommandé pour les scripts d'arrière-plan des extensions. Principalement utile si votre extension a une page popup ou une page d'options HTML/JavaScript et que vous voulez stocker des données spécifiques à cette page dans le contexte de cette page, ou si un script de contenu a besoin de stocker des données pour l'origine du site web qu'il manipule.
  • Avantages : API simple, synchrone.
  • Inconvénients : Déconseillé pour les scripts d'arrière-plan ou pour les données globales de l'extension car il est synchrone (peut bloquer le thread principal) et lié à l'origine, ce qui peut rendre l'accès et la gestion plus complexes dans le contexte d'une extension. Non accessible directement depuis le service_worker ou les scripts d'arrière-plan des extensions V3.
  • Permissions : Aucune permission spécifique n'est requise.

L'API sessionStorage (Web Storage API)

Similaire à localStorage, mais les données sont éphémères.

  • Description : Stocke des données pour la durée de la session de la page ou de l'onglet. Les données sont effacées lorsque l'onglet est fermé.
  • Cas d'usage : Stockage temporaire d'informations qui n'ont pas besoin de persister au-delà de la session d'une page spécifique de l'extension (ex: un formulaire incomplet).
  • Inconvénients : Mêmes limitations que localStorage concernant l'accessibilité dans les contextes d'extension (non accessible directement depuis le service_worker).

Comparaison Rapide

| Caractéristique | chrome.storage.local | chrome.storage.sync | localStorage | sessionStorage | | :-------------------- | :--------------------------------- | :----------------------------- | :--------------------------- | :--------------------------- | | Persistance | Oui (localement) | Oui (synchronisé) | Oui (par origine) | Non (par session/onglet) | | Synchronisation | Non | Oui (via compte utilisateur) | Non | Non | | Asynchrone ? | Oui | Oui | Non | Non | | Accès Contextes | Tous (background, popup, content*) | Tous (background, popup, content*) | Selon l'origine (popup, content) | Selon l'origine (popup, content) | | Capacité | ~5MB (extensible avec unlimitedStorage) | ~8MB total, ~100KB par item | ~5-10MB par origine | ~5-10MB par origine | | Permissions | storage | storage | Aucune | Aucune | | Recommandé pour Ext. | Oui (par défaut) | Oui (pour prefs synchronisées) | Non (sauf cas très spécifiques) | Non (sauf cas très spécifiques) | | Notes | Stocke des objets JS, pas seulement chaînes | API limitée en taille et débit | Stocke chaînes de caractères | Stocke chaînes de caractères |

* Note : Pour accéder à chrome.storage depuis un script de contenu, le script de contenu doit envoyer un message au script d'arrière-plan, qui effectue l'opération de stockage et renvoie la réponse.

Les Permissions : La Clé de Voûte de la Sécurité et de la Fonctionnalité

Les permissions sont des déclarations faites dans le fichier manifest.json de votre extension qui informent le navigateur (et l'utilisateur) des capacités et des accès que votre extension requiert. Elles sont essentielles car elles :

  • Protègent l'utilisateur : Empêchent les extensions malveillantes d'accéder à des données sensibles ou d'effectuer des actions non autorisées.
  • Définissent les capacités de l'extension : Permettent à votre extension d'utiliser des APIs spécifiques du navigateur (comme tabs, alarms, notifications, storage) ou d'interagir avec certaines parties du web.
  • Gèrent le consentement : Lors de l'installation, le navigateur affiche à l'utilisateur la liste des permissions demandées. L'utilisateur doit les accepter pour installer l'extension.

Déclarer les Permissions dans manifest.json

Les permissions sont déclarées dans la section permissions de votre manifest.json. Elles sont généralement des chaînes de caractères représentant le nom d'une API (ex: "storage") ou des URL pour les permissions d'hôte.

{
  "name": "Mon Extension Avancée",
  "version": "1.0",
  "manifest_version": 3,
  "permissions": [
    "storage",          
    "activeTab",        
    "scripting",        
    "tabs",             
    "alarms",           
    "notifications"     
  ],
  "host_permissions": [ 
    "https://api.example.com/*",
    "*://*.google.com/"
  ],
  "background": {
    "service_worker": "background.js"
  }
}

Explication du manifest.json :

  • permissions: Liste les accès aux APIs du navigateur (comme storage pour le stockage, activeTab pour accéder à l'onglet actif temporairement, scripting pour injecter du code, tabs pour manipuler les onglets, alarms pour planifier des tâches, notifications pour afficher des messages).
  • host_permissions: Spécifie les domaines avec lesquels l'extension est autorisée à interagir. Cela inclut la capacité d'injecter des scripts de contenu ou de faire des requêtes fetch/XMLHttpRequest vers ces domaines.
    • "https://api.example.com/*" permet à l'extension d'accéder à n'importe quel chemin sur api.example.com via HTTPS.
    • "*://*.google.com/" permet d'accéder à n'importe quel sous-domaine de google.com (ex: www.google.com, mail.google.com) via HTTP ou HTTPS.

Types de Permissions Courantes Liées aux Données et APIs Avancées

  • storage : Indispensable pour utiliser l'API chrome.storage.
  • unlimitedStorage : Pour dépasser la limite de 5MB de chrome.storage.local.
  • tabs : Permet d'accéder aux propriétés des onglets (URL, titre), de créer, modifier ou fermer des onglets. Utile si votre extension doit manipuler l'historique ou l'état de navigation.
  • activeTab : Une permission temporaire qui est accordée uniquement lorsque l'utilisateur invoque l'extension (clique sur l'icône, utilise un raccourci clavier, ou sélectionne un élément dans le menu contextuel). Elle permet d'accéder à l'onglet actif sans demander un accès persistant à tous les onglets. C'est une excellente alternative à tabs pour des actions contextuelles.
  • scripting (Manifest V3) : Remplace executeScript de Manifest V2. Permet d'injecter du code JavaScript ou CSS dans les pages web. Nécessite des host_permissions correspondantes.
  • alarms : Permet de planifier des tâches répétitives ou uniques dans le futur, même si l'extension est inactive.
  • notifications : Permet d'afficher des notifications système à l'utilisateur.
  • declarativeNetRequest : API puissante pour bloquer ou modifier des requêtes réseau sans intercepter le trafic. Très performant et sécurisé.
  • host_permissions : Permettent à votre extension d'interagir avec des ressources hébergées sur des domaines spécifiques. Cela inclut :
    • Effectuer des requêtes HTTP/HTTPS (via fetch ou XMLHttpRequest).
    • Injecter des scripts de contenu.
    • Lire des cookies pour ces domaines.

Permissions "Optionnelles" ou "Runtime"

Pour minimiser le nombre de permissions demandées à l'installation, vous pouvez déclarer certaines permissions comme optionnelles. L'utilisateur peut alors choisir de les accorder ou non après l'installation, via l'interface de gestion de l'extension ou par une interaction déclenchée par votre extension.

{
  "name": "Mon Extension avec Permissions Optionnelles",
  "version": "1.0",
  "manifest_version": 3,
  "permissions": [
    "storage"
  ],
  "optional_permissions": [
    "tabs",
    "https://api.external.com/*"
  ],
  "background": {
    "service_worker": "background.js"
  }
}

Pour demander une permission optionnelle au runtime :

// Dans un script de l'extension (ex: popup.js)
document.getElementById('requestTabsPermission').addEventListener('click', async () => {
  try {
    const granted = await chrome.permissions.request({
      permissions: ['tabs'],
      origins: ['https://api.external.com/*']
    });
    if (granted) {
      console.log('Permissions "tabs" et "https://api.external.com/*" accordées.');
      // Maintenant, vous pouvez utiliser les APIs correspondantes
    } else {
      console.log('Permissions refusées.');
    }
  } catch (error) {
    console.error('Erreur lors de la demande de permissions :', error);
  }
});

// Pour vérifier si une permission est déjà accordée
async function checkPermissions() {
  const hasTabs = await chrome.permissions.contains({ permissions: ['tabs'] });
  console.log('Possède la permission "tabs" :', hasTabs);
}

checkPermissions();

Explication du code : Ce code JavaScript montre comment demander dynamiquement des permissions optionnelles à l'utilisateur.

  • Un événement de clic sur un bouton déclenche chrome.permissions.request(), qui ouvre une boîte de dialogue au navigateur demandant à l'utilisateur d'accorder les permissions spécifiées (ici, tabs et un host_permission).
  • Le await permet de savoir si l'utilisateur a accordé ou refusé les permissions.
  • chrome.permissions.contains() permet de vérifier à tout moment si une permission donnée est déjà accordée à l'extension.

Bonnes Pratiques et Considérations

  1. Principe du moindre privilège : Demandez uniquement les permissions absolument nécessaires au fonctionnement de votre extension. Moins de permissions = plus de confiance de l'utilisateur.
  2. Utilisez activeTab quand c'est possible : Pour des actions sur l'onglet courant, activeTab est préférable à tabs car il ne donne un accès qu'au moment de l'invocation de l'extension.
  3. Choisissez le bon stockage :
    • chrome.storage.sync pour les préférences utilisateur synchronisées et petites.
    • chrome.storage.local pour les grandes quantités de données spécifiques à la machine.
    • Évitez localStorage et sessionStorage pour la logique centrale de l'extension.
  4. Gérez les erreurs : Les opérations de stockage et de permission peuvent échouer. Utilisez des blocs try...catch ou des .catch() pour gérer ces scénarios.
  5. Performance : Les opérations chrome.storage sont asynchrones. Ne bloquez pas le thread principal. Pour des volumes de données très importants ou des requêtes complexes, envisagez des solutions comme IndexedDB (bien que plus complexe à implémenter).
  6. Sécurité des données : Ne stockez jamais d'informations sensibles (mots de passe, données personnelles non cryptées) sans précautions. chrome.storage n'est pas un mécanisme de stockage crypté.
  7. Informez l'utilisateur : Si votre extension demande beaucoup de permissions ou des permissions spécifiques, expliquez clairement pourquoi elles sont nécessaires dans votre description sur le store ou dans votre page d'options.

Conclusion

La gestion des données persistantes et l'utilisation judicieuse des permissions sont des piliers fondamentaux pour créer des extensions de navigateur robustes, fiables et appréciées des utilisateurs.

  • L'API chrome.storage est votre allié principal pour stocker des données, offrant local pour les grandes quantités et sync pour la synchronisation multi-appareils.
  • Les permissions, déclarées dans manifest.json, définissent le champ d'action de votre extension. Elles sont cruciales pour la sécurité et le consentement de l'utilisateur.
  • Le principe du moindre privilège est votre guide : ne demandez que ce qui est strictement nécessaire, et utilisez les permissions optionnelles pour donner plus de contrôle à l'utilisateur.

En maîtrisant ces concepts, vous serez en mesure de concevoir des extensions qui non seulement répondent aux besoins fonctionnels, mais respectent également la vie privée et la sécurité de vos utilisateurs, créant ainsi une expérience positive et digne de confiance.