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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容