最近在做WebOS的远程调试功能,效果如同Chrome for android和Safari for ios一样,具体可见:
- chrome: https://developers.google.com/chrome-developer-tools/docs/remote-debugging
- ios: https://developer.apple.com/library/ios/#documentation/AppleApplications/Reference/SafariWebContent/DebuggingSafarioniPhoneContent/DebuggingSafarioniPhoneContent.html
首先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调试的主要原理,这也是一个看上去很神奇的事情。