鍍金池/ 教程/ C++/ 擴(kuò)展 DevTools
谷歌瀏覽器開發(fā)工具綜述
在安卓設(shè)備上使用 Chrome 遠(yuǎn)程調(diào)試功能
命令行 API 參考
快捷鍵
通過工作空間保存更改
展示 Chrome 調(diào)試協(xié)議客戶端實(shí)例
技巧和竅門
控制臺 API 參考
遠(yuǎn)程調(diào)試協(xié)議
Settings
管理應(yīng)用存儲空間
擴(kuò)展 DevTools
遠(yuǎn)程調(diào)試協(xié)議
使用 CSS 預(yù)處理器
分析 JavaScript 性能
使用控制臺
DevTools 插件實(shí)例
使用時間軸
編輯樣式以及 DOM
郵件列表
樹形提示 (不穩(wěn)定)
時間軸示例:強(qiáng)制同步布局的診斷
評估網(wǎng)絡(luò)性能
博客帖子
設(shè)備模式&移動仿真
開發(fā)工作流程
視頻 Videos
調(diào)試 JavaScript 腳本
JavaScript 內(nèi)存分析
整合 DevTools
對 Chrome 開發(fā)工具的貢獻(xiàn)

擴(kuò)展 DevTools

總覽

一個 DevTools 插件能增加功能到 Chrome DevTools 中來.它能夠增加新的 UI 面板和側(cè)邊欄,能與被檢查的頁面進(jìn)行通信,能獲得關(guān)于網(wǎng)絡(luò)請求的信息,以及其他的功能。詳見顯式的 DevTools 插件。DevTools 插件能夠訪問一組額外特定 DevTools 擴(kuò)展 API:

一個 DevTools 插件的結(jié)構(gòu)像其他插件一樣 : 它可以有一個后臺頁面,內(nèi)容腳本和其他主體。此外,每個 DevTools 插件有一個 DevTools 頁面,能訪問到它的 DevTools API。

http://wiki.jikexueyuan.com/project/chrome-devtools/images/ref_devtools-extension.png" alt="devtools-extension.png" />

DevTools 頁面

一個插件 DevTools 頁面的實(shí)例每次隨著 DevTools 窗口打開而被創(chuàng)建。DevTools 頁面在 DevTools 窗口生命周期內(nèi)存在。DevTools 頁面能訪問 DevTools API 和有限的一組擴(kuò)展 API。具體來說,DevTools 頁面可以:

DevTools 頁面不能直接使用大多數(shù)的擴(kuò)展 API。它可以訪問擴(kuò)展并運(yùn)行著的 API 子集,因?yàn)檫@些 API 的內(nèi)容腳本可以被訪問到。像一個內(nèi)容腳本一樣,一個 DevTools 頁面可以使用 消息傳遞(Message Passing)與后臺頁面交互,詳見注入內(nèi)容腳本 Message Passing。

創(chuàng)建一個 DevTools 插件

為你的插件創(chuàng)建一個 DevTools 頁面,在插件的注冊清單文件中添加 devtools_page域:

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

每個 DevTools 窗口被打開時,在插件清單中指定的 devtools_page 的實(shí)例都會被創(chuàng)建。該頁面可以使用 devtools.panels API 添加其它擴(kuò)展程序的網(wǎng)頁,作為面板和側(cè)邊欄到 DevTools 窗口。

devtools_page 域必須指向一個 HTML 頁面。這與 background 域不同, background 域用來具體一個后臺頁面,能讓你直接指定 JavaScript 文件。

chrome.develop.* API 模型只能在載入了 DevTools 窗口的頁面使用。內(nèi)容腳本和其他擴(kuò)展頁面并沒有這些 API 。因此,這些 API 只有在 DevTools 窗口生命周期內(nèi)才能使用。

也有一些 DevTools 的 API 仍然是在測試狀態(tài)。請參閱 chrome.experimental.* API,例舉了用于測試的 API 和如何使用它們的指南。

DevTools 界面元素:面板和側(cè)邊欄窗格

