从 window.external 到 Mojo深度解析 Chromium 中 JS 与 C++ 的 7 种通信机制

一、前言:为什么 JS ↔ C++ 通信是浏览器内核的核心问题

在很多人眼里,浏览器开发似乎分得很清楚:

  • 前端:HTML / CSS / JavaScript

  • 后端:C++ / 网络 / 系统能力

但真正做过浏览器内核开发的人都清楚一件事:

浏览器是一个"前端驱动后端"的系统

几乎所有用户行为,最终都是从 JS 开始的:

  • 点击设置页的一个按钮

  • 打开 chrome://extensions

  • 触发下载、文件选择、系统配置

  • 企业定制浏览器中调用本地能力

而这些行为,最终都必须落到 C++ 层才能完成。

因此,一个问题贯穿了整个浏览器内核设计:

JavaScript 是如何安全、可控、高性能地调用 C++ 的?

在 Chromium 的发展历史中,这个问题并没有一个"一步到位"的答案,而是逐步演进,形成了多种 JS ↔ C++ 通信方式并存的局面。

本文将从浏览器内核工程师视角,系统性梳理:

Chromium 中 JS 与 C++ 通信的 7 种方式

并结合工程实践,给出选型与演进建议。


二、理解 JS ↔ C++ 通信之前:Chromium 的架构前提

在深入具体方案之前,必须先统一一个基础认知。

2.1 多进程架构是前提条件

Chromium 采用严格的多进程模型:

复制代码

几个关键结论:

  • JavaScript 永远运行在 Renderer 进程

  • 绝大多数系统能力只存在于 Browser 进程

  • JS ↔ C++ 的本质 = 跨进程通信(IPC)


2.2 所谓"通信方式"的本质差异是什么?

所有 JS ↔ C++ 的方案,最终差异都体现在:

  1. JS 从哪里调用

  2. C++ 如何暴露接口

  3. 中间通信通道是什么

  4. 安全模型是否明确

  5. 是否符合 Chromium 的长期演进方向


三、JS ↔ C++ 通信方式全景:7 种方式总览

在 Chromium / 桌面浏览器语境下,完整的 7 种方式如下:

编号 方式 是否直接 JS → C++
1 Web 标准 API 是
2 Chrome Extensions API 是
3 window.external(V8 注入) 是
4 Chrome WebUI(chrome.send) 是
5 Mojo WebUI / Mojo JS Bindings 是
6 postMessage(JS ↔ JS 间接) 否
7 WebView / CEF 宿主桥 是(非 Chromium 主线)

下面逐一展开。


四、方式一:Web 标准 API(最"正统"的通信)

4.1 示例

复制代码
navigator.clipboard.readText(); navigator.geolocation.getCurrentPosition(...) 

4.2 内部调用链(简化)

复制代码
JS → Blink WebIDL → Renderer C++ → Mojo → Browser Service → OS 

4.3 工程评价

优点:

  • 完全标准化

  • 安全模型成熟

  • 生命周期与页面一致

缺点:

  • 能力受限

  • 无法满足浏览器私有需求

📌 结论:

Web API 是浏览器对 Web 的承诺
不是浏览器给自己用的扩展能力


五、方式二:Chrome Extensions API(扩展通信)

5.1 示例

复制代码
chrome.runtime.sendMessage({ cmd: 'readFile' }); 

5.2 调用链

复制代码
Extension JS → ExtensionBindings → Mojo → Browser Extension System 

5.3 特点

  • 权限通过 manifest.json 声明

  • 生命周期独立于页面

  • IPC 封装完善

📌 结论:

