目录
1 cef调用js
CefFrame::ExecuteJavaScript执行JavaScript() 函数。但是这个方法没有返回值。
c++:
std::list<CefRefPtr<CefBrowser> > cefBrowerList = m_browserEvent->getCefBrowerList();
CefRefPtr<CefBrowser> Browser = cefBrowerList.back();
CefRefPtr<CefFrame> frame = Browser->GetMainFrame();
frame->ExecuteJavaScript("input.value = '12';",frame->GetURL(), 0);
Js:
<input id="input">cef调用js</input>
c++调用后,js界面会将input的值改为12,结果:
2 js调用Cef
js调用cef有两种方法,窗口绑定(Window Binding)和js扩展( Extensions)。窗口绑定是在CefRenderProcessHandler::OnContextCreated闯将V8对象,将V8对象注册到上下文中;js拓展是在CefRenderProcessHandler::OnWebKitInitialized中注册新的V8扩展关联指定的js。
2.1 窗口绑定
将函数或对象绑定到CefFrame相应的window对象上。JS代码通过window对象访问native代码导出的函数或对象
1)simpleApp继承CefRenderProcessHandler,重写GetRenderProcessHandler和OnContextCreated方法。
#pragma once
#include "include/cef_app.h"
class SimpleApp
: public CefApp
, public CefBrowserProcessHandler
, public CefRenderProcessHandler
{
public:
SimpleApp(void);
virtual ~SimpleApp() OVERRIDE;
public:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE;
//通过返回值获取render线程,必须重写
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE;
//上下文对象创建后,进入这个函数
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE;
//webkit初始化
virtual void OnWebKitInitialized()OVERRIDE;
protected:
IMPLEMENT_REFCOUNTING(SimpleApp);
};
2)在 OnContextCreated中,将函数或者对象绑定到frame。oncontextCreated方法是在context上下文创建的时候进入的的,他在render线程中,不能在主线程中加断点。
void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context)
{
//QMessageBox::information(NULL, "OnContextCreated", QString::number(num11++), QMessageBox::Yes);
// The var type can accept all object or variable
CefRefPtr<CefV8Value> window = context->GetGlobal();
// bind value into window[or you can bind value into window sub node]
//1.绑定变量
CefRefPtr<CefV8Value> strValue = CefV8Value::CreateString("say yes");
window->SetValue("say_yes", strValue, V8_PROPERTY_ATTRIBUTE_NONE);
//2.绑定变量对象,可以在js中设置和获取
CefRefPtr<CefV8Accessor> accessor = new MyV8Accessor();
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor, nullptr);
obj->SetValue("myval", V8_ACCESS_CONTROL_DEFAULT, V8_PROPERTY_ATTRIBUTE_NONE);
window->SetValue("MyValObj", obj, V8_PROPERTY_ATTRIBUTE_NONE);
//3.绑定函数
// 创建函数处理器
CefRefPtr<CV8JsHandler> pJsHandler(new CV8JsHandler());
//创建函数
CefRefPtr<CefV8Value> myFunc = CefV8Value::CreateFunction("addFunction", pJsHandler);
//将函数绑定为 window 的 add_Function 属性,供js使用
window->SetValue("add_Function", myFunc, V8_PROPERTY_ATTRIBUTE_NONE);
//4.绑定函数
CefRefPtr<CefV8Handler> handler1 = new CV8JsHandler();
window->SetValue("register",
CefV8Value::CreateFunction("register", handler1),
V8_PROPERTY_ATTRIBUTE_NONE);
}
3) js中调用cef中的对象方法
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<script type="text/javascript" >
function showValue()
{
alert("value:" + window.say_yes);//1. window bing变量say_yes
}
function v8Accessor() {//2. window bing变量myval
alert("myval: " + window.MyValObj.myval);
window.MyValObj.myval = "js setting";
}
function sayHellow()
{
alert(g_value);
}
function add()//3. window bing函数
{
// add 函数名不能与window对象挂接addFunction相同
try {
var result = window.add_Function(10, 20);// C++提供的接口,bind到window对象上
alert("10 + 20 = " + result);
} catch (err) {
alert("add error message is: " + err.message);
}
}
function jsExt()
{
alert(test.myfunc());
}
function registerfunction( a, b) {
return a + b + 1;
}
function registerCallBack()
{
// window.register(registerfunction);
alert("registerCallBack:" + window.register(registerfunction) );
}
function asyregisterfunction(a, b) {
return a + b + 2;
}
function asyregisterCallBack() {
var asyr = window.asyregister("test",asyregisterfunction)
alert("registerCallBack:" + asyr);
}
</script>
</head>
<body style="width:100%;height:100%;background-color:green;">
<p>这是c++与JS交互测试脚本</p>
<div>
<p>
<input id="input">cef调用js</input>
</p>
<p>
<button onclick="showValue();">window bind 变量say_yes</button>
</p>
<p>
<button onclick="sayHellow();">拓展 g_value</button>
</p>
<p>
<button onclick="v8Accessor();">window bind v8Accessor</button>
</p>
<p>
<button onclick="add();">两个数相加</button>
</p>
<p>
<button onclick="jsExt();">JS扩展</button>
</p>
<p>
<button onclick="registerCallBack();">回调</button>
</p>
<p>
<button onclick="asyregisterCallBack();">异步回调</button>
</p>
</div>
</body>
</html>
2.1.1 绑定变量
//创建字符串say yes 的对象strlue
CefRefPtr<CefV8Value> strValue = CefV8Value::CreateString("say yes");
//将对象绑定到say_yes的key值。js可以通过window.say_yes访问字符串对象strValue
window->SetValue("say_yes", strValue, V8_PROPERTY_ATTRIBUTE_NONE);
可以绑定的类型有int,double,string,arry,object。
js调用方式:window.say_yes
2.1.2 绑定存储对象
不同与上面的变量,通过继承MyV8Accessor的对象,自带get和set方法,就是在js中不只是可以对变量进行取值,也可以对cef中的变量进行赋值。
首先继承MyV8Accessor,定义变量myval。
#ifndef MYV8ACCESSOR_H
#define MYV8ACCESSOR_H
#include "include/cef_v8.h"
class MyV8Accessor : public CefV8Accessor
{
public:
MyV8Accessor();
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;
private:
// Variable used for storing the value.
CefString myval_ = "myvalue";
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(MyV8Accessor);
};
#endif // MYV8ACCESSOR_H
然后实现get和set方法
#include "MyV8Accessor.h"
#include <QMessageBox>
MyV8Accessor::MyV8Accessor()
{
}
bool MyV8Accessor::Get(const CefString &name, const CefRefPtr<CefV8Value> object, CefRefPtr<CefV8Value> &retval, CefString &exception)
{
if (name == "myval")
{
QMessageBox::information(NULL, "titile_Get", QString(myval_.ToString().c_str()), QMessageBox::Yes);
retval = CefV8Value::CreateString(myval_);
return true;
}
// Value does not exist.
return false;
}
bool MyV8Accessor::Set(const CefString &name, const CefRefPtr<CefV8Value> object, const CefRefPtr<CefV8Value> value, CefString &exception)
{
if (name == "myval")
{
if (value->IsString())
{
myval_ = value->GetStringValue();
QMessageBox::information(NULL, "titile_Set", QString(myval_.ToString().c_str()),QMessageBox::Yes);
}
else
{
// Throw an exception.
exception = "Invalid value type";
}
return true;
}
// Value does not exist.
return false;
}
然后声明v8对象
CefRefPtr<CefV8Accessor> accessor = new MyV8Accessor();
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor, nullptr);
设置对象的变量名称
obj->SetValue("myval", V8_ACCESS_CONTROL_DEFAULT, V8_PROPERTY_ATTRIBUTE_NONE);
最后v8对象绑定key值
window->SetValue("MyValObj", obj, V8_PROPERTY_ATTRIBUTE_NONE);
js的调用方式:
取值: window.MyValObj.myval;
设值: window.MyValObj.myval = "js setting";
2.1.3 绑定函数
顾名思义就是js可以直接调用cef中的函数。
首先需要创建一个函数处理器,他继承CefV8Handler类,需要实现Execute方法,在这个方法中定义函数的实现和返回值。
#pragma once
#include "include/cef_v8.h"
class CV8JsHandler : public CefV8Handler
{
public:
CV8JsHandler(void);
virtual ~CV8JsHandler() OVERRIDE;
public:
virtual bool Execute(const CefString& name,//函数名称
CefRefPtr<CefV8Value> object,//调用对象
const CefV8ValueList& arguments,//函数参数
CefRefPtr<CefV8Value>& retval,//返回值
CefString& exception) OVERRIDE;//异常信息
IMPLEMENT_REFCOUNTING(CV8JsHandler);
};
然后实现addFuntion函数,这里我们实现了一个整数的加法运算。
#include "V8JsHandler.h"
#include<./qmessagebox.h>
CV8JsHandler::CV8JsHandler(void)
{
}
CV8JsHandler::~CV8JsHandler(void)
{
}
bool CV8JsHandler::Execute(const CefString& funcName,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception)
{
if (funcName == "addFunction")
{
int32 nSum = 0;
for (size_t i = 0; i < arguments.size(); ++i)
{
if(!arguments[i]->IsInt())
return false;
nSum += arguments[i]->GetIntValue();
}
retval = CefV8Value::CreateInt(nSum);
return true;
}
else if (funcName == "addFunction1")
{
retval = CefV8Value::CreateString( "call addFunction1");
return true;
}
else if (funcName == "addFunction3")
{
int32 nSum = 0;
for (size_t i = 0; i < arguments.size(); ++i)
{
if(!arguments[i]->IsInt())
return false;
nSum += arguments[i]->GetIntValue();
}
retval = CefV8Value::CreateInt(nSum);
exception = "not found!";
return true;
}
if (funcName == "register")
{
if (arguments.size() == 1 && arguments[0]->IsFunction())
{
CefRefPtr<CefV8Value> callbackFunc = arguments[0];
CefRefPtr<CefV8Context> callbackContext = CefV8Context::GetCurrentContext();
CefV8ValueList args;
args.push_back(CefV8Value::CreateInt(3));
args.push_back(CefV8Value::CreateInt(4));
//retval = callbackFunc->ExecuteFunctionWithContext(callbackContext, object, args);
retval = callbackFunc->ExecuteFunction(NULL, args);
QMessageBox::information(nullptr, "title", QString("%1").arg(retval->GetIntValue()));
return true;
}
}
return false;
}
然后创建函数处理器,创建addFuncion函数对象。
CefRefPtr<CV8JsHandler> pJsHandler(new CV8JsHandler());
CefRefPtr<CefV8Value> myFunc = CefV8Value::CreateFunction("addFunction", pJsHandler);
绑定函数对象
window->SetValue("add_Function", myFunc, V8_PROPERTY_ATTRIBUTE_NONE);
js的调用方式:var result = window.add_Function(10, 20);
2.2 拓展
使用CefRegisterExtension()注冊JS扩展,JS直接訪问注冊到JS Context中的对象
拓展同Window Binding类似,但是他相当于直接将字符串写入html中。
2.2.1 注册变量
std::string extensionCode =
"var g_value=\"g_value set value\";";
// Register the extension.
CefRegisterExtension("v8/mycode", extensionCode, NULL);
js中的调用方法:直接使用g_value
2.2.2 注册函数
首先需要在函数处理器中定义函数,实现函数addFunction1,同Window Binding。
然后声明test对象,声明test对象函数test.myfunc。关联cef函数addFunction1():native functon addFunction1。
然后声明函数处理器对象handler
最后将代码和注册到js中。需要注意的是,CefRegisterExtension第一个参数不能与其他的CefRegisterExtension重复,不然后注册的不起作用。
std::string extensionCode1 =
"var test;"
"if (!test)"
" test = {};"
"(function() {"
" test.myfunc = function() {"
" native function addFunction1();"
" return addFunction1();"
" };"
"})();";
// Create an instance of my CefV8Handler object.
CefRefPtr<CefV8Handler> handler = new CV8JsHandler();
// Register the extension.
CefRegisterExtension("v8/mycode1", extensionCode1, handler);
js中调用方法:test.myfunc()