擴充開發人員工具

總覽

開發人員工具擴充功能可為 Chrome 開發人員工具新增功能。這項擴充功能可以新增 UI 面板和側欄、與檢查的網頁互動、取得網路要求相關資訊等。查看精選開發人員工具擴充功能。開發人員工具擴充功能可存取一組額外的開發人員工具專用擴充功能 API:

開發人員工具擴充功能的結構與其他擴充功能相同,可以有背景網頁、內容指令碼和其他項目。此外,每個開發人員工具擴充功能都有開發人員工具頁面,可存取開發人員工具 API。

架構圖:顯示開發人員工具頁面與檢查視窗和背景頁面之間的通訊。背景網頁會與內容指令碼通訊,並存取擴充功能 API。開發人員工具頁面可存取開發人員工具 API,例如建立面板。

開發人員工具頁面

每次開啟開發人員工具視窗時,系統都會建立擴充功能開發人員工具頁面的執行個體。開發人員工具頁面會在開發人員工具視窗的生命週期內存在。開發人員工具頁面可存取開發人員工具 API 和一組有限的擴充功能 API。具體來說,開發人員工具頁面可以:

開發人員工具頁面無法直接使用大多數的擴充功能 API。它可存取與內容指令碼相同的 extensionruntime API 子集。與內容指令碼類似,開發人員工具頁面可以使用訊息傳遞與背景頁面通訊。如需範例,請參閱「插入內容指令碼」。

建立開發人員工具擴充功能

如要為擴充功能建立開發人員工具頁面,請在擴充功能資訊清單中新增 devtools_page 欄位:

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

系統會為開啟的每個開發人員工具視窗,建立擴充功能資訊清單中指定的 devtools_page 執行個體。網頁可能會使用 devtools.panels API,將其他擴充功能頁面新增為面板和側欄,加入開發人員工具視窗。

chrome.devtools.* API 模組僅適用於在開發人員工具視窗中載入的網頁。內容指令碼和其他擴充功能網頁沒有這些 API。因此,這些 API 只能在開發人員工具視窗的生命週期內使用。

此外,部分開發人員工具 API 仍處於實驗階段。請參閱 chrome.experimental.* API,查看實驗性 API 清單,以及如何使用這些 API 的指南。

開發人員工具 UI 元素:面板和側邊窗格

除了瀏覽器動作、內容選單和彈出式視窗等常見的擴充功能 UI 元素,開發人員工具擴充功能還可以在開發人員工具視窗中新增 UI 元素:

  • 「面板」是頂層分頁,例如「元素」、「來源」和「網路」面板。
  • 側邊窗格會顯示與面板相關的補充 UI。「元素」面板上的「樣式」、「計算樣式」和「事件監聽器」窗格都是側欄窗格的例子。(請注意,側欄窗格的外觀可能與圖片不符,具體取決於您使用的 Chrome 版本,以及開發人員工具視窗的固定位置)。

開發人員工具視窗,顯示「元素」面板和「樣式」側欄窗格。

每個面板都是獨立的 HTML 檔案,可包含其他資源 (JavaScript、CSS、圖片等)。建立基本面板的程式碼如下:

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

在面板或側欄窗格中執行的 JavaScript,可存取與開發人員工具頁面相同的 API。

為「元素」面板建立基本側邊窗格,如下所示:

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。開發人員工具會在檢查的網頁環境中評估運算式,並顯示傳回值。

對於 setObjectsetExpression,窗格會顯示值,就像在開發人員工具控制台中一樣。不過,setExpression 可讓您顯示 DOM 元素和任意 JavaScript 物件,而 setObject 僅支援 JSON 物件。

在擴充功能元件之間通訊

以下各節說明在開發人員工具擴充功能的不同元件之間進行通訊的一些典型情境。

插入內容指令碼

開發人員工具頁面無法直接呼叫 tabs.executeScript。如要從開發人員工具頁面插入內容指令碼,您必須使用 inspectedWindow.tabId 屬性擷取檢查視窗分頁的 ID,然後將訊息傳送至背景頁面。從背景頁面呼叫 tabs.executeScript 注入指令碼。

