机智的 WebUI

2025-09-22 pv

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 的例子有:

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 等),定义与前端通信的消息通道
  • 通信层(Mojo IPC / WebUIMessageHandler)

    • mojom + Mojo,基于强类型接口的跨进程通信,更加高效、安全,连接不同进程,并将用户意图传导给浏览器

一个 WebUI 的加载流程大致如下:

  • 用户输入 chrome://example/
  • WebUIControllerFactory 根据 URL 匹配到对应的 WebUIController
  • Controller 通过 content::WebUIDataSource 提供前端资源
  • 前端加载完成后,通过 MojoWebUIMessageHandler 向 C++ 发送请求
  • 浏览器端执行(如获取配置、调用系统 API),再把结果返回前端

和一个 B/S 架构的网页很像,只不过 Server 变成了浏览器。

3. 如何使用

chrome://hello 为例,看一个极简的 WebUI 是如何实现的。

3.1. 定义 WebUI Controller

hello_ui.h
class HelloUI : public ui::MojoWebUIController {
public:
explicit HelloUI(content::WebUI* web_ui);
~HelloUI() override;
};
// hello_ui.cc
HelloUI::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 接口

hello.mojom
module hello.mojom;
interface HelloPageHandler {
SayHello() => (string response);
};

生成后,会得到对应的 C++ 和 JS 绑定。

3.3. 实现 Handler

hello_page_handler_impl.cc
class HelloPageHandlerImpl : public hello::mojom::HelloPageHandler {
public:
void SayHello(SayHelloCallback callback) override {
std::move(callback).Run("Hello from C++!");
}
};

3.4. 前端调用

hello.html
<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 上的优势,以及用户交互上的便捷性和易用性。

十分巧妙。

思路一旦打开,发现这个世界其实很辽阔。

这印证了,技术世界里,没有绝对的非黑即白,只有一个个针对具体案例的权衡和取舍

用合理的成本解决一个痛点问题,就是一个好的设计。

(完)

参考

  1. Chromium Docs - WebUI Explainer🔗
在 GitHub 上编辑本页面

最后更新于: 2026-01-05T06:49:23+08:00