寫此文章的起源是我目前正在作的一個開源項目miniblink(歡迎訪問miniblink.net來了解,這是個精簡chromium內核),一直沒有devtools功能,由於被精簡了嘛。因此打算把這功能加上。html
但要加devtools,以前的想法是可能會有巨大困難。由於miniblink把chromium的基本除了blink以外的全部組件都裁剪掉了,包括content層。瞭解chromium的人都知道,content是chromium最重要的組件,構建了整個體系。以前很早的時候我就研究了下devtools的架構,發現除了blink外,其餘全是content層的東西。這讓我一時研究受阻。前端
不過此次決心不同了,非實現不可。我翻出同事以前研究過的資料,有個架構圖畫的頗有收穫(建議保存下來慢慢看):node
這裏類咋看之下不少,結構龐大。但我調試後發現,其實devtools架構簡單的不得了!c++
對我有用的類其實就兩個,DevToolsClient和DevToolsAgent,其餘都是輔助性,或者由於chromium多進程架構帶來的類。web
咱們來仔細分析下結構:chrome
Devtools分紅兩部分,一個叫front_end,前端部分。這個其實就是devtools的調試頁面。也就是說,咱們平時看到的devtools,以下圖所示:json
整個就是個網頁而已。代碼路徑在third_party\WebKit\Source\devtools\front_end。入口是inspector.html。這個inspector是老的叫法,webkit時代,devtools就是叫inspector。bash
負責和inspector.html加載和對接的第一個類,就是以前我提到的content::DevToolsClient。因此chromium看來,front_end又是client。微信
DevToolsClient下面管着blink::WebDevToolsFrontend。DevToolsClient表明content層的devtools部分和blink層對接的最底層的類,而WebDevToolsFrontend就是blink層和content層最上層的類了。這兩個貨到底有啥用呢?這就涉及到devtools是如何工做的了。websocket
Devtools頁面是html,而實際邏輯全都是js完成的。那就遇到個問題,devtools如何獲取被調試頁面發送來的消息?Devtools又是如何發消息給被調試頁面?二者發的都是什麼格式的消息?帶着這三個問題,咱們拿個devtools最基本的功能,節點區域高亮來講明。
如圖,咱們在devtools裏劃到一個節點,如<i>上,被調試頁面就會有個陰影,恰好蓋住<i>所在的區域。這個devtools叫作DOM.highlightNode。
那麼就來回答上面三個問題。首先,devtools和被調試頁面通訊,全部協議都是走的json格式。
下面就是DOM.highlightNode的json請求樣子:
{
"id": 61,
"method": "DOM.highlightNode",
"params": {
"highlightConfig": {
"showInfo": true,
"showRulers": false,
"showExtensionLines": false,
"contentColor": {
"r": 111,
"g": 168,
"b": 220,
"a": 0.66
},
"paddingColor": {
"r": 147,
"g": 196,
"b": 125,
"a": 0.55
},
"borderColor": {
"r": 255,
"g": 229,
"b": 153,
"a": 0.66
},
"marginColor": {
"r": 246,
"g": 178,
"b": 107,
"a": 0.66
},
"eventTargetColor": {
"r": 255,
"g": 196,
"b": 196,
"a": 0.66
},
"shapeColor": {
"r": 96,
"g": 82,
"b": 177,
"a": 0.8
},
"shapeMarginColor": {
"r": 96,
"g": 82,
"b": 127,
"a": 0.6
},
"showLayoutEditor": false
},
"nodeId": 7
}
}複製代碼
這是由devtools頁面發送給被調試頁面,要求被調試頁面把nodeId=7的節點高亮。
這個消息是從
third_party\WebKit\Source\devtools\front_end\devtools.js的
DevToolsAPIImpl.prototype.sendMessageToEmbedder發出。
sendMessageToEmbedder: function(method, args, callback)
{
var callId = ++this._lastCallId;
if (callback)
this._callbacks[callId] = callback;
var message = { "id": callId, "method": method };
if (args.length)
message.params = args;
DevToolsHost.sendMessageToEmbedder(JSON.stringify(message));
}複製代碼
這裏的DevToolsHost.sendMessageToEmbedder就是個原生方法了。這就是由上文說的
blink::WebDevToolsFrontendImpl註冊。
WebDevToolsFrontendImpl註冊了v8的DevToolsHost對象和sendMessageToEmbedder方法。但這個方法在c++裏是個虛函數,須要繼承blink::WebDevToolsFrontendClient去實現。哪誰來實現呢?就是content::DevToolsClient了。
因此blink::WebDevToolsFrontendImpl和content::DevToolsClient就是這樣被關聯起來了。
從上面代碼還能夠看出有個calback,這個表示有些devtools發出的請求,須要返回數據。例如獲取整個頁面的node數據就是。
這個接收返回數據的地方,就是
DevToolsFrontendHostDelegateImpl::HandleMessageFromDevToolsFrontend。這個類我感受和content::DevToolsClient做用是差很少的,沒搞明白爲啥又要開個新類。反正我直接把這個和DevToolsClient合併了。
DevToolsFrontendHostDelegateImpl收到返回數據後,經過執行
"DevToolsAPI.dispatchMessage(" + *response + ");" 這句js,把被調試頁面的返回數據,又通知了devtools頁面。
Devtools頁面的js,拿到返回數據後,又執行了以下堆棧:
at callback [143]:[21]at callback [937]:[58]at InspectorBackendClass.AgentPrototype.dispatchResponse [978]:[18]at InspectorBackendClass.Connection.dispatch [483]:[41]at InspectorBackendClass.MainConnection._dispatchMessage [637]:[14]at WebInspector.Object.dispatchEventToListeners [108]:[35]at innerDispatch [496]:[54]at InspectorFrontendAPIImpl._dispatch [489]:[17]at DevToolsAPIImpl._dispatchOnInspectorFrontendAPI [61]:[21]at DevToolsAPIImpl.dispatchMessage [128]:[14]:[devtools.js]at (anonymous function) [1]:[13]:[]複製代碼
說完了devtools頁面,那麼被調試頁面,又是如何響應devtools頁面發來的json請求呢?
咱們先省略devtools到被調試頁面之間複雜的通訊層,直接看最終接受消息的地方。這個在blink層就是content::DevToolsAgent類。此類有個blink::WebDevToolsAgent* webAgent 成員,並重載了
blink::WebDevToolsAgentClient
接口,也就是要實現
blink::WebDevToolsAgentClient::sendProtocolMessage、createClientMessageLoop等接口。
正是這個blink::WebDevToolsAgent* webAgent 成員,把來自devtools的消息,如上面的DOM.highlightNode json字符串,派發給了blink層。
webAgent->dispatchOnInspectorBackend正是這個派發的函數。
下面看個堆棧:
content.dll!content::DevToolsAgent::SendChunkedProtocolMessagecontent.dll!content::DevToolsAgent::sendProtocolMessageblink_web.dll!blink::WebDevToolsAgentImpl::sendProtocolResponsewebcore_shared.dll!blink::InspectorBackendDispatcherImpl::sendResponsewebcore_shared.dll!blink::InspectorBackendDispatcherImpl::sendResponsewebcore_shared.dll!blink::InspectorBackendDispatcherImpl::Console_enablewebcore_shared.dll!blink::InspectorBackendDispatcherImpl::dispatchblink_web.dll!blink::WebDevToolsAgentImpl::dispatchMessageFromFrontendblink_web.dll!blink::WebDevToolsAgentImpl::dispatchOnInspectorBackendcontent.dll!content::DevToolsAgent::OnDispatchOnInspectorBackend複製代碼
這個堆棧,是devtools發出了console.enable消息,通知被調試頁面開啓console功能。
此消息由WebDevToolsAgentImpl::dispatchOnInspectorBackend收到後,派發給了
InspectorBackendDispatcherImpl::Console_enable。這個InspectorBackendDispatcherImpl就是個統一的派發和處理類。console.enable是個比較簡單的消息,直接處理了,複雜的好比獲取node相關信息,就會直接給blink內部的類,如Node\Element等來處理。
處理完畢後,就會調用咱們上面提到的WebDevToolsAgentImpl來發送返回消息。
正是這個blink::WebDevToolsAgentClient::sendProtocolMessage接口的實現類,DevToolsAgent::SendChunkedProtocolMessage,把反饋消息又經過一系列複雜的通訊機制,傳給了devtools頁面。Devtools接受這個返回消息的正是上文提到的DevToolsFrontendHostDelegateImpl::DispatchProtocolMessage。
因此看完這些,就知道若是不記中間複雜的通訊,devtools和被調試頁面,互相啓動一個DevToolsClient、DevToolsAgent就徹底搞定了兩邊消息的執行流程。那麼剩下那一堆
DevToolsAgentHostImpl、RenderFrameDevToolsAgentHost就只是爲了實現chromium多進程架構而搞出的各類代理、轉發類。固然,還有些功能須要content給出必要信息,這個也須要content裏開幾個類來實現。
至於中間那堆複雜的通訊,其實也沒那麼可怕。Chromium支持跨平臺調試。你在安卓上打開webview,是能在pc上用chrome調試的。這個過程是在圖中AwDevtoolsServer\
DevToolsHttpHandle等類裏實現的。原理是用websocket把剛纔那堆json字符串發送和接收。這個我沒繼續研究下去了。咱們組的qingmei大師改造了後,能夠實現遠程調試了,你在本機使用微信的x5內核,他在別的城市能夠直接調試你的webview。
另外,devtools裏的js其實挺龐大的,這個我大體調試了下,記錄了一堆零散筆記。有空的人能夠去csdn上個人博客看下。