Étendre les outils de développement

Présentation

Une extension DevTools ajoute des fonctionnalités aux outils pour les développeurs Chrome. Elle peut ajouter de nouveaux panneaux et barres latérales d'interface utilisateur, interagir avec la page inspectée, obtenir des informations sur les requêtes réseau, etc. Consultez la sélection des extensions DevTools. Les extensions DevTools ont accès à un ensemble supplémentaire d'API d'extension propres à DevTools:

Une extension DevTools est structurée comme n'importe quelle autre extension: elle peut comporter une page d'arrière-plan, des scripts de contenu et d'autres éléments. En outre, chaque extension DevTools dispose d'une page "DevTools" ayant accès aux API DevTools.

Schéma de l'architecture montrant la page "Outils de développement" communiquant avec la fenêtre inspectée et la page en arrière-plan. La page d'arrière-plan est affichée en train de communiquer avec les scripts de contenu et d'accéder aux API d'extension.
       La page "Outils de développement" a accès aux API DevTools (par exemple, pour créer des panneaux).

Page "Outils de développement"

Une instance de la page "Outils de développement" de l'extension est créée chaque fois qu'une fenêtre "Outils de développement" s'ouvre. Cette page existe pendant toute la durée de vie de la fenêtre "Outils de développement". La page "Outils de développement" a accès aux API DevTools et à un ensemble limité d'API d'extension. Plus précisément, la page "Outils de développement" peut:

  • Créez des panneaux et interagissez avec eux à l'aide des API devtools.panels.
  • Obtenez des informations sur la fenêtre inspectée et évaluez le code dans celle-ci à l'aide des API devtools.inspectedWindow.
  • Obtenez des informations sur les requêtes réseau à l'aide des API devtools.network.

La page "Outils de développement" ne peut pas utiliser directement la plupart des API des extensions. Il a accès au même sous-ensemble d'API extension et runtime auquel un script de contenu a accès. Comme un script de contenu, une page d'outils de développement peut communiquer avec la page d'arrière-plan à l'aide de la transmission de messages. Pour obtenir un exemple, consultez la section Injecter un script de contenu.

Créer une extension DevTools

Pour créer une page d'outils de développement pour votre extension, ajoutez le champ devtools_page dans le fichier manifeste de l'extension:

{
  "name": ...
  "version": "1.0",
  "minimum_chrome_version": "10.0",
  "devtools_page": "devtools.html",
  ...
}

Une instance du devtools_page spécifié dans le fichier manifeste de votre extension est créée pour chaque fenêtre DevTools ouverte. La page peut ajouter d'autres pages d'extension sous forme de panneaux et de barres latérales à la fenêtre DevTools à l'aide de l'API devtools.panels.

Les modules de l'API chrome.devtools.* ne sont disponibles que pour les pages chargées dans la fenêtre "Outils de développement". Les scripts de contenu et les autres pages d'extensions ne disposent pas de ces API. Par conséquent, les API ne sont disponibles que pendant la durée de vie de la fenêtre "Outils de développement".

Certaines API DevTools sont encore en phase expérimentale. Consultez chrome.experimental*. API pour obtenir la liste des API expérimentales et des consignes pour les utiliser.

Éléments de l'interface utilisateur des outils de développement: panneaux et volets de la barre latérale

En plus des éléments d'interface utilisateur habituels de l'extension, tels que les actions du navigateur, les menus contextuels et les pop-ups, une extension DevTools peut ajouter des éléments d'interface utilisateur à la fenêtre DevTools:

  • Un panneau est un onglet de premier niveau, comme les panneaux "Éléments", "Sources" et "Réseau".
  • Un volet de barre latérale présente des UI supplémentaires liées à un panneau. Les volets "Styles", "Styles calculés" et "Écouteurs d'événements" du panneau "Éléments" sont des exemples de volets de barre latérale. Notez que l'apparence des volets de la barre latérale peut ne pas correspondre à l'image, selon la version de Chrome que vous utilisez et l'emplacement de la fenêtre "Outils de développement".

Fenêtre "DevTools" affichant le panneau "Elements" et le volet "Styles" de la barre latérale.

Chaque panneau correspond à son propre fichier HTML, qui peut inclure d'autres ressources (JavaScript, CSS, images, etc.). La création d'un panneau de base se présente comme suit:

chrome.devtools.panels.create("My Panel",
    "MyPanelIcon.png",
    "Panel.html",
    function(panel) {
      // code invoked on panel creation
    }
);

Le code JavaScript exécuté dans un panneau ou un volet de la barre latérale a accès aux mêmes API que la page "Outils de développement".

La création d'un volet de barre latérale de base pour le panneau "Elements" se présente comme suit:

chrome.devtools.panels.elements.createSidebarPane("My Sidebar",
    function(sidebar) {
        // sidebar initialization code here
        sidebar.setObject({ some_data: "Some data to show" });
});

Il existe plusieurs façons d'afficher du contenu dans un volet de barre latérale:

  • Contenu HTML. Appelez setPage pour spécifier une page HTML à afficher dans le volet.
  • Données JSON. Transmettez un objet JSON à setObject.
  • Expression JavaScript. Transmettez une expression à setExpression. Les outils de développement évaluent l'expression dans le contexte de la page inspectée et affichent la valeur renvoyée.

Pour setObject et setExpression, le volet affiche la valeur telle qu'elle apparaîtrait dans la console DevTools. Cependant, setExpression vous permet d'afficher des éléments DOM et des objets JavaScript arbitraires, tandis que setObject n'est compatible qu'avec les objets JSON.

Communiquer entre les composants des extensions

Les sections suivantes décrivent des scénarios types de communication entre les différents composants d'une extension DevTools.

Injecter un script de contenu

La page "Outils de développement" ne peut pas appeler directement tabs.executeScript. Pour injecter un script de contenu à partir de la page "Outils de développement", vous devez récupérer l'ID de l'onglet de la fenêtre inspectée à l'aide de la propriété inspectedWindow.tabId et envoyer un message à la page d'arrière-plan. Depuis la page d'arrière-plan, appelez tabs.executeScript pour injecter le script.

Les extraits de code suivants montrent comment injecter un script de contenu à l'aide de executeScript.

// DevTools page -- devtools.js
// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "devtools-page"
});

backgroundPageConnection.onMessage.addListener(function (message) {
    // Handle responses from the background page, if any
});

// Relay the tab ID to the background page
chrome.runtime.sendMessage({
    tabId: chrome.devtools.inspectedWindow.tabId,
    scriptToInject: "content_script.js"
});

Code de la page d'arrière-plan:

// Background page -- background.js
chrome.runtime.onConnect.addListener(function(devToolsConnection) {
    // assign the listener function to a variable so we can remove it later
    var devToolsListener = function(message, sender, sendResponse) {
        // Inject a content script into the identified tab
        chrome.tabs.executeScript(message.tabId,
            { file: message.scriptToInject });
    }
    // add the listener
    devToolsConnection.onMessage.addListener(devToolsListener);

    devToolsConnection.onDisconnect.addListener(function() {
         devToolsConnection.onMessage.removeListener(devToolsListener);
    });
});

Évaluer JavaScript dans la fenêtre inspectée

Vous pouvez utiliser la méthode inspectedWindow.eval pour exécuter du code JavaScript dans le contexte de la page inspectée. Vous pouvez appeler la méthode eval à partir d'une page "Outils de développement", d'un panneau ou d'un volet de barre latérale.

Par défaut, l'expression est évaluée dans le contexte du frame principal de la page. Vous connaissez peut-être les fonctionnalités de l'API de ligne de commande des outils de développement, telles que l'inspection d'éléments (inspect(elem)), le plantage des fonctions (debug(fn)), la copie dans le presse-papiers (copy()), etc. inspectedWindow.eval() utilise le même contexte d'exécution de script et les mêmes options que le code saisi dans la console DevTools, ce qui permet d'accéder à ces API dans l'évaluation. Par exemple, SOAK l'utilise pour inspecter un élément:

chrome.devtools.inspectedWindow.eval(
  "inspect($$('head script[data-soak=main]')[0])",
  function(result, isException) { }
);

Vous pouvez également utiliser l'option useContentScriptContext: true pour inspectedWindow.eval() afin d'évaluer l'expression dans le même contexte que les scripts de contenu. L'appel de eval avec useContentScriptContext: true ne crée pas de contexte de script de contenu. Vous devez donc charger un script de contexte avant d'appeler eval, soit en appelant executeScript, soit en spécifiant un script de contenu dans le fichier manifest.json.

Une fois le contexte du script de contexte défini, vous pouvez utiliser cette option pour injecter des scripts de contenu supplémentaires.

La méthode eval est puissante lorsqu'elle est utilisée dans le bon contexte et dangereuse lorsqu'elle n'est pas utilisée de manière appropriée. Utilisez la méthode tabs.executeScript si vous n'avez pas besoin d'accéder au contexte JavaScript de la page inspectée. Pour obtenir des avertissements détaillés et une comparaison des deux méthodes, consultez inspectedWindow.

Transmettre l'élément sélectionné à un script de contenu

Le script de contenu n'a pas d'accès direct à l'élément actuellement sélectionné. Toutefois, tout code que vous exécutez avec inspectedWindow.eval a accès à la console DevTools et aux API de ligne de commande. Par exemple, dans le code évalué, vous pouvez utiliser $0 pour accéder à l'élément sélectionné.

Pour transmettre l'élément sélectionné à un script de contenu:

  • Créez dans le script de contenu une méthode qui accepte l'élément sélectionné comme argument.
  • Appelez la méthode à partir de la page "Outils de développement" à l'aide de inspectedWindow.eval avec l'option useContentScriptContext: true.

Le code de votre script de contenu peut se présenter comme suit:

function setSelectedElement(el) {
    // do something with the selected element
}

Appelez la méthode sur la page "Outils de développement" comme suit:

chrome.devtools.inspectedWindow.eval("setSelectedElement($0)",
    { useContentScriptContext: true });

L'option useContentScriptContext: true indique que l'expression doit être évaluée dans le même contexte que les scripts de contenu, afin de pouvoir accéder à la méthode setSelectedElement.

Obtenir le window d'un panneau de référence

Pour effectuer une opération postMessage à partir d'un panneau d'outils de développement, vous devez faire référence à son objet window. Récupérez la fenêtre iFrame d'un panneau à partir du gestionnaire d'événements panel.onShown:

onShown.addListener(function callback)
extensionPanel.onShown.addListener(function (extPanelWindow) {
    extPanelWindow instanceof Window; // true
    extPanelWindow.postMessage( // …
});

Envoi de messages à partir des scripts de contenu pour accéder à la page "Outils de développement"

L'échange de messages entre la page "Outils de développement" et les scripts de contenu est indirect, par le biais de la page en arrière-plan.

Lors de l'envoi d'un message à un script de contenu, la page d'arrière-plan peut utiliser la méthode tabs.sendMessage, qui dirige un message vers les scripts de contenu d'un onglet spécifique, comme indiqué dans la section Injecter un script de contenu.

Lorsque vous envoyez un message à partir d'un script de contenu, aucune méthode prête à l'emploi ne permet de le transmettre à l'instance de page DevTools associée à l'onglet actuel. Pour contourner ce problème, vous pouvez demander à la page "Outils de développement" d'établir une connexion de longue durée avec la page en arrière-plan. Celle-ci conserve un mappage des ID d'onglet avec les connexions, afin qu'elle puisse acheminer chaque message vers la bonne connexion.

// background.js
var connections = {};

chrome.runtime.onConnect.addListener(function (port) {

    var extensionListener = function (message, sender, sendResponse) {

        // The original connection event doesn't include the tab ID of the
        // DevTools page, so we need to send it explicitly.
        if (message.name == "init") {
          connections[message.tabId] = port;
          return;
        }

    // other message handling
    }

    // Listen to messages sent from the DevTools page
    port.onMessage.addListener(extensionListener);

    port.onDisconnect.addListener(function(port) {
        port.onMessage.removeListener(extensionListener);

        var tabs = Object.keys(connections);
        for (var i=0, len=tabs.length; i < len; i++) {
          if (connections[tabs[i]] == port) {
            delete connections[tabs[i]]
            break;
          }
        }
    });
});

// Receive message from content script and relay to the devTools page for the
// current tab
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    // Messages from content scripts should have sender.tab set
    if (sender.tab) {
      var tabId = sender.tab.id;
      if (tabId in connections) {
        connections[tabId].postMessage(request);
      } else {
        console.log("Tab not found in connection list.");
      }
    } else {
      console.log("sender.tab not defined.");
    }
    return true;
});

