前一篇文章介紹過CEF在WIN32程序中嵌入chrome內核浏覽器的例子:http://blog.csdn.net/mfcing/article/details/43973377
這裡介紹的是嵌入浏覽器後,網頁的JS腳本函數與C++代碼的交互,這個很多地方都用得到。比如:音樂播放器裡網頁上的播放,客戶端資源中心裡的資源下載……
首先需要重寫CefRenderProcessHandler的OnContextCreated接口,為什麼呢?學習CEF庫的使用必須仔細閱讀他的頭文件裡的注視部分:
// Called immediately after the V8 context for a frame has been created. To // retrieve the JavaScript 'window' object use the CefV8Context::GetGlobal() // method. V8 handles can only be accessed from the thread on which they are // created. A task runner for posting tasks on the associated thread can be // retrieved via the CefV8Context::GetTaskRunner() method. /// /*--cef()--*/ virtual void OnContextCreated(CefRefPtr這個接口實在chrome的V8引擎創建後調用的,在這裡我們需要將JS裡面調用的函數和C++的執行函數關聯起來,這樣JS就可以“執行”C++代碼了。browser, CefRefPtr frame, CefRefPtr context) {}
我們的函數都是定義在window對象中的(不知道JS中這個是不是叫對象),根據注視我們需要GetGlobal獲取這個對象。
我的代碼是這樣的:
CefRefPtrwindow = context->GetGlobal(); CefRefPtr myV8Acc = new CCefV8Accessor; CefRefPtr val = CefV8Value::CreateString(L"Application"); CefString cefException; myV8Acc->Set(L"name", window, val, cefException); CefRefPtr pObjApp = CefV8Value::CreateObject(myV8Acc); window->SetValue(L"Application", pObjApp, V8_PROPERTY_ATTRIBUTE_NONE); CefRefPtr myV8handle = new CCefV8Handler(); CefRefPtr myFun = CefV8Value::CreateFunction(L"SetAppState", myV8handle); static_cast (myV8handle.get())->AddFun(L"SetAppState", &CChromeJsCallback::JsSetAppState); pObjApp->SetValue(L"SetAppState", myFun, V8_PROPERTY_ATTRIBUTE_NONE); myFun = CefV8Value::CreateFunction(L"OneClickInstall", myV8handle); static_cast (myV8handle.get())->AddFun(L"OneClickInstall", &CChromeJsCallback::JsOneKeyInstall); pObjApp->SetValue(L"OneClickInstall", myFun, V8_PROPERTY_ATTRIBUTE_NONE); myFun = CefV8Value::CreateFunction(L"DownLoadFile", myV8handle); static_cast (myV8handle.get())->AddFun(L"DownLoadFile", &CChromeJsCallback::JsDownloadFile); pObjApp->SetValue(L"DownLoadFile", myFun, V8_PROPERTY_ATTRIBUTE_NONE);
創建函數對象,並將JS函數綁定到C++函數指針上面的過程是重點詳細介紹下
CefRefPtrmyV8handle = new CCefV8Handler(); CefRefPtr myFun = CefV8Value::CreateFunction(L"SetAppState", myV8handle); static_cast (myV8handle.get())->AddFun(L"SetAppState", &CChromeJsCallback::JsSetAppState); pObjApp->SetValue(L"SetAppState", myFun, V8_PROPERTY_ATTRIBUTE_NONE);
//定義JS回調函數指針 typedef bool(* JS_CALLBACK_FUN)(const CefV8ValueList& , CefRefPtr& ); typedef map FunctionMap; class CCefV8Handler : public CefV8Handler { public: CCefV8Handler(); virtual bool Execute(const CefString& name, CefRefPtr object, \ const CefV8ValueList& arguments, CefRefPtr & retval, CefString& exception); bool AddFun(const CefString& strName, JS_CALLBACK_FUN pFun); private: FunctionMap m_fun; // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(CCefV8Handler); };
CCefV8Handler::CCefV8Handler() { } bool CCefV8Handler::Execute( const CefString& name, CefRefPtrobject, \ const CefV8ValueList& arguments, CefRefPtr & retval, CefString& exception ) { FunctionMap::iterator itor = m_fun.find(name); if ( itor == m_fun.end() ) return false; itor->second(arguments, retval); return true; } bool CCefV8Handler::AddFun( const CefString& strName, JS_CALLBACK_FUN pFun ) { if ( strName.empty() ) return false; m_fun.insert(std::pair (strName, pFun)); return true; }
最上面那段代碼中,我們又添加了三個函數 SetAppState、OneClickInstall、DownLoadFile到window.application對象上面。
C++代碼中,這三個函數是這樣寫的:
//JS函數,在其他進程中調用 static bool JsSetAppState(const CefV8ValueList& argList, CefRefPtr& retValue); static bool JsOneKeyInstall(const CefV8ValueList& argList, CefRefPtr & retValue); static bool JsDownloadFile(const CefV8ValueList& argList, CefRefPtr & retValue);
這裡要注意:CEF可以使用單進程和多進程模式,我程序裡使用的是多進程模式,因此V8引擎的執行是在渲染引擎裡的,不要嘗試在這裡直接去對界面進行處理。界面進程和渲染進程是分開的,數據的話用共享內存來做,界面更新發消息來做。
C++調用JS函數相對簡單多了,因為CEF有接口可以直接使用CefFrame::ExecuteJavaScript,看看注釋:
// Execute a string of JavaScript code in this frame. The |script_url| // parameter is the URL where the script in question can be found, if any. // The renderer may request this URL to show the developer the source of the // error. The |start_line| parameter is the base line number to use for error // reporting. /// /*--cef(optional_param=script_url)--*/ virtual void ExecuteJavaScript(const CefString& code, const CefString& script_url, int start_line) =0;
void CChromeBrowserUI::ExecuteJavascript( const wstring& strCode ) { if ( m_pWebBrowser.get() ) { CefRefPtr注意:函數名和參數名需要用單引號分隔。frame = m_pWebBrowser->GetMainFrame(); if ( frame.get() ) { CefString strCode(strCode.c_str()), strUrl(L""); frame->ExecuteJavaScript(strCode, strUrl, 0); } } }
我的字符串格式化部分:
CString strJsCode; strJsCode.Format(L"setInstallStatus('%s','%s','%d');", lpData->strId.c_str(), strStatus, nPercent);