参考出处:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF_JavaScript_Cpp.md
JavaScript和Cpp交互示例(Custom Implementation),交互分为js函数中带有callback和没有callback的实例
一个CEF应用程序也可以提供自己的异步JavaScript绑定。
此处演示(js在html中使用,render进程中):
JavaScript注册函数给Render进程,Render进程保存该JavaScript函数
Render进程发消息通知Browser进程
Browser进程处理后,回发消息给Render进程
Render进程调用之前保存的JavaScript函数
步骤:
1、首先在CefRenderProcessHandler的子类里覆写虚方法OnWebKitInitialized,并在该方法的实现里注册一个C++方法给JavaScript(js调用C++方法,render进程使用)
//假设CefRenderProcessHandler的子类为CefRenderProcessHandlerImpl
void CefRenderProcessHandlerImpl::OnWebKitInitialized(){
std::string app_code =
//-----------------------------------
//声明JavaScript里要调用的Cpp方法
"var app;"
"if (!app)"
" app = {};"
"(function() {"
// Send message ,不是回调函数实例
" app.sendMessage = function(name, arguments) {"
" native function sendMessage();"
" return sendMessage(name, arguments);"
" };"
// Registered Javascript Function, which will be called by Cpp,有回调函数示例
" app.registerJavascriptFunction = function(name,callback) {"
" native function registerJavascriptFunction();"
" return registerJavascriptFunction(name,callback);"
" };"
"})();";
//------------------------------------
// Register app extension module
// JavaScript里调用app.registerJavascriptFunction时,就会去通过CefRegisterExtension注册的CefV8Handler列表里查找
// 找到"v8/app"对应的CefV8HandlerImpl,就调用它的Execute方法
// 假设m_v8Handler是CefRenderProcessHandlerImpl的一个成员变量
m_v8Handler = new CefV8HandlerImpl();
CefRegisterExtension("v8/app", app_code,m_v8Handler);
}
2、在CefV8Handler的子类的Execute方法里实现sendMessage和registerJavascriptFunction,js执行C++函数时,便是调用此处对应的方法。
// in CefV8HandlerImpl.h
class CefV8HandlerImpl : publice CefV8Handler{
public:
CefV8HandlerImpl();
~CefV8HandlerImpl();
public:
/**
* CefV8Handler Methods, Which will be called when the V8 extension
* is called in the Javascript environment
*/
virtual bool Execute(const CefString& name
,CefRefPtr object
,const CefV8ValueList& arguments
,CefRefPtr& retval
,CefString& exception);
private:
// Map of message callbacks.
typedef std::map<std::pair<std::string, int>,
std::pair, CefRefPtr > >
CallbackMap;
CallbackMap callback_map_;//成员变量,便于js函数中设置有callback时进行调用
}
CefV8HandlerImpl::CefV8HandlerImpl()
{
}
CefV8HandlerImpl::~CefV8HandlerImpl()
{
// Remove any JavaScript callbacks registered for the context that has been released.
if (!callback_map_.empty()) {
CallbackMap::iterator it = callback_map_.begin();
for (; it != callback_map_.end();) {
if (it->second.first->IsSame(context))
callback_map_.erase(it++);
else
++it;
}
}
}
// in CefV8HandlerImpl.cpp
bool CefV8HandlerImpl::Execute(const CefString& name //JavaScript调用的C++方法名字
,CefRefPtr object //JavaScript调用者对象
,const CefV8ValueList& arguments //JavaScript传递的参数
,CefRefPtr& retval //需要返回给JavaScript的值设置给这个对象
,CefString& exception) //通知异常信息给JavaScript
{
// In the CefV8Handler::Execute implementation for “registerJavascriptFunction”.
bool handle=false;
if(name=="registerJavascriptFunction"){
//保存JavaScript设置的回答函数,使得render进程持有这个callback函数
//以便js执行带有这个callback为参数的函数后,能够执行这个callback,详见步骤3
if (arguments.size() == 2 && arguments[0]->IsString() &&
arguments[1]->IsFunction()) {
std::string message_name = arguments[0]->GetStringValue();
CefRefPtr context = CefV8Context::GetCurrentContext();
int browser_id = context->GetBrowser()->GetIdentifier();
//message_name和browser_id是用于区分当js回调发生时,执行的是哪个js函数的回调
callback_map_.insert(
std::make_pair(std::make_pair(message_name, browser_id),
std::make_pair(context, arguments[1])));
handle = true;
// 此时可以发送一个信息给Browser进程,见第4个步骤
//registerJavascriptFunction有一个callback函数,
//因此render进程需要持有这个callback
}
if(name=="sendMessage"){
//js调用sendMessage之后,c++处理SendMessage,这个没有回调函数,
//Execute函数return true,此次交互便结束。
handle = true;
}
// 如果没有处理,则发异常信息给js
if(!handle){
exception="not implement function";
}
return true;
}
3、在HTML的JavaScript里,通过上面注册的方法向Render进程注册一个回调函数。
// In JavaScript register the callback function.
app.setMessageCallback('binding_test', function(name, args) {
document.getElementById('result').value = "Response: "+args[0];
});
// function(name, args)为一个callback,args[0]
//消息名为binding_test,
(以下为有回调函数才有的步骤)
4、 Render进程发送异步进程间通信到Browser进程。
5、 Browser进程接收到进程间消息,并处理。
6、 Browser进程处理完毕后,发送一个异步进程间消息给Render进程,返回结果。
7、 Render进程接收到进程间消息,则调用最开始保存的JavaScript注册的回调函数处理之。(将render进程持有的这个registerJavascriptFunction的callback函数执行)
// Execute the registered JavaScript callback if any.
if (!callback_map_.empty()) {
const CefString& message_name = message->GetName();
CallbackMap::const_iterator it = callback_map_.find(
std::make_pair(message_name.ToString(),
browser->GetIdentifier()));
//根据消息名和浏览器id找到对应的回调函数进行执行,
//message_name为binding_test时则执行js的callback,it不是空
if (it != callback_map_.end()) {
// Keep a local reference to the objects. The callback may remove itself
// from the callback map.
CefRefPtr<CefV8Context> context = it->second.first;
CefRefPtr<CefV8Value> callback = it->second.second;
// Enter the context.
context->Enter();
CefV8ValueList arguments;
// First argument is the message name.
arguments.push_back(CefV8Value::CreateString(message_name));
// Second argument is the list of message arguments.
CefRefPtr<CefListValue> list = message->GetArgumentList();
CefRefPtr<CefV8Value> args = CefV8Value::CreateArray(list->GetSize());
SetList(list, args); // Helper function to convert CefListValue to CefV8Value.
arguments.push_back(args);
// Execute the callback.
CefRefPtr<CefV8Value> retval = callback->ExecuteFunction(NULL, arguments);
if (retval.get()) {
if (retval->IsBool())
handled = retval->GetBoolValue();
}
// Exit the context.
context->Exit();
}
}
8、在CefRenderProcessHandlerImpl::OnContextReleased()里释放JavaScript注册的回调函数以及其他V8资源。
void CefRenderProcessHandlerImpl::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
if(m_v8Handler->Get()){
m_v8Handler->Release();
}
}