Extending DevTools(扩展开发者工具)

警告

此页面直接从 MV2 文档集迁移而来。它尚未经过验证是否符合 Manifest V3。

# Overview

DevTools 扩展为 Chrome DevTools 添加了功能。它可以添加新的 UI 面板和侧边栏,与检查的页面交互,获取有关网络请求的信息等等。查看特色 DevTools 扩展(featured DevTools extensions)。 DevTools 扩展可以访问一组额外的 DevTools 特定的扩展 API:

DevTools 扩展的结构与任何其他扩展一样:它可以有一个背景页面、内容脚本和其他项目。此外,每个 DevTools 扩展都有一个 DevTools 页面,可以访问 DevTools API。

53

# The DevTools page(开发者工具页面)

每次打开 DevTools 窗口时,都会创建一个扩展的 DevTools 页面实例。 DevTools 页面在 DevTools 窗口的整个生命周期内都存在。DevTools 页面可以访问 DevTools API 和一组有限的扩展 API。具体来说,DevTools 页面可以:

DevTools 页面不能直接使用大多数扩展 API。它可以访问与内容脚本可以访问的相同的扩展(extension)和运行时(runtime) API 子集。与内容脚本一样,DevTools 页面可以使用消息传递(Message Passing)与后台页面进行通信。有关示例,请参阅注入内容脚本(Injecting a Content Script)。

# Creating a DevTools extension(创建 DevTools 扩展)

要为您的扩展创建 DevTools 页面,请在扩展清单中添加 devtools_page 字段:

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

为每个打开的 DevTools 窗口创建一个在您的扩展清单中指定的 devtools_page 实例。该页面可以使用 devtools.panels API 将其他扩展页面作为面板和侧边栏添加到 DevTools 窗口。

devtools_page 字段必须指向一个 HTML 页面。这与用于指定背景页面的背景字段不同,后者可让您直接指定 JavaScript 文件。DevTools 页面必须位于您的扩展程序的本地,因此最好使用相对 URL 指定它。

chrome.devtools.* API 模块仅适用于在 DevTools 窗口中加载的页面。内容脚本和其他扩展页面没有这些 API。因此,这些 API 仅在 DevTools 窗口的生命周期内可用。

还有一些 DevTools API 仍处于试验阶段。请参阅 chrome.experimental.* APIs 以获取实验性 API 的列表以及有关如何使用它们的指南。

# DevTools UI elements: panels and sidebar panes(DevTools UI 元素:面板和侧边栏面板)

除了通常的扩展 UI 元素(例如浏览器操作、上下文菜单和弹出窗口)之外,DevTools 扩展还可以将 UI 元素添加到 DevTools 窗口:

  • 面板是顶级选项卡,与元素、来源和网络面板类似。
  • 侧边栏窗格显示与面板相关的补充 UI。 Elements 面板上的 Styles、Computed Styles 和 Event Listeners 窗格是侧边栏窗格的示例。(请注意,侧边栏窗格的外观可能与图像不匹配,具体取决于您使用的 Chrome 版本以及 DevTools 窗口的停靠位置。)

54

每个面板都是自己的 HTML 文件,其中可以包含其他资源(JavaScript、CSS、图像等)。创建一个基本面板如下所示:

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

在面板或侧边栏面板中执行的 JavaScript 可以访问与 DevTools 页面相同的 API。

为 Elements 面板创建一个基本的侧边栏面板如下所示:

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。 DevTools 在被检查页面的上下文中计算表达式,并显示返回值。

对于 setObjectsetExpression,该窗格显示的值与 DevTools 控制台中的值相同。但是,setExpression 允许您显示 DOM 元素和任意 JavaScript 对象,而 setObject 仅支持 JSON 对象。

# Communicating between extension components(扩展组件之间的通信)

以下部分描述了 DevTools 扩展的不同组件之间通信的一些典型场景。

# Injecting a content script(注入内容脚本)

DevTools 页面不能直接调用 scripting.executeScript。要从 DevTools 页面注入内容脚本,您必须使用 inspectedWindow.tabId 属性检索被检查窗口选项卡的 ID,并向后台页面发送消息。从后台页面调用 scripting.executeScript 注入脚本。

如果已注入内容脚本,则可以使用 eval 方法添加其他上下文脚本。有关更多信息,请参阅将选定元素传递给内容脚本Passing the Selected Element to a Content Script

以下代码片段展示了如何使用 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
backgroundPageConnection.postMessage({
    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.scripting.executeScript(message.tabId,
            { file: message.scriptToInject });
    }
    // add the listener
    devToolsConnection.onMessage.addListener(devToolsListener);

    devToolsConnection.onDisconnect.addListener(function() {
         devToolsConnection.onMessage.removeListener(devToolsListener);
    });
});

