cef中c++和javascript数据交互

基础知识

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函数的引用。这可以如下实现。

  1. 在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);
}
  1. 在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;
    }
    
  2. 通过JavaScript注册JS回调。

    <script language="JavaScript">
    function myFunc() {
      // do something in JS.
    }
    window.register(myFunc);
    </script>
    
  3. 稍后执行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 进程响应

参考

cef / JavaScript 集成

CEF General Usage(CEF3预览)

菜鸟与 cef 的邂逅之旅(一):cef 源码获取与编译

JavaScript和Cpp交互示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值