Bootstrap

探索WebKit内核(四)------ Inspector

最近在做WebOS的远程调试功能,效果如同Chrome for android和Safari for ios一样,具体可见:

初次接触这个任务时感觉是件很神奇的事儿,如何能做到外部一个工具能这么大限度地控制WebKit内核。后来才发现,这件事情不难,主要的架子WebKit已为我们搭好,只需要在里面做点手脚把开发者工具移植到远程即可,Chrome实现方式如此,Safari也是如此。所以重点还是要搞清楚WebKit有关Inspector的架构,这次就分析它的原理和脉络,后面有时间再讲如何做到远程调试WebKit。

首先Inspector的大部分代码都在WebCore/inspector下,看看它主要类之间的关系:


这张类图有些杂乱,看它之前,我先讲讲WebKit调试的原理。WebKit的内核为调试都埋下了点,在运行流程的各个环节中都会通过这些埋点把各种消息都发给Inspector,然后由Inspector传递给前端的调试工具,另外Inspector也暴露了接收消息的接口,可以接收外部调试工具的各项指令,比如查询dom树信息,给js打断点,运行console命令等等,更难能可贵的是WebKit为这些进出的消息规范为某种协议,而这个协议是通过JSON来包装,这样一来只要遵循这个协议,外部的第三方工具都能做到通过发送JSON消息来调试WebKit的目的。

好了,大致知道原理后再来看这张图,其中最关键的是我标有颜色的三个类:

  • InspectorController
  • InspectorFrontendChannel
  • Inspectorinstrumentation

1)InspectorController

InspectorController是Inspector整个某块的控制中心,有它来初始化各个其他模块,并且它是挂靠在某个Page下,所以每个Page都会附属一套Inspector,所以Inspector需要从Page来追溯。另外,InspectorController提供了一个非常重要的方法:

void dispatchMessageFromFrontend(const String& message);

这个方法就是前面说到的外部调试工具把调试指令发送到WebKit内核中的入口。它的实现很简单就是调用InspectorBackendDispatcher.dispatch。InspectorBackendDispatcher算是WebKit接收指令的Facade,它会维护每个指令对应的Handle,而这些Handle其实就是调用各种InspectorAgent来响应这些指令,然后再把结果包装为JSON通过InspectorFrontendChannel发送给外部的调试工具。这里要讲讲InspectorAgent,InspectorAgent是一组类,比如InspectorDOMAgent,是WebKit各个核心元素的调试代理,是响应调试指令的主要完成者,每个核心元素都会有个Agent类对应,所以DOM相关的调试需求都会由InspectorDOMAgent来完成,js debug的需求由InspectorDebuggerAgent来完成。

2)InspectorFrontendChannel

由上面可知,InspectorFrontendChannel是WebKit向外部调试工具发送协议信息的通道,这个通道主要是由调试工具来实现方法:

bool sendMessageToFrontend(const String& message)

比如本地的开发者工具可以实现这个方法,把消息和调试工具Page发给InspectorClient,从而让调试工具Page得到协议信息。再比如我现在做的远程调试,那这里就需要通过网络把消息发送出去,并在远程调试工具中接收网络数据。

3)InspectorInstrumentation

InspectorInstrumentation就是上述原理中所提到的埋点,它提供了各种静态方法,这样在WebKit各个正常流程中需要调试的地方都会调用InspectorInstrumentation相应的静态方法即可。而这些静态方法都是要从InstrumentAgent中得到相应的InspectorAgent,由这些InspectorAgent去完成埋点任务,并都会通过InspectorFront把消息包装为JSON数据最终也是通过InspectorFrontendChannel发送出去。

所以说,搞清楚以上几个核心类后,基本上也就掌握了Inspector的主要脉络。所以说各大移动平台浏览器能做到远程调试,还是得益于WebKit在这块优雅的布局,WebKit在进出的几个关键口都提供了外部接入,使得外部工具能很优雅地参与到调试的各个环节中。

下次我再仔细分析一下JS调试的主要原理,这也是一个看上去很神奇的事情。

;