一、简介
这一期的精读 GitHub 系列是 servo 浏览器,我们将从源码的角度去拆解 servo 浏览器,逐步厘清整个浏览器的工作原理。
servo 是一款实验性质的现代浏览器引擎,采用 Rust 语言编写(其实是为了写 servo 创建了 Rust 这门语言,但 servo 没火,Rust 先火起来了!),servo 目前还没有独立的 App,需要使用 servoshell 来运行网页;servo 不仅展示了 Rust 语言在系统编程中的强大能力,还开创了并行化布局和渲染的全新理念(其中很多已经被 Firefox 吸收),是一个学习浏览器原理与 Rust 语言非常棒的开源项目。
自 2012 年以来,目前已收获 30K+的 star:
二、安装使用
这里我们以 macOS 为例,介绍如何在编译 servo,以及在 servo 上运行一个网页,其他操作系统按照官方 README 步骤操作即可。
macOS 上编译 servo 步骤:
1)下载 servo 源码:
Shell
git clone https://github.com/servo/servo
3)安装uv
Shell
curl -LsSf https://astral.sh/uv/install.sh | sh
4)安装 rustup
Shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
5)重启命令行保证安装的工具生效,在 servo 根目录下运行./mach bootstrap安装依赖
6)在 servo 根目录下运行./mach build进行编译:mach 是 servo 的构建和管理工具
编译成功后,可以通过如下命令运行一个网页:
Shell
# 运行网页
./mach run https://example.com
# 无头浏览器模式运行:不启动图形界面
./mach run --headless https://example.com
效果如下:
三、架构介绍
3.1 目录结构
以./mach run https://baidu.com命令运行为例,来深入理解这些目录之间是如何协同工作的:
1)ports/servoshell 启动程序,调用 components/constellation 创建一个新的 Tab
2)constellation 指挥 components/net 去下载网页
3)下载的 HTML 传给 components/script 解析成 DOM 树
4)components/style 并行计算样式,附着在 DOM 上
5)components/layout 读取 DOM 和样式,计算位置,生成布局树(Layout Tree)
6)最后,结果被送到 components/compositing 显示到屏幕上
3.2 架构图
上图是单内容进程时的架构图,实际上每个 tab 都可以视为一个进程。
关键点:
1) Constellation
核心协调者,可以理解为 tab 管理器,用于管理多个任务管线(Pipeline)
2)多 Pipeline 并行
图中展示了 A 和 B 两个 Script 线程,servo 在设计上支持一个进程内运行多个独立的渲染管线,比如 iframe 可以有自己独立的管线
3)Script 与 Layout 分离
每个 Script 线程都有一个专属的 Layout 线程
从图中可以看出,网页处理主要分为三个阶段:
| 阶段 | 核心职责 | 说明 |
|---|---|---|
| Script | 所有权 DOM 树,执行 JavaScript,处理导航事件等 | 当需要知道元素位置(如 offsetWidth)时,必须发消息询问 Layout |
| Layout | 快照 DOM 树,计算样式,构建 Box Tree 和 Fragment Tree | 最终生成 Display List 并发送给 Compositor |
| Compositor | UI 线程,接收显示列表,转发给 WebRender 进行 GPU 渲染 | UI 事件(如点击、滚动)的第一接收者,通常会将事件转发给 Script 处理 |
3.3 servo 的加速秘籍
servo 在哪些地方变快了,又是怎么做到的呢?
可以总结如下:
四、总结
通过本文,我们对 servo 有了整体的了解,后续,我们将从源码角度逐步拆解各部分。
更多精彩内容在公众号 「非专业程序员Ping」,欢迎订阅交流!