扩展开发者工具

注意:本页面介绍了 Firefox 55 中的 devtools API。虽然这些 API 基于 Chrome devtools APIs,但 Firefox 并不实现所有这些功能;因此,并非所有功能都在此处记录。要查看哪些功能缺失,请参阅 devtools APIs 的局限性

您可以使用 WebExtensions APIs 来扩展浏览器内置的开发者工具。要创建 devtools 扩展,请在您的 manifest.json 文件中包含 "devtools_page" 键。

json
"devtools_page": "devtools/devtools-page.html"

此键的值是指向随您的扩展一起打包的 HTML 文件的 URL,这是一个特殊的扩展页面,称为 devtools 页面。URL 必须相对于 manifest.json 文件。

此 manifest 键会隐式设置 "devtools" 权限,这会触发 安装时有关 devtools 的权限警告。为避免此警告,请通过在 optional_permissions manifest 键中列出 "devtools" 权限,将该功能标记为可选。设置可选权限在更新中引入 devtools 功能时可能特别有用,因为它可防止扩展被禁用(在 Chrome 中)或阻止更新(在 Firefox 中)。

Devtools 页面

Devtools 页面在浏览器开发者工具打开时加载,在关闭时卸载。请注意,由于 devtools 窗口与单个标签页相关联,因此很可能同时存在多个 devtools 窗口(因此也就有多个 devtools 页面)。

Devtools 页面没有可见的 DOM,但可以使用 <script> 标签包含 JavaScript 源文件。源文件必须随扩展本身一起打包。这些源文件可以访问:

请注意,devtools 页面无法访问任何其他 WebExtension API,background 页面也无法访问 devtools API。相反,devtools 页面和 background 页面必须使用 runtime 消息 API 进行通信。这是一个示例:

html
<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>DevTools Extension</title>
  </head>
  <body>
    <script src="devtools.js"></script>
  </body>
</html>

devtools.js 文件将包含创建您的 dev tools 扩展的实际代码。

创建面板

Devtools 窗口托管了许多单独的工具——JavaScript 调试器、网络监视器等。顶部的标签行允许用户在不同工具之间切换。托管每个工具用户界面的窗口称为“面板”。

使用 devtools.panels.create() API,您可以在 devtools 窗口中创建自己的面板。

js
browser.devtools.panels
  .create(
    "My Panel", // title
    "/icons/star.png", // icon
    "/devtools/panel/panel.html", // content
  )
  .then((newPanel) => {
    newPanel.onShown.addListener(initializePanel);
    newPanel.onHidden.addListener(unInitializePanel);
  });

这需要三个必需参数:面板的标题、图标和内容。它返回一个 Promise,该 Promise 解析为代表新面板的 devtools.panels.ExtensionPanel 对象。

与目标窗口交互

开发者工具始终附加到特定的浏览器标签页。这被称为开发者工具的“目标”或“检查的窗口”。您可以使用 devtools.inspectedWindow API 与被检查的窗口进行交互。

在目标窗口中运行代码

devtools.inspectedWindow.eval() 提供了一种在被检查窗口中运行代码的方式。

这在某种程度上类似于使用 tabs.executeScript() 来注入内容脚本,但有一个重要的区别:

  • 与内容脚本不同,使用 devtools.inspectedWindow.eval() 加载的脚本不会获得 “干净的 DOM 视图”:也就是说,它们可以看到页面脚本对页面的更改。

注意:干净的 DOM 视图是一项安全功能,旨在帮助防止恶意页面通过重新定义原生 DOM 函数的行为来欺骗扩展。这意味着您在使用 eval() 时需要非常小心,如果可以的话,应该使用普通的内容脚本。

使用 devtools.inspectedWindow.eval() 加载的脚本也看不到内容脚本定义的任何 JavaScript 变量。

使用内容脚本

devtools 文档不能直接访问 tabs.executeScript(),因此如果您需要注入内容脚本,devtools 文档必须向 background 脚本发送一条消息,要求它注入脚本。devtools.inspectedWindow.tabId 提供了目标标签页的 ID:devtools 文档可以将此 ID 传递给 background 脚本,然后 background 脚本可以将其传递给 tabs.executeScript()

js
// devtools-panel.js

const scriptToAttach = "document.body.innerHTML = 'Hi from the devtools';";

window.addEventListener("click", () => {
  browser.runtime.sendMessage({
    tabId: browser.devtools.inspectedWindow.tabId,
    script: scriptToAttach,
  });
});
js
// background.js

function handleMessage(request, sender, sendResponse) {
  browser.tabs.executeScript(request.tabId, {
    code: request.script,
  });
}

browser.runtime.onMessage.addListener(handleMessage);

如果您需要在目标窗口中运行的内容脚本与 devtools 文档之间交换消息,建议使用 runtime.connect()runtime.onConnect 来建立 background 页面和 devtools 文档之间的连接。然后,background 页面可以维护标签 ID 与 runtime.Port 对象之间的映射,并使用此映射来路由这两个作用域之间的消息。

The background page tab ID is connected to the content script on the content page by a runtime.sendMessage() object. The Port of the background page is connected to the port of the DevTools document by a port.postMessage() object.

Devtools APIs 的局限性

这些 API 基于 Chrome devtools API,但与 Chrome 相比,仍有许多功能缺失。本节列出了截至 Firefox 54 仍未实现的有功能。请注意,devtools API 正在积极开发中,我们预计将在未来的版本中添加对其中大部分功能的支持。

devtools.inspectedWindow

以下功能不受支持:

  • inspectedWindow.getResources()
  • inspectedWindow.onResourceAdded
  • inspectedWindow.onResourceContentCommitted

inspectedWindow.eval() 的所有选项均不受支持。

使用 inspectedWindow.eval() 注入的脚本不能使用所有 Console 的命令行辅助函数,但 $0inspect() 都受支持(从 Firefox 55 开始)。

devtools.panels

以下功能不受支持:

  • panels.elements
  • panels.sources
  • panels.setOpenResourceHandler()
  • panels.openResource()
  • panels.ExtensionPanel.createStatusBarButton()
  • panels.Button
  • panels.ElementsPanel
  • panels.SourcesPanel

示例

GitHub 上的 webextensions-examples 存储库包含几个使用 devtools 面板的扩展示例。