深入 WebKit:架构概览与代码库导航
前置知识
- ›通用软件工程基础知识
- ›对浏览器工作原理的基本了解(解析、渲染、执行)
- ›能够阅读 C++ 文件和目录结构
深入 WebKit:架构概览与代码库导航
WebKit 是所有 Apple 设备上 Safari 的核心引擎,同时也驱动着 Mail、Books、News 和 App Store 等应用,并通过 GTK 和 WPE 两个移植版本在 Linux 上运行。整个仓库体量庞大——数百万行 C++ 代码分散在几十个顶层目录中。在真正参与贡献、乃至读懂这份代码之前,你需要先建立一张清晰的心智地图。本文正是为此而写。
我们将依次梳理仓库的目录结构、直接编码在构建系统中的严格六层依赖层次、将 Web 内容与系统其余部分隔离的多进程安全架构,以及让同一份源码能够构建出 macOS、iOS、Linux、PlayStation 和 Windows 版本的条件编译宏体系。
仓库目录结构一览
顶层目录清晰地划分为源代码、工具和测试三大部分:
| 目录 | 用途 |
|---|---|
Source/ |
所有生产源代码——从 bmalloc 到 WebDriver 的每个组件 |
Tools/ |
构建脚本、测试运行器、开发者工具以及 git-webkit CLI |
JSTests/ |
JavaScriptCore 的 JavaScript 合规性测试与回归测试 |
LayoutTests/ |
HTML/CSS/DOM 渲染测试(WebCore 的主要测试套件) |
PerformanceTests/ |
性能基准测试,包括 JetStream 和 MallocBench |
Configurations/ |
用于 Apple 平台构建的 Xcode .xcconfig 文件 |
Source/cmake/ |
所有非 Apple 移植版本共用的 CMake 模块 |
根目录的 CMakeLists.txt 声明了项目,将模块路径指向 Source/cmake/,然后将 Source/ 作为唯一子目录添加进来:
cmake_minimum_required(VERSION 3.20)
project(WebKit LANGUAGES C CXX)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/Source/cmake")
其余所有内容都由此派生。根目录的 Makefile 只是一个薄薄的包装层,根据平台将实际工作委托给 Xcode 或 CMake。
提示: 第一次浏览仓库时,建议从
Source/CMakeLists.txt入手。其中的add_subdirectory()调用按顺序列出了整个引擎的组成部分,相当于一份目录索引。
六层构建栈
打开 Source/CMakeLists.txt,依赖层次就直接体现在构建顺序中。每个 add_subdirectory() 调用只能引用在它之前已声明的库:
graph BT
bmalloc["bmalloc<br/><i>Memory allocator</i>"]
WTF["WTF<br/><i>Web Template Framework</i>"]
JSC["JavaScriptCore<br/><i>JS engine</i>"]
WebCore["WebCore<br/><i>Web standards</i>"]
WebKit2["WebKit (WebKit2)<br/><i>Multi-process embedding</i>"]
WebDriver["WebDriver<br/><i>Automation</i>"]
WTF --> bmalloc
JSC --> WTF
WebCore --> JSC
WebKit2 --> WebCore
WebDriver --> WebKit2
构建文件开头的前四行无条件声明了这一结构:
add_subdirectory(bmalloc)
add_subdirectory(WTF)
...
add_subdirectory(JavaScriptCore)
这四行是无条件的——所有移植版本都必须依赖它们。WebCore、WebKit 和 WebDriver 等后续条目则受 ENABLE(...) 标志控制,从而支持只包含 JS 引擎的最小化构建。
各层的职责如下:
| 层级 | 目录 | 职责 |
|---|---|---|
| bmalloc | Source/bmalloc/ |
自定义内存分配器,使用类型隔离的 IsoHeap 页面以提升安全性 |
| WTF | Source/WTF/ |
模板库:容器(Vector、HashMap)、智能指针(Ref、RefPtr、WeakPtr)、线程原语 |
| JavaScriptCore | Source/JavaScriptCore/ |
完整的 JavaScript 引擎,具备四级 JIT 编译流水线 |
| WebCore | Source/WebCore/ |
HTML/CSS/DOM 解析、布局、渲染,以及 30 余个 Web API 模块 |
| WebKit | Source/WebKit/ |
多进程架构:IPC、进程管理、嵌入 API(WKWebView) |
| WebDriver | Source/WebDriver/ |
W3C WebDriver 自动化协议实现 |
严格的层次划分是一种刻意为之的架构约束。WebCore 永远不会调用 WebKit2 中的代码;JavaScriptCore 永远不会调用 WebCore 中的代码。依赖关系只能单向向上流动。
多进程安全架构
多进程隔离是 WebKit 最核心的架构特性。每个网页都运行在独立的沙盒进程中,与应用程序完全隔离。某个页面崩溃不会影响整个应用;即使攻击者在渲染器中发现了漏洞,也会被困在一个系统访问权限极为有限的沙盒里。
flowchart LR
UI["UI Process<br/>(Safari, Mail)"]
WC1["WebContent Process<br/>(Tab 1)"]
WC2["WebContent Process<br/>(Tab 2)"]
NP["Network Process"]
GP["GPU Process"]
UI <-->|IPC| WC1
UI <-->|IPC| WC2
UI <-->|IPC| NP
UI <-->|IPC| GP
WC1 <-->|IPC| NP
WC2 <-->|IPC| NP
WC1 <-->|IPC| GP
各进程类型的职责如下:
- UI 进程 — 应用程序本身(Safari、Mail 或任何使用
WKWebView的自定义应用)。负责地址栏、标签页及所有 UI 界面,绝不运行不受信任的 Web 代码。 - WebContent 进程 — 负责为一个或多个网页执行 HTML 解析、CSS 布局、JavaScript 运行和渲染,处于严格沙盒中。在 macOS/iOS 上通过 XPC 启动,入口见
WebContentServiceEntryPoint.mm。 - Network 进程 — 处理所有 HTTP/HTTPS 请求、Cookie 和存储,在一个会话中的所有 WebContent 进程之间共享。
- GPU 进程 — 管理 WebGL、WebGPU、媒体解码和合成,将 GPU 驱动的攻击面与渲染器隔离开来。
Apple 平台上 WebContent 进程的入口文件出奇地简洁:
Source/WebKit/WebProcess/EntryPoint/Cocoa/XPCService/WebContentServiceEntryPoint.mm#L44-L61
该函数初始化主线程、清理环境变量,然后调用 XPCServiceInitializer<WebKit::WebProcess>——一个单一的模板实例化,负责引导整个 WebContent 进程启动。
提示: 每种进程类型都派生自
AuxiliaryProcess,这个基类提供了 IPC 连接管理和消息分发功能。我们将在第五篇文章中详细介绍。
平台移植与条件编译
WebKit 从同一份源码构建出 macOS、iOS、watchOS、tvOS、Linux(GTK 和 WPE 移植)、PlayStation 和 Windows 版本。控制编译内容的宏分为三个系列:
| 宏系列 | 用途 | 示例 |
|---|---|---|
PLATFORM(X) |
目标操作系统或硬件平台 | PLATFORM(COCOA)、PLATFORM(GTK)、PLATFORM(IOS_FAMILY) |
ENABLE(X) |
可按需开关的可选 Web 平台特性 | ENABLE(WEBGL)、ENABLE(FTL_JIT)、ENABLE(CSS_SELECTOR_JIT) |
USE(X) |
可替换的实现方案 | USE(TZONE_MALLOC)、USE(LIBWEBRTC)、USE(SKIA) |
这些宏定义在平台头文件中,其值来源于 CMake 缓存变量或 Xcode 构建设置。每个源文件开头都会 #include "config.h",在其他任何代码之前引入平台定义。
CMake 构建路径使用 CMakePresets.json 定义具名的构建配置。例如,GTK 移植有一个预设,用于设置 Ninja 生成器和 GTK 专用的缓存变量,配置命令如下:
cmake --preset gtk-debug
Apple 平台构建完全绕过 CMake,直接使用 Xcode workspace 和 Configurations/ 目录下的 .xcconfig 文件。
flowchart TD
A[Developer runs build-webkit] --> B{Host platform?}
B -->|macOS/iOS| C[Xcode workspace<br/>Configurations/*.xcconfig]
B -->|Linux/Windows/PS| D[CMake + Presets<br/>CMakePresets.json]
C --> E[Build products]
D --> E
双构建系统是一个务实的选择:Apple 工程师日常在 Xcode 中工作,而非 Apple 移植版本的维护者则需要 CMake。Tools/Scripts/ 中的 build-webkit 脚本会自动检测平台并调用对应的构建后端。
源码目录速查
在 Source/ 目录内,每个组件目录都遵循一致的内部结构:
graph TD
Source["Source/"]
Source --> bmalloc["bmalloc/"]
Source --> WTF["WTF/wtf/"]
Source --> JSC["JavaScriptCore/"]
Source --> WC["WebCore/"]
Source --> WK["WebKit/"]
Source --> WD["WebDriver/"]
Source --> TP["ThirdParty/"]
JSC --> parser["parser/"]
JSC --> bytecode["bytecode/"]
JSC --> jit["jit/, dfg/, ftl/, b3/"]
JSC --> runtime["runtime/"]
JSC --> heap["heap/"]
WC --> dom["dom/"]
WC --> css["css/"]
WC --> layout["layout/"]
WC --> rendering["rendering/"]
WC --> Modules["Modules/"]
WC --> platform["platform/"]
WK --> UIProcess["UIProcess/"]
WK --> WebProcess["WebProcess/"]
WK --> NetworkProcess["NetworkProcess/"]
WK --> GPUProcess["GPUProcess/"]
WK --> IPC["Platform/IPC/"]
几个值得注意的规律:
- JavaScriptCore 按编译阶段组织:
parser/→bytecompiler/→llint/→jit/→dfg/→ftl/→b3/。 - WebCore 按 Web 标准组织:
dom/、css/、html/、svg/,另有一个Modules/目录包含 30 余个 Web API 的实现。 - WebKit(多进程层)按进程类型组织:
UIProcess/、WebProcess/、NetworkProcess/、GPUProcess/。
Source/ThirdParty/ 目录收录了 WebKit 内置的外部依赖:ANGLE(OpenGL ES)、libwebrtc、Skia(非 Apple 移植的可选图形后端)、BoringSSL 等。
与系列后续文章的衔接
有了这张地图,你已经清楚地知道各部分代码在哪里,以及为什么要以这种方式分层。下一篇文章将深入基础层——WTF 和 bmalloc——并详细解析贯穿仓库几乎每一个文件的所有权惯用语(Ref、RefPtr、WeakPtr、ProtectedThis)。理解这些模式,是高效阅读任何 WebKit 代码的前提。