深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析

深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

一、前言 移动互联网时代,网页依旧是内容展示的重要媒介,这离不开 WebKit 浏览内核技术的支持与发展。在 iOS 平台下开发者们需要通过 WKWebView 框架来与 WebKit 打交道。虽然苹果官方提供了关于 WKWebView 的 API 与使用说明,但这并不能满足开发者们的需求,各类复杂场景依旧让我们焦头烂额,而解决方案却不易寻找。此时,优秀的开发者们将目光移向苹果开源的 WebKit 内核代码,试图从中寻找解惑之道,却发现依旧困难重重,坎坷不断,主要问题如下:

  1. 内核源码复杂难懂:动辄几个 G 的源码,且缺乏关键代码注释与说明,跟踪分析工作量大;系统兼容分支较多,一块代码可能区分 iOS、Mac、嵌入式等分支;历史代码或实验功能较多,导致查看源码并不容易缕清逻辑。
  2. 无法结合业务代码分析:异常问题往往在复杂场景下才会出现,缺乏业务代码的结合,问题无法复现,我们也就无法定位问题,最终容易走上猜测原因、更换方案尝试修复的路子。
无论你是出于兴趣还是以上原因,想要探索 WebKit 源码而不得其法,本文都将帮助你快速入门。接下来,我们将按照源码下载、源码编译、创建调试工程、源码实战分析的步骤助力你深入浏览内核探索之路。
二、源码下载 编译及调试之前我们首先需要获取一份苹果官方的 WebKit 源码。
  • github下载 (推荐): https://github.com/WebKit/WebKit
  • 官网下载:https://WebKit.org/
下载后的 WebKit 工程通过 Xcode( Xcode 是苹果官方推荐的 iOS 应用开发工具)打开后目录如下图。
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

WebKit工程目录
其中gtest / MiniBrowser / MobblieMiniBrowser / TestWebKitAPI / WebKitTestRunner仓库为测试仓库。考虑到编译效率的问题,通常情况下不需要编译测试仓库。由于本文后面将描述如何有效利用这些测试仓库,我们此处选择进行全源码编译。
三、源码编译 获取到源码后,接下来我们介绍下命令行及 Xcode 的编译方式。
本文推荐先使用命令行编译一遍,再用 Xcode 编译。从实践来看,如果编译过程中出错,命令行编译方式更易追踪到具体异常信息。
1)Embedded Builds下载的 WebKit 目录里面有一个Tools/Scripts 目录,这里面有各种脚本,包括使用命令行编译 WebKit 的脚本,其中一个重要的脚本就是 configure-Xcode-for-embedded-development,在 Mac 终端控制台运行如下命令:
sudo Tools/Scripts/configure-Xcode-for-embedded-development

之所以需要执行这个脚本,是因为 iOS 属于嵌入式平台,编译嵌入式平台的 WebKit 需要用到一些命令行工具,Xcode 正是利用该脚本构建这些命令行工具。否则,在编译诸如 JavaScriptCore 等工程的时候,就会报如下错误:‘com.apple.product-type.tool’, but there’s no such product type for the embedded platform,找不到对应的架构。
2)通过 Xcode 进行编译,设置构建产物存储位置
在打开工程后,选择 Xcode 的 File 菜单,选择Workspace Settings,然后打开 Workspace 设置窗口,如下图所示:
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

接下来我们选择 Advanced 按钮,打开如下窗口,按红框所示,将工程编译目录配置为 WebKitBuild,点击完成:
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

准备工作终于完成了,接下来我们可以开始编译了。
3)开始编译
首先选中 All Source 选项,配置 scheme 选择模拟器运行,然后点击 Xcode 的构建按钮开始构建。
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

