CEF和JS交互
CefClient提供所有瀏覽器事件處理的接口,重寫CefClient類中的方法處理瀏覽器事件:包括Browser的生命周期,右鍵菜單,對話框,狀態通知顯示,下載事件,拖曳事件,焦點事件,鍵盤事件,離屏渲染事件等,對Cef進行行為控制的方法一般都集中在這些接口。
①
/*
註冊瀏覽器生命周期的事件類CefLifeSpanHandler的實例。
重寫CefLifeSpanHandler接口類中的方法實現對browser對象周期回調事件的處理:
*/
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() { return NULL;}
//當新browser對象被創建後馬上觸發,重寫此方法保存所有創建的browser對象的指針。
void OnAfterCreated(CefRefPtr<CefBrowser> browser)
//當調用CloseBrowser函數後觸發
bool DoClose(CefRefPtr<CefBrowser> browser)
//當browser對象被銷毀前觸發,必須重寫此方法且完成釋放對所有browser對象的指針。
void OnBeforeClose(CefRefPtr<CefBrowser> browser)
②
//當window render無法完成時采用離屏渲染,重寫CefRenderHandler類中的方法實現離屏渲染回調事件的處理:
virtual CefRefPtr<CefRenderHandler> GetRenderHandler() { return NULL; }
//根窗口創建後觸發,通過此接口可獲取根窗口在屏幕的位置坐標。
bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) { return false; }
//當browser對象初始化或者大小改變時觸發,通過此接口獲取browser對象在窗口的位置坐標。
bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect)=0
③
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() {return NULL;}
Browser對象顯示狀態的事件處理。
④
virtual CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() { return NULL;}
與JavaScript dialogs相關的回調事件的處理,CefJSDialogHandler接口類中的方法在UI線程中調用。
⑤
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefProcessId source_process,CefRefPtr<CefProcessMessage> message) {return false;}
進程間通信接口,實現browser進程和render進程間的通信。當收到其他進程的消息時觸發,當消息被處理後返回true。此回調函數外不可使用其他進程的消息或此消息的引用。
其他瀏覽器的回調事件(拖拽、下載、查找、焦點、鍵盤等)都在CefClient類中有對應的註冊接口。
CefApp負責進程的實例化及退出。重寫Cef類中的GetBrowserProcessHandler()及GetRenderProcessHandler()方法創建browser進程及render進程的實例。
CefBrowser及CefFrame為不可實例化的抽象類。其中,CefBrowser類表示一個瀏覽器窗口,CefFrame類表示瀏覽器窗口中的一塊區域,一個瀏覽器窗口可包含多個Frame塊。兩者中的方法在browser進程中使用時一般可在所有線程中使用,在render進程中一般只能在主線程中使用。
啟動Render進程:
1) 單一執行:browser主進程調用CefExcuteProcess()方法之後創建render子進程。分析CefExcuteProcess()方法的代碼:首先根據命令行參數的type參數,如果為NULL,說明為主進程,返回-1,回到主程序,執行創建窗口等主進程代碼。如果不為NULL,會在chrome代碼裏創建render進程。
2) 獨立子進程執行:需要兩個分開的工程和分開的入口函數。在主程序中的CefSettings settings中增加CefString(&settings.browser_subprocess_path)=子進程執行路徑。
JS加載及與Cef的交互由Render進程處理,因此CefApp的GetRenderProcessHandler()方法必須提供實現,否則OnContextCreate()和OnWebktiInitialized()接口無法被調用。定義類繼承自CefApp和CefRenderProcessHandler,重寫CefApp中的GetRenderProcessHandler()方法並返回render進程實例的指針。CefRenderProcessHandler中的OnContextCreate()接口會在Frame的context創建後被調用,提供js與Cef代碼交互的功能。
Cef基於Chrome,Chrome使用v8jsEngine執行內部的js。根據v8的規範,執行一段js代碼必須顯式指定context,因此在調用js代碼的時候必須先進入context,而每一個Frame有屬於自己的context,每一次Frame加載都會調用OnContextCreate()方法。
1) Cef調用JS:
Cef代碼可以通過調用CefFrame::ExecuteJavaScript()方法執行js代碼。
frame->ExecuteJavaScript(const CefString& jsCode,const CefString& frame->GetURL(), int startLine);
context所在的CefRefPtr<CefFrame> frame和CefRefPtr<CefBrowser> browser在OnContextCreate()接口中獲取。
2) JS調用Cef
①重寫OnContextCreate()方法,從OnContextCreate()接口提供的context中通過context-GetGlobal()獲取window對象,window對象聲明為cefV8Value類型(相當於js中所有對象和值都以var聲明)。通過window-> SetValue(const CefString& key, CefRefPtr<CefV8Value> value, PropertyAttribute attribute)方法實現綁定該窗口對象下某一個CefV8Value類型對象(方法中參數value)和一特定標識符(方法中參數key)。
用來綁定的CefV8Value類型對象包含js基本數據類型(int,double,string,bool等)用CefV8Value::Create靜態方法創建,例如string類型用CefV8Value::CreateString創建;以及js數組和對象類型,用靜態方法CefV8Value::CreateArray()或CefV8Value::CreateObject ()創建。
②用來綁定的CefV8Value類型對象可以是函數對象定義一個類(例如MyCefV8Handler)繼承自CefV8Handler,
利用
CefRefPtr<MyCefV8Handler> handler (new MyCefV8Handler());
CefV8Value func=CefV8Value::CreateFunction(const CefString& name, CefRefPtr<CefV8Handler> handler) 方法創建函數對象並調用window-> SetValue()進行綁定。CreateFunction傳入的參數為函數名和實例化的MyCefV8Handler對象,並且在MyCefV8Handler類中必須重寫CefV8Handler中的Execute()方法判斷js調用的Cef方法並接收參數及傳遞返回值。
JS調用Cef中的方法示例代碼:
//自定義SimRender類繼承CefApp類和CefRenderProcessHandler類
class simpleRender :public CefApp, public CefRenderProcessHandler
{
public:
//重寫GetRenderProcessHandler方法,render進程啟動時被喚醒來創建render進程的示例
void CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE
{
return this;
}
//重寫OnContextCreated方法,每一次Frame加載都會調用OnContextCreate()方法,綁定window對象
void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE
{
//獲取window對象
CefRefPtr<CefV8Value> window = context->GetGlobal();
//創建MyCefV8Handler對象實例
CefRefPtr<MyCefV8Handler> handler(new MyCefV8Handler());
//創建函數對象myFunc
CefRefPtr<CefV8Value> myFunc = CefV8Value::CreateFunction(_T("func"), handler);
//綁定函數對象myFunc和以標識符“func”,js通過window.func調用Cef代碼
window->SetValue(_T("func"), myFunc, V8_PROPERTY_ATTRIBUTE_NONE);
}
}
//自定義MyCefV8Handler類繼承CefV8Handler類
class MyCefV8Handler :public CefV8Handler
{
public:
//重寫Execute方法判斷js調用的Cef方法
bool Execute(const CefString& func_name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception)
{
if (func_name == _T("func"))
{
retval = CefV8Value::CreateString(_T("func: C++!"));
return true;
}
return false;
}
}
js中:
<script type="text/javascript">
Function f()
{
var retval=window.func();
window.alert(retval);
}
</script>
CEF和JS交互