Extender Herramientas para desarrolladores

Las extensiones de DevTools agregan funciones a las Herramientas para desarrolladores de Chrome, ya que acceden a APIs de extensiones específicas de DevTools a través de una página de DevTools que se agrega a la extensión.

Diagrama de arquitectura que muestra la página de Herramientas para desarrolladores que se comunica con la ventana inspeccionada y el service worker. Se muestra el service worker comunicándose con las secuencias de comandos de contenido y accediendo a las APIs de extensión.
         La página de DevTools tiene acceso a las APIs de DevTools, por ejemplo, para crear paneles.
Arquitectura de la extensión de DevTools.

Las APIs de extensión específicas de DevTools incluyen las siguientes:

La página de Herramientas para desarrolladores

Cuando se abre una ventana de Herramientas para desarrolladores, una extensión de Herramientas para desarrolladores crea una instancia de su página de Herramientas para desarrolladores que existe mientras la ventana esté abierta. Esta página tiene acceso a las APIs de DevTools y de extensiones, y puede hacer lo siguiente:

La página de Herramientas para desarrolladores puede acceder directamente a las APIs de extensiones. Esto incluye la capacidad de comunicarse con el service worker a través del paso de mensajes.

Crea una extensión de DevTools

Para crear una página de DevTools para tu extensión, agrega el campo devtools_page en el manifiesto de la extensión:

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

El campo devtools_page debe dirigir a una página HTML. Dado que la página de Herramientas para desarrolladores debe ser local para tu extensión, te recomendamos que la especifiques con una URL relativa.

Los miembros de la API de chrome.devtools solo están disponibles para las páginas cargadas dentro de la ventana de Herramientas para desarrolladores mientras esa ventana está abierta. Las secuencias de comandos de contenido y otras páginas de extensión no tienen acceso a estas APIs.

El espacio de nombres del navegador y las extensiones de DevTools

El espacio de nombres browser introducido en Chrome 148 está desactivado para las extensiones que declaran devtools_page. La inhabilitación se aplica a toda la extensión, no solo a la página de Herramientas para desarrolladores, sino a cada contexto de secuencia de comandos en el que se ejecutan las APIs de la extensión. Sigue usando chrome.* en todas estas extensiones.

El motivo es una brecha de compatibilidad con webextension-polyfill. Las APIs de chrome.devtools.* son solo de devolución de llamada (aún no devuelven promesas de forma nativa), por lo que las extensiones de DevTools suelen depender del polyfill para encapsularlas. El polyfill omite la encapsulación siempre que se defina browser, ya que supone que el host ya realizó el trabajo. Si Chrome habilitara browser para estas extensiones, el polyfill no haría nada y las llamadas a chrome.devtools.* dejarían de devolver Promises. Si mantienes browser desactivado, el polyfill seguirá envolviendo.

La misma inhabilitación también inhabilita los otros cambios en la API de mensajería de Chrome 148 para estas extensiones, incluidas las respuestas de Promise en runtime.onMessage. La restricción se levantará una vez que las APIs de Herramientas para desarrolladores admitan Promises de forma nativa.

Elementos de la IU de Herramientas para desarrolladores: paneles y paneles laterales

Además de los elementos de IU habituales de las extensiones, como las acciones del navegador, los menús contextuales y las ventanas emergentes, una extensión de Herramientas para desarrolladores puede agregar elementos de IU a la ventana de Herramientas para desarrolladores:

  • Un panel es una pestaña de nivel superior, como los paneles Elements, Sources y Network.
  • Un panel lateral presenta una IU complementaria relacionada con un panel. Los paneles Styles, Computed Styles y Event Listeners del panel Elements son ejemplos de paneles laterales. Según la versión de Chrome que uses y dónde esté anclada la ventana de DevTools, es posible que los paneles de la barra lateral se vean como en la siguiente imagen de ejemplo:
Ventana de Herramientas para desarrolladores que muestra el panel Elementos y el panel lateral Estilos.
Ventana de Herramientas para desarrolladores que muestra el panel Elementos y el panel lateral Estilos.

Cada panel es su propio archivo HTML, que puede incluir otros recursos (JavaScript, CSS, imágenes, etc.). Para crear un panel básico, usa el siguiente código:

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

El código JavaScript que se ejecuta en un panel o en un panel lateral tiene acceso a las mismas APIs que la página de Herramientas para desarrolladores.

Para crear un panel lateral básico, usa el siguiente código:

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

Hay varias formas de mostrar contenido en un panel lateral:

  • Contenido HTML: Llama a setPage() para especificar una página HTML que se mostrará en el panel.
  • Datos JSON: Pasa un objeto JSON a setObject().
  • Expresión de JavaScript: Pasa una expresión a setExpression(). Las Herramientas para desarrolladores evalúan la expresión en el contexto de la página inspeccionada y, luego, muestran el valor devuelto.

Tanto para setObject() como para setExpression(), el panel muestra el valor tal como aparecería en la consola de Herramientas para desarrolladores. Sin embargo, setExpression() te permite mostrar elementos del DOM y objetos JavaScript arbitrarios, mientras que setObject() solo admite objetos JSON.

Comunicación entre los componentes de la extensión

En las siguientes secciones, se describen algunas formas útiles de permitir que los componentes de la extensión de DevTools se comuniquen entre sí.

Cómo insertar una secuencia de comandos de contenido

Para insertar una secuencia de comandos de contenido, usa scripting.executeScript():

// DevTools page -- devtools.js
chrome.scripting.executeScript({
  target: {
    tabId: chrome.devtools.inspectedWindow.tabId
  },
  files: ["content_script.js"]
});

