cef中c++和javascript数据交互的几种方法

17 篇文章 6 订阅
6 篇文章 2 订阅

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)的方法

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值