核对日期:2026-10-13。依据 JavaScript Integration 和 General Usage 的 Asynchronous JavaScript Bindings。
V8 只在渲染进程的 TID_RENDERER 上跑。每个 frame 有自己的 CefV8Context。OnContextCreated 到 OnContextReleased 是这个上下文的寿命。上下文释放之后不能再碰它,也不能把 V8 对象一直留在成员里。
宿主执行一段脚本。 CefFrame::ExecuteJavaScript 在浏览器进程和渲染进程都可以调用,不要求当前已经进入 V8 上下文。适合改 DOM、调用页面上已经存在的函数。它没有返回值。需要结果时,让页面再发一条消息回来。
往 window 上挂值。 在 CefRenderProcessHandler::OnContextCreated 里取 context->GetGlobal(),用 CefV8Value 挂属性或函数。每次文档创建都会再来一次,所以可以按 frame 给不同的能力。函数的实现是 CefV8Handler::Execute,它仍然跑在渲染进程里。
异步桥到浏览器进程。 页面要读文件、调你的业务对象、改宿主窗口时,渲染进程只负责收调用、发消息、在回来时执行 JS 回调。官方提供的实现是 CefMessageRouter,头文件在 include/wrapper/cef_message_router.h,独立样本是二进制包里的 message_router 示例。
CefRegisterExtension 和 native function 这套 V8 扩展,官方在 M154 标为废弃。扩展在 DOM 存在之前注入,加载时访问 DOM 会崩溃,而且一旦注册就不能按 frame 修改。新代码用 OnContextCreated 的 window binding,或用消息路由。
路由分两侧,配置(JS 函数名等)必须一致。
渲染进程:
- 在
OnWebKitInitialized或路由要求的初始化点创建 renderer 侧 router。 OnContextCreated里OnContextCreated,让路由注入window.cefQuery一类函数(具体名字由配置决定)。OnContextReleased里通知路由释放。OnProcessMessageReceived里把消息交给路由,返回值表示已处理。
浏览器进程:
- 创建 browser 侧 router,并注册一个或多个
Handler。 CefLifeSpanHandler、CefRequestHandler::OnBeforeBrowse、CefClient::OnProcessMessageReceived里按头文件要求的那几个回调转发给路由。漏掉OnBeforeBrowse时,跨文档的查询会串到旧页面上。
Handler::OnQuery 在浏览器进程执行。处理完调用 callback->Success 或 Failure。可以先返回 true 把查询留住,等工作完成再调用 callback。callback 只能调用一次。
查询的参数是字符串。结构化数据在两侧自己约定 JSON。不要把 CefV8Value 或裸函数指针塞进进程消息。
不用路由、直接 SendProcessMessage 时:
- 渲染侧用 map 记住
(消息名, browser id)到(CefV8Context, 函数)的对应。 - 执行回调前
context->Enter(),之后Exit()。 OnContextReleased里把这个 context 的条目删掉。留着的 V8 句柄会在文档销毁后被用到。- 浏览器侧的结果用另一条消息送回,在渲染侧再执行函数。
CefV8Value::SetRethrowExceptions(true) 只在调用栈上面还有 JS 时使用。从原生菜单直接进 ExecuteFunction 时不要重抛,否则异常没人接,进程会坏掉。