基础知识
cef中有两种进程,render进程和browser进程。
render进程
render进程负责显示web页面,运行javascript代码。
v8引擎的初始化是在render进程中调用的,所以你的javascript代码是在render进程中执行的。
即使你在browser进程中调用
frame->ExecuteJavaScript()
你也要清楚,代码是发送到render进程执行的。
browser进程
browser进程是创建windows系统的客户端窗口的进程。
一般我们的webrtc sdk和我们的c++代码应该执行在browser进程。
进程通信
这样就引入了本文要讲的问题,运行在browser进程的c++代码如何给render进程中javascript传递数据。
两个进程的通信是通过发送进程间消息来完成的。
//发消息给browser进程
browser->SendProcessMessage(PID_BROWSER, message);
//发消息给render进程
browser->SendProcessMessage(PID_RENDERER, message);
CEF 进程间消息传递
CEF Render进程和Browser进程之间的通信,往往使用CefBrowser::SendProcessMessage()方法。由于CefBrowser 与CefFrame对象同时存在于browser与render进程中,所以不管在渲染(Render)进程还是浏览(Browser)进程中,都能调用到SendProcessMessage()方法。
所有render进程提供利用相同的消息传递方式:通过browser进程中的CefBrowserProcessHandler::OnRenderProcessThreadCreated()将消息传递给render进程中的CefRenderProcessHandler::OnRenderThreadCreated()。
从browser进程发到render进程的消息,被CefRenderProcessHandler::OnProcessMessageReceived()接收,从render进程发到browser进程的消息,被CefBrowserProcessHandler::OnProcessMessageReceived()接收。见下图
窗口绑定
窗口绑定允许客户端应用程序将值附加到框架的window
对象。窗口绑定是使用CefRenderProcessHandler :: OnContextCreated()方法实现的。
void MyRenderProcessHandler::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
// Create a new V8 string value. See the "Basic JS Types" section below.
CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");
// Add the string to the window object as "window.myval". See the "JS Objects" section below.
object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
}
然后,框架中的JavaScript可以与窗口绑定进行交互。
<script language="JavaScript">
alert(window.myval); // Shows an alert box with "My Value!"
</script>
功能和窗口绑定
函数可用于创建复杂的窗口绑定。
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
// Create an instance of my CefV8Handler object.
CefRefPtr<CefV8Handler> handler = new MyV8Handler();
// Create the "myfunc" function.
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
// Add the "myfunc" function to the "window" object.
object->SetValue("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
}
<script language="JavaScript">
alert(window.myfunc()); // Shows an alert box with "My Value!"
</script>
功能和扩展
函数可用于创建复杂的扩展。请注意,使用扩展时需要使用“native function”前向声明。
void MyRenderProcessHandler::OnWebKitInitialized() {
// Define the extension contents.
std::string extensionCode =
"var test;"
"if (!test)"
" test = {};"
"(function() {"
" test.myfunc = function() {"
" native function myfunc();"
" return myfunc();"
" };"
"})();";
// Create an instance of my CefV8Handler object.
CefRefPtr<CefV8Handler> handler = new MyV8Handler();
// Register the extension.
CefRegisterExtension("v8/test", extensionCode, handler);
}
<script language="JavaScript">
alert(test.myfunc()); // Shows an alert box with "My Value!"
</script>
使用JS回调
在使用本机代码注册JS函数回调时,应用程序应在本机代码中存储对当前上下文和JS函数的引用。这可以如下实现。
- 在OnJSBinding()中创建一个“注册”函数。
void MyRenderProcessHandler::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
CefRefPtr<CefV8Handler> handler = new MyV8Handler(this);
object->SetValue("register",
CefV8Value::CreateFunction("register", handler),
V8_PROPERTY_ATTRIBUTE_NONE);
}
-
在MyV8Handler :: Execute()实现中,“注册”函数保留对上下文和函数的引用。
bool MyV8Handler::Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception) { if (name == "register") { if (arguments.size() == 1 && arguments[0]->IsFunction()) { callback_func_ = arguments[0]; callback_context_ = CefV8Context::GetCurrentContext(); return true; } } return false; }
-
通过JavaScript注册JS回调。
<script language="JavaScript"> function myFunc() { // do something in JS. } window.register(myFunc); </script>
-
稍后执行JS回调。
CefV8ValueList args; CefRefPtr<CefV8Value> retval; CefRefPtr<CefV8Exception> exception; if (callback_func_->ExecuteFunctionWithContext(callback_context_, NULL, args, retval, exception, false)) { if (exception.get()) { // Execution threw an exception. } else { // Execution succeeded. } }
总结
Cef3 中 C++ 与 JavaScript 的互相调用其实并不复杂,其根本原理还是 Browser 进程与 Render 进程的互相通信。
C++ 调用 JavaScript:
其中 Browser 进程向 Render 进程发送消息,Render 进程响应
JavaScript 调用 C++:
Render 进程向 Browser 进程发送消息,Browser 进程响应