课程表

Chrome 开发者工具课程

工具箱
速查手册

扩展 DevTools

一个 DevTools 插件能增加功能到 Chrome DevTools 中来.它能够增加新的 UI 面板和侧边栏,能与被检查的页面进行通信,能获得关于网络请求的信息,以及其他的功能。详见显式的 DevTools 插件。DevTools 插件能够访问一组额外特定 DevTools 扩展 API:

一个 DevTools 插件的结构像其他插件一样 : 它可以有一个后台页面,内容脚本和其他主体。此外,每个 DevTools 插件有一个 DevTools 页面,能访问到它的 DevTools API。

devtools-extension.png

DevTools 页面

一个插件 DevTools 页面的实例每次随着 DevTools 窗口打开而被创建。DevTools 页面在 DevTools 窗口生命周期内存在。DevTools 页面能访问 DevTools API 和有限的一组扩展 API。具体来说,DevTools 页面可以:

DevTools 页面不能直接使用大多数的扩展 API。它可以访问扩展并运行着的 API 子集,因为这些 API 的内容脚本可以被访问到。像一个内容脚本一样,一个 DevTools 页面可以使用 消息传递(Message Passing)与后台页面交互,详见注入内容脚本 Message Passing

创建一个 DevTools 插件

为你的插件创建一个 DevTools 页面,在插件的注册清单文件中添加 devtools_page域:

  1. {
  2. "name": ...
  3. "version": "1.0",
  4. "minimum_chrome_version": "10.0",
  5. "devtools_page": "devtools.html",
  6. ...
  7. }

每个 DevTools 窗口被打开时,在插件清单中指定的 devtools_page 的实例都会被创建。该页面可以使用 devtools.panels API 添加其它扩展程序的网页,作为面板和侧边栏到 DevTools 窗口。

devtools_page 域必须指向一个 HTML 页面。这与 background 域不同, background 域用来具体一个后台页面,能让你直接指定 JavaScript 文件。

chrome.develop.* API 模型只能在载入了 DevTools 窗口的页面使用。内容脚本和其他扩展页面并没有这些 API 。因此,这些 API 只有在 DevTools 窗口生命周期内才能使用。

也有一些 DevTools 的 API 仍然是在测试状态。请参阅 chrome.experimental.* API,例举了用于测试的 API 和如何使用它们的指南。

DevTools 界面元素:面板和侧边栏窗格

除了常用扩展 UI 元素,如浏览器的行为,文本菜单和弹出窗口,一个 DevTools 插件可以添加 UI 元素到 DevTools 窗口:

  • 面板是一个顶级标签,像元素(Elements),源(Sources)和网络(Network)板。
  • 侧边栏窗格 显示补充 UI 相关的面板。固有样式,设定的样式以及元素 (Elements) 面板上的事件监听器窗格的都是侧边栏窗格的实例。目前你的插件只能在元素 (Elements) 面板加侧边栏窗格。 (请注意,侧边栏面板的外观可能与图像不匹配,这取决于你正在使用 Chrome 浏览器的版本和其中 DevTools 窗口停靠的位置。)

devtools-extension-ui.png

每个面板都是其自身的 HTML 文件,可以包括其它资源(JavaScript,CSS,图片,等等)。像这样创建一个基本的面板:

  1. chrome.devtools.panels.create("My Panel",
  2. "MyPanelIcon.png",
  3. "Panel.html",
  4. function(panel) {
  5. // code invoked on panel creation
  6. }
  7. );

在面板上或者在侧边栏窗格中执行的 JavaScript 对象能访问 DevTools 页面有权访问的 API。

如下,为元素面板创建一个基础的侧边窗格,像这样:

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

有几种方法来显示一个侧边栏窗格中的内容:

  • 利用HTML 文档。调用 setPage 指定一个 HTML 页面在窗格中显示。

  • 利用 JSON 数据。传递一个JSON 对象给 setObject方法。

  • 利用 JavaScript 表达式。传递一个表达式给 setExpression方法。 DevTools 在被检查页面的文档中的执行表达,并输出该返回值。

对于这两种方法 setObjectsetExpression,当他们它输入进 DevTools 控制台后,窗格会输出该值,但是,setExpression可以显示 DOM 元素和任意 JavaScript 对象,而 setObject 只支持 JSON 对象。

插件组件之间的通信

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

注入脚本内容

