Devtools架构浅析

写此文章的起源是我目前正在做的一个开源项目miniblink(欢迎访问miniblink.net来了解,这是个精简chromium内核),一直没有devtools功能,因为被精简了嘛。所以打算把这功能加上。

但要加devtools,之前的想法是可能会有巨大困难。因为miniblink把chromium的基本除了blink之外的所有组件都裁剪掉了,包括content层。了解chromium的人都知道,content是chromium最重要的组件,构建了整个体系。之前很早的时候我就研究了下devtools的架构,发现除了blink外,其他全是content层的东西。这让我一时研究受阻。

不过这次决心不一样了,非实现不可。我翻出同事之前研究过的资料,有个架构图画的很有收获(建议保存下来慢慢看):


这里类咋看之下很多,结构庞大。但我调试后发现,其实devtools架构简单的不得了!

对我有用的类其实就两个,DevToolsClient和DevToolsAgent,其他都是辅助性,或者因为chromium多进程架构带来的类。

我们来仔细分析下结构:

Devtools分成两部分,一个叫front_end,前端部分。这个其实就是devtools的调试页面。也就是说,我们平时看到的devtools,如下图所示:


整个就是个网页而已。代码路径在third_party\WebKit\Source\devtools\front_end。入口是inspector.html。这个inspector是老的叫法,webkit时代,devtools就是叫inspector。


负责和inspector.html加载和对接的第一个类,就是之前我提到的content::DevToolsClient。所以chromium看来,front_end又是client。

DevToolsClient下面管着blink::WebDevToolsFrontend。DevToolsClient代表content层的devtools部分和blink层对接的最底层的类,而WebDevToolsFrontend就是blink层和content层最上层的类了。这两个货到底有啥用呢?这就涉及到devtools是如何工作的了。

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上我的博客看下。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值