的 WebUI 框架,但在定制浏览器、增加功能或修改 UI 时,如果不了解其机制,很容易踩坑。本文将从源码层、架构设计、i18n 安全、定制实践三个维度,深度解析 Chromium WebUI 的运行机制,并提供工程级定制方案,特别针对密码管理(password_manager)模块,确保安全、可维护。
一、loadTimeData:从 C++ 注入到 JS 的全链路机制
WebUI 最核心、最容易被误解的一条路径是 loadTimeData。它承担了 浏览器内核到前端页面的同步数据注入任务,包括字符串文案、布尔值、数字和 feature flag。理解其全链路,是安全定制的第一步。
1️⃣ 宏观视角
一句话总结:
C++ 在页面加载前,把一份只读配置 + 字符串字典注入到 JS 全局对象
loadTimeData中,JS 只能读,不能写。
2️⃣ C++ 侧:WebUIDataSource(起点)
Chromium 的 WebUI 数据源由 content::WebUIDataSource 提供。以密码管理模块为例:
content::WebUIDataSource* source = content::WebUIDataSource::Create("password-manager");
本质上为 chrome://password-manager 创建了一个“虚拟站点数据源”。
(1)注入字符串资源
source->AddLocalizedString( "passwordManagerTitle", IDS_PASSWORD_MANAGER_TITLE);
底层逻辑:
-
记录 key → resource id;
-
页面加载时根据语言映射文本;
-
最终序列化成 JSON,待 JS 侧读取。
(2)注入布尔 / 数字 / Feature Flag
source->AddBoolean("enableAccountStorage", true); source->AddInteger("maxPasswords", 1000);
这是定制浏览器最安全的前后端桥梁,无需触碰业务 JS。
(3)绑定到 WebUI
content::WebUIDataSource::Add(profile, source);
到这一步为止:
-
没有 JS;
-
没有 DOM;
-
仅仅准备数据。
3️⃣ 页面加载时发生了什么
当 WebUI 页面加载:
-
Chromium 内部生成 JS,将 JSON 注入:
window.loadTimeData = new LoadTimeData({...});
-
这段 JS 先于业务 JS 执行;
-
页面 JS 永远拿到已完成注入的对象。
这就是 loadTimeData 名称的由来。
4️⃣ JS 侧 runtime
核心 runtime 文件位于 src/ui/webui/resources/js/load_time_data.js:
class LoadTimeData {
getString(id) {
return this.data_[id];
}
getBoolean(id) {
return !!this.data_[id];
}
}
window.loadTimeData = new LoadTimeData(...);
所有业务 JS 都通过
loadTimeData获取 C++ 注入的数据,而不直接访问底层 JSON。
5️⃣ 使用示例
// 纯文本 const title = loadTimeData.getString('passwordManagerTitle');
// i18n 封装 this.i18n('passwordManagerTitle');
6️⃣ 为什么不用 Mojo / IPC
| 方案 | 问题 |
|---|---|
| Mojo | 异步、复杂、性能损耗 |
| IPC | 安全模型复杂 |
| loadTimeData | 同步、只读、安全,最适合静态配置和文案 |
结论:80% UI 配置 = loadTimeData,20% 动态数据 = Mojo
二、i18n vs i18nAdvanced:设计取舍 & 坑位清单
i18n 系列 API 是 WebUI 文案安全的核心,选择不当会导致 XSS 或页面炸裂。
1️⃣ 设计目标对比
| API | 目标 |
|---|---|
i18n() | 100% 安全、纯文本 |
i18nAdvanced() | 有限 HTML,但必须可控 |
2️⃣ i18n() 强烈推荐
this.textContent = this.i18n('title');
特点:
-
自动 escape;
-
永远不会 XSS;
-
不允许 HTML。
工程实践中,90% 文案都应使用 i18n()。
3️⃣ i18nAdvanced() 高危
this.innerHTML = this.i18nAdvanced('description');
内部调用流程:
loadTimeData.getString() ↓ sanitizeInnerHtml() ↓ parseHtmlSubset()
4️⃣ parse_html_subset 白名单示例
允许标签:
-
<a href> -
<b> -
<i> -
<br>
禁止或高风险:
| 内容 | 结果 |
|---|---|
<br/> | ❌ |
<strong> | ❌(部分版本) |
<a target="_blank"> | ❌ |
<span style=""> | ❌ |
| ❌ |
<img> | ❌ |
总结:能用 i18n 就不要用 i18nAdvanced,i18nAdvanced 仅保留历史兼容或特殊 HTML 场景。
5️⃣ 定制浏览器建议
-
密码管理、同步、账号页面:
-
一律禁 HTML 文案;
-
用 CSS / DOM 组合代替。
-
三、定制浏览器:安全扩展 WebUI runtime
为了保证定制浏览器安全和可维护,有三种正确扩展方式。
1️⃣ ❌ 绝对不要做的事
-
改 parse_html_subset.js;
-
放宽 HTML 白名单;
-
翻译文件包含复杂 HTML;
-
业务 JS 开后门。
短期爽,长期炸。
2️⃣ ✅ 安全方式一:C++ → loadTimeData
source->AddBoolean("is360Browser", true); source->AddString("brandName", "360安全浏览器");
JS 使用:
if (loadTimeData.getBoolean('is360Browser')) { // 定制逻辑 }
优点:
-
无 XSS;
-
无运行期依赖;
-
可测试。
3️⃣ ✅ 安全方式二:新增 runtime helper
新增文件 src/ui/webui/resources/js/brand_util.js:
export function is360() { return loadTimeData.getBoolean('is360Browser'); }
业务层使用:
import { is360 } from 'chrome://resources/js/brand_util.js';
可维护、安全、分层清晰。
4️⃣ ✅ 安全方式三:WebUIMessageHandler(动态)
适合:
-
实时数据;
-
用户操作;
-
状态变更。
动态数据使用 Mojo / message handler,不用于静态配置。
5️⃣ 推荐分层铁律
C++(配置 / feature) ↓ loadTimeData ↓ JS runtime(resources) ↓ helper / mixin ↓ 业务 WebUI
永远不要反向依赖。
四、password_manager i18n 重构示例(零炸)
原始文案存在 XSS 风险:
<message name="password_manager_description"> Please <a href="javascript:doSomething()">click here</a> to manage passwords. </message>
重构策略:
-
移除危险 HTML;
-
链接事件绑定而不是
javascript:URL; -
使用安全 API。
strings.xtb
<message name="password_manager_description"> Please click here to manage passwords. </message>
passwords_section.js
ready() {
const desc = this.i18n('password_manager_description');
this.shadowRoot.querySelector('#desc').textContent = desc;
const link = this.shadowRoot.querySelector('#manage-link');
link.addEventListener('click', () => {
this.managePasswords();
});
}
HTML 模板
<div id="desc"></div> <button id="manage-link">Manage</button>
✅ 安全:
-
不依赖 innerHTML;
-
不违反 parse_html_subset;
-
i18n 文案安全;
-
用户事件绑定明确。
五、工程级总结
-
数据流清晰:C++ → loadTimeData → resources → 页面
-
安全边界明确:parse_html_subset + i18n / i18nAdvanced
-
定制方案:
-
新文案 / feature → loadTimeData + JS helper;
-
动态数据 → Mojo / WebUIMessageHandler;
-
不碰 infra,不改 parse_html_subset;
-
-
密码管理 / 同步页面:特别注意 HTML 安全,避免 innerHTML;
-
工程规范:
-
loadTimeData 只读;
-
i18n 优先,i18nAdvanced 限用;
-
JS runtime 不改动,新增 helper 扩展;
-
Mojo 仅处理动态、异步逻辑。
-
总结一句话:Chromium WebUI 是“C++ 数据 + runtime 安全 + 业务展示”的闭环,正确理解和分层定制,是工程级浏览器安全和稳定的核心。

32

被折叠的 条评论
为什么被折叠?