# Evaluating JavaScript in the inspected window(在检查窗口中评估 JavaScript)

您可以使用inspectedWindow.eval 方法在被检查页面的上下文中执行JavaScript 代码。您可以从 DevTools 页面、面板或侧边栏窗格调用 eval 方法。

默认情况下,表达式是在页面主框架的上下文中计算的。现在,您可能熟悉 DevTools commandline API 功能,例如元素检查 (inspect(elem))、中断函数(debug(fn))、复制到剪贴板 (copy()) 等。inspectedWindow.eval() 使用与在 DevTools 控制台键入的代码相同的脚本执行上下文和选项,这允许在 eval 中访问这些 API。例如,SOAK 使用它来检查元素:

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

或者,使用 inspectedWindow.eval()useContentScriptContext: true 选项在与内容脚本相同的上下文中评估表达式。使用 useContentScriptContext: true 调用 eval 不会创建内容脚本上下文,因此您必须在调用 eval 之前加载上下文脚本,方法是调用 executeScript 或在 manifest.json 文件中指定内容脚本。

一旦存在上下文脚本上下文,您就可以使用此选项来注入其他内容脚本。

eval 方法在正确的上下文中使用时功能强大,而在使用不当时则很危险。如果您不需要访问被检查页面的 JavaScript 上下文,请使用 scripting.executeScript 方法。有关详细注意事项和两种方法的比较,请参阅inspectedWindow

# Passing the selected element to a content script(将所选元素传递给内容脚本)

内容脚本不能直接访问当前选定的元素。但是,您使用 inspectedWindow.eval 执行的任何代码都可以访问 DevTools 控制台和命令行 API。例如,在评估代码中,您可以使用 $0访问所选元素。

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

  • 在内容脚本中创建一个将所选元素作为参数的方法。
  • 使用inspectedWindow.evaluseContentScriptContext: true 选项从DevTools 页面调用该方法。

您的内容脚本中的代码可能如下所示:

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

从 DevTools 页面调用该方法,如下所示:

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

useContentScriptContext: true 选项指定表达式必须在与内容脚本相同的上下文中计算,以便它可以访问 setSelectedElement 方法。

# Getting a reference panel's window (获取参考面板的窗口)

要从 devtools 面板 postMessage,您需要对其 window 对象的引用。从 panel.onShown 事件处理程序中获取面板的 iframe 窗口:

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

# Messaging from content scripts to the DevTools page(从内容脚本到 DevTools 页面的消息传递)

DevTools 页面和内容脚本之间的消息传递是间接的,通过后台页面的方式。

向内容脚本发送消息时,后台页面可以使用 tabs.sendMessage 方法,该方法将消息定向到特定选项卡中的内容脚本,如注入内容脚本所示Injecting a Content Script

从内容脚本发送消息时,没有现成的方法可以将消息传送到与当前选项卡关联的正确 DevTools 页面实例。作为一种解决方法,您可以让 DevTools 页面与后台页面建立长期连接,并让后台页面保留一个选项卡 ID 到连接的映射,以便它可以将每条消息路由到正确的连接。

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

DevTools 页面(或面板或侧边栏面板)建立这样的连接:

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

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

# Messaging from injected scripts to the DevTools page(从注入的脚本向 DevTools 页面发送消息)

虽然上述解决方案适用于内容脚本,但直接注入页面的代码(例如通过附加<script> 标记或通过 inspectedWindow.eval)需要不同的策略。在这种情况下,runtime.sendMessage 不会按预期将消息传递给后台脚本。

作为一种解决方法,您可以将注入的脚本与充当中介的内容脚本结合起来。要将消息传递给内容脚本,您可以使用 window.postMessage API。这是一个示例,假设使用上一节中的后台脚本:

// 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 页面。

您还可以在此处考虑两种替代的消息传递技术(two alternative message passing techniques here)。

# Detecting when DevTools opens and closes(检测 DevTools 何时打开和关闭)

如果你的扩展需要跟踪 DevTools 窗口是否打开,你可以在后台页面添加一个 onConnect 监听器,并从 DevTools 页面调用 connect。由于每个选项卡都可以打开自己的 DevTools 窗口,因此您可能会收到多个连接事件。要跟踪是否有任何 DevTools 窗口打开,您需要计算连接和断开连接事件,如下所示:

// 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 页面创建一个这样的连接:

// devtools.js

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

# DevTools extension examples(DevTools 扩展示例)

浏览这些 DevTools 扩展示例的来源:

# More information(更多信息)

有关扩展可以使用的标准 API 的信息,请参阅 chrome.* APIs

给我们反馈(Give us feedback!)!您的意见和建议有助于我们改进 API。

# Examples

您可以在示例中找到使用 DevTools API 的Samples

By.一粒技术服务.

results matching ""

    No results matching ""