Puedes recuperar el ID de la pestaña de la ventana inspeccionada con la propiedad inspectedWindow.tabId.

Si ya se insertó una secuencia de comandos de contenido, puedes usar las APIs de mensajería para comunicarte con ella.

Evalúa JavaScript en la ventana inspeccionada

Puedes usar el método inspectedWindow.eval() para ejecutar código JavaScript en el contexto de la página inspeccionada. Puedes invocar el método eval() desde una página, un panel o un panel lateral de Herramientas para desarrolladores.

De forma predeterminada, la expresión se evalúa en el contexto del marco principal de la página. inspectedWindow.eval() usa el mismo contexto y las mismas opciones de ejecución de secuencias de comandos que el código ingresado en la consola de Herramientas para desarrolladores, lo que permite acceder a las funciones de la API de Console Utilities de Herramientas para desarrolladores cuando se usa eval(). Por ejemplo, úsalo para inspeccionar el primer elemento de secuencia de comandos dentro de la sección <head> del documento HTML:

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

También puedes establecer useContentScriptContext en true cuando llames a inspectedWindow.eval() para evaluar la expresión en el mismo contexto que las secuencias de comandos de contenido. Para usar esta opción, usa una declaración de secuencia de comandos de contenido estática antes de llamar a eval(), ya sea llamando a executeScript() o especificando una secuencia de comandos de contenido en el archivo manifest.json. Después de que se cargue el contexto de la secuencia de comandos de contenido, también puedes usar esta opción para insertar secuencias de comandos de contenido adicionales.

Cómo pasar el elemento seleccionado a un script de contenido

La secuencia de comandos de contenido no tiene acceso directo al elemento seleccionado actual. Sin embargo, cualquier código que ejecutes con inspectedWindow.eval() tendrá acceso a la consola de Herramientas para desarrolladores y a las APIs de Console Utilities. Por ejemplo, en el código evaluado, puedes usar $0 para acceder al elemento seleccionado.

Para pasar el elemento seleccionado a una secuencia de comandos de contenido, haz lo siguiente:

  1. Crea un método en la secuencia de comandos de contenido que tome el elemento seleccionado como argumento.

    function setSelectedElement(el) {
        // do something with the selected element
    }
    
  2. Llama al método desde la página de Herramientas para desarrolladores con inspectedWindow.eval() y la opción useContentScriptContext: true.

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

La opción useContentScriptContext: true especifica que la expresión se debe evaluar en el mismo contexto que las secuencias de comandos de contenido, por lo que puede acceder al método setSelectedElement.

Obtén el window de un panel de referencia

Para llamar a postMessage() desde un panel de Herramientas para desarrolladores, necesitarás una referencia a su objeto window. Obtén la ventana del iframe de un panel desde el controlador de eventos panel.onShown:

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

Envía mensajes desde secuencias de comandos insertadas a la página de Herramientas para desarrolladores

El código insertado directamente en la página sin una secuencia de comandos de contenido, incluso si se agrega una etiqueta <script> o se llama a inspectedWindow.eval(), no puede enviar mensajes a la página de Herramientas para desarrolladores con runtime.sendMessage(). En cambio, te recomendamos que combines tu secuencia de comandos insertada con una secuencia de comandos de contenido que pueda actuar como intermediaria y que uses el método window.postMessage(). En el siguiente ejemplo, se usa el script de segundo plano de la sección anterior:

// 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. Note that this is not foolproof
  // and the page can easily spoof messages if it wants to.
  if (typeof message !== 'object' || message === null ||
      message.source !== 'my-devtools-extension') {
    return;
  }

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

Puedes encontrar otras técnicas alternativas de transmisión de mensajes en GitHub.

Detecta cuándo se abren y cierran las Herramientas para desarrolladores

Para hacer un seguimiento de si la ventana de Herramientas para desarrolladores está abierta, agrega un objeto de escucha onConnect al service worker y llama a connect() desde la página de Herramientas para desarrolladores. Dado que cada pestaña puede tener su propia ventana de Herramientas para desarrolladores abierta, es posible que recibas varios eventos de conexión. Para hacer un seguimiento de si alguna ventana de Herramientas para desarrolladores está abierta, cuenta los eventos de conexión y desconexión como se muestra en el siguiente ejemplo:

// 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 página de Herramientas para desarrolladores crea una conexión como esta:

// devtools.js

// Create a connection to the service worker
const serviceWorkerConnection = chrome.runtime.connect({
    name: "devtools-page"
});

// Send a periodic heartbeat to keep the port open.
setInterval(() => {
  port.postMessage("heartbeat");
}, 15000);

Ejemplos de extensiones de DevTools

Los ejemplos de esta página provienen de las siguientes páginas:

  • Extensión de Polymer Devtools: Usa muchos asistentes que se ejecutan en la página host para consultar el estado del DOM/JS y enviarlo de vuelta al panel personalizado.
  • Extensión de React DevTools: Usa un submódulo del renderizador para reutilizar los componentes de la IU de DevTools.
  • Ember Inspector: Es el núcleo de la extensión compartida con adaptadores para Chrome y Firefox.
  • Coquette-inspect: Una extensión limpia basada en React con un agente de depuración insertado en la página host.
  • Las extensiones de muestra tienen más extensiones valiosas para instalar, probar y aprender.

Más información

Para obtener información sobre las APIs estándar que pueden usar las extensiones, consulta chrome.* APIs y APIs web.

Envíanos tus comentarios Tus comentarios y sugerencias nos ayudan a mejorar las APIs.

Ejemplos

Puedes encontrar ejemplos que usan las APIs de DevTools en Samples.