WebKit代码分析
- https://github.com/WebKit/WebKit/blob/ae4576e425cf4eee9960dcd6de57b5ec693cab91/Introduction.md?plain=1#L3
- Source/bmalloc:内存分配
- Source/WTF (Web Template Framework):cpp基础库,以及平台无关的API,如
- Source/WTF (JavaScriptCore)
- Source/WebCore:渲染引擎,Document、WebAPIs
-
Source/WebKit
- 多进程架构
- UIProcess - 管理用户界面的应用程序进程(例如,Safari、邮件)。
- WebProcess - (WebProcess)加载和执行 Web 内容的沙盒进程
- 该进程是多例的,可多Page共享一个(比如
window.open()创建的页面Source/WebKit/UIProcess/WebProcessPool.cpp:1334-1340,WebProcessPool::processForSite,(relatedPage && !relatedPage->isClosed() && relatedPage->hasSameGPUAndNetworkProcessPreferencesAs(pageConfiguration) && !siteIsolationEnabled)) - 关于单进程设置
- 该进程是多例的,可多Page共享一个(比如
- NetworkProcess - 处理所有网络请求和存储管理
- GPUProcess - 管理图形操作,包括 WebGL 和合成。
- ModelProcess:渲染和管理 3D 模型内容
- 进程都在WebKit下有一个目录xxx,主文件xxx,以及用不同平台的xxxMain(main函数),main都是用Source\WebKit\Shared\AuxiliaryProcessMain.h处理
- 多进程架构
-
UI Porcess可以等同于MiniBrowser这个简单的App,或者说Safari,因此主要调用的API是此处的。
- 跨平台:Source\WebKit\UIProcess\API\C(里面有依然有平台专属如mac)
- 平台专属如:Source\WebKit\UIProcess\API\ios
- 跨平台:Source\WebKit\UIProcess\API\C(里面有依然有平台专属如mac)
数据存储:¶
- Source\WebKit\UIProcess\API\APIPageConfiguration.h
- Source\WebCore\page\PageConfiguration.h
- Source\WebKit\UIProcess\WebsiteData\WebsiteDataStore.h
- parameters使用处
- Source\WebKit\NetworkProcess\NetworkProcess.cpp,NetworkProcess::addWebsiteDataStore
- Source\WebKit\WebProcess\WebProcess.cpp,WebProcess::setWebsiteDataStoreParameters
- parameters使用处
- Source\WebKit\Shared\WebsitePoliciesData.h
- Source\WebKit\UIProcess\API\APIProcessPoolConfiguration.h
- 这四个类的职责层次完全不同,并不重复。下面逐一说明:
层次关系总览¶
ProcessPoolConfiguration ← 进程池级别(WebProcess 管理策略)
↓ 用于创建
WebProcessPool
↓ 被引用于
PageConfiguration ← 单个页面创建时的静态配置
├── WebsiteDataStore ← 数据存储运行时对象(不是配置类)
└── defaultWebsitePolicies
↓ 导航时动态下发
WebsitePoliciesData ← 每次导航的动态策略(非创建时配置)
各类职责¶
1. API::ProcessPoolConfiguration¶
作用: 控制 WebProcessPool(Web 进程池)的行为,是进程管理层的配置。
关键字段:
- injectedBundlePath — 注入的 bundle 路径
- processSwapsOnNavigation — 导航时是否切换进程(Site Isolation)
- isAutomaticProcessWarmingEnabled — 是否预热进程
- usesSingleWebProcess — 是否强制单进程
- isJITEnabled — 是否启用 JIT
使用时机: 创建 WebProcessPool 之前,一次性设置,之后不可更改。
2. API::PageConfiguration¶
作用: 创建单个 WKWebView/WebPageProxy 时的静态配置,是最常用的配置入口。
它聚合了其他对象的引用:
关键字段:
- processPool — 使用哪个进程池
- websiteDataStore — 使用哪个数据存储(决定 session)
- preferences — WebPreferences(JS 开关、功能开关等)
- userContentController — 注入脚本/内容规则
- defaultWebsitePolicies — 默认网站策略
使用时机: 创建 WebView 时传入,之后大部分字段不可修改。
3. WebsiteDataStore¶
作用: 这不是一个"配置类",而是一个运行时数据管理对象,对应一个浏览 session。
管理的内容:
- Cookies、LocalStorage、IndexedDB、Cache、ServiceWorker 注册等
- 隐私保护(ITP/Tracking Prevention)
- 数据的增删查(fetchData、removeData)
- 对应一个 PAL::SessionID(persistent 或 ephemeral)
使用时机: 通过 PageConfiguration::setWebsiteDataStore() 关联到页面。多个 WebView 可以共享同一个 WebsiteDataStore(共享 session)。
4. WebsitePoliciesData¶
作用: 这是每次导航时动态下发给 WebProcess 的策略,不是创建时的配置。
关键字段:
- autoplayPolicy — 自动播放策略
- customUserAgent — 覆盖 User-Agent
- allowsContentJavaScript — 是否允许 JS
- preferredContentMode — 桌面/移动模式
- customHeaderFields — 自定义请求头
- contentExtensionEnablement — 内容拦截规则开关
使用时机: 在 decidePolicyForNavigationAction 回调中,通过 WKWebpagePreferences(ObjC 层)动态设置,每次导航都可以不同。
一句话总结¶
| 类 | 层次 | 时机 | 可变性 |
|---|---|---|---|
ProcessPoolConfiguration |
进程池 | 创建 Pool 前 | 一次性 |
PageConfiguration |
单个页面 | 创建 WebView 时 | 创建时 |
WebsiteDataStore |
数据/Session | 运行时管理 | 持续可操作 |
WebsitePoliciesData |
单次导航 | 每次导航回调中 | 每次导航可变 |
IPC机制¶
- 如Source\WebKit\UIProcess\WebPageProxy.cpp、WebPageProxy::setUserAgent
webProcess.send(Messages::WebPage::SetUserAgent(m_userAgent), pageID)- 即WebPage实现SetUserAgent
- Source\WebKit\WebProcess\WebPage\WebPage.messages.in
- Source\WebKit\WebProcess\WebPage\WebPage.cpp
Playwright curlProxy修改思路¶
- 配置WebsiteDataStore
- 在Source\WebKit\UIProcess\WebsiteData\curl\WebsiteDataStoreCurl.cpp添加setNetworkProxySettings
- 设置属性m_proxySettings
- platformSetNetworkParameters时设置参数
- 给networkProcess发送m_proxySettings IPC
- 给现存的进程设置
- 设置属性m_proxySettings
- 在Source\WebKit\NetworkProcess\NetworkProcess.h、Source\WebKit\NetworkProcess\NetworkProcess.messages.in添加setNetworkProxySettings
- 在Source\WebKit\NetworkProcess\curl\NetworkProcessCurl.cpp实现setNetworkProxySettings
- networkStorageSession->setProxySettings
- CurlContext::singleton().setProxySettings(proxySettings)
- 然后在CurlContext用这个proxySettings
- 在Source\WebKit\UIProcess\WebsiteData\curl\WebsiteDataStoreCurl.cpp添加setNetworkProxySettings
Playwright App思路¶
- MainWindow:窗口,也是一个页面,只做了单窗口单页面功能
- WebKitBrowserWindow:WebView持有对象,控制WebView
- Tools\Playwright\win\WinMain.cpp:
configureDataStore(WKWebsiteDataStoreRef dataStore):配置每个configureDataStore,包含此处创建第一个以及inspector的browserContext创建的- 其重新设置了proxy
- 自己可以重新设置doh等
WebKitBrowserWindow::createPageCallback(WKPageConfigurationRef configuration)- 创建的每一个page的callback,包含此处创建第一个以及inspector的browserContext创建的
- Inspector
- Source\WebKit\UIProcess\InspectorPlaywrightAgent.cpp
- InspectorPlaywrightAgent就是PlaywrightBackendDispatcherHandler
- Source\WebKit\UIProcess\InspectorPlaywrightAgentClient.cpp
- inspector命令实现方法
- 在ource\JavaScriptCore\inspector\protocol定义类型、命令(请求+响应)、监听事件(监听如Dom事件发送给客户端)
- 如Playwright.json
- 命令名=函数名、函数参数、函数返回值
- 实现这些,如Source\WebKit\UIProcess\InspectorPlaywrightAgent.cpp
- Source/JavaScriptCore/inspector/scripts/generate-inspector-protocol-bindings.py生成
WebKitBuild\Release\JavaScriptCore\DerivedSources\inspector\InspectorFrontendDispatchers.cpp类(监听事件,发送消息)- InspectorFrontend(如PlaywrightFrontendDispatcher)
- 生成 WebKitBuild\Release\JavaScriptCore\DerivedSources\inspector\InspectorBackendDispatchers.cpp(处理命令,接收和分发消息)
- InspectorBackendDispatcher(如PlaywrightBackendDispatcher、PlaywrightBackendDispatcherHandler(全是虚函数))
- 生成类型定义
- WebKit原生实现的在Source\WebCore\inspector\agents中,如InspectorPageAgent.cpp
- 每次修改,重新编译JavaScriptCore
- 因为在Source\JavaScriptCore\CMakeLists.txt里执行了generate-inspector-protocol-bindings.py
- 在ource\JavaScriptCore\inspector\protocol定义类型、命令(请求+响应)、监听事件(监听如Dom事件发送给客户端)
- Source\WebKit\UIProcess\InspectorPlaywrightAgent.cpp
WebProcess¶
WebProcess::Singleton¶
usesSingleWebProcess 的设置时机和方式取决于平台和配置:
1. Cocoa 平台(macOS/iOS)
- 在 MiniBrowser 中,通过设置控制器的 perWindowWebProcessesDisabled 属性来决定
- Tools/MiniBrowser/mac/AppDelegate.m (L294-297)
2. GLib 平台(GTK)
- GLib API 已经废弃了单进程模式的选择。从 2.26 版本开始,只允许多进程模式
- Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp (L1790-1811)
JS API¶
bom对象navigator¶
- Source\WebCore\page\Navigator.cpp
绑定配置¶
Source\WebCore\page\Navigator.idl
生成的代码¶
- WebKitBuild\Release\WebCore\DerivedSources\JSNavigator.cpp
- JSNavigatorDOMConstructor::initializeProperties:初始化对象
- JSNavigatorPrototype::finishCreation:做一些条件过滤
- 如userAgentData不满足添加就删除
!(jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext()->isSecureContext()[SecureContext]
downcast<Document>(jsCast<JSDOMGlobalObject*>(globalObject())->scriptExecutionContext())->settingsValues().navigatorUserAgentDataJavaScriptAPIEnabled[EnabledBySetting=NavigatorUserAgentDataJavaScriptAPIEnabled]- WebKitBuild\Release\WebCore\DerivedSources\Settings.cpp
- Source\WebCore\Scripts\SettingsTemplates\Settings.cpp.erb
- Source\WebCore\Scripts\GenerateSettings.rb
- Source\WebCore\CMakeLists.txt
- 执行了GenerateSettings.rb,settings文件有
- Source\WebCore\page\Settings.yaml
- Source\WTF\Scripts\Preferences\UnifiedWebPreferences.yaml
- 其实这个settings和preferences是一个意思,比如在Source\WebKit\WebProcess\WebPage\WebPage.cpp,WebPage::updatePreferences,就会复制prefeerences
- `downcast
(jsCast (globalObject())->scriptExecutionContext())->quirks().needsNavigatorUserAgentDataQuirk())`` [EnabledByQuirk=needsNavigatorUserAgentData]
- 即要SecureContext
- settings.navigatorUserAgentDataJavaScriptAPIEnabled:设置Source\WTF\Scripts\Preferences\UnifiedWebPreferences.yaml中设置NavigatorUserAgentDataJavaScriptAPIEnabled中的webcore为true
- 或者简单点在Source\WebKit\UIProcess\API\C\WKPreferences.cpp中,添加WebPreferences::setNavigatorUserAgentDataJavaScriptAPIEnabled的函数,并在app里调用
- quirks.needsNavigatorUserAgentDataQuirk:在Source\WebCore\page\Quirks.cpp添加域名handler 启用 NeedsNavigatorUserAgentData,或者直接设置Quirks#needsNavigatorUserAgentDataQuirk返回true
- settings.navigatorUserAgentDataJavaScriptAPIEnabled:设置Source\WTF\Scripts\Preferences\UnifiedWebPreferences.yaml中设置NavigatorUserAgentDataJavaScriptAPIEnabled中的webcore为true
- 如userAgentData不满足添加就删除
XXXRef指针机制¶
- 为什么不直接用 WebPageProxy*?
因为 WebKit 的 C API 需要向前兼容和二进制兼容。如果直接暴露 WebPageProxy*,C
代码就能看到类的内部(成员函数、成员变量),后续版本改了 WebPageProxy 的内部结构就会破坏所有已编译的代码。
typedef const struct OpaqueWKPage* WKPageRef 是一个不透明指针(opaque pointer):
// 在 .h 里只声明了 struct 名,没定义它的成员
typedef const struct OpaqueWKPage* WKPageRef;
// 使用者只能声明和传递指针,无法解引用
WKPageRef page = GetSomePage(); // ✅ 可以
page->doSomething(); // ❌ 编译错误,struct 不完整
这样 C 侧代码对这个指针什么也做不了,只能把它传给其他 C API 函数。WebKit 内部在 toImpl 里再把它转回真实的
WebPageProxy*。这就是接口与实现分离。
- WK_ADD_API_MAPPING 是什么?
它是一个宏,用来给模板提供类型映射表。不是 toImpl 的声明也不是实现。
\#define WK_ADD_API_MAPPING(TheAPIType, TheImplType) \
template<> struct APITypeInfo<TheAPIType> { \
using ImplType = TheImplType; \
};
template<typename APIType> struct APITypeInfo;
// 使用:
WK_ADD_API_MAPPING(WKPageRef, WebPageProxy)
// 展开为:
template<> struct APITypeInfo<WKPageRef> {
using ImplType = WebPageProxy; // 类型别名
};
// 也就是说定义APITypeInfo<WKPageRef>类型
这相当于说:"给模板系统注册一条记录:WKPageRef → 对应的实现类是 WebPageProxy"。
- toImpl 是什么?
toImpl 是模板函数(在 WKSharedAPICast.h 里):
template<typename T, typename ImplType = typename APITypeInfo<T>::ImplType>
auto toImpl(T t) -> ImplType*
{
return downcast<ImplType>(API::Object::unwrap(/* ... */));
}
它的工作原理:
toImpl(wkPageRef) // T = WKPageRef
→ APITypeInfo<WKPageRef> // 模板推导,找到映射:ImplType = WebPageProxy
→ 返回类型 = WebPageProxy* // 自动推导,无需手动指定
// 这就解释了为什么 toProtectedImpl(pageRef) 返回 RefPtr<WebPageProxy>:
toProtectedImpl(pageRef)
→ toImpl(pageRef) // WKPageRef → WebPageProxy*
→ RefPtr<WebPageProxy>(webPageProxy) // 自动包装成智能指针
4. 整个模式的总结
┌─ C API(对外公开)────────────────────────────────┐
│ WKPageRef = OpaqueWKPage ← 不透明指针 │
│ void WKPageSetCustomUserAgent(WKPageRef, ...) │
└────────────────────────────────────────────────────┘
│
▼
┌─ WKAPICast(内部转换)─────────────────────────────┐
│ WK_ADD_API_MAPPING(WKPageRef, WebPageProxy) │
│ toImpl(wkRef) → WebPageProxy │
└────────────────────────────────────────────────────┘
│
▼
┌─ WebKit 内部(真正的实现)────────────────────────┐
│ WebPageProxy::setCustomUserAgent(...) │
└────────────────────────────────────────────────────┘
不用直接 WebPageProxy* 的原因:
- 二进制兼容性:不透明指针的 ABI 不会变
- 语言边界:C 接口可以被任何语言调用(Python、Rust 等通过 FFI)
- 编译隔离:C 侧不需要 include WebKit 内部头文件
- 安全:C 测无法直接操作对象内部
日志¶
Release日志¶
- 用法:
RELEASE_LOG(channel, "xxxx")- 分等级:RELEASE_LOG_DEBUG、RELEASE_LOG_INFO、RELEASE_LOG_ERROR
-
\#define RELEASE_LOG_DISABLED !(USE(OS_LOG) || ENABLE(JOURNALD_LOG) || OS(ANDROID))- 默认stderr,因此如果没有实现则可以定义RELEASE_LOG_DISABLED=0
-
Source/WebKit/Platform/Logging.h:定义chanel
- Source\WTF\wtf\Assertions.h
\#define DECLARE_LOG_CHANNEL(name) \ extern WTFLogChannel LOG_CHANNEL(name); \#define LOG_CHANNEL(name) JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, name) \#define LOG_CHANNEL_ADDRESS(name) &LOG_CHANNEL(name), \#define JOIN_LOG_CHANNEL_WITH_PREFIX(prefix, channel) JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) \#define JOIN_LOG_CHANNEL_WITH_PREFIX_LEVEL_2(prefix, channel) prefix ## channel typedef struct { WTFLogChannelState state; const char* name; WTFLogLevel level; \#if !RELEASE_LOG_DISABLED && USE(OS_LOG) SUPPRESS_UNRETAINED_MEMBER __unsafe_unretained os_log_t osLogChannel; \#endif } WTFLogChannel; - 如 对WEBKIT2_LOG_CHANNELS里的每一个channel
extern WTFLogChannel WebKit2LogAPI(#\#是字符串拼接)
在 Windows 上如何实现¶
Windows 目前走的是 RELEASE_LOG_DISABLED 分支,原因是没有定义 ENABLE_RELEASE_LOG。要在 Windows 上启用,有两种思路:
方案 A:启用 fallback 的 fprintf(stderr) 分支
在 CMake/构建系统中加入:
这会让 RELEASE_LOG_DISABLED 变为 0,然后走到 else 分支,用 fprintf(stderr) 输出。这是改动最小的方式。
方案 B:添加 Windows 原生分支(ETW 或 OutputDebugString)
在 Assertions.h 的 \#elif ENABLE(JOURNALD_LOG) 之后、\#else 之前,添加:
\#elif OS(WINDOWS) \#define RELEASE_LOG(channel, ...) do { \ auto& logChannel = LOG_CHANNEL(channel); \ if (logChannel.state != WTFLogChannelState::Off) { \ char buf[1024]; \ snprintf(buf, sizeof(buf), "[WebKit:%s] " __VA_ARGS__); \ OutputDebugStringA(buf); \ } \ } while (0)
OutputDebugString 输出到 Windows 调试器(Visual Studio Output 窗口 / DebugView 工具)。更完整的方案是用 ETW(Event Tracing for Windows),类似 os_log 的结构化日志,但实现复杂得多。
进程关系和启动¶
- 类似点
- 进程:XXXProcess
- 客户端控制Process的类:XXXProcessProxy类
- XXXCreationParameters.h、XXXCreationParameters.serialization.in:进程IPC initialize的参数
- 每个进程相关的类都继承AuxiliaryProcess,通用的可以放在此处
- Source\WebKit\UIProcess\AuxiliaryProcessProxy.cpp
- 一创建立刻connect、initialize(使用创建参数调用初始化IPC))
- Source\WebKit\Shared\AuxiliaryProcess.cpp
- Source\WebKit\Shared\AuxiliaryProcessCreationParameters.h
- Source\WebKit\Shared\AuxiliaryProcessCreationParameters.serialization.in
- Source\WebKit\Shared\AuxiliaryProcessMain.h
- 主程序,直接
- platformInitialize
- parseCommandLine
- InitializeWebKit2
- initializeAuxiliaryProcess
- run
- platformFinalize
- 平台覆盖,如Source\WebKit\Shared\win\AuxiliaryProcessMainWin.cpp
- 各进程不同平台有不同实现,如
- Source\WebKit\NetworkProcess\NetworkProcessMain.h
- Source\WebKit\NetworkProcess\EntryPoint\win\NetworkProcessMain.cpp
- Source\WebKit\NetworkProcess\curl\NetworkProcessMainCurl.cpp
AuxiliaryProcessMain<NetworkProcessMainCurl>(argc, argv)- Source\WebKit\WebProcess\EntryPoint\win\WebProcessMain.cpp
- Source\WebKit\WebProcess\win\WebProcessMainWin.cpp
AuxiliaryProcessMain<WebProcessMainWin>(argc, argv)
- 主程序,直接