任何做过 Web 开发的同学,都避免不了在浏览器内进行调试。而大部分同学的首选工具,就是 Chrome DevTools。DevTools 本身我们无需多说,是一个大家不能再熟悉的工具了。但是埋藏在 DevTools 下面的开放协议以及它赋予的众多可能性,至今仍未见到充分的剖析和应用。
Webkit 的远程调试协议是 Webkit 在 2012 年引入的。目前所有 Webkit 内核的浏览器都支持这一特性。但是我们还是以 DevTools 和 Chrome 为出发点,来做讨论。
为什么我们关注 DevTools:
- 原因 1:DevTools 是开源项目
DevTools 的源码就在 Google 的 blink 项目 中,高度的开放。目前这么多丰富的功能,正是 Google 和其社区的共同贡献。同时它的 License 也不拒绝任何的二次开发。
- 原因 2:它足够简单
DevTools 仅仅是简单的由 HTML、JavaScript、CSS、Images 组成的,本质上就是一个 WebApp,纯粹的前端应用。当你去了解、修改它时,你不需要理解 C++ 和任何编译的知识。
- 原因 3:它的应用架构足够开放,满足任何形式的功能扩展。
事实上 Devtools 是一个充分模块化的 JavaScript 网页应用。它的每个功能你都可以去扩展(仅需要了解 JavaScript)。
- 原因 4:大部分前端都已经习惯它并且喜欢它。
Webkit 的远程调试特性
谈到远程调试前,有必要先了解各组件之间的关系。
- 浏览器拥有多个 Tab,并为每个 Tab 单独提供 WebSocket 的 Endpoint URI
- 每个 DevTool 实例只能检视一个 Tab,即只能与一个 Tab 保持通讯
DevTools 的界面是数据驱动的。数据的来源就是 WebSocket API。Google 对 Webkit 的调试协议做了进一步的封装,提供了以 JSON 为序列化格式的 WebSocket 界面。
大家在本地电脑上就可以体验这个远程调试是怎样一回事。执行如下步骤:
- 彻底关闭当前 Chrome 进程
在 Chrome 的启动参数上加上
--remote-debugging-port=9222
,例如 Mac 平台:open -a Google\ Chrome –args –remote-debugging-port=9222
在开启的 Chrome 浏览器里打开任意网页,例如:http://www.taobao.com/
在其他浏览器或者 Chrome 的新 Tab 打开 http://localhost:9222,你会得到这样的界面:
点击 “淘宝网” 的方框,就进入页面的调试界面了:
注意看地址栏,我们访问的是一个标准的 HTTP 协议下的网页,不是 Chrome 的私有协议。这里,你可以用 DevTools 再次检视这个页面,即按下 CMD
+ OPTION
+ i
。你会发现,这真的就是一个 HTML 应用。
再观察一下这个 URL:
通过 QueryString,我们告诉了 DevTools 的前端应用,它应该连接到哪个 WebSocket 服务。
你可以再你刚打开的检视 DevTools 的 DevTools(好绕口)里面,观察整个调试过程中的 WebSocket 通讯。例如:
以前用 WebSocket 做过 RPC 的同学应该看得出来,Google 实现的的确就是一个远程调用的接口。这个接口里面有两种通讯模式:
- request/response:就如同一个异步调用,通过请求的信息,获取相应的返回结果。这样的通讯必然有一个 message id,否则两方都无法正确的判断请求和返回的匹配状况。
- notification:和第一种不同,这种模式用于由一方单方面的通知另一方某个信息。和 “事件” 的概念类似。
通过调试协议来获取页面加载的所有网络请求并打印。为了简单,我们编写一个 Node.js 的应用来实现。大致步骤如下:
- 用 WebSocket 客户端连接调试服务
- 分别监听
Network.requestWillBeSent
、Network.loadingFailed
、Network.loadingFinished
、Network.responseReceived
、Network.requestServedFromCache 的 Notification
,并且打印相关的 log。 - 发送
Page.navigate
的请求,将页面跳转到某个页面,例如:http://www.taobao.com/
这里拿到的数据足以绘制一个非常准确的页面加载的瀑布图。从调试协议里拿到的数据具有以下特点:
- 准确,这是 Webkit 内核反馈的数据;而不是外层 JavaScript 接口的统计,也不是通过代理监控网络数据拿到的结果。
- 丰富,有很多数据,别的方法根本拿不到。例如,缓存状况、JavaScript 方法执行情况。
- 标准,调试协议本身已经定义了大量的 JSON 数据结构,你不需要再次进行抽象设计。
完整代码如下(请先安装好相应的 npm 模块,并且打开 Chrome 本地的 9222 调试端口):
1 | var WebSocketClient = require("websocket").client, |
运行后的结果如下:
结语
本篇内容仅仅介绍调试协议这个概念,以及它的通讯原理。并且,我们通过一个实验,来展示这套协议的强大特性。后面,我们还会讨论其他浏览器的调试协议,以及移动设备的调试。