我想调用CefV8Context :: Eval函数并在浏览器进程的UI线程中获取返回值。但CEF3 C ++ API Docs声明V8句柄只能从创建它们的线程访问。用于创建V8句柄的有效线程包括呈现进程主线程(TID_RENDERER)和WebWorker线程。这是否意味着我应该使用进程间通信(CefProcessMessage)来调用该方法并获取返回值?如果是这样,如何在同步模式下执行此操作?
答案 0 :(得分:9)
简短回答:简单请求的CefFrame::ExecuteJavaScript
将起作用。对于更复杂的,您必须放弃一个级别的同步性或使用自定义消息循环。
我理解你想要做的是执行一些Javascript代码作为本机App的UI线程的一部分。有两种可能性:
它是通用的JS代码,并不真正访问JS中的任何变量或函数,因此没有 context 。这意味着Cef可以启动新的V8上下文并执行您的代码 - 请参阅CefFrame::ExecuteJavaScript()
。引用CEF's JS Integration link上的示例:
CefRefPtr浏览器= ...; CefRefPtr frame = browser-> GetMainFrame(); frame-> ExecuteJavaScript(“alert('ExecuteJavaScript works!');”, frame-> GetURL(),0);
这是带有上下文的JS代码。在这种情况下,请继续阅读。
是 - CEF的设计使得只有RenderProcess可以访问V8引擎,您必须使用CefProcessMessage
转到浏览器并在那里进行评估。你听起来像你已经知道如何做到这一点。我将把我的答案与其他没有的人联系起来,可能会在以后偶然发现:Background process on the native function at Chromium Embedded Framework
从浏览器到渲染进程的CEFProcessMessage是必须同步请求的地方。
因此,在将逻辑发送到渲染过程之后,您需要执行javascript代码的实际执行。谢天谢地,这很简单 - 同样的JS集成链接继续说:
本机代码可以使用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从浏览器发布到渲染器进程?。不幸的是,班级本身并没有这样做。更重要的是,IPC Wiki Page明确禁止它:
这是一件大事吗?嗯,我真的不知道,因为我没有遇到这种需要 - 对我来说,没关系,因为浏览器的消息循环将继续旋转等待某事做,并且在渲染器发送进程消息之前不会收到任何信息。 JS的结果。浏览器获取其他内容的唯一方法是在发生某些交互时,由于渲染器阻塞,因此无法进行交互。从渲染器的角度来看,某些消息应该是同步的。 这种情况主要发生在我们所谓的WebKit调用时 返回一些东西,但我们必须在浏览器中做。示例 这种类型的消息是拼写检查并获取cookie JavaScript的。禁止同步浏览器到渲染器IPC 防止在可能存在碎片的渲染器上阻塞用户界面。
如果您确实肯定需要同步性,我建议您使用自定义MessageLoop,在每次迭代时调用CefDoMessageLoopWork()
。这样,您可以设置一个标志来暂停循环工作,直到从渲染器收到消息。请注意,CefDoMessageLoopWork()
和CefRunMessageLoop()
互斥并且无法相互协作 - 您可以自行管理循环,也可以让CEF为您完成。
这很长,涵盖了你可能想做的大部分事情 - 希望它有所帮助!