La page "Outils de développement" (ou le panneau ou le volet de la barre latérale) établit la connexion comme suit:

// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "panel"
});

backgroundPageConnection.postMessage({
    name: 'init',
    tabId: chrome.devtools.inspectedWindow.tabId
});

Envoi de messages à partir de scripts injectés vers la page "Outils de développement"

Bien que la solution ci-dessus fonctionne pour les scripts de contenu, le code injecté directement dans la page (par exemple, en ajoutant une balise <script> ou via inspectedWindow.eval) nécessite une stratégie différente. Dans ce contexte, runtime.sendMessage ne transmettra pas de messages au script en arrière-plan comme prévu.

Pour contourner ce problème, vous pouvez combiner le script injecté avec un script de contenu qui joue le rôle d'intermédiaire. Pour transmettre des messages au script de contenu, vous pouvez utiliser l'API window.postMessage. Voici un exemple, en supposant le script d'arrière-plan de la section précédente:

// injected-script.js

window.postMessage({
  greeting: 'hello there!',
  source: 'my-devtools-extension'
}, '*');
// content-script.js

window.addEventListener('message', function(event) {
  // Only accept messages from the same frame
  if (event.source !== window) {
    return;
  }

  var message = event.data;

  // Only accept messages that we know are ours
  if (typeof message !== 'object' || message === null ||
      !message.source === 'my-devtools-extension') {
    return;
  }

  chrome.runtime.sendMessage(message);
});

Votre message passe maintenant du script injecté au script de contenu, au script d'arrière-plan et enfin à la page des outils de développement.

Vous pouvez également envisager deux autres techniques de transmission de message ici.

Détecter quand les outils de développement s'ouvrent et se ferment

Si votre extension doit vérifier si la fenêtre "Outils de développement" est ouverte, vous pouvez ajouter un écouteur onConnect à la page en arrière-plan et appeler connect depuis cette page. Étant donné que chaque onglet peut avoir sa propre fenêtre "Outils de développement", vous pouvez recevoir plusieurs événements d'association. Pour savoir si une fenêtre DevTools est ouverte, vous devez comptabiliser les événements de connexion et de déconnexion, comme indiqué ci-dessous:

// background.js
var openCount = 0;
chrome.runtime.onConnect.addListener(function (port) {
    if (port.name == "devtools-page") {
      if (openCount == 0) {
        alert("DevTools window opening.");
      }
      openCount++;

      port.onDisconnect.addListener(function(port) {
          openCount--;
          if (openCount == 0) {
            alert("Last DevTools window closing.");
          }
      });
    }
});

La page "Outils de développement" crée une connexion comme celle-ci:

// devtools.js

// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "devtools-page"
});

Exemples d'extensions DevTools

Parcourez la source des exemples d'extensions DevTools suivants:

Plus d'informations

Pour plus d'informations sur les API standards dont peuvent se servir les extensions, consultez chrome*. des API et des API Web.

Donnez-nous votre avis. Ils nous aident à améliorer les API.

Exemples

Vous trouverez des exemples d'utilisation des API DevTools dans la section Exemples.