适合扩展生态
不适合浏览器内置页面(chrome://)


六、方式三:window.external(历史遗产)

6.1 JS 调用

复制代码
window.external.ReadFile("C:\\test.txt"); 

6.2 C++ 注入(Renderer)

复制代码
void ChromeRenderFrameObserver::DidClearWindowObject() { v8::Local<v8::Object> external = v8::Object::New(isolate); external->Set(context, v8_str("ReadFile"), v8::Function::New(context, ReadFileCallback)); global->Set(context, v8_str("external"), external); } 

6.3 工程评价

优点 缺点
实现简单 非标准
灵活 安全边界模糊
兼容历史 难以维护

📌 结论:

window.external 是"能用但不该再扩展"的方案


七、方式四:Chrome WebUI(官方推荐方案)

7.1 JS 侧

复制代码
chrome.send('readFile', ['path']); 

7.2 C++ 侧

复制代码
web_ui()->RegisterMessageCallback( "readFile", base::BindRepeating(&Handler::ReadFile, base::Unretained(this))); 

7.3 回调 JS

复制代码
ResolveJavascriptCallback(callback_id, base::Value(result)); 

7.4 特点

  • Chrome 内置页面广泛使用

  • 权限模型清晰

  • 生命周期受控

📌 结论:

WebUI 是 Chromium 当前阶段最稳妥的 JS ↔ C++ 通信方案


八、方式五:Mojo WebUI(未来方向)

8.1 Mojo IDL

复制代码
module browser.mojom; interface FileService { ReadFile(string path) => (string content); }; 

8.2 C++ 实现

复制代码
class FileServiceImpl : public mojom::FileService { public: void ReadFile(const std::string& path, ReadFileCallback callback) override; }; 

8.3 JS 调用

复制代码
import {FileService} from './file_service.mojom-webui.js'; const service = FileService.getRemote(); const {content} = await service.readFile(path); 

8.4 为什么 Mojo 是未来

  • 强类型接口

  • 自动生成 JS / C++

  • IPC 原生支持

  • 安全性和可维护性极高

📌 结论:

Mojo WebUI 是 Chromium 官方长期演进方向


九、方式六:postMessage(间接通信)

9.1 示例

复制代码
window.postMessage({ type: 'CMD' }, '*'); 

9.2 关键事实

❌ postMessage 永远不会直接进入 C++

真实路径是:

复制代码
JS → JS →(WebUI / Extension)→ C++ 

📌 结论:

postMessage 是 JS ↔ JS 的工具
不是 JS ↔ C++ 的解决方案


十、方式七:WebView / CEF 宿主桥

10.1 JS

复制代码
window.chrome.webview.postMessage({ cmd: 'readFile' }); 

10.2 C++(宿主)

复制代码
webview->add_WebMessageReceived(...); 

10.3 架构定位

  • 属于宿主应用

  • 不属于 Chromium 浏览器自身

  • 不走 Browser / Renderer 模型

📌 结论:

WebView / CEF 是"浏览器被嵌入"时的方案
不是浏览器内核的通信主线


十一、7 种方式工程对比总结

方式 官方 安全 可维护 推荐度
Web API 是 高 高 低
Extension API 是 高 中 中
window.external 否 低 低 不推荐
WebUI 是 高 高 推荐
Mojo WebUI 是 极高 极高 强烈推荐
postMessage 是 中 中 不适合
WebView Bridge 否 中 中 不适合

十二、给定制浏览器的最终建议

结合 Chrome 132 / 360 浏览器等场景:

  • 历史能力:window.external(只维护,不新增)

  • 现有内置页面:WebUI

  • 所有新能力:Mojo WebUI

这是安全、可维护、符合 Chromium 演进方向的最优解。


十三、结语

JS ↔ C++ 通信方式的演进,本质上是:

浏览器从"功能堆砌"走向"系统级平台"的过程

理解这些通信机制,不只是为了"把功能跑通",

而是为了写出能长期维护、能持续演进的内核代码。

相关推荐
溪语流沙几秒前
【Web全栈进阶】Vue3起步:组件、模板语法、响应式数据
前端·javascript·vue.js·css3
ZoeLandia1 小时前
TypeScript 01:类型注解与类型推断
前端·javascript·typescript
第六种自由2 小时前
[路飞]_Leetcode 22. 括号生成
javascript·算法·leetcode
JAVA老架构AI智能化2 小时前
多图叠加比对
前端·javascript·chrome
踏月了无痕2 小时前
01写一个简单的API服务
javascript
志尊宝2 小时前
Vue3 零基础每日笔记(081):富文本编辑器集成 wangEditor 5——封装
前端·javascript·vue.js·笔记·信息可视化·echarts
志尊宝3 小时前
Vue3 零基础每日笔记(080):Vue3 + ECharts 数据可视化——封装可复用图表组件
前端·javascript·vue.js·笔记·信息可视化·echarts
2603_965896623 小时前
JS ES6常用新特性|let/const、解构赋值、展开运算符
前端·javascript·es6
衡石科技3 小时前
一个门户只保留一个智能助理:衡石嵌入式 Data Agent 的运行与渲染边界
前端·javascript·html·data agent
Hilaku3 小时前
一个前端工程师除了写代码,还能靠什么赚钱?
前端·javascript·程序员