除了常用擴(kuò)展 UI 元素,如瀏覽器的行為,文本菜單和彈出窗口,一個 DevTools 插件可以添加 UI 元素到 DevTools 窗口:

  • 面板是一個頂級標(biāo)簽,像元素(Elements),源(Sources)和網(wǎng)絡(luò)(Network)板。
  • 側(cè)邊欄窗格 顯示補(bǔ)充 UI 相關(guān)的面板。固有樣式,設(shè)定的樣式以及元素 (Elements) 面板上的事件監(jiān)聽器窗格的都是側(cè)邊欄窗格的實(shí)例。目前你的插件只能在元素 (Elements) 面板加側(cè)邊欄窗格。 (請注意,側(cè)邊欄面板的外觀可能與圖像不匹配,這取決于你正在使用 Chrome 瀏覽器的版本和其中 DevTools 窗口??康奈恢?。)

http://wiki.jikexueyuan.com/project/chrome-devtools/images/ref_devtools-extension-ui.png" alt="devtools-extension-ui.png" />

每個面板都是其自身的 HTML 文件,可以包括其它資源(JavaScript,CSS,圖片,等等)。像這樣創(chuàng)建一個基本的面板:

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

在面板上或者在側(cè)邊欄窗格中執(zhí)行的 JavaScript 對象能訪問 DevTools 頁面有權(quán)訪問的 API。

如下,為元素面板創(chuàng)建一個基礎(chǔ)的側(cè)邊窗格,像這樣:

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

有幾種方法來顯示一個側(cè)邊欄窗格中的內(nèi)容:

  • 利用HTML 文檔。調(diào)用 setPage 指定一個 HTML 頁面在窗格中顯示。

  • 利用 JSON 數(shù)據(jù)。傳遞一個JSON 對象給 setObject方法。

  • 利用 JavaScript 表達(dá)式。傳遞一個表達(dá)式給 setExpression方法。 DevTools 在被檢查頁面的文檔中的執(zhí)行表達(dá),并輸出該返回值。

對于這兩種方法 setObjectsetExpression,當(dāng)他們它輸入進(jìn) DevTools 控制臺后,窗格會輸出該值,但是,setExpression可以顯示 DOM 元素和任意 JavaScript 對象,而 setObject 只支持 JSON 對象。

插件組件之間的通信

下面的部分描述了 DevTools 插件的不同組件之間通信的一些典型場景。

注入腳本內(nèi)容

該 DevTools 頁不能直接調(diào)用tabs.executeScript。為了從 DevTools 頁面注入內(nèi)容腳本,必須使用inspectedWindow.tabId屬性檢索的檢查窗口選項(xiàng)卡的 ID ,并且發(fā)送一個消息到后臺頁面。在后臺頁面,調(diào)用 tabs.executeScript 注入腳本。

如果內(nèi)容腳本已經(jīng)被注入,你可以使用 eval方法來添加其他內(nèi)容腳本。請參見傳遞選定元素為內(nèi)容腳本(Passing the Selected Element to a Content Script ) 以獲取更多信息。

下面的代碼片段展示了如何使用 executeScript 注入一個腳本內(nèi)容:

// 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(function() {
         devToolsConnection.onMessage.removeListener(devToolsListener);
    });
}

在檢查窗口測試 JavaScript 代碼

你可以使用 inspectedWindow.eval 方法在檢查頁面的上下文中執(zhí)行 JavaScript 代碼。然后你可以在DevTools頁,面板或側(cè)邊欄窗格中調(diào)用 eval 方法。

默認(rèn)情況下,表達(dá)式在頁面的主框架文檔中被計算。現(xiàn)在,你可能熟悉 DevTools 命令行API(commandline API) 功能像元素檢查(inspect(elem)), 函數(shù)中斷(debug(fn)),復(fù)制內(nèi)容到剪貼板(copy()) ,或許更多。

inspectedWindow.eval() 使用相同的腳本執(zhí)行上下文,在 DevTools 控制臺的選項(xiàng)輸入代碼,它允許使在測試范圍內(nèi)訪問這些 API。例如,SOAK 使用它來檢測一個元素:

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

或者,使用 inspectedWindow.eval()useContentScriptContext:true 選項(xiàng),以計算在和內(nèi)容腳本相同的上下文內(nèi)容中的表達(dá)式。在 useContentScriptContext:true 域調(diào)用 eval 不會創(chuàng)建內(nèi)容腳本的環(huán)境,所以你必須在調(diào)用 evel 之前,載入內(nèi)容腳本,或者通過在 manifest.json 文件中指定內(nèi)容腳本來調(diào)用執(zhí)行腳本(executeScript)。

