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);
方法一、window binding (即给window对象创建全局变量)
如果c++希望发送一些数据给javascript,可以使用cef中的window binding技术。
给javascript中的window对象添加一个全局变量。
更详细的教程可以看cef官网的文档 cef window binding
比如视频会议的房间信息 roomInfo。
browser进程发送roomInfo给render进程
假设只有一个bool值要发送
void EngineCallback::OnRoomInfo(const truman::RoomInfo& room) {
CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
if (browser.get() == NULL) {
return;
}
CefRefPtr<CefProcessMessage> response = CefProcessMessage::Create("onRoomInfo");
CefRefPtr<CefListValue> response_args = response->GetArgumentList();
bool is_start = room.IsStart();
response_args->SetBool(0, is_start);
browser->SendProcessMessage(PID_RENDERER, response);
return;
}
render收到消息,执行window binding,给window对象创建全局变量window.roomInfo
bool RenderDelegate::OnProcessMessageReceived(
CefRefPtr<ClientApp> app,
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
std::string message_name = message->GetName();
if (message_name == "onRoomInfo") {
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
CefRefPtr<CefV8Context> context = frame->GetV8Context();
CefRefPtr<CefListValue> args = message->GetArgumentList();
bool is_start = args->GetBool(2);
context->Enter();
CefRefPtr<CefV8Value> is_start_v8 = CefV8Value::CreateBool(is_start);
CefRefPtr<CefV8Value> global = context->GetGlobal();
CefRefPtr<CefV8Value> roomInfo;
if (global->HasValue("roomInfo")) {
roomInfo = global->GetValue("roomInfo");
} else {
roomInfo = CefV8Value::CreateObject(NULL);
global->SetValue("roomInfo", roomInfo, V8_PROPERTY_ATTRIBUTE_READONLY);
}
roomInfo->SetValue("isStart", is_start_v8, V8_PROPERTY_ATTRIBUTE_READONLY);
context->Exit();
return true;
}
}
每次roomInfo变更,就需要执行一次上面的流程,更新window.roomInfo
方法二、使用Objects with Accessors
这种方法也是给window绑定给一个全局变量roomInfo
但是给roomInfo设置get和set方法,当使用语法roomInfo.isStart
就会调用到get方法,get方法运行在render进程
在get方法里返回isStart的值。
声明一个accessor
#include "include/cef_v8.h"
class RoomInfoAccessor : public CefV8Accessor {
public:
RoomInfoAccessor();
virtual ~RoomInfoAccessor();
virtual bool Get(const CefString& name, const CefRefPtr<CefV8Value> object, CefRefPtr<CefV8Value>& retval, CefString& exception) OVERRIDE;
virtual bool Set(const CefString& name, const CefRefPtr<CefV8Value> object, const CefRefPtr<CefV8Value> value, CefString& exception) OVERRIDE;
IMPLEMENT_REFCOUNTING(RoomInfoAccessor);
};
实现Get
bool RoomInfoAccessor::Get(const CefString& name, const CefRefPtr<CefV8Value> object
, CefRefPtr<CefV8Value>& retval, CefString& exception)
{
if (name == "isStart") {
// Return the value.
retval = CefV8Value::CreateBool(false);
return true;
}
// Value does not exist.
return false;
}
Accessor如何拿到数据?
上面的代码并没有去拿真实的数据,只是返回了一个false。
如果要获取真实的数据,有以下几种方法:
1、可以在c++中发送进程消息,在render进程中将数据进行存储
类似于windows binding中发消息的部分。render进程将数据存储到本进程中的变量如:g_roomInfo
在Get方法中读取g_roomInfo
2、开启单进程模式,直接从内存中获取c++数据
在Get方法中直接读取browser中的全局变量g_roomInfo
3、多进程模式下,共享内存
broswer进程中,对共享内存进行修改,render中的Get方法进行读取。
方法三、使用cefQuery
不同于以上两种同步数据交互方式,cefQuery是一个异步数据交互方式
cefQuery封装了进程间消息交互的流程。
我猜测的大致流程为:
1、javascript调用window.cefQuery发送请求,并提交一个回调函数:callback,js继续执行,不阻塞
2、browser进程收到消息,保存messageId,并执行请求的运算
3、当browser进程完成运算后,产生messageId对应的response,发送进程消息给render进程
4、render进程收到消息,调用和messageId对应的callback
可以参考官网教程:examples/message_router
方法四、使用XMLHttpRequest
XMLHttpRequest可以通过post方法将二进制数据如arraybuffer发送到browser进程。
1、当javascript需要传递大量二进制数据给browser进程的c++代码时,可以使用POST方法,
2、当javascript需要从browser进程的c++代码拿到大量二进制数据时,可以使用GET方法。
注意:XMLHttpRequest只支持标准的http、https协议,其他自定义scheme无法支持。
所以我们需要创建自定义的http scheme,通过限定域名domain,来对特定的domain请求进行处理。
而其他domain的请求,依然通过默认处理来完成。
具体如何使用XMLHttpRequest发送二进制数据,我将单独写一篇博客。
cef中javascript和c++交换二进制数据(arraybuffer)的方法