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\n",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;
}
自己開發了一個股票智能分析軟件,功能很強大,需要的點擊下面的鏈接獲取: