机智的 WebUI
2025-09-22
Chromium 是一个浏览器。
除了网页使用到了诸如 HTML 等 Web 技术外,其余组件,像按钮、窗体等,都是 Native。
很多人,包括我,在最开始的时候都是这么想的。
其实不是。
有些复杂 UI,同样是通过 Web 技术实现的。比如大家最常用的:chrome://settings/。
看起来不像,但它的确是个 Web 页面。你不妨用 F12 打开调试栏看看。
当然,这不是常规 B/S 模式下的网页,而是 Chromium 内在支持的一种,借助 Web 技术快速实现复杂用户交互的手段,即 WebUI。
1. WebUI 概念
按照 Chromium 官方文档的说法:
“WebUI” is a term used to loosely describe parts of Chrome’s UI implemented with web technologies (i.e. HTML, CSS, JavaScript).
“WebUI” 是一个术语,用于宽泛地描述使用 Web 技术(即 HTML、CSS、JavaScript)实现的 Chrome UI 部分。
构建 UI 通常有两种方式,Native 或 Web。Native 就是借助 WinForms、QT 这种,偏底层,性能好;Web 则更适合敏捷开发,灵活,使用效率高。
Chromium 作为一个典型的 Native 应用,内部复杂的 UI 却用 Web 实现。这种 Hybrid 的方案,无疑兼顾了两者之长。
可以这么说,WebUI 就是 Chromium 内置的“网页”,通过类似网页的方式加载,但内容完全来自本地,通信也是和 Chromium 自身。
几个常见的使用 WebUI 的例子有:
- Settings (chrome://settings🔗)
- History (chrome://history🔗)
- Downloads (chrome://downloads🔗)
2. 架构设计
虽说 WebUI 使用到了 Web 技术,但与普通网页又存在诸多不同。最明显的,就是“高权限”。
WebUIs are granted super powers so that they can manage Chrome itself.
WebUI 被赋予了超能力,以便它们可以管理 Chrome 本身。
WebUI 可以与浏览器内部敏感的,比如隐私和安全服务通信。
这需要一系列底层机制的支持:
- 允许 Render 进程加载形如
chrome://的 URL - 允许通过
CallJavascriptFunction()🔗 执行任意 JavaScript - 允许使用
chrome.send()🔗 实现 Render 和 Browser 的通信 - 忽略有关显示图像或执行 JavaScript 的内容设置(Content Settings)
从架构角度,WebUI 可分为三层:前端层;WebUI Controller 层;通信层。
-
前端层(HTML/CSS/JS)
- 和普通 Web 页面类似,运行在 Blink 渲染引擎中
-
WebUI Controller(C++ 层)
- 每个 WebUI 页面对应一个
WebUIController派生类 - 负责注册资源(HTML、JS、Image 等),定义与前端通信的消息通道
- 每个 WebUI 页面对应一个
-
通信层(Mojo IPC / WebUIMessageHandler)
mojom+Mojo,基于强类型接口的跨进程通信,更加高效、安全,连接不同进程,并将用户意图传导给浏览器
一个 WebUI 的加载流程大致如下:
- 用户输入
chrome://example/ WebUIControllerFactory根据 URL 匹配到对应的WebUIController- Controller 通过
content::WebUIDataSource提供前端资源 - 前端加载完成后,通过
Mojo或WebUIMessageHandler向 C++ 发送请求 - 浏览器端执行(如获取配置、调用系统 API),再把结果返回前端
和一个 B/S 架构的网页很像,只不过 Server 变成了浏览器。
3. 如何使用
以 chrome://hello 为例,看一个极简的 WebUI 是如何实现的。
3.1. 定义 WebUI Controller
class HelloUI : public ui::MojoWebUIController { public: explicit HelloUI(content::WebUI* web_ui); ~HelloUI() override;};// hello_ui.ccHelloUI::HelloUI(content::WebUI* web_ui) : ui::MojoWebUIController(web_ui, true /* enable Mojo */) { auto source = content::WebUIDataSource::Create("hello"); source->AddResourcePath("hello.js", IDR_HELLO_JS); source->SetDefaultResource(IDR_HELLO_HTML); content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(), source);}3.2. 定义 Mojo 接口
module hello.mojom;
interface HelloPageHandler { SayHello() => (string response);};生成后,会得到对应的 C++ 和 JS 绑定。
3.3. 实现 Handler
class HelloPageHandlerImpl : public hello::mojom::HelloPageHandler { public: void SayHello(SayHelloCallback callback) override { std::move(callback).Run("Hello from C++!"); }};3.4. 前端调用
<html> <body> <h1 id="msg"></h1> <script type="module" src="hello.js"></script> </body></html>// hello.js import {HelloPageHandler} from 'hello.mojom-webui.js'; const handler= HelloPageHandler.getRemote(); handler.sayHello().then(response => {document.getElementById('msg').innerText = response; });访问 chrome://hello,就能看到 C++ 返回的 “Hello from C++!”。
4. 注意事项
因为 WebUI 有比较高的权限,因此安全问题是重中之重。
因此从设计上,
- WebUI 无法嵌入 HTTP/HTTPS 资源
- WebUI 不能发起 HTTP/HTTPS 请求
在极其少数的情况下,WebUI 确实需要包含 Web 内容,安全的办法是使用 <iframe> 标签,以实现进程的完全隔离。
毕竟,未经约束的自由是危险之源。
5. 总结
Chromium WebUI 提供了一种浏览器内部服务与前端界面交互的统一方式 ,充分利用了 Web 在实现复杂 UI 上的优势,以及用户交互上的便捷性和易用性。
十分巧妙。
思路一旦打开,发现这个世界其实很辽阔。
这印证了,技术世界里,没有绝对的非黑即白,只有一个个针对具体案例的权衡和取舍。
用合理的成本解决一个痛点问题,就是一个好的设计。
(完)
参考
- 本文作者:Plantree
- 本文链接:https://plantree.me/blog/2025/chromium-webui/
- 版权声明:所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
最后更新于: 2026-01-05T06:49:23+08:00