Расширение инструментов разработчика

Обзор

Расширение DevTools добавляет функциональность в инструменты разработчика Chrome. Оно может добавлять новые панели пользовательского интерфейса и боковые панели, взаимодействовать с проверяемой страницей, получать информацию о сетевых запросах и многое другое. Посмотреть рекомендуемые расширения DevTools . Расширения DevTools имеют доступ к дополнительному набору API, специфичных для DevTools:

Расширение DevTools имеет такую ​​же структуру, как и любое другое расширение: оно может включать фоновую страницу, скрипты контента и другие элементы. Кроме того, каждое расширение DevTools имеет страницу DevTools, которая предоставляет доступ к API DevTools.

Архитектурная схема, показывающая взаимодействие страницы DevTools с проверяемым окном и фоновой страницей. Фоновая страница показана взаимодействующей со скриптами содержимого и получающей доступ к API расширений. Страница DevTools имеет доступ к API DevTools, например, для создания панелей.

Страница DevTools

Экземпляр страницы DevTools расширения создается каждый раз при открытии окна DevTools. Страница DevTools существует в течение всего времени работы окна DevTools. Страница DevTools имеет доступ к API DevTools и ограниченному набору API расширений. В частности, страница DevTools может:

  • Создавайте панели и взаимодействуйте с ними, используя API devtools.panels .
  • Получите информацию об проверяемом окне и выполните код в проверяемом окне, используя API devtools.inspectedWindow .
  • Получайте информацию о сетевых запросах с помощью API devtools.network .

Страница DevTools не может напрямую использовать большинство API расширений. Она имеет доступ к тому же подмножеству API extension и runtime , что и скрипт контента. Подобно скрипту контента, страница DevTools может взаимодействовать с фоновой страницей с помощью передачи сообщений . Пример см. в разделе «Внедрение скрипта контента» .

Создание расширения для инструментов разработчика

Чтобы создать страницу инструментов разработчика для вашего расширения, добавьте поле devtools_page в манифест расширения:

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

Для каждого открытого окна DevTools создается экземпляр страницы devtools_page указанной в манифесте вашего расширения. Эта страница может добавлять другие страницы расширений в качестве панелей и боковых панелей в окно DevTools, используя API devtools.panels .

Модули API chrome.devtools.* доступны только для страниц, загруженных в окне инструментов разработчика. Скрипты контента и другие страницы расширений не имеют этих API. Таким образом, API доступны только в течение всего времени работы окна инструментов разработчика.

Также существуют некоторые API инструментов разработчика, которые все еще находятся в экспериментальной стадии. Список экспериментальных API и рекомендации по их использованию см. в разделе chrome.experimental.* APIs.

Элементы пользовательского интерфейса DevTools: панели и боковые панели.

Помимо обычных элементов пользовательского интерфейса расширений, таких как действия браузера, контекстные меню и всплывающие окна, расширение DevTools может добавлять элементы пользовательского интерфейса в окно DevTools:

  • Панель — это вкладка верхнего уровня, подобная панелям «Элементы», «Источники» и «Сеть».
  • Боковая панель отображает дополнительный пользовательский интерфейс, связанный с панелью. Примерами боковых панелей являются панели «Стили», «Вычисляемые стили» и «Обработчики событий» на панели «Элементы». (Обратите внимание, что внешний вид боковых панелей может не соответствовать изображению в зависимости от используемой версии Chrome и положения окна «Инструменты разработчика».)

Окно инструментов разработчика, отображающее панель «Элементы» и боковую панель «Стили».

Каждая панель представляет собой отдельный HTML-файл, который может содержать другие ресурсы (JavaScript, CSS, изображения и т. д.). Создание базовой панели выглядит следующим образом:

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

JavaScript, выполняемый в панели или боковой панели, имеет доступ к тем же API, что и страница инструментов разработчика.

Создание базовой боковой панели для панели «Элементы» выглядит следующим образом:

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

Существует несколько способов отображения контента в боковой панели:

  • HTML-содержимое. Вызовите setPage , чтобы указать HTML-страницу для отображения в панели.
  • Данные в формате JSON. Передайте объект JSON в setObject .
  • Выражение JavaScript. Передайте выражение в setExpression . Инструменты разработчика вычислят выражение в контексте проверяемой страницы и отобразят возвращаемое значение.

Для методов setObject и setExpression панель отображает значение так, как оно выглядело бы в консоли инструментов разработчика. Однако setExpression позволяет отображать элементы DOM и произвольные объекты JavaScript, в то время как setObject поддерживает только объекты JSON.

Взаимодействие между компонентами расширения

В следующих разделах описаны некоторые типичные сценарии взаимодействия между различными компонентами расширения DevTools.

Внедрение скрипта контента

Страница «Инструменты разработчика» не может напрямую вызывать tabs.executeScript . Чтобы внедрить скрипт содержимого со страницы «Инструменты разработчика», необходимо получить идентификатор вкладки проверяемого окна, используя свойство inspectedWindow.tabId , и отправить сообщение на фоновую страницу. С фоновой страницы вызовите tabs.executeScript для внедрения скрипта.

Приведенные ниже фрагменты кода показывают, как внедрить скрипт содержимого с помощью 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"
});

Код для фоновой страницы:

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

Выполнение кода JavaScript в проверяемом окне

You can use the inspectedWindow.eval method to execute JavaScript code in the context of the inspected page. You can invoke the eval method from a DevTools page, panel or sidebar pane.