此处请耐心等待,首次编译耗时较长,本文测试是在 i5 处理器 8G 内存 Mac Pro 机器上测试的,测试全源码编译耗时1h。编译成功后会弹出 MiniBrowser 不可用 警告(属于 Mac 应用工程),我们忽视即可。此时内核编译工作结束,接下来我们继续进入下一步,创建调试工程,进行源码探索。
四、创建调试工程 本文按照两类调试需求进行区分介绍,分别使用官方Demo工程和自定义工程进行调试,具体如下所示。
1)了解 WebKit 运行机制及源码:使用官方Demo工程调试
编译完成后,在我们的工程产物 WebKitBuild 目录中会有一个 MobileMiniBrower APP。此时我们可以在工程 scheme 配置中选择 MobileMiniBrowser APP 进行工程构建,该 APP 是苹果官方的浏览器 Demo (如下图所示),可通过地址栏执行地址输入,前进/后退以及多 Tab 等功能,可在源码里进行断点测试。
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

2)分析实际业务问题:使用自定义工程调试
针对这类需求,我们就需要按照如下步骤在工程中使用我们编译成功的 WebKit.framework 去替换系统的 WebKit.framework:
  1. 首先,用 Xcode 新建一个新的 Project,示例里面是 TestWKWebView,并将这个 Project 添加到 WebKit 的工程空间 WebKit.xcworkspace 中,编译产物按照 WebKit 编译所述,同样输出到 WebKitBuild 目录。
  2. 做好上面的设置之后,就可以编写测试程序,在测试程序中打上断点,这时你会发现系统 WebKit 库已经被替换,断点可跳转源码,即可愉快的进行源码探索了。
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

走到这一步后,大家可以发现,WebKit 源码很庞大,哪怕代码 run 起来了,如何下断点分析问题依旧很难把控。因此我们需要进行一些知识点的补充与理解,本文将进入实战环节,用 Demo 工程进行分析说明,给大家提供源码分析的思路。
五、源码实战分析 1)WebKit 的多进程机制
在 iOS 系统中,通常一个应用对应一个进程,但是在 WebKit 的发展过程中,基于稳定性与安全性考虑,引入了多进程的概念,避免单一页面的异常影响整体 app 运行,首先本文简单介绍下几个常见的 WebKit 进程,如下所示。
  • UIProcess —— 应用程序所在进程,WKWebView 代码和 WebKit 框架已加载到你的进程空间中;
  • WebContent —— 又称 WebProcess,JS 和 DOM 内存分配所在的位置,即网页内容渲染与 js 执行所处进程;
  • Network Process —— 负责发出与 Web 请求关联的基础网络请求;
  • Storage Process —— 用于数据库和服务工作者的存储。
接下来,我们用两个 Demo 进行内核分析:
Demo1 —— 单 webview 模型:
我们在 Demo1 工程中简单使用一个 WKWebView 来进行网络加载,以百度首页为例,运行项目后,点击调试模式中的 show the debug navigator 选项,该功能是 debug 下的资源分析模块。
现在我们可以查看各进程的 CPU、内存、磁盘、网络使用情况,当然也可以进行 Instruments 分析。
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

进程分布如下:
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

Demo2 —— 多 webview 模型:
使用多个 WKWebView 进行网络加载,每加载一个网页,创建一个新的 WKWebView 实例。
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

进程分布如下 :
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

结合以上Demo工程,我们可以有一个直观上的理解:
  1. WebContent 进程对应的是每一个新开的网页,该进程视内存情况可进行复用,某一 WebContent 进程的异常并不会影响到主 app 进程,常见的异常现象为白屏。
  2. UIProcess 进程为 app 所在进程,WKWebView 在该进程中提供了大量 API 供开发者与内核交互,也是开发者最熟悉的一部分。
  3. NetWorking 进程,无论多 WKWebView 还是单 WKWebView 场景,都只有唯一的 NetWorking 进程,这种设计主要便于网络请求管理以及保证网络缓存、cookie 等管理的一致性。
