Chrome開發(fā)工具 擴(kuò)展 DevTools

2018-03-01 18:51 更新

擴(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。

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 窗口??康奈恢?。)

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á)式在頁面的主框架文檔中被計算?,F(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 示例源代碼:

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號