一旦上下文文腳本內(nèi)容環(huán)境存在,你可以使用此選項(xiàng)來注入額外的內(nèi)容腳本。

eval方法是強(qiáng)大的當(dāng)它在正確的應(yīng)用情景中使用的時候,但,如果沒有被正確使用,它同樣也是危險的。如果你不需要獲取檢查頁的 JavaScript 的內(nèi)容,使用 tabs.executeScript 方法。有關(guān)詳細(xì)的注意事項(xiàng)和兩種方法的比較,請參閱 inspectedWindow

傳遞選定元素到內(nèi)容腳本

內(nèi)容腳本不能直接訪問當(dāng)前選中的元素。但是,任何使用 inspectedWindow.eval 來執(zhí)行的代碼都可以在 DevTools 控制臺和命令行的 API 中使用。例如,在測試代碼時,你可以使用 $0 訪問當(dāng)前被選定的元素。

要傳遞選中的元素到內(nèi)容腳本,可以如下完成:

  • 在內(nèi)容腳本中,創(chuàng)建一個函數(shù),將選定參數(shù)作為這個函數(shù)的參數(shù)。
  • 在 DevTools 頁面中使用在useContentScriptContext:true的選項(xiàng)中的inspectedWindow.eval來該函數(shù)方法。

在內(nèi)容腳本中你的函數(shù)代碼可能是這個樣子:

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

在 DevTools 頁面調(diào)用這個方法,像這樣:

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

useContentScriptContext:true 選項(xiàng)限定的是表達(dá)必須在相同的上下文中的內(nèi)容腳本中進(jìn)行計算,所以它可以使用setSelectedElement方法。

獲得一個參考板的窗口

從 devtools 面板 postMessage ,你需要它的 window對象的一個參考。獲取面板的 iframe 窗口從該 panel.onShown 事件處理程序;

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

從內(nèi)容腳本傳遞信息到 DevTools 頁面

在 DevTools 頁面和內(nèi)容腳本之間傳遞消息并不是直接的,而是通過后臺頁面。

當(dāng)將消息發(fā)送到內(nèi)容腳本,后臺頁面可以使用 tabs.sendMessage 方法,該方法在指定的選項(xiàng)卡中發(fā)送消息到內(nèi)容腳本,就如同注入一個內(nèi)容腳本。

當(dāng)從內(nèi)容腳本發(fā)送消息出來,也沒有現(xiàn)成的方法來傳遞消息到與當(dāng)前選項(xiàng)卡相關(guān)聯(lián)的確切的 DevTools 頁面的實(shí)例。作為一種變通方法,你可以讓 DevTools 頁面與后臺頁面建立長生命周期的連接,并讓后臺頁持有 ID 選項(xiàng)卡到連接的映射,這樣它可以路由的每條消息到正確連接處。

// 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 頁面(面板或側(cè)邊欄窗格)像這樣建立連接:

// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "panel"
});
backgroundPageConnection.postMessage({
    name: 'init',
    tabId: chrome.devtools.inspectedWindow.tabId
});

從注入腳本到 DevTools 頁通信

雖然上述的解決方案適用于內(nèi)容腳本,即直接注入頁面代碼(例如通過附加一個 script 標(biāo)簽或通過 inspectedWindow.eval)需要一個不同的策略。在這方面,runtime.sendMessage 不會如預(yù)期一樣向后臺腳本傳遞消息。

作為一種變通方法,你可以將內(nèi)容腳本和注入腳本結(jié)合起來。將消息傳遞給內(nèi)容腳本,你可以使用 API window.postMessage。這里有一個例子,假設(shè)后臺腳本是上一節(jié)中的:

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

你的信息現(xiàn)在將從注入腳本,傳遞到內(nèi)容腳本,再傳遞到后臺腳本,最后傳到 DevTools 頁。

你也可以在這里參考兩種備選消息傳遞技術(shù)。

檢測 DevTools 打開和關(guān)閉狀態(tài)

如果你的插件需要跟蹤 DevTools 窗口是否打開,你可以添加一個 onConnect 的監(jiān)聽器到后臺頁面中,并在 DevTools 頁調(diào)用 connect 方法。由于每個標(biāo)簽可以讓它自己的 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 插件的例子

瀏覽這些 DevTools 示例源代碼:

上一篇:使用時間軸下一篇:Settings