简短回答:CefFrame::ExecuteJavaScript 用于简单的请求。对于更复杂的,您必须放弃一级同步或使用自定义消息循环。
我了解您想要做的是执行一些 Javascript 代码作为您本机应用程序 UI 线程的一部分。有两种可能:
-
它是通用的 JS 代码,不会真正访问 JS 中的任何变量或函数,因此没有 上下文。这意味着 Cef 可以启动一个新的 V8 上下文并执行您的代码 - 请参阅 CefFrame::ExecuteJavaScript()。引用CEF's JS Integration link上的例子:
CefRefPtr 浏览器 = ...;
CefRefPtr frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('ExecuteJavaScript 有效!');",
框架->GetURL(), 0);
这是 JS 代码带有上下文。在这种情况下,请继续阅读。
是 - CEF 的设计使得只有 RenderProcess 可以访问 V8 引擎,您必须使用 CefProcessMessage 前往浏览器并在那里进行评估。你听起来好像你已经知道该怎么做。我会为其他不知道并且以后可能会偶然发现的人链接我的答案:Background process on the native function at Chromium Embedded Framework
从 Browser 到 Render 进程的 CEFProcessMessage 是请求必须同步的地方。
因此,在您将逻辑发送到渲染进程之后,您需要实际执行 javascript 代码。幸运的是,这很容易——同样的 JS 集成链接继续说:
Native 代码可以使用 ExecuteFunction() 来执行 JS 函数
和 ExecuteFunctionWithContext() 方法
最好的部分 - 执行似乎是同步的(我说似乎是,因为我找不到具体的文档)。示例中的用法说明了这一点:
if (callback_func_->ExecuteFunctionWithContext(callback_context_, NULL, args, retval, exception, false)) {
if (exception.get()) {
// Execution threw an exception.
} else {
// Execution succeeded.
}
}
您会注意到,第二行假定第一行已完成执行,并且所述执行的结果对其可用。因此,CefV8Value::ExecuteFunction() 调用本质上是同步的。
所以问题归结为 - 如何将 CefProcessMessage 从 Browser 同步发布到 Renderer 进程?。不幸的是,类本身并没有设置为这样做。更重要的是,IPC Wiki Page 明确禁止它:
从渲染器的角度来看,某些消息应该是同步的。
这主要发生在应该有一个 WebKit 调用给我们时
返回一些东西,但是我们必须在浏览器中做。示例
这种类型的消息是拼写检查和获取 cookie
JavaScript。不允许同步浏览器到渲染器 IPC
防止在可能不稳定的渲染器上阻塞用户界面。
这有什么大不了的吗?好吧,我真的不知道,因为我没有遇到过这种需求 - 对我来说,没关系,因为浏览器的消息循环会一直旋转等待某事做,并且在您的渲染器发送带有JS的结果。浏览器需要做其他事情的唯一方法是发生某些交互时,因为渲染器阻塞,所以不能。
如果您真的肯定需要同步,我建议您使用自定义 MessageLoop,它在每次迭代时调用 CefDoMessageLoopWork()。这样,您可以设置一个标志来暂停循环工作,直到从渲染器收到您的消息。请注意,CefDoMessageLoopWork() 和 CefRunMessageLoop() 是互斥的,不能相互协作 - 你要么自己管理循环,要么让 CEF 为你做。
这很长,涵盖了您可能想做的大部分事情 - 希望对您有所帮助!