从 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++ 通信方式的演进,本质上是:

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

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

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

相关推荐
老前端的功夫2 小时前
TypeScript 类型守卫:从编译原理到高级模式
前端·javascript·架构·typescript
未来之窗软件服务2 小时前
幽冥大陆(七十二) 东方仙盟-在线IP归属地自己封装—东方仙盟练气期
前端·javascript·tcp/ip·仙盟创梦ide·东方仙盟·阿雪技术观
ttod_qzstudio2 小时前
备忘录之事件监听器绑定陷阱:为什么 .bind(this) 会移除失败?
javascript·typescript·内存泄漏·事件监听
扶我起来还能学_3 小时前
Vue3 proxy 数据响应式的简单实现
前端·javascript·vue
Dragon Wu3 小时前
前端项目架构 项目格式化规范篇
前端·javascript·react.js·前端框架
QQ 31316378903 小时前
文华财经软件指标公式期货买卖信号提示软件
java·前端·javascript
狂龙骄子3 小时前
svg实现蚂蚁线动画
javascript·蚂蚁线动画·蚂蚁线·虚线动画
俩毛豆4 小时前
【毛豆工具集】【文件】【目录操作】生成沙盒目录
前端·javascript·鸿蒙
霁月的小屋4 小时前
从Vue3与Vite的区别切入:详解Props校验与组件实例
开发语言·前端·javascript·vue.js