下列程式碼片段說明如何使用 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
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.addListener(function() {
         devToolsConnection.onMessage.removeListener(devToolsListener);
    });
});

在檢查的視窗中評估 JavaScript

您可以使用 inspectedWindow.eval 方法,在檢查的網頁環境中執行 JavaScript 程式碼。您可以從開發人員工具頁面、面板或側邊面板窗格叫用 eval 方法。

根據預設,系統會在網頁的主要框架中評估運算式。您可能已熟悉開發人員工具的指令列 API 功能,例如元素檢查 (inspect(elem))、函式中斷 (debug(fn))、複製到剪貼簿 (copy()) 等。inspectedWindow.eval() 使用的指令碼執行環境和選項,與在開發人員工具控制台中輸入的程式碼相同,因此可在 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 環境,請使用 tabs.executeScript 方法。如需詳細注意事項和兩種方法的比較,請參閱inspectedWindow

將所選元素傳遞至內容指令碼

內容指令碼無法直接存取目前所選元素。不過,您使用 inspectedWindow.eval 執行的任何程式碼,都能存取開發人員工具控制台和指令列 API。舉例來說,您可以在評估的程式碼中使用 $0 存取所選元素。

如要將所選元素傳遞至內容指令碼,請按照下列步驟操作:

  • 在內容指令碼中建立方法,將所選元素做為引數。
  • 使用 inspectedWindow.evaluseContentScriptContext: true 選項,從開發人員工具頁面呼叫方法。

內容指令碼中的程式碼可能如下所示:

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

從開發人員工具頁面叫用方法,如下所示:

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

useContentScriptContext: true 選項會指定運算式必須在與內容指令碼相同的環境中評估,因此可以存取 setSelectedElement 方法。

取得參照面板的 window

如要從開發人員工具面板 postMessage,您需要參照其 window 物件。 從 panel.onShown 事件處理常式取得面板的 iframe 視窗:

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

從內容指令碼傳送訊息至開發人員工具頁面

開發人員工具頁面和內容指令碼之間的訊息傳遞是間接的,必須透過背景頁面。

將訊息傳送至內容指令碼時,背景網頁可以使用 tabs.sendMessage 方法,將訊息導向特定分頁中的內容指令碼,如「插入內容指令碼」所示。

從內容指令碼傳送訊息時,沒有現成的方法可將訊息傳送至與目前分頁相關聯的正確開發人員工具頁面執行個體。解決方法是讓開發人員工具頁面與背景頁面建立長期連線,並讓背景頁面保留分頁 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;
});

開發人員工具頁面 (或面板/側欄面板) 會建立如下連線:

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

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

從插入的指令碼傳送訊息至開發人員工具頁面

上述解決方案適用於內容指令碼,但如果程式碼是直接插入網頁 (例如透過附加 <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);
});

現在,訊息會從插入的指令碼流向內容指令碼、背景指令碼,最後流向開發人員工具頁面。

您也可以考慮這裡的兩種替代訊息傳遞技術

偵測開發人員工具開啟和關閉的時間

如果擴充功能需要追蹤開發人員工具視窗是否開啟,您可以將 onConnect 監聽器新增至背景網頁,並從開發人員工具網頁呼叫 connect。由於每個分頁都可以開啟專屬的開發人員工具視窗,您可能會收到多個連線事件。如要追蹤是否有任何開發人員工具視窗開啟,您需要計算連線和中斷連線事件,如下所示:

// 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.js

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

開發人員工具擴充功能範例

瀏覽這些開發人員工具擴充功能範例的來源:

更多資訊

如要瞭解擴充功能可使用的標準 API,請參閱 chrome.* APIWeb API

請提供寶貴意見!您的意見和建議有助於我們改善 API。

範例

您可以在「範例」中找到使用開發人員工具 API 的範例。