CEF
中一个页面的框架如下图所示:
CefBrowser:一个普通的浏览器页面(HTML)
CefFrame:每一个页面都由至少一个frame组成,最顶层的为mainframe
context:JS执行环境,每个frame都有自己独立的context,CEF中使用V8JavaScriptEngine解析和执行JS代码
后端和前端的通信分为两个部分:
一.C++ call JS:
由框架图可以知道,后端执行JS的实质是:在特定的frame上执行JS代码片段
示例:
- CefRefPtr<CefBrowser> browser = ...;
- CefRefPtr<CefFrame> frame = browser->GetMainFrame();
- frame->ExecuteJavaScript("alert('ExecuteJavaScript works!');",
- frame->GetURL(), 0);
可以得知,C++端执行JS的通用流程为:
1.获取页面引用
2.获取目标frame引用
3.在目标frame上调用ExecuteJavaScript执行JS
槽点:函数ExecuteJavaScript的返回值为空,函数中也没有其他形式获取JS执行的返回值。那么JS的返回值如何获取?
二.JS call C++
C++和JS对象的映射关系:
CefValue表示JS环境中的一个对象,其可以表示JS中所有变量,切其自身可以嵌套。特别重要的一点,前端的window对象在后端也是一个CefV8Value,并且可以通过CefV8Context取到它。
JS调用C++的实质是:以CefV8Value的形式包装C++对象,并将其绑定到特定frame的JS执行环境的window对象上,如此JS代码就可以直接对其读写(属性)或者调用(函数)。
A. window binding,将自定义数据绑定到JS context的window对象上
1.在回调CefRenderProcessHandler的OnContextCreated()中注册。注册过程:
1.1获取CefV8Value形式的window对象
1.2构造数据
1.3绑定到window对象上
代码示例:
- void MyRenderProcessHandler::OnContextCreated(
- CefRefPtr<CefBrowser> browser,
- CefRefPtr<CefFrame> frame,
- CefRefPtr<CefV8Context> context) {
- CefRefPtr<CefV8Value> object = context->GetGlobal();
- CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");
- object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
- }
2.特别注意:在frame reload的时候,需要重新绑定。frame reload其实就是重新创建了context,需要重新执行绑定过程,这很好理解。
3.CefV8Value的setValue方法就是将数据绑定到对象上
B.extensions,将预定义的JS代码注册到context中,并且一旦注册,不可修改。这种方式威力很弱,我想只会在特殊的场景下选它。
在CefRenderProcessHandler的回调OnWebKitInitialized()中执行注册
示例:
- void MyRenderProcessHandler::OnWebKitInitialized() {
- std::string extensionCode =
- "var test;"
- "if (!test)"
- " test = {};"
- "(function() {"
- " test.myval = 'My Value!';"
- "})();";
-
-
- CefRegisterExtension("v8/test", extensionCode, NULL);
- }
C.数据构造
1. JS基本类型,CEF支持创建基本JS数据类型,比如undefined, null, bool, int, double, date and string。使用静态函数CefV8Value::Create*()
2.JS数组,CefV8Value::CreateArray()
3.JS对象,CefV8Value::CreateObject(NULL)
4.JS function, CefV8Value::CreateFunction();
D.属性读写和函数实现
1.对象读写
创建CefV8Value时,关联一个CefV8Accessor,并实现它的get和set,示例:
//创建时关联CefV8Accessor
CefRefPtr<CefV8Accessor> accessor = …;
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor);
//实现自己的CefV8Accessor
- class MyV8Accessor : public CefV8Accessor {
- public:
- MyV8Accessor() {}
- virtual bool Get(const CefString& name,
- const CefRefPtr<CefV8Value> object,
- CefRefPtr<CefV8Value>& retval,
- CefString& exception) OVERRIDE {
- if (name == "myval") {
-
- retval = CefV8Value::CreateString(myval_);
- return true;
- }
- return false;
- }
-
- virtual bool Set(const CefString& name,
- const CefRefPtr<CefV8Value> object,
- const CefRefPtr<CefV8Value> value,
- CefString& exception) OVERRIDE {
- if (name == "myval") {
- if (value.IsString()) {
- myval_ = value.GetStringValue();
- } else {
- exception = "Invalid value type";
- }
- return true;
- }
- return false;
- }
-
- CefString myval_;
- IMPLEMENT_REFCOUNTING(MyV8Accessor);
- };
2.函数实现
创建函数时,绑定自己实现的CefV8Handler,示例:
CefRefPtr<CefV8Handler> handler = …;
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
//实现自己的CefV8Handler
- class MyV8Handler : public CefV8Handler {
- public:
- MyV8Handler() {}
-
- virtual bool Execute(const CefString& name,
- CefRefPtr<CefV8Value> object,
- const CefV8ValueList& arguments,
- CefRefPtr<CefV8Value>& retval,
- CefString& exception) OVERRIDE {
- if (name == "myfunc") {
- retval = CefV8Value::CreateString("My Value!");
- return true;
- }
- return false;
- }
-
- IMPLEMENT_REFCOUNTING(MyV8Handler);
- };