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

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

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

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

相关推荐
jktantan1 天前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
前端·javascript·vue.js
总要学点什么1 天前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
前端精髓1 天前
调用openrouter的免费API模型
java·前端·javascript
螺丝钉tina1 天前
[Playwright] [02]常用命令与调试:Debug UI、Mode和 Trace Viewer
开发语言·javascript·测试工具·ui·自动化
开开心心就好2 天前
竖式除法出题工具,整除余数混合模式
运维·服务器·前端·javascript·ocr·perl·1024程序员节
এ慕ོ冬℘゜2 天前
JS 事件循环、宏任务微任务:单线程底层原理 + 执行顺序
开发语言·javascript·ecmascript
其实防守也摸鱼2 天前
Chrome 扩展安全攻防实录:从攻击面到防御实践
开发语言·前端·网络·chrome·安全·web安全·php
web打印社区2 天前
Lodop排障总纲-常见报错一张表
开发语言·前端·javascript·chrome·websocket
程序员大阳2 天前
副队长JavaScript教程(56)-DOM之表单操作
javascript·dom·表单操作
Dxy12393102162 天前
JS AES加密详解
开发语言·javascript·ecmascript