花了几天时间学习了下C++和JS交互,写下笔记记录下 ,也欢迎大神指教
https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md#markdown-header-js-functions
上记录了三种C++与JS交互方式,以下我来讨论下。
首先引用下博客中的关于CEF架构的图
参考博客:
https://blog.csdn.net/foruok/article/details/50573612
https://blog.csdn.net/chenlycly/article/details/53355670
https://blog.csdn.net/swimming_in_it_/article/details/78869549
1. C++调用JS
1)ExecuteJavaScript
最简单的C++调用JS方法,需要Browser的对象,我这里调用一般是在CefClient的实现类中使用,但是有个缺点就是执行函数获得不了返回值。
void SimpleHandler::OnTitleChange(CefRefPtr browser,
const CefString& title) {
CEF_REQUIRE_UI_THREAD();
CefRefPtr frame = browser->GetMainFrame();
frame->ExecuteJavaScript("sayHello()", frame->GetURL(), 0);
这里是在OnTitleChange调用ExecuteJavaScript,调用sayHello函数是在JS代码里声明的。
该类的声明是:
class SimpleHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler
而OnTitleChange是SimpleHandler重写CefDisplayHandler里的虚函数,在标题改变时调用OnTitleChange函数。
JS代码:
JS代码里设置过5秒后修改标题,然后在OnTitleChange函数调用sayHello()
2. JS调用C++
1) Window Binding
这种方式就是在C++里声明的对象或函数绑定到JS中的window对象上,这样在JS代码使用window对象调用。
这种需要在一个类继承CefApp和CefRenderProcessHandler,官方建议重新写一个类,而不是与继承
CefBrowserProcessHandler写成同一个类,具体情况我也不知道,但是CefRenderProcessHandler的函数是在一个进程,
CefBrowserProcessHandler的函数是另一个进程,而我试试同时继承两个类(SimpleApp),然后在CefInitialize启动这各类
SimpleApp对象, 但是继承自CefRenderProcessHandler的函数是没有在执行,具体还可以请大神赐教。然后我就写了另个类
RenderApp,将原来的SimpleApp改写成BrowerApp,RenderApp继承CefApp和CefRenderProcessHandler,BrowerApp
继承CefApp和CefBrowserProcessHandler中。
我们把重点放在RenderApp,JS交互C++是在RenderApp实现,Window Binding实现方式是在 OnContextCreated,如下:
void RenderApp::OnContextCreated(CefRefPtr browser,
CefRefPtr frame,
CefRefPtr context)
{
CefRefPtr object = context->GetGlobal();
CefRefPtr str = CefV8Value::CreateString("My Value");
object->SetValue("myvalue", str, V8_PROPERTY_ATTRIBUTE_NONE);
CefRefPtr function = CefV8Value::CreateFunction("loadStuList", m_V8Handler);
object->SetValue("loadStuList", function, V8_PROPERTY_ATTRIBUTE_NONE);
}
使用context获得对象,然后将object获function绑定到这个对象上,V8是一个JS引擎,V8引擎是一个JavaScript引擎实现,最
初由一些语言方面专家设计,后被谷歌收购,随后谷歌对其进行了开源。V8使用C++开发,,在运行JavaScript之前,相比其它
的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86-64, ARM, or MIPS CPUs),并且使用
了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。
V8支持众多操作系统,如windows、linux、android等,也支持其他硬件架构,如IA32,X64,ARM等,具有很好的可移植和跨平
台特性。(见:https://blog.csdn.net/swimming_in_it_/article/details/78869549)。CefV8Value是一个类似的万能类型,
具体可以参考源码。上图是绑定了string对象和function(loadStuList),然后在JS代码里调用就好了。如下:
只需要在JS里调用sayValue和sayUserName函数就会到C++里执行,那究竟是在C++里怎样执行呢?回到OnContextCreated
函数里,CefRefPtr
m_V8Handler,所以JS调用C++的函数执行是和m_V8Handler有关,我是这样声明的:
CefRefPtr m_V8Handler;
ClientV8Handler的声明是:
class ClientV8Handler : public CefV8Handler
{
public:
virtual bool Execute(const CefString& name,
CefRefPtr object,
const CefV8ValueList& arguments,
CefRefPtr& retval,
CefString& exception) OVERRIDE;
private:
IMPLEMENT_REFCOUNTING(ClientV8Handler)
};
而调用C++就是在Execute中:
bool ClientV8Handler::Execute(const CefString& name,
CefRefPtr object,
const CefV8ValueList& arguments,
CefRefPtr& retval,
CefString& exception)
{
if (name == "loadStuList") {
if (arguments.size() == 2) {
CefString strUser = arguments.at(0)->GetStringValue();
CefString strPass = arguments.at(1)->GetStringValue();
retval = CefV8Value::CreateString(strPass);
}
else {
retval = CefV8Value::CreateInt(2);
}
return true;
}
return false;
}
当JS调用loadStuList时就会到这个函数,retval是loadStuList的返回值等等。
2)Extensions
这种方式很像window binding,不同点是可以将extensions记载到任意一个Frame且不能改变。注意的是加载extensions时
DOM对象还没生成,就是说extensions的代码不能使用DOM对象。该方式使用是在RenderApp的OnWebKitInitialized中实现
void RenderApp::OnWebKitInitialized()
{
std::string app_code =
"var app;"
"if (!app)"
" app = {};"
"(function() {"
" app.GetId = function(x) {"
" native function GetId(x);"
" };"
"})();";
CefRegisterExtension("v8app", app_code, m_V8Handler);
}
上边的app_code是js代码,意思是在C++给JS声明,然后在JS中调用就好,以上app_code意思是声明app对象,为该类声明
GetId函数,在JS代码中调用是:
在ClientV8Handler实现是:
else if (name == "GetId") {
CefRefPtr frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();
frame->ExecuteJavaScript("alert('Hello from extension');", frame->GetURL(), 0);
retval = CefV8Value::CreateInt(arguments.size());
return true;
}
写到这里发现有点不对劲,怎么全是JS调用C++, 而C++调用JS只有ExecuteJavaScript,但是拿不到返回值,这很奇怪,不过可
以在JS里设置回调函数,然后在C++里需要的时候调用这个回调函数就能达到C++调用JS了
最后,就是启动这个RenderApp就好了,如下:
CefRefPtr renderApp(new RenderApp);
int exit_code = CefExecuteProcess(main_args, renderApp.get(), sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
CefExecuteProcess这个函数时用来启动子进程的,所以将RenderApp启动是在这个函数里。
下边是我写的代码,希望交流。
https://download.csdn.net/download/leapmotion/10508861
是我从官方例子cefsimple拿来做的一些修改,可以单独跑起来,中间我遇到了问题是在release可以跑起来,但是在debug下就
会有跑起来然后过一会儿退出了一个进程,是少加了manifest文件,具体做法是将官方例子里的
cefclient\resources\compatibility.manifest文件拷贝到工程目录下引入(属性->配置属性->清单工具->输入输出->附加清单
文件->填写".\compatibility.manifest"<没有引号或直接写绝对路径>)。