Devtools架構淺析

寫此文章的起源是我目前正在作的一個開源項目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上個人博客看下。

相關文章
相關標籤/搜索