Cómo extender Herramientas para desarrolladores

Descripción general

Una extensión de Herramientas para desarrolladores agrega funcionalidad a las Herramientas para desarrolladores de Chrome. Puede agregar nuevos paneles de IU y barras laterales, interactuar con la página inspeccionada, obtener información sobre solicitudes de red y mucho más. Consulta las extensiones destacadas de Herramientas para desarrolladores. Las extensiones de Herramientas para desarrolladores tienen acceso a un conjunto adicional de APIs de extensión específicas de Herramientas para desarrolladores:

Una extensión de Herramientas para desarrolladores se estructura como cualquier otra extensión: puede tener una página en segundo plano, secuencias de comandos de contenido y otros elementos. Además, cada extensión de Herramientas para desarrolladores tiene una página de Herramientas para desarrolladores, que tiene acceso a sus APIs.

Diagrama de arquitectura en el que se muestra la página de Herramientas para desarrolladores que se comunica con la ventana inspeccionada y la página en segundo plano. Se muestra la página en segundo plano en comunicación con las secuencias de comandos de contenido y el acceso a las APIs de extensiones.
       La página Herramientas para desarrolladores tiene acceso a las API de Herramientas para desarrolladores; por ejemplo, para crear paneles.

La página Herramientas para desarrolladores

Cada vez que se abre una ventana de Herramientas para desarrolladores, se crea una instancia de la página de Herramientas para desarrolladores de la extensión. La página de Herramientas para desarrolladores existe durante toda la ventana de Herramientas para desarrolladores. La página de Herramientas para desarrolladores tiene acceso a las APIs de Herramientas para desarrolladores y a un conjunto limitado de APIs de extensiones. Específicamente, la página de Herramientas para desarrolladores puede hacer lo siguiente:

  • Crea paneles y, además, interactúa con ellos mediante las APIs de devtools.panels.
  • Obtén información sobre la ventana inspeccionada y evalúa el código en ella con las APIs de devtools.inspectedWindow.
  • Obtén información sobre las solicitudes de red con las APIs de devtools.network.

La página de Herramientas para desarrolladores no puede usar la mayoría de las APIs de extensiones directamente. Tiene acceso al mismo subconjunto de las APIs de extension y runtime a las que tiene acceso una secuencia de comandos de contenido. Al igual que una secuencia de comandos de contenido, una página de Herramientas para desarrolladores puede comunicarse con la página en segundo plano mediante la función Paso de mensajes. Para ver un ejemplo, consulta Cómo insertar una secuencia de comandos de contenido.

Crea una extensión de Herramientas para desarrolladores

A fin de crear una página de Herramientas para desarrolladores para tu extensión, agrega el campo devtools_page en el manifiesto de la extensión:

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

Se crea una instancia del devtools_page especificado en el manifiesto de tu extensión por cada ventana de Herramientas para desarrolladores abierta. La página puede agregar otras páginas de extensiones como paneles y barras laterales a la ventana de Herramientas para desarrolladores mediante la API de devtools.panels.

Los módulos de la API chrome.devtools.* solo están disponibles para las páginas cargadas dentro de la ventana de Herramientas para desarrolladores. Las secuencias de comandos de contenido y otras páginas de extensiones no tienen estas APIs. Por lo tanto, las APIs solo están disponibles durante la vida útil de la ventana de Herramientas para desarrolladores.

También hay algunas APIs de Herramientas para desarrolladores que aún son experimentales. Consulta chrome.experimental.* APIs para obtener la lista de APIs experimentales y los lineamientos sobre su uso.

Elementos de la IU de Herramientas para desarrolladores: paneles y paneles de la barra lateral

Además de los elementos habituales de la IU de la extensión, como las acciones del navegador, los menús contextuales y las ventanas emergentes, una extensión de DevOps puede agregar elementos de la 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 de la barra lateral presenta una IU complementaria relacionada con un panel. Los paneles Styles, Computed Styles y Event Listeners del panel Elements son ejemplos de paneles de la barra lateral. (ten en cuenta que la apariencia de los paneles de la barra lateral puede no coincidir con la imagen, según la versión de Chrome que uses y el lugar donde esté anclada la ventana de Herramientas para desarrolladores).

Ventana de Herramientas para desarrolladores que muestra el panel Elements y el panel de la barra lateral Styles.

Cada panel tiene su propio archivo HTML, que puede incluir otros recursos (JavaScript, CSS, imágenes, etcétera). La creación de un panel básico se ve de la siguiente manera:

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

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

La creación de un panel básico de la barra lateral para el panel de elementos se verá de la siguiente manera:

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

Existen varias formas de mostrar contenido en un panel de la barra 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. Herramientas para desarrolladores evalúa la expresión en el contexto de la página inspeccionada y muestra el valor que se muestra.

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

Comunicación entre los componentes de extensión

En las siguientes secciones, se describen algunas situaciones típicas para la comunicación entre los diferentes componentes de una extensión de Herramientas para desarrolladores.

Cómo insertar una secuencia de comandos del contenido

La página de Herramientas para desarrolladores no puede llamar a tabs.executeScript directamente. Para insertar una secuencia de comandos de contenido desde la página de Herramientas para desarrolladores, debes recuperar el ID de la pestaña de la ventana inspeccionada con la propiedad inspectedWindow.tabId y enviar un mensaje a la página en segundo plano. En la página en segundo plano, llama a tabs.executeScript para insertar la secuencia de comandos.

En los siguientes fragmentos de código, se muestra cómo insertar una secuencia de comandos de contenido con 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"
});

Código para la página de fondo:

// 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);
    });
});

Evaluar JavaScript en la ventana inspeccionada

Puedes usar el método inspectedWindow.eval para ejecutar el 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 de la barra lateral de Herramientas para desarrolladores.

De forma predeterminada, la expresión se evalúa en el contexto del marco principal de la página. Es posible que conozcas las funciones de la API de línea de comandos de las Herramientas para desarrolladores, como la inspección de elementos (inspect(elem)), el desglose de funciones (debug(fn)), la copia en el portapapeles (copy()) y mucho más. inspectedWindow.eval() usa el mismo contexto de ejecución de secuencias de comandos y las mismas opciones que el código escrito en la consola de Herramientas para desarrolladores, lo que permite el acceso a estas APIs dentro de la evaluación. Por ejemplo, SOAK lo usa para inspeccionar un elemento:

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

Como alternativa, usa la opción useContentScriptContext: true para que inspectedWindow.eval() evalúe la expresión en el mismo contexto que las secuencias de comandos de contenido. Llamar a eval con useContentScriptContext: true no crea un contexto de secuencia de comandos de contenido, por lo que debes cargar una secuencia de comandos contextual antes de llamar a eval, ya sea llamando a executeScript o especificando una secuencia de comandos de contenido en el archivo manifest.json.

Una vez que exista el contexto de la secuencia de comandos contextual, puedes usar esta opción para insertar secuencias de comandos de contenido adicionales.

El método eval es potente cuando se usa en el contexto correcto y peligroso cuando se usa de manera inapropiada. Usa el método tabs.executeScript si no necesitas acceso al contexto de JavaScript de la página inspeccionada. Para obtener precauciones detalladas y una comparación de los dos métodos, consulta inspectedWindow.

Pasa el elemento seleccionado a una secuencia de comandos del contenido

La secuencia de comandos del contenido no tiene acceso directo al elemento seleccionado actualmente. Sin embargo, cualquier código que ejecutes con inspectedWindow.eval tiene acceso a la consola de Herramientas para desarrolladores y a las APIs de línea de comandos. 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 del contenido, haz lo siguiente:

  • Crea un método en la secuencia de comandos del contenido que tome el elemento seleccionado como argumento.
  • Llama al método desde la página de Herramientas para desarrolladores mediante inspectedWindow.eval con la opción useContentScriptContext: true.

El código de tu secuencia de comandos de contenido podría ser similar al siguiente:

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

Invoca el método desde la página de Herramientas para desarrolladores de la siguiente manera:

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 para que pueda acceder al método setSelectedElement.

Obtén el window de un panel de referencia

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

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

Mensajes de las secuencias de comandos de contenido a la página de Herramientas para desarrolladores

La mensajería entre la página de Herramientas para desarrolladores y las secuencias de comandos de contenido es indirecta, a través de la página en segundo plano.

Cuando envías un mensaje a una secuencia de comandos de contenido, la página en segundo plano puede usar el método tabs.sendMessage, que dirige un mensaje a las secuencias de comandos de contenido de una pestaña específica, como se muestra en Cómo insertar una secuencia de comandos de contenido.

Cuando envías un mensaje desde una secuencia de comandos de contenido, no hay un método listo para enviar un mensaje a la instancia de la página de Herramientas para desarrolladores correcta asociada con la pestaña actual. Como solución alternativa, puedes hacer que la página de Herramientas para desarrolladores establezca una conexión de larga duración con la página en segundo plano, y esta última mantenga un mapa de los IDs de pestaña a las conexiones para que pueda enrutar cada mensaje a la conexión correcta.

// 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 página de Herramientas para desarrolladores (o panel o panel de la barra lateral) establece la conexión de la siguiente manera:

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

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

Mensaje de las secuencias de comandos insertadas en la página de Herramientas para desarrolladores

Si bien la solución anterior funciona para secuencias de comandos de contenido, el código que se inserta directamente en la página (p.ej., mediante la adición de una etiqueta <script> o a través de inspectedWindow.eval) requiere una estrategia diferente. En este contexto, runtime.sendMessage no pasará mensajes a la secuencia de comandos en segundo plano como se espera.

Como solución alternativa, puedes combinar la secuencia de comandos insertada con una secuencia de comandos de contenido que actúe como intermediario. Para pasar mensajes a la secuencia de comandos de contenido, puedes usar la API de window.postMessage. A continuación, se muestra un ejemplo, suponiendo que se trata de la secuencia de comandos en 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
  if (typeof message !== 'object' || message === null ||
      !message.source === 'my-devtools-extension') {
    return;
  }

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

Tu mensaje fluirá desde la secuencia de comandos inyectada hasta la secuencia de comandos de contenido, la secuencia de comandos en segundo plano y, por último, la página de Herramientas para desarrolladores.

También puedes considerar dos técnicas alternativas para transmitir mensajes aquí.

Cómo detectar cuándo se abre y se cierra Herramientas para desarrolladores

Si tu extensión necesita hacer un seguimiento para saber si la ventana de Herramientas para desarrolladores está abierta, puedes agregar un objeto de escucha onConnect a la página en segundo plano y llamar 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 DevOps está abierta, debes contar los eventos de conexión y desconexión como se muestra a continuación:

// 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 Herramientas para desarrolladores crea una conexión como la que se muestra a continuación:

// devtools.js

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

Ejemplos de extensión de Herramientas para desarrolladores

Explora la fuente de estos ejemplos de extensión de Herramientas para desarrolladores:

Más información

Para obtener información sobre las API 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 en los que se usen las APIs de Herramientas para desarrolladores en la sección Muestras.