苹果官方文档中描述:配置同一 WKProcessPool 的多个 WKWebView 共享同一 WebContent 进程,即可以配置 WebContent 进程唯一( https://developer.apple.com/d... )。
但源码头文件中的注释与官方文档不一致,源码头文件描述配置同一 WKProcessPool 的多个 WKWebView 共享的是同一 WebContent 进程池,该配置未限制 WebContent 进程数量,而是共享进程池。
从 Demo 实际测试看,官方文档描述并不准确,我们以源码注释为准。
有了上述理解,我们再去看 Xcode 下 WebKit 的文件目录,目录也按照进程职责进行了较为合理的划分。
深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析
文章图片

因此,在调试过程中,除了根据已知关联 API 或代码堆栈进行全局搜索或单步断点调试外,我们还可以多结合三大进程的工作职责进行问题分析与查找。另外,既然可以查看各进程的 CPU、内存、磁盘、网络等状态了,对这方面有性能要求的,可以用来查看一个网页加载时各进程具体的资源消耗是多少。
2)TestWebKitAPI 工程
使用源码工程,除了代码分析外,苹果系统还提供了大量的系统 API 相关功能测试,这些测试基于 gtest 框架实现,集成在 TestWebKitAPI 工程里,实践中按照如下思路可利用 TestWebKitAPI 工程进行一些接口分析与测试:
  1. 了解各类 API(包括私有 API )的测试用例,通过这类代码示范与说明,便于我们深入了解接口的使用规范,更好的理解 API 的设计思路。
  2. 利用该框架可进行 gtest 测试,gtest 是一个跨平台的 (Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian) C++单元测试框架,由 google 公司发布,它能在不同平台上编写 C++ 测试代码。gtest 框架提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等。在 WebKit 内核源码中已有大量的基于gtest 框架的测试代码积累,当我们做了一些 trick 操作时,基于 TestWebKitAPI 工程做自动化测试,也是一种不错的选择。
六、Tips
  • WebKit 源码调试可能在一般情况下不会用到,但是对于 WebKit 复杂问题的分析与解决,结合业务对 WebKit 源码进行探索与分析,还是有一定意义的。
  • 特殊场景下,开发者可能对一些 API 进行特殊使用,这个时候可断点调试的源码能更好帮助我们规避风险。
  • 苹果官方禁止了在真机上替换 WebKit 内核,我们可以编译对应的真机库,但是无法进行安装调试,因此本文里的内容都是在模拟器进行的。
  • 因webkit源码在不断更新,因此下载编译过程中可能会遇到一些不兼容问题,一般可通过注释相关不兼容代码解决。
七、结语 本文作为入门篇章不再详述 WebKit 内核关键技术分析,你现在可以调试 WebKit 源代码,或在运行 Web 应用程序时使用 Instruments 来分析 WebKit 进程。希望本文能带领你够更深入地了解使用 WKWebView 应用的堆栈细节,并更好地了解 WebKit 层如何工作,后续作者会继续抽丝剥茧,基于业务详述浏览内核关键技术,与君共勉~
敬请期待:
深入理解 WKWebView(基础篇)-- WKWebView 加载生命周期与代理方法剖析
深入理解 WKWebView(基础篇)-- 聊聊 cookie 管理那些事
深入理解 WKWebView(基础篇)-- 探究 WebKit 网络资源缓存
参考资料:
1. WebKit 源码:https://github.com/WebKit/WebKit
2. WebKit 官网:https://webkit.org/
推荐阅读:
|基于etcd实现大规模服务治理应用实战
|短视频个性化Push工程精进之路
|百度爱番番数据分析体系的架构与实践
---------- END ----------
百度 Geek 说
百度官方技术公众号上线啦!
技术干货 · 行业资讯 · 线上沙龙 · 行业大会
招聘信息 · 内推信息 · 技术书籍 · 百度周边
【深入理解 WKWebView(入门篇)—— WebKit 源码调试与分析】欢迎各位同学关注

    推荐阅读