По умолчанию выражение вычисляется в контексте основного фрейма страницы. Возможно, вам уже знакомы возможности API командной строки DevTools, такие как проверка элементов ( inspect(elem) ), остановка выполнения при выполнении функций ( debug(fn) ), копирование в буфер обмена ( copy() ) и многое другое. Метод inspectedWindow.eval() использует тот же контекст выполнения скрипта и параметры, что и код, вводимый в консоли DevTools, что позволяет получить доступ к этим API внутри функции eval. Например, SOAK использует его для проверки элемента:

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

В качестве альтернативы используйте параметр useContentScriptContext: true для inspectedWindow.eval() , чтобы оценить выражение в том же контексте, что и скрипты содержимого. Вызов eval с useContentScriptContext: true не создает контекст скрипта содержимого, поэтому перед вызовом eval необходимо загрузить контекстный скрипт, либо вызвав executeScript , либо указав скрипт содержимого в файле manifest.json .

После создания контекстного скрипта вы можете использовать эту опцию для внедрения дополнительных скриптов содержимого.

Метод eval эффективен при правильном использовании и опасен при неправильном применении. Используйте метод tabs.executeScript , если вам не нужен доступ к контексту JavaScript проверяемой страницы. Подробные предостережения и сравнение двух методов см. в inspectedWindow .

Передача выбранного элемента в скрипт контента.

Скрипт содержимого не имеет прямого доступа к текущему выбранному элементу. Однако любой код, выполняемый с помощью inspectedWindow.eval имеет доступ к консоли DevTools и API командной строки. Например, в выполняемом коде вы можете использовать $0 для доступа к выбранному элементу.

Чтобы передать выбранный элемент в скрипт содержимого:

  • Создайте в скрипте содержимого метод, который принимает выбранный элемент в качестве аргумента.
  • Вызовите метод со страницы DevTools, используя inspectedWindow.eval с параметром useContentScriptContext: true .

Код в вашем скрипте контента может выглядеть примерно так:

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

Вызовите метод со страницы инструментов разработчика следующим образом:

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

Параметр useContentScriptContext: true указывает, что выражение должно вычисляться в том же контексте, что и скрипты содержимого, чтобы иметь доступ к методу setSelectedElement .

Получение window справочной панели

Для postMessage из панели инструментов разработчика вам потребуется ссылка на её объект window . Получить iframe-окно панели можно из обработчика события panel.onShown :

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

Передача сообщений из скриптов контента на страницу инструментов разработчика.

Обмен сообщениями между страницей «Инструменты разработчика» и скриптами контента происходит косвенно, через фоновую страницу.

При отправке сообщения скрипту содержимого фоновая страница может использовать метод tabs.sendMessage , который направляет сообщение скриптам содержимого в определенной вкладке, как показано в разделе «Внедрение скрипта содержимого» .

When sending a message from a content script, there is no ready-made method to deliver a message to the correct DevTools page instance associated with the current tab. As a workaround, you can have the DevTools page establish a long-lived connection with the background page, and have the background page keep a map of tab IDs to connections, so it can route each message to the correct connection.

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

На странице (или в панели, или в боковой панели) инструменты разработчика устанавливают соединение следующим образом:

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

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

Передача сообщений от внедренных скриптов на страницу инструментов разработчика.

Хотя описанное выше решение работает для скриптов содержимого, код, внедряемый непосредственно на страницу (например, путем добавления тега <script> или через inspectedWindow.eval ), требует другой стратегии. В этом контексте runtime.sendMessage не будет передавать сообщения фоновому скрипту должным образом.

В качестве обходного пути вы можете объединить внедренный скрипт со скриптом содержимого, который выступает в качестве посредника. Для передачи сообщений в скрипт содержимого вы можете использовать API window.postMessage . Вот пример, предполагающий использование фонового скрипта из предыдущего раздела:

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

Теперь ваше сообщение будет передаваться от внедренного скрипта к скрипту содержимого, затем к фоновому скрипту и, наконец, на страницу инструментов разработчика.

Здесь также можно рассмотреть два альтернативных метода передачи сообщений .

Определение момента открытия и закрытия DevTools

Если вашему расширению необходимо отслеживать, открыто ли окно «Инструменты разработчика», вы можете добавить обработчик события onConnect на фоновую страницу и вызывать метод connect со страницы «Инструменты разработчика». Поскольку каждая вкладка может иметь открытое окно «Инструменты разработчика», вы можете получать несколько событий connect. Чтобы отслеживать, открыто ли какое-либо окно «Инструменты разработчика», вам необходимо подсчитать события connect и disconnect, как показано ниже:

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

На странице «Инструменты разработчика» устанавливается соединение следующим образом:

// devtools.js

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

Примеры расширений DevTools

Просмотрите исходный код этих примеров расширений DevTools:

  • Расширение Polymer Devtools — использует множество вспомогательных функций, работающих на главной странице, для запроса состояния DOM/JS и отправки его обратно в пользовательскую панель.
  • Расширение React DevTools — использует подмодуль Blink для повторного использования компонентов пользовательского интерфейса DevTools.
  • Ember Inspector — общее ядро ​​расширений с адаптерами для Chrome и Firefox.
  • Coquette-inspect — это простое расширение на основе React с агентом отладки, внедряемым в главную страницу.
  • В нашей галерее расширений DevTools и в разделе «Примеры расширений» вы найдете еще больше полезных приложений, которые можно установить, попробовать и изучить.

Более подробная информация

Для получения информации о стандартных API, которые могут использовать расширения, см. API chrome.* и веб-API .

Оставляйте отзывы! Ваши комментарии и предложения помогают нам улучшать API.

Примеры

Примеры использования API DevTools можно найти в разделе «Примеры» .