从13年到现在一直在做前端开发,一直想了解一下浏览器究竟是如何加载并渲染排版出一个网页的流程,clone了WebKit的源码到本地后结果发现是OC和C++写的,额。。。还是算了。近2年学习了iOS开发并参与了实际的项目,在这块积累了足够的经验。最近2个月又把学生时代的C++捡起来复习了一遍,突然就发现自己已经具备阅读WebKit源码的能力了。因此重新又把仓库clone下来,准备在空闲时间好好学习下。
先过一下WebKit的基本概念:
概念
关于webkit引擎内核:
WebKit是苹果自己的开发的浏览器引擎,使用在了苹果的Safari浏览器、邮件、appStore等软件中。其核心包含了WebCore排版引擎、JavaScriptCore的渲染引擎、WebKit平台等。
Chrome使用的是Webkit还是Blink的问题?
Blink is a fork of the WebCore component of WebKit, which was originally a fork of the KHTML and KJS libraries from KDE. It is used in Chrome starting at version 28, Microsoft Edge starting at version 79, Opera (15+), Vivaldi, Brave, Amazon Silk and other Chromium-based browsers and frameworks.
IOS上的Chrome使用的是什么内核?
Chrome for iOS continues to use WebKit because Apple requires that web browsers on that platform must do so.
所以同样的HTML\CSS\JS在不同浏览器执行不一致,原因就是其内核不同导致的,比如我在开发中遇到过的js的Date对象格式化规则不一致导致在iPhone中出现的BUG,原因就是因为在本地调试的使用Chomre是V8的js引擎,而iPhone上使用的Safari使用的是JSCore引擎。
借用《WebKit技术内幕》一书中的架构图:
图中WebKit和WebKit2的区别就是,如果我们做Mac和IOS开发中初始化一个UIWebView对象,则使用的WebKit,如果初始化的是一个WKWebView对象,则使用的WebKit2。它们使用的是同一套底层库,但是WebKit2因为其多进程的模型,在内存等性能指标上要好不少,这块做IOS开发的同学应该都有这个概念。 如下图:
然后是项目的下载和编译:
前提首先必须是一台Mac电脑,然后需要安装Xcode和Xcode Command Line。
步骤1:
将在github中clone代码仓库到本地: github.com/WebKit/WebK…
步骤2
然后在仓库目录中执行:
Tools/Scripts/set-webkit-configuration --debug
Tools/Scripts/build-webkit --debug
然后根据电脑配置不同,大概编译时间在半个小时到一个小时不等。
编译完成后进入仓库目录,然后点击WebKit.xcworkspace的Xcode项目空间的文件就可以打开WebKit的内核工程。
注意:编译完成后的仓库体积在60G加,首先要确定自己的硬盘的存储空间是否足够,我这里编译完了后,直接报磁盘空间不足了。
关于build location的修改:
工程目录介绍
用Xcode打开的WebKit的项目目录的截图如下:
bmalloc:C++工程,实现动态内存分配,该库提供了一个成为IsoHeap的重要安全特性,它将每种类型的对象隔离到它自己的页面中,以防止对释放后使用的类型混淆攻击;
WTF:补充C++标准模板库外的自定义模板库,包含常见的容器类,如Vector、HashMap(无序)、HashSet等等;
JavaScriptCore:C++项目,苹果自己的jsc引擎,还有v8、quickJS、facebook的Hermes,比如前端这里chrome浏览器和nodejs就是基于v8引擎进行的开发;
ANGLE:Almost Native Graphics Layer Engine,在 Direct X 9.0c API 的基础上实现一层 OpenGL ES 2.0 API中 的 Web GL 子集接口;
libwebrtc:实时音视频库;
WebGPU:浏览器的图形API;
WebCore:排版引擎,实现了HTML、XML和CSS解析器,资源加载等,C++实现;
WebInspectorUI:safari浏览器中浏览器的开发者工具实现,通过js开发实现,通过上述提到的jscore来运行;
WebKitLegacy:原有UIWebView对象的实现,已经彻底的过期,使用会导致app被苹果拒审;
WebKit:即WKWebView对象,实现了WebKit的多进程架构-UI进程、WebContent进程、NetWorking进程;
DumpReaderTree:页面渲染树的测试工具;
MiniBrowser:一个简单的测试用的浏览器,object-c实现;
MobileMiniBrowser:一个简单的测试用的移动端浏览器,object-c实现;
启动本地测试浏览器和调试代码
浏览完项目后,我们这里开始在Mac上启动并尝试调试进行代码的调试,在Xcode中编译的target选择MiniBrowser,然后点击启动。编译完成后,会启动一个简单的浏览器:
MobileMiniBrowser工程中,核心功能是通过WK1BrowserWindowController实现UIWebView的加载和显示,WK2BrowserWindowController实现WKWebView的加载和显示,它们通过共用的基类BrowserWindowController来实现浏览器Window的UI展示。
启动后,在Xcode的左侧面的debug nav面板中就可以看到都都打开了个2个网页的UIWebView和WKWebView的进程信息和基本的资源消耗: UIWebView:
WKWebView:
可以看到WkWebView都2个WebContent进程以及一个Networking进程。
调试: 关于调试,OC工程直接断点调试即可,同样C++的工程如WebCore也是同样的道理,Html一个网页的根节点是,在WebCore中通过HTMLHtmlElement的类来实现,我们在该类的名称叫做create静态成员方法中打上断点,然后启动MiniBrower打开一个网页,就可以看到断点会进入该方法的实现和调用的堆栈:
在查看源码中还发现一个问题,C++的工程中代码文件中include引入头文件或者外部类方法调用没有办法跳转到定义。后边我通过使用配置C++插件的vscode来打开工程才实现了跳转外部的定义,另外通过vscode也可以方便通过cmake来创建C++工程,因为WebKit中的C++工程看源码都是通过CMakeLists的配置来实现的编译,当然这又是另外一个话题。