一個 DevTools 插件能增加功能到 Chrome DevTools 中來.它能夠增加新的 UI 面板和側(cè)邊欄,能與被檢查的頁面進行通信,能獲得關于網(wǎng)絡請求的信息,以及其他的功能。詳見顯式的 DevTools 插件。DevTools 插件能夠訪問一組額外特定 DevTools 擴展 API:
一個 DevTools 插件的結(jié)構像其他插件一樣 : 它可以有一個后臺頁面,內(nèi)容腳本和其他主體。此外,每個 DevTools 插件有一個 DevTools 頁面,能訪問到它的 DevTools API。
一個插件 DevTools 頁面的實例每次隨著 DevTools 窗口打開而被創(chuàng)建。DevTools 頁面在 DevTools 窗口生命周期內(nèi)存在。DevTools 頁面能訪問 DevTools API 和有限的一組擴展 API。具體來說,DevTools 頁面可以:
DevTools 頁面不能直接使用大多數(shù)的擴展 API。它可以訪問擴展并運行著的 API 子集,因為這些 API 的內(nèi)容腳本可以被訪問到。像一個內(nèi)容腳本一樣,一個 DevTools 頁面可以使用 消息傳遞(Message Passing)與后臺頁面交互,詳見注入內(nèi)容腳本 Message Passing。
為你的插件創(chuàng)建一個 DevTools 頁面,在插件的注冊清單文件中添加 devtools_page
域:
{
"name": ...
"version": "1.0",
"minimum_chrome_version": "10.0",
"devtools_page": "devtools.html",
...
}
每個 DevTools 窗口被打開時,在插件清單中指定的 devtools_page
的實例都會被創(chuàng)建。該頁面可以使用 devtools.panels API 添加其它擴展程序的網(wǎng)頁,作為面板和側(cè)邊欄到 DevTools 窗口。
devtools_page 域必須指向一個 HTML 頁面。這與
background
域不同,background
域用來具體一個后臺頁面,能讓你直接指定 JavaScript 文件。
chrome.develop.*
API 模型只能在載入了 DevTools 窗口的頁面使用。內(nèi)容腳本和其他擴展頁面并沒有這些 API 。因此,這些 API 只有在 DevTools 窗口生命周期內(nèi)才能使用。
也有一些 DevTools 的 API 仍然是在測試狀態(tài)。請參閱 chrome.experimental.* API,例舉了用于測試的 API 和如何使用它們的指南。
除了常用擴展 UI 元素,如瀏覽器的行為,文本菜單和彈出窗口,一個 DevTools 插件可以添加 UI 元素到 DevTools 窗口:
每個面板都是其自身的 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 頁面有權訪問的 API。
如下,為元素面板創(chuàng)建一個基礎的側(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方法。
對于這兩種方法 setObject
和setExpression
,當他們它輸入進 DevTools 控制臺后,窗格會輸出該值,但是,setExpression
可以顯示 DOM 元素和任意 JavaScript 對象,而 setObject
只支持 JSON 對象。
下面的部分描述了 DevTools 插件的不同組件之間通信的一些典型場景。
該 DevTools 頁不能直接調(diào)用tabs.executeScript。為了從 DevTools 頁面注入內(nèi)容腳本,必須使用inspectedWindow.tabId
屬性檢索的檢查窗口選項卡的 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);
});
}
你可以使用 inspectedWindow.eval 方法在檢查頁面的上下文中執(zhí)行 JavaScript 代碼。然后你可以在DevTools頁,面板或側(cè)邊欄窗格中調(diào)用 eval
方法。
默認情況下,表達式在頁面的主框架文檔中被計算?,F(xiàn)在,你可能熟悉 DevTools 命令行API(commandline API) 功能像元素檢查(inspect(elem)
), 函數(shù)中斷(debug(fn)
),復制內(nèi)容到剪貼板(copy()
) ,或許更多。
inspectedWindow.eval()
使用相同的腳本執(zhí)行上下文,在 DevTools 控制臺的選項輸入代碼,它允許使在測試范圍內(nèi)訪問這些 API。例如,SOAK 使用它來檢測一個元素:
chrome.devtools.inspectedWindow.eval(
"inspect($$('head script[data-soak=main]')[0])",
function(result, isException) { }
);
或者,使用 inspectedWindow.eval()
的 useContentScriptContext
:true 選項,以計算在和內(nèi)容腳本相同的上下文內(nèi)容中的表達式。在 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)境存在,你可以使用此選項來注入額外的內(nèi)容腳本。
eval
方法是強大的當它在正確的應用情景中使用的時候,但,如果沒有被正確使用,它同樣也是危險的。如果你不需要獲取檢查頁的 JavaScript 的內(nèi)容,使用 tabs.executeScript 方法。有關詳細的注意事項和兩種方法的比較,請參閱 inspectedWindow。
內(nèi)容腳本不能直接訪問當前選中的元素。但是,任何使用 inspectedWindow.eval 來執(zhí)行的代碼都可以在 DevTools 控制臺和命令行的 API 中使用。例如,在測試代碼時,你可以使用 $0
訪問當前被選定的元素。
要傳遞選中的元素到內(nèi)容腳本,可以如下完成:
useContentScriptContext:true
的選項中的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
選項限定的是表達必須在相同的上下文中的內(nèi)容腳本中進行計算,所以它可以使用setSelectedElement
方法。
從 devtools 面板 postMessage
,你需要它的 window
對象的一個參考。獲取面板的 iframe 窗口從該 panel.onShown 事件處理程序;
onShown.addListener(function callback)
extensionPanel.onShown.addListener(function (extPanelWindow) {
extPanelWindow instanceof Window; // true
extPanelWindow.postMessage( // …
});
在 DevTools 頁面和內(nèi)容腳本之間傳遞消息并不是直接的,而是通過后臺頁面。
當將消息發(fā)送到內(nèi)容腳本,后臺頁面可以使用 tabs.sendMessage 方法,該方法在指定的選項卡中發(fā)送消息到內(nèi)容腳本,就如同注入一個內(nèi)容腳本。
當從內(nèi)容腳本發(fā)送消息出來,也沒有現(xiàn)成的方法來傳遞消息到與當前選項卡相關聯(lián)的確切的 DevTools 頁面的實例。作為一種變通方法,你可以讓 DevTools 頁面與后臺頁面建立長生命周期的連接,并讓后臺頁持有 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;
});
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
});
雖然上述的解決方案適用于內(nèi)容腳本,即直接注入頁面代碼(例如通過附加一個 script 標簽或通過 inspectedWindow.eval
)需要一個不同的策略。在這方面,runtime.sendMessage
不會如預期一樣向后臺腳本傳遞消息。
作為一種變通方法,你可以將內(nèi)容腳本和注入腳本結(jié)合起來。將消息傳遞給內(nèi)容腳本,你可以使用 API window.postMessage
。這里有一個例子,假設后臺腳本是上一節(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 頁。
你也可以在這里參考兩種備選消息傳遞技術。
如果你的插件需要跟蹤 DevTools 窗口是否打開,你可以添加一個 onConnect 的監(jiān)聽器到后臺頁面中,并在 DevTools 頁調(diào)用 connect 方法。由于每個標簽可以讓它自己的 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 示例源代碼:
更多建議: