Read OSS

深入 WebKit:架构概览与代码库导航

初级

前置知识

  • 通用软件工程基础知识
  • 对浏览器工作原理的基本了解(解析、渲染、执行)
  • 能够阅读 C++ 文件和目录结构

深入 WebKit:架构概览与代码库导航

WebKit 是所有 Apple 设备上 Safari 的核心引擎,同时也驱动着 Mail、Books、News 和 App Store 等应用,并通过 GTK 和 WPE 两个移植版本在 Linux 上运行。整个仓库体量庞大——数百万行 C++ 代码分散在几十个顶层目录中。在真正参与贡献、乃至读懂这份代码之前,你需要先建立一张清晰的心智地图。本文正是为此而写。

我们将依次梳理仓库的目录结构、直接编码在构建系统中的严格六层依赖层次、将 Web 内容与系统其余部分隔离的多进程安全架构,以及让同一份源码能够构建出 macOS、iOS、Linux、PlayStation 和 Windows 版本的条件编译宏体系。

仓库目录结构一览

顶层目录清晰地划分为源代码、工具和测试三大部分:

目录 用途
Source/ 所有生产源代码——从 bmallocWebDriver 的每个组件
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)

这四行是无条件的——所有移植版本都必须依赖它们。WebCoreWebKitWebDriver 等后续条目则受 ENABLE(...) 标志控制,从而支持只包含 JS 引擎的最小化构建。

各层的职责如下:

层级 目录 职责
bmalloc Source/bmalloc/ 自定义内存分配器,使用类型隔离的 IsoHeap 页面以提升安全性
WTF Source/WTF/ 模板库:容器(VectorHashMap)、智能指针(RefRefPtrWeakPtr)、线程原语
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——并详细解析贯穿仓库几乎每一个文件的所有权惯用语(RefRefPtrWeakPtrProtectedThis)。理解这些模式,是高效阅读任何 WebKit 代码的前提。