擴充開發人員工具

總覽

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

DevTools 擴充功能的結構與其他擴充功能相同:可以包含背景頁面、內容指令碼和其他項目。此外,每個開發人員工具擴充功能都有開發人員工具頁面,可供存取開發人員工具 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。

此外,有些 DevTools API 仍在實驗階段。詳情請參閱 chrome.experimental。* API

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

除了一般擴充功能 UI 元素 (例如瀏覽器動作、內容選單和彈出式視窗) 以外,開發人員工具擴充功能還可以在開發人員工具視窗中新增 UI 元素:

  • 面板是頂層分頁,例如「元素」、「來源」和「聯播網」面板。
  • 側欄窗格可顯示與面板相關的補充 UI。「Elements」面板上的「Styles」、「Computed Styles」和「Event Listeners」窗格都是側欄窗格的範例。(請注意,視您使用的 Chrome 版本和開發人員工具視窗固定的位置而定,側欄窗格的外觀可能與圖片不符)。

「開發人員工具」視窗,顯示「Elements」面板和「Styles」側欄窗格。

每個面板都有自己的 HTML 檔案,可包含其他資源 (JavaScript、CSS、圖片等)。建立基本面板的外觀如下所示:

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

在面板或側欄窗格中執行的 JavaScript 可以存取與開發人員工具頁面相同的 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。開發人員工具會根據已檢查的頁面內容評估運算式,並顯示傳回值。

對於 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 會透過 SOAK 檢查元素:

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

或者,您也可以針對 inspectedWindow.eval() 使用 useContentScriptContext: true 選項,在與內容指令碼相同的背景資訊中評估運算式。使用 useContentScriptContext: true 呼叫 eval 並不會「建立」內容指令碼結構定義,因此您必須先呼叫 executeScript 或在 manifest.json 檔案中指定內容指令碼,才能呼叫 eval 內容指令碼。

結構定義指令碼內容存在後,您就可以使用這個選項插入其他內容指令碼。

eval 方法適用於在正確的情境下使用,且在不當使用時具有危險性。如果不需要存取已檢查網頁的 JavaScript 結構定義,請使用 tabs.executeScript 方法。如要進一步瞭解這兩種方法的詳細注意事項,並比較這兩種方法,請參閱 inspectedWindow

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

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

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

  • 在內容指令碼中建立方法,將所選元素做為引數。
  • 使用 inspectedWindow.eval 搭配 useContentScriptContext: 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.* API網路 API

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

範例

您可以在範例中找到使用 DevTools API 的範例。