该 DevTools 页不能直接调用tabs.executeScript。为了从 DevTools 页面注入内容脚本,必须使用inspectedWindow.tabId属性检索的检查窗口选项卡的 ID ,并且发送一个消息到后台页面。在后台页面,调用 tabs.executeScript 注入脚本。

如果内容脚本已经被注入,你可以使用 eval方法来添加其他内容脚本。请参见传递选定元素为内容脚本(Passing the Selected Element to a Content Script ) 以获取更多信息。

下面的代码片段展示了如何使用 executeScript 注入一个脚本内容:

  1. // DevTools page -- devtools.js
  2. // Create a connection to the background page
  3. var backgroundPageConnection = chrome.runtime.connect({
  4. name: "devtools-page"
  5. });
  6. backgroundPageConnection.onMessage.addListener(function (message) {
  7. // Handle responses from the background page, if any
  8. });
  9. // Relay the tab ID to the background page
  10. chrome.runtime.sendMessage({
  11. tabId: chrome.devtools.inspectedWindow.tabId,
  12. scriptToInject: "content_script.js"
  13. });

后台页面的代码:

  1. // Background page -- background.js
  2. chrome.runtime.onConnect.addListener(function(devToolsConnection) {
  3. // assign the listener function to a variable so we can remove it later
  4. var devToolsListener = function(message, sender, sendResponse) {
  5. // Inject a content script into the identified tab
  6. chrome.tabs.executeScript(message.tabId,
  7. { file: message.scriptToInject });
  8. }
  9. // add the listener
  10. devToolsConnection.onMessage.addListener(devToolsListener);
  11. devToolsConnection.onDisconnect(function() {
  12. devToolsConnection.onMessage.removeListener(devToolsListener);
  13. });
  14. }

在检查窗口测试 JavaScript 代码

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

默认情况下,表达式在页面的主框架文档中被计算。现在,你可能熟悉 DevTools 命令行API(commandline API) 功能像元素检查(inspect(elem)), 函数中断(debug(fn)),复制内容到剪贴板(copy()) ,或许更多。

inspectedWindow.eval() 使用相同的脚本执行上下文,在 DevTools 控制台的选项输入代码,它允许使在测试范围内访问这些 API。例如,SOAK 使用它来检测一个元素:

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

或者,使用 inspectedWindow.eval()useContentScriptContext:true 选项,以计算在和内容脚本相同的上下文内容中的表达式。在 useContentScriptContext:true 域调用 eval 不会创建内容脚本的环境,所以你必须在调用 evel 之前,载入内容脚本,或者通过在 manifest.json 文件中指定内容脚本来调用执行脚本(executeScript)。

一旦上下文文脚本内容环境存在,你可以使用此选项来注入额外的内容脚本。

eval方法是强大的当它在正确的应用情景中使用的时候,但,如果没有被正确使用,它同样也是危险的。如果你不需要获取检查页的 JavaScript 的内容,使用 tabs.executeScript 方法。有关详细的注意事项和两种方法的比较,请参阅 inspectedWindow

传递选定元素到内容脚本

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

要传递选中的元素到内容脚本,可以如下完成:

  • 在内容脚本中,创建一个函数,将选定参数作为这个函数的参数。
  • 在 DevTools 页面中使用在useContentScriptContext:true的选项中的inspectedWindow.eval来该函数方法。

在内容脚本中你的函数代码可能是这个样子:

  1. function setSelectedElement(el) {
  2. // do something with the selected element
  3. }

在 DevTools 页面调用这个方法,像这样:

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

useContentScriptContext:true 选项限定的是表达必须在相同的上下文中的内容脚本中进行计算,所以它可以使用setSelectedElement方法。

获得一个参考板的窗口

从 devtools 面板 postMessage ,你需要它的 window对象的一个参考。获取面板的 iframe 窗口从该 panel.onShown 事件处理程序;

  1. onShown.addListener(function callback)
  2. extensionPanel.onShown.addListener(function (extPanelWindow) {
  3. extPanelWindow instanceof Window; // true
  4. extPanelWindow.postMessage( // …
  5. });

从内容脚本传递信息到 DevTools 页面

在 DevTools 页面和内容脚本之间传递消息并不是直接的,而是通过后台页面。

当将消息发送到内容脚本,后台页面可以使用 tabs.sendMessage 方法,该方法在指定的选项卡中发送消息到内容脚本,就如同注入一个内容脚本

当从内容脚本发送消息出来,也没有现成的方法来传递消息到与当前选项卡相关联的确切的 DevTools 页面的实例。作为一种变通方法,你可以让 DevTools 页面与后台页面建立长生命周期的连接,并让后台页持有 ID 选项卡到连接的映射,这样它可以路由的每条消息到正确连接处。

  1. // background.js
  2. var connections = {};
  3. chrome.runtime.onConnect.addListener(function (port) {
  4. var extensionListener = function (message, sender, sendResponse) {
  5. // The original connection event doesn't include the tab ID of the
  6. // DevTools page, so we need to send it explicitly.
  7. if (message.name == "init") {
  8. connections[message.tabId] = port;
  9. return;
  10. }
  11. // other message handling
  12. }
  13. // Listen to messages sent from the DevTools page
  14. port.onMessage.addListener(extensionListener);
  15. port.onDisconnect.addListener(function(port) {
  16. port.onMessage.removeListener(extensionListener);
  17. var tabs = Object.keys(connections);
  18. for (var i=0, len=tabs.length; i < len; i++) {
  19. if (connections[tabs[i]] == port) {
  20. delete connections[tabs[i]]
  21. break;
  22. }
  23. }
  24. });
  25. });
  26. // Receive message from content script and relay to the devTools page for the
  27. // current tab
  28. chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  29. // Messages from content scripts should have sender.tab set
  30. if (sender.tab) {
  31. var tabId = sender.tab.id;
  32. if (tabId in connections) {
  33. connections[tabId].postMessage(request);
  34. } else {
  35. console.log("Tab not found in connection list.");
  36. }
  37. } else {
  38. console.log("sender.tab not defined.");
  39. }
  40. return true;
  41. });

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

  1. // Create a connection to the background page
  2. var backgroundPageConnection = chrome.runtime.connect({
  3. name: "panel"
  4. });
  5. backgroundPageConnection.postMessage({
  6. name: 'init',
  7. tabId: chrome.devtools.inspectedWindow.tabId
  8. });

从注入脚本到 DevTools 页通信

虽然上述的解决方案适用于内容脚本,即直接注入页面代码(例如通过附加一个 script 标签或通过 inspectedWindow.eval)需要一个不同的策略。在这方面,runtime.sendMessage 不会如预期一样向后台脚本传递消息。

作为一种变通方法,你可以将内容脚本和注入脚本结合起来。将消息传递给内容脚本,你可以使用 API window.postMessage。这里有一个例子,假设后台脚本是上一节中的:

  1. // injected-script.js
  2. window.postMessage({
  3. greeting: 'hello there!',
  4. source: 'my-devtools-extension'
  5. }, '*');
  1. // content-script.js
  2. window.addEventListener('message', function(event) {
  3. // Only accept messages from the same frame
  4. if (event.source !== window) {
  5. return;
  6. }
  7. var message = event.data;
  8. // Only accept messages that we know are ours
  9. if (typeof message !== 'object' || message === null ||
  10. !message.source === 'my-devtools-extension') {
  11. return;
  12. }
  13. chrome.runtime.sendMessage(message);
  14. });

你的信息现在将从注入脚本,传递到内容脚本,再传递到后台脚本,最后传到 DevTools 页。

你也可以在这里参考两种备选消息传递技术。

检测 DevTools 打开和关闭状态

如果你的插件需要跟踪 DevTools 窗口是否打开,你可以添加一个 onConnect 的监听器到后台页面中,并在 DevTools 页调用 connect 方法。由于每个标签可以让它自己的 DevTools 窗口打开,你可能会收到多个连接的事件。要跟踪 DevTools 窗口何时打开,你需要计算连接事件和断开事件,如下所示:

  1. // background.js
  2. var openCount = 0;
  3. chrome.runtime.onConnect.addListener(function (port) {
  4. if (port.name == "devtools-page") {
  5. if (openCount == 0) {
  6. alert("DevTools window opening.");
  7. }
  8. openCount++;
  9. port.onDisconnect.addListener(function(port) {
  10. openCount--;
  11. if (openCount == 0) {
  12. alert("Last DevTools window closing.");
  13. }
  14. });
  15. }
  16. });

在 DevTools 页面建立连接,如下:

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

DevTools 插件的例子

浏览这些 DevTools 示例源代码:

转载本站内容时,请务必注明来自W3xue,违者必究。
 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号