cef中c++和javascript數(shù)據(jù)交互的幾種方法
基礎(chǔ)知識(shí)
cef中有兩種進(jìn)程途茫,render進(jìn)程和browser進(jìn)程削葱。
render進(jìn)程
render進(jìn)程負(fù)責(zé)顯示web頁面半抱,運(yùn)行javascript代碼辑甜。
v8引擎的初始化是在render進(jìn)程中調(diào)用的率翅,所以你的javascript代碼是在render進(jìn)程中執(zhí)行的材原。
即使你在browser進(jìn)程中調(diào)用
frame->ExecuteJavaScript()
你也要清楚沸久,代碼是發(fā)送到render進(jìn)程執(zhí)行的。
browser進(jìn)程
browser進(jìn)程是創(chuàng)建windows系統(tǒng)的客戶端窗口的進(jìn)程余蟹。
一般我們的webrtc sdk和我們的c++代碼應(yīng)該執(zhí)行在browser進(jìn)程卷胯。
進(jìn)程通信
這樣就引入了本文要講的問題,運(yùn)行在browser進(jìn)程的c++代碼如何給render進(jìn)程中javascript傳遞數(shù)據(jù)威酒。
兩個(gè)進(jìn)程的通信是通過發(fā)送進(jìn)程間消息來完成的窑睁。
//發(fā)消息給browser進(jìn)程
browser->SendProcessMessage(PID_BROWSER, message);
//發(fā)消息給render進(jìn)程
browser->SendProcessMessage(PID_RENDERER, message);
方法一、window binding (即給window對(duì)象創(chuàng)建全局變量)
如果c++希望發(fā)送一些數(shù)據(jù)給javascript葵孤,可以使用cef中的window binding技術(shù)担钮。
給javascript中的window對(duì)象添加一個(gè)全局變量。
更詳細(xì)的教程可以看cef官網(wǎng)的文檔 cef window binding
比如視頻會(huì)議的房間信息 roomInfo佛呻。
browser進(jìn)程發(fā)送roomInfo給render進(jìn)程
假設(shè)只有一個(gè)bool值要發(fā)送
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收到消息裳朋,執(zhí)行window binding,給window對(duì)象創(chuàng)建全局變量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變更吓著,就需要執(zhí)行一次上面的流程鲤嫡,更新window.roomInfo
方法二、使用Objects with Accessors
這種方法也是給window綁定給一個(gè)全局變量roomInfo
但是給roomInfo設(shè)置get和set方法绑莺,當(dāng)使用語法roomInfo.isStart
就會(huì)調(diào)用到get方法暖眼,get方法運(yùn)行在render進(jìn)程
在get方法里返回isStart的值。
聲明一個(gè)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);
};
實(shí)現(xiàn)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如何拿到數(shù)據(jù)纺裁?
上面的代碼并沒有去拿真實(shí)的數(shù)據(jù)诫肠,只是返回了一個(gè)false司澎。
如果要獲取真實(shí)的數(shù)據(jù),有以下幾種方法:
1栋豫、可以在c++中發(fā)送進(jìn)程消息挤安,在render進(jìn)程中將數(shù)據(jù)進(jìn)行存儲(chǔ)
類似于windows binding中發(fā)消息的部分。render進(jìn)程將數(shù)據(jù)存儲(chǔ)到本進(jìn)程中的變量如:g_roomInfo
在Get方法中讀取g_roomInfo
2丧鸯、開啟單進(jìn)程模式蛤铜,直接從內(nèi)存中獲取c++數(shù)據(jù)
在Get方法中直接讀取browser中的全局變量g_roomInfo
3、多進(jìn)程模式下丛肢,共享內(nèi)存
broswer進(jìn)程中围肥,對(duì)共享內(nèi)存進(jìn)行修改,render中的Get方法進(jìn)行讀取蜂怎。
方法三穆刻、使用cefQuery
不同于以上兩種同步數(shù)據(jù)交互方式,cefQuery是一個(gè)異步數(shù)據(jù)交互方式
cefQuery封裝了進(jìn)程間消息交互的流程杠步。
我猜測(cè)的大致流程為:
1氢伟、javascript調(diào)用window.cefQuery發(fā)送請(qǐng)求,并提交一個(gè)回調(diào)函數(shù):callback,js繼續(xù)執(zhí)行篮愉,不阻塞
2腐芍、browser進(jìn)程收到消息差导,保存messageId试躏,并執(zhí)行請(qǐng)求的運(yùn)算
3、當(dāng)browser進(jìn)程完成運(yùn)算后设褐,產(chǎn)生messageId對(duì)應(yīng)的response,發(fā)送進(jìn)程消息給render進(jìn)程
4颠蕴、render進(jìn)程收到消息,調(diào)用和messageId對(duì)應(yīng)的callback
可以參考官網(wǎng)教程:examples/message_router
方法四助析、使用XMLHttpRequest
XMLHttpRequest可以通過post方法將二進(jìn)制數(shù)據(jù)如arraybuffer發(fā)送到browser進(jìn)程犀被。
1、當(dāng)javascript需要傳遞大量二進(jìn)制數(shù)據(jù)給browser進(jìn)程的c++代碼時(shí)外冀,可以使用POST方法寡键,
2、當(dāng)javascript需要從browser進(jìn)程的c++代碼拿到大量二進(jìn)制數(shù)據(jù)時(shí)雪隧,可以使用GET方法西轩。
注意:XMLHttpRequest只支持標(biāo)準(zhǔn)的http、https協(xié)議脑沿,其他自定義scheme無法支持藕畔。
所以我們需要?jiǎng)?chuàng)建自定義的http scheme,通過限定域名domain庄拇,來對(duì)特定的domain請(qǐng)求進(jìn)行處理注服。
而其他domain的請(qǐng)求韭邓,依然通過默認(rèn)處理來完成。
具體如何使用XMLHttpRequest發(fā)送二進(jìn)制數(shù)據(jù)溶弟,我將單獨(dú)寫一篇博客女淑。
cef中javascript和c++交換二進(jìn)制數(shù)據(jù)(arraybuffer)的方法