扩展开发者工具

开发者工具扩展程序通过访问添加到扩展程序的开发者工具页面中的开发者工具专用扩展程序 API,为 Chrome 开发者工具添加功能。

架构图,显示了 DevTools 页面与受检查窗口和服务工作器之间的通信。该图显示了 Service Worker 与内容脚本之间的通信,以及对扩展程序 API 的访问。开发者工具页面可以访问开发者工具 API,例如创建面板。
开发者工具扩展程序架构。

开发者工具专用扩展程序 API 包括以下内容:

开发者工具页面

当开发者工具窗口打开时,开发者工具扩展程序会创建其开发者工具页面的实例,该实例在窗口打开期间存在。此页面可以访问开发者工具 API 和扩展程序 API,并且可以执行以下操作:

开发者工具页面可以直接访问扩展程序 API。这包括能够 使用 消息传递与 Service Worker 通信。

创建开发者工具扩展程序

如需为扩展程序创建开发者工具页面,请在扩展程序清单中添加 devtools_page 字段:

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

devtools_page 字段必须指向 HTML 页面。由于开发者工具页面必须是扩展程序的本地页面,因此我们建议您使用相对网址指定该页面。

chrome.devtools API 的成员仅在开发者工具窗口打开时可供该窗口内加载的页面使用。内容脚本和其他扩展程序页面无权访问这些 API。

浏览器命名空间和开发者工具扩展程序

在 Chrome 152 及更高版本中,具有开发者工具页面的扩展程序可以使用 browser 命名空间。

在低于 152 的 Chrome 版本中,browser 命名空间 对于声明 devtools_page 的扩展程序处于关闭状态。选择停用适用于整个扩展程序,而不仅仅是开发者工具页面,而是扩展程序 API 运行的每个脚本上下文。

原因是与 webextension-polyfill存在兼容性差距。 Chrome 152 之前的 chrome.devtools.* API 仅支持回调,它们不会原生返回 Promise,因此开发者工具扩展程序通常依赖于 polyfill 来封装它们。每当定义 browser 时,polyfill 都会跳过封装,并假定宿主已完成此工作。如果 Chrome 为这些扩展程序启用了 browser,polyfill 将不会执行任何操作,并且 chrome.devtools.* 调用将停止返回 Promise。关闭 browser 可让 polyfill 继续封装。

相同的选择停用也会为这些扩展程序停用其他 Chrome 148 消息传递 API 更改 ,包括 Promise responses in runtime.onMessage。 一旦开发者工具 API 原生支持 Promise,此限制就会解除。

开发者工具界面元素:面板和边栏窗格

除了常见的扩展程序界面元素(例如浏览器操作、上下文菜单和弹出式窗口)之外,开发者工具扩展程序还可以向开发者工具窗口添加界面元素:

  • 面板是一种顶级标签页,例如“元素”“来源”和“网络”面板。
  • 边栏窗格会显示与面板相关的补充界面。 “元素”面板上的“样式”“计算样式”和“事件监听器”窗格是边栏窗格的示例。根据您使用的 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 对象。

在扩展程序组件之间通信

以下部分介绍了一些有用的方法,可让开发者工具扩展程序组件彼此通信。

注入内容脚本

如需注入内容脚本,请使用 scripting.executeScript()

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

您可以使用 inspectedWindow.tabId 属性检索检查窗口的标签页 ID。

如果已注入内容脚本,您可以使用消息传递 API 与其通信。

在检查窗口中评估 JavaScript

您可以使用 inspectedWindow.eval() 方法在检查页面的上下文中执行 JavaScript 代码。您可以从开发者工具页面、面板或边栏窗格调用 eval() 方法。

默认情况下,表达式会在页面的主框架的上下文中进行评估。 inspectedWindow.eval() 使用与代码 在开发者工具控制台中输入的相同的脚本执行上下文和选项,这允许在使用 eval() 时访问开发者工具 控制台实用程序 API 功能。例如,使用它来检查 HTML 文档的 <head> 部分中的第一个脚本元素:

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

您还可以在调用 inspectedWindow.eval() 时将 useContentScriptContext 设置为 true,以便在与内容脚本相同的上下文中评估表达式。如需使用此选项,请先使用静态内容脚本声明,然后再调用 eval(),方法是调用 executeScript() 或在 manifest.json 文件中指定内容 脚本。内容脚本上下文加载后,您还可以使用此选项注入其他内容脚本。

将所选元素传递给内容脚本

内容脚本无法直接访问当前所选元素。不过,您使用 inspectedWindow.eval() 执行的任何代码都可以访问开发者工具 控制台和控制台实用程序 API。例如,在评估的代码中,您可以使用 $0 访问所选元素。

如需将所选元素传递给内容脚本,请执行以下操作:

  1. 在内容脚本中创建一个方法,该方法将所选元素作为实参。

    function setSelectedElement(el) {
        // do something with the selected element
    }
    
  2. 使用 inspectedWindow.eval()useContentScriptContext: true 选项从开发者工具页面调用该方法。

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

useContentScriptContext: true 选项指定表达式必须在与内容脚本相同的上下文中进行评估,因此它可以访问 setSelectedElement 方法。

获取参考面板的 window

如需从开发者工具面板调用 postMessage(),您需要引用其 window 对象。从panel.onShown事件处理脚本获取 面板的 iframe 窗口:

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

将消息从注入的脚本发送到开发者工具页面

直接注入到页面的代码(不使用内容脚本,包括通过附加 <script> 标记或调用 inspectedWindow.eval())无法使用 runtime.sendMessage() 将消息发送到 开发者工具页面。相反,我们建议您将注入的脚本与可以充当中间媒介的内容脚本相结合,并使用 the 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. 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);
});

您可以在 GitHub 上找到其他替代消息传递技术。

检测开发者工具何时打开和关闭

onConnect 监听器 添加到 Service Worker,并从开发者工具页面调用 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 service worker
const serviceWorkerConnection = chrome.runtime.connect({
    name: "devtools-page"
});

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

开发者工具扩展程序示例

本页面的示例来自以下页面:

更多信息

如需了解扩展程序可以使用的标准 API,请参阅 chrome.* APIWeb API

欢迎提供反馈! 您的意见和建议有助于我们改进 API。

示例

您可以在示例中找到使用开发者工具 API 的示例。