3.1 CEF和JavaScript交互
3.1.1 在CEF执行JavaScript脚本
3.1.2 窗口绑定方式实现CEF设置JavaScript的变量
3.1.3 扩展方式(Extension)实现CEF设置JavaScript的变量
3.1.4 窗口绑定方式实现CEF给JavaScript提供函数
3.1.5 Extension方式实现CEF给JavaScript提供函数
3.1 CEF和JavaScript交互
https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.md
CEF使用的V8 JavaScript 引擎用于内部JavaScript实现,每一个frame都有JS上下文(context),为JS代码执行提供范围和安全。CEF暴露了很多JS特性可以和客户端程序进行交互。
3.1.1 在CEF执行JavaScript脚本
应用场景是需要在CEF中拦截一个URL请求,并把它重定向到另外一个URL,可以调用pFrame->ExecuteJavaScript来执行一个JavaScript脚本,实现跳转。当然也可以实现其他应用功能。
CefRefPtr<CefFrame> pFrame = browser->GetMainFrame();
std::string strurl = pFrame->GetURL().ToString();
std::string strname = pFrame->GetName().ToString();
pFrame->GetName().ToString().c_str());
if (pFrame->GetURL() == "https://10.19.141.75/portal/")
{
pFrame->ExecuteJavaScript("var param= { url:'https://10.19.141.75/ishelf-web/personalCenter' };
window.goToApp(param);
var paramEx = { isExtend:true };
window.extendScreen(paramEx);
", pFrame->GetURL(), 0);
}
3.1.2 窗口绑定方式实现CEF设置JavaScript的变量
在CEF程序中,创建一个CefV8Value对象,获取上下文的窗口对象,注入窗口对象一个变量值,网页中就可以使用JavaScript获取这个变量值。窗口绑定在CefRenderProcessHandler::OnContextCreated()函数中。是上下文创建响应函数,窗口绑定方式在每次frame重新加载(context创建)时都会加载一遍,CEF程序可以在OnContextCreated()给每一个frame设置不同的变量值。
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
// Create a new V8 string value. See the "Basic JS Types" section below.
CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");
// Add the string to the window object as "window.myval". See the "JS Objects" section below.
object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
}
JavaScript in the frame can then interact with the window bindings.
<script language="JavaScript">
alert(window.myval); // Shows an alert box with "My Value!"
</script>
3.1.3 扩展方式(Extension)实现CEF设置JavaScript的变量
Extension方式和窗口绑定方式类似,但是Extension方式是为每一个frame加载到上下文context,一旦加载变不能在修改,没有加载之前,DOM是不存在的,尝试范围这个值的DOM会出现崩溃。Extension方式是在CefRenderProcessHandler::OnWebKitInitialized()函数中用CefRegisterExtension() 函数注册的,是在初始化函数中实现的,所以对于每一个frame都是一样的。
void MyRenderProcessHandler::OnWebKitInitialized() {
// Define the extension contents.
std::string extensionCode =
"var test;"
"if (!test)"
" test = {};"
"(function() {"
" test.myval = 'My Value!';"
"})();";
// Register the extension.
CefRegisterExtension("v8/test", extensionCode, NULL);
}
JS中调用变量值
<script language="JavaScript">
alert(test.myval); // Shows an alert box with "My Value!"
</script>
3.1.4 窗口绑定方式实现CEF给JavaScript提供函数
(1) 自定义类实现CefV8Handler类,实现Execute接口,JavaScript执行函数后,会将函数名称、参数和返回值引用传递给Execute函数,Execute函数根据函数名去调用函数,函数的具体实现在Execute中,然后执行返回返回值。
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") {
// Return my string value.
retval = CefV8Value::CreateString("My Value!");
return true;
}
// Function does not exist.
return false;
}
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(MyV8Handler);
};
(2)将函数名称设置到窗口对象,提供接受调用的handle
void MyRenderProcessHandler::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
// Create an instance of my CefV8Handler object.
CefRefPtr<CefV8Handler> handler = new MyV8Handler();
// Create the "myfunc" function.
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);
// Add the "myfunc" function to the "window" object.
object->SetValue("myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);
}
(3)JavaScript执行函数调用,就会进入Execute函数,返回返回值,alert会以弹窗形式展示结果。
<script language="JavaScript">
alert(window.myfunc()); // Shows an alert box with "My Value!"
</script>
3.1.5 Extension方式实现CEF给JavaScript提供函数
JavaScript调用CEF中的函数步骤:
(1)实现app类,继承与CefApp,重写OnWebKitInitialized,在OnWebKitInitialized函数内部使用字符串定义函数。CEF调用CefRegisterExtension函数向JavaScript注册函数,并提供处理调用的handler。
//CefClientApp.h
class CCefClientApp : public CefApp, public CefBrowserProcessHandler, CefRenderProcessHandler
{
public:
CCefClientApp();
~CCefClientApp();
//所有的CEF接口 都需要重载GetXXXHandler,并且return this,才会有效
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override { return this; }
//===========CefRenderProcessHandler
virtual void OnWebKitInitialized() override;
private:
CefRefPtr<CCEFV8HandlerEx> v8Handler_;
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(CCefClientApp);
private:
};
//CefClientApp.cpp
void CCefClientApp::OnWebKitInitialized()
{
//MessageBox(NULL,L"OnWebKitInitialized ",0,0);
std::string app_code =
//-----------------------------------
//声明JavaScript里要调用的Cpp方法
"var app;"
"if (!app)"
" app = {};"
"(function() {"
// jsInvokeCPlusPlus 实例函数
" app.jsInvokeCPlusPlus = function(v1, v2) {"
" native function jsInvokeCPlusPlus();"
" return jsInvokeCPlusPlus(v1, v2);"
" };"
//函数
" app.jsTransform = function(v1) {"
" native function jsTransform();"
" return jsTransform(v1);"
" };"
"})();";
// Register app extension module
// JavaScript里调用app.jsInvokeCPlusPlus时,就会去通过CefRegisterExtension注册的CefV8Handler列表里查找
// 找到"v8/app"对应的CCEFV8HandlerEx,就调用它的Execute方法
// 假设v8Handler_是CCefClientApp的一个成员变量
//v8Handler_ = new CCEFV8HandlerEx();
CefRegisterExtension("v8/app", app_code, v8Handler_);
}
(2)JavaScript调用函数
<html>
<script>
app.jsInvokeCPlusPlus("123","xyz");
app.jsTransform("hello world");
</script>
</html>
(3) handler的Execute函数接收调用响应,根据函数名称判断是调用哪个函数,获取参数调用函数。
//CEFV8HandlerEx.h
classCCEFV8HandlerEx
:
public
CefV8Handler
{
public:
CCEFV8HandlerEx();
~CCEFV8HandlerEx();
public:
virtual
bool
Execute(const
CefString
&name
,CefRefPtr
<CefV8Value
>object
,const
CefV8ValueList
&arguments
,CefRefPtr
<CefV8Value
>&retval
,CefString
&exception
)override
;
private:
// Map of message callbacks.
typedef
std
::map
<std
::pair
<std
::string
,int>,
std
::pair
<CefRefPtr
<CefV8Context
>,CefRefPtr
<CefV8Value
>>
>
CallbackMap
;
CallbackMap callback_map_
;
public:
IMPLEMENT_REFCOUNTING(
CCEFV8HandlerEx
);
};
//CEFV8HandlerEx.cpp
//JS调用C++函数的回调
boolCCEFV8HandlerEx
::Execute(constCefString
&name
/*JavaScript调用的C++方法名字*/,CefRefPtr
<CefV8Value
>object
/*JavaScript调用者对象*/,const
CefV8ValueList
&arguments
/*JavaScript传递的参数*/,CefRefPtr
<CefV8Value
>&retval
/*返回给JS的值设置给这个对象*/,CefString
&exception
/*通知异常信息给JavaScript*/)
{
if
(
name
==_T("jsInvokeCPlusPlus"))
{
if
(
arguments
.size()==
2)
{
CefString strParam1
=arguments
.at(0)->GetStringValue();
CefString strParam2
=arguments
.at(1)->GetStringValue();
TCHAR szBuffer
[512];
StringCbPrintf(
szBuffer
,sizeof(
szBuffer
),_T("jsInvokeCPlusPlus(%s,%s)"),
strParam1
.c_str(),strParam2
.c_str());
::MessageBox(GetForegroundWindow(),
szBuffer
,_T("jsInvokeCPlusPlus"),
MB_OK
);
retval
=CefV8Value
::CreateInt(0);
}
else
{
retval
=CefV8Value
::CreateInt(2);
}
return
true;
}
if
(
name
==L
"jsTransform"){
CefString strParam1
=arguments
.at(0)->GetStringValue();
TCHAR szBuffer
[512];
StringCbPrintf(
szBuffer
,sizeof(
szBuffer
),_T("jsTransform(%s)"),
strParam1
.c_str());
::MessageBox(GetForegroundWindow(),
szBuffer
,_T("jsTransform"),
MB_OK
);
}
return
false;
}
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取: