CEF使用說明書
目錄
1 前言
1.1 CEF的作用
1.2 CEF的下載和編譯
1.3 CEF結構
1.3.1 CEF進程和窗口之間的結構關系
1.3.2 Renderer進程的實現結構
1.3.3 browser進程的實現結構
1.4 CEF多進程和多線程
1.4.1 進程
1.4.2 線程
1.5 網頁嵌入應用程序結構
1.5.1 入口函數
1.5.2 CEF單實例進程
1.5.3 主子進程的模式
1.6 CefBrowser 瀏覽器窗口類
1.7 CefBrowserHost瀏覽器操作控制類
1.8 CefFrame網頁界面類
1.9 CefApp應用程序類
1.10 CEF引用計數
1.11 CEF自定義字符串
1.11.1 為什么自定義字符串類型
1.11.2 字符串操作函數CefString
1.11.3 CEF與String的轉換
2 Cef常用接口類介紹
2.1 CefClient
2.2 CefContextMenuHandler右鍵菜單處理類
2.3 CefDisplayHandler網頁顯示處理類
2.4 CefDownloadHandler網頁下載處理類
2.5 CefDragHandler鼠標拖動到網頁處理類
2.6 CefKeyboardHandler鍵盤事件響應處理類
2.7 CefLifeSpanHandler生命周期處理類
2.7.1 DoClose標准關閉處理
2.7.2 DoClose非標准關閉處理
2.8 CefLoadHandler網頁加載處理類
2.9 CefRequestHandler網絡請求處理類
3 CEF高級應用
3.1 CEF和JavaScript交互
3.1.1 在CEF執行JavaScript腳本
3.1.2 Extension方式實現JavaScript執行CEF中的函數
3.2 CEF進程間通訊
3.2.1 進程間通訊函數
3.2.2 進程通訊實例
3.2.3 CEF指定frame通訊
1 前言
1.1 CEF的作用
CEF全稱Chromium Embedded Framework,是一個基於Google Chromium 的開源項目。Google Chromium項目主要是為Google Chrome應用開發的,而CEF的目標則是為第三方應用提供可嵌入瀏覽器支持。CEF作用是在客戶端嵌入網頁界面。
- 嵌入一個兼容HTML5的瀏覽器控件到一個已經存在的本地應用。
- 創建一個輕量化的殼瀏覽器,用以托管主要用Web技術開發的應用。
- 有些應用有獨立的繪制框架,使用CEF對Web內容做離線渲染。
- 使用CEF做自動化Web測試。
1.2 CEF的下載和編譯
https://blog.csdn.net/csdnyonghu123/article/details/87982333
如圖所示,CefClient工程是一個簡單版的網頁瀏覽器demo,有網址輸入、前進后退等。
CefSimple更簡單的網頁工程,網址在代碼設置,打開 直接渲染網頁。
CefTest測試工程。
AllBuild是一個偽工程,用來執行編譯其他工程。
libcef_dll_wrapper是cef的動態庫工程,。
include是頭文件
Release文件夾包含輸出的庫文件和依賴庫文件,自己開發項目時將include和release文件復制到工程即可。參考CefSimple和CefClient代碼實例。
1.3 CEF結構
1.3.1 CEF進程和窗口之間的結構關系
一個瀏覽器有很多個CefBrowser窗口,這些窗口都是在Browser進程中創建。browser進程用來管理和處理回調函數消息。
Renderer進程用來實現網頁的渲染,每個renderer進程包含有一個主網頁mainframe和多個子網頁subframe,。
1.3.2 Renderer進程的實現結構
renderer程序繼承CefApp和CefRenderProcessHandler類,在main函數中初始化。通過CefSettings.browser_subprocess_path配置render可執行程序路徑。browser進程就會去啟動這個進程去渲染網頁。
1.3.3 browser進程的實現結構
browserapp要繼承CefApp和CefBrowserProcessHandler類。實現browserapp的定義。同時要新建clienthandler類實現圖中的回調函數接口類,用來處理攔截響應請求、管理生命周期、下載、顯示加載、右鍵菜單等。在mian函數中初始化、啟動消息循環。調用CefBrowserHost的靜態方法創建browser窗口對象,在render進程的Frame中加載渲染內容。
1.4 CEF多進程和多線程
1.4.1 進程
CEF3是多進程架構的,CEF3進程主要有一個Browser(瀏覽器)進程和多個Renderer(渲染)進程。Browser被定義為主進程,負責窗口管理,網絡請求,網頁管理 、網絡交互。browser從服務器器請求到了響應,將html文本發送給Renderer 進程,render進程加載html,進行渲染,展示網頁的內容;除此之外,Renderer進程還負責Js Binding和對Dom節點的訪問。Browser和Renderer進程可以通過發送異步消息進行雙向通信。主應用程序很大,加載時間比較長,或者不能在非瀏覽器進程里使用,則宿主程序可使用獨立的可執行文件去運行這些Renderer進程。這可以通過配置CefSettings.browser_subprocess_path變量做到。
1.4.2 線程
Browser進程中包含如下主要的線程:
- TID_UI 線程是瀏覽器的主線程。如果應用程序在調用調用CefInitialize()時,傳遞CefSettings.multi_threaded_message_loop=false,這個線程也是應用程序的主線程。
- TID_IO 線程主要負責處理IPC消息以及網絡通信。
- TID_FILE 線程負責與文件系統交互。
1.5 網頁嵌入應用程序代碼構成和實例
每個CEF3應用程序都是相同的結構
- 提供入口函數,用於初始化CEF、運行子進程執行邏輯或者CEF消息循環。
- 提供CefApp實現,用於處理進程相關的回調。
- 提供CefClient實現,用於處理Browser實例相關的回調。
- 執行CefBrowserHost::CreateBrowser()創建一個Browser實例,使用CefLifeSpanHandler管理Browser對象生命周期。
1.5.1 入口函數
需要在程序的入口函數執行cef對象創建,初始化,命令行參數解析,CEF屬性設置,和CEF消息循環開啟等。CEF需要啟動一個主進程來管理網頁見面。這個主進程就是在man函數中啟動,進入消息循環,直到程序結束,才退出main函數。
1.5.2 CEF單實例進程
單例進程模式就是CEF的啟動,網頁的打開都在
int main(int argc, char* argv[]) {
// Structure for passing command-line arguments.
// The definition of this structure is platform-specific.
CefMainArgs main_args(argc, argv);
// Optional implementation of the CefApp interface.
CefRefPtr<MyApp> app(new MyApp);
// Execute the sub-process logic, if any. This will either return immediately for the browser
// process or block until the sub-process should exit.
int exit_code = CefExecuteProcess(main_args, app.get());
if (exit_code >= 0) {
// The sub-process terminated, exit now.
return exit_code;
}
// Populate this structure to customize CEF behavior.
CefSettings settings;
// Initialize CEF in the main process.
CefInitialize(main_args, settings, app.get());
// Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
1.5.3 主子進程的模式
主進程和子進程分開的模式,主進程運行browser進程,網頁加載放在子進程render進程中。這是需要創建兩個進程和兩個主函數。
主程序的入口函數:
// Program entry-point function.
// 程序入口函數
int main(int argc, char* argv[]) {
// Structure for passing command-line arguments.
// The definition of this structure is platform-specific.
// 傳遞命令行參數的結構體。
// 這個結構體的定義與平台相關。
CefMainArgs main_args(argc, argv);
// Optional implementation of the CefApp interface.
// 可選擇性地實現CefApp接口
CefRefPtr<MyApp> app(new MyApp);
// Populate this structure to customize CEF behavior.
// 填充這個結構體,用於定制CEF的行為。
CefSettings settings;
// Specify the path for the sub-process executable.
// 指定子進程的執行路徑
CefString(&settings.browser_subprocess_path).FromASCII(“/path/to/subprocess”);
// Initialize CEF in the main process.
// 在主進程中初始化CEF
CefInitialize(main_args, settings, app.get());
// Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
// 執行消息循環,此時會堵塞,直到CefQuitMessageLoop()函數被調用。
CefRunMessageLoop();
// Shut down CEF.
// 關閉CEF
CefShutdown();
return 0;
}
子進程程序的入口函數:
// Program entry-point function.
// 程序入口函數
int main(int argc, char* argv[]) {
// Structure for passing command-line arguments.
// The definition of this structure is platform-specific.
// 傳遞命令行參數的結構體。
// 這個結構體的定義與平台相關。
CefMainArgs main_args(argc, argv);
// Optional implementation of the CefApp interface.
// 可選擇性地實現CefApp接口
CefRefPtr<MyApp> app(new MyApp);
// Execute the sub-process logic. This will block until the sub-process should exit.
// 執行子進程邏輯,此時會堵塞直到子進程退出。
return CefExecuteProcess(main_args, app.get());
}
1.6 CefBrowser 瀏覽器窗口類
CefBrowser是瀏覽器窗口類,相當於瀏覽器的外殼框架窗口,包含向前、向后、加載、獲取內部frame的等方法。調用CefBrowserHost的靜態方法創建一個CefBrowser對象,表示一個網頁窗口。
///
// Class used to represent a browser window. When used in the browser process
// the methods of this class may be called on any thread unless otherwise
// indicated in the comments. When used in the render process the methods of
// this class may only be called on the main thread.
///
/*--cef(source=library)--*/
class CefBrowser : public virtual CefBaseRefCounted {
public:
///
// Returns the browser host object. This method can only be called in the
// browser process.
///
/*--cef()--*/
virtual CefRefPtr<CefBrowserHost> GetHost() = 0;
///
// Returns true if the browser can navigate backwards.
///
/*--cef()--*/
virtual bool CanGoBack() = 0;
///
// Navigate backwards.
///
/*--cef()--*/
virtual void GoBack() = 0;
///
// Returns true if the browser can navigate forwards.
///
/*--cef()--*/
virtual bool CanGoForward() = 0;
///
// Navigate forwards.
///
/*--cef()--*/
virtual void GoForward() = 0;
///
// Returns true if the browser is currently loading.
///
/*--cef()--*/
virtual bool IsLoading() = 0;
///
// Reload the current page.
///
/*--cef()--*/
virtual void Reload() = 0;
///
// Reload the current page ignoring any cached data.
///
/*--cef()--*/
virtual void ReloadIgnoreCache() = 0;
///
// Stop loading the page.
///
/*--cef()--*/
virtual void StopLoad() = 0;
///
// Returns the globally unique identifier for this browser. This value is also
// used as the tabId for extension APIs.
///
/*--cef()--*/
virtual int GetIdentifier() = 0;
///
// Returns true if this object is pointing to the same handle as |that|
// object.
///
/*--cef()--*/
virtual bool IsSame(CefRefPtr<CefBrowser> that) = 0;
///
// Returns true if the window is a popup window.
///
/*--cef()--*/
virtual bool IsPopup() = 0;
///
// Returns true if a document has been loaded in the browser.
///
/*--cef()--*/
virtual bool HasDocument() = 0;
///
// Returns the main (top-level) frame for the browser window.
///
/*--cef()--*/
virtual CefRefPtr<CefFrame> GetMainFrame() = 0;
///
// Returns the focused frame for the browser window.
///
/*--cef()--*/
virtual CefRefPtr<CefFrame> GetFocusedFrame() = 0;
///
// Returns the frame with the specified identifier, or NULL if not found.
///
/*--cef(capi_name=get_frame_byident)--*/
virtual CefRefPtr<CefFrame> GetFrame(int64 identifier) = 0;
///
// Returns the frame with the specified name, or NULL if not found.
///
/*--cef(optional_param=name)--*/
virtual CefRefPtr<CefFrame> GetFrame(const CefString& name) = 0;
///
// Returns the number of frames that currently exist.
///
/*--cef()--*/
virtual size_t GetFrameCount() = 0;
///
// Returns the identifiers of all existing frames.
///
/*--cef(count_func=identifiers:GetFrameCount)--*/
virtual void GetFrameIdentifiers(std::vector<int64>& identifiers) = 0;
///
// Returns the names of all existing frames.
///
/*--cef()--*/
virtual void GetFrameNames(std::vector<CefString>& names) = 0;
///
// Send a message to the specified |target_process|. Returns true if the
// message was sent successfully.
///
/*--cef()--*/
virtual bool SendProcessMessage(CefProcessId target_process,
CefRefPtr<CefProcessMessage> message) = 0;
};
1.7 CefBrowserHost瀏覽器操作控制類
CefBrowserHost是CefBrowser的一個主對象,CefBrowser中有方法virtual CefRefPtr<CefBrowserHost> GetHost() = 0;返回CefBrowser的主CefBrowserHost對象。然而CefBrowser對象又是通過CefBrowserHost類的靜態方法來創建的。CefBrowserHost類包含一些CefBrowser操作方法,相當於是控制類,可以控制CefBrowser的創建、獲取、關閉;獲取打開CefBrowser窗口的窗口句柄、請求上下文(RequestContext);下載、查找、鼠標、鍵盤觸發事件、焦點控制、拖拽事件等。
class CefBrowserHost : public virtual CefBaseRefCounted {
public:
typedef cef_drag_operations_mask_t DragOperationsMask;
typedef cef_file_dialog_mode_t FileDialogMode;
typedef cef_mouse_button_type_t MouseButtonType;
typedef cef_paint_element_type_t PaintElementType;
///
// Create a new browser window using the window parameters specified by
// |windowInfo|. All values will be copied internally and the actual window
// will be created on the UI thread. If |request_context| is empty the
// global request context will be used. This method can be called on any
// browser process thread and will not block.
///
/*--cef(optional_param=client,optional_param=url,
optional_param=request_context)--*/
static bool CreateBrowser(const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefString& url,
const CefBrowserSettings& settings,
CefRefPtr<CefRequestContext> request_context);
///
// Create a new browser window using the window parameters specified by
// |windowInfo|. If |request_context| is empty the global request context
// will be used. This method can only be called on the browser process UI
// thread.
///
/*--cef(optional_param=client,optional_param=url,
optional_param=request_context)--*/
static CefRefPtr<CefBrowser> CreateBrowserSync(
const CefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
const CefString& url,
const CefBrowserSettings& settings,
CefRefPtr<CefRequestContext> request_context);
///
// Returns the hosted browser object.
///
/*--cef()--*/
virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
///
// Request that the browser close. The JavaScript 'onbeforeunload' event will
// be fired. If |force_close| is false the event handler, if any, will be
// allowed to prompt the user and the user can optionally cancel the close.
// If |force_close| is true the prompt will not be displayed and the close
// will proceed. Results in a call to CefLifeSpanHandler::DoClose() if the
// event handler allows the close or if |force_close| is true. See
// CefLifeSpanHandler::DoClose() documentation for additional usage
// information.
///
/*--cef()--*/
virtual void CloseBrowser(bool force_close) = 0;
///
// Helper for closing a browser. Call this method from the top-level window
// close handler. Internally this calls CloseBrowser(false) if the close has
// not yet been initiated. This method returns false while the close is
// pending and true after the close has completed. See CloseBrowser() and
// CefLifeSpanHandler::DoClose() documentation for additional usage
// information. This method must be called on the browser process UI thread.
///
/*--cef()--*/
virtual bool TryCloseBrowser() = 0;
///
// Set whether the browser is focused.
///
/*--cef()--*/
virtual void SetFocus(bool focus) = 0;
///
// Retrieve the window handle for this browser. If this browser is wrapped in
// a CefBrowserView this method should be called on the browser process UI
// thread and it will return the handle for the top-level native window.
///
/*--cef()--*/
virtual CefWindowHandle GetWindowHandle() = 0;
///
// Retrieve the window handle of the browser that opened this browser. Will
// return NULL for non-popup windows or if this browser is wrapped in a
// CefBrowserView. This method can be used in combination with custom handling
// of modal windows.
///
/*--cef()--*/
virtual CefWindowHandle GetOpenerWindowHandle() = 0;
//……………………
}
1.8 CefFrame網頁界面類
每個CefBrowser窗口包含一個主CefFrame對象和若干個子Frame對象。主CefFrame相當於網頁的主界面,子CefFrame相當於主頁面的子頁面。CefFrame包含獲取網頁上的文字、HTML源碼字符串、網頁名字標題、加載的url、所屬的CefBrowser指針、V8Context等。還可以用LoadURL或LoadRequest加載頁面。
// Class used to represent a frame in the browser window. When used in the
// browser process the methods of this class may be called on any thread unless
// otherwise indicated in the comments. When used in the render process the
// methods of this class may only be called on the main thread.
///
/*--cef(source=library)--*/
class CefFrame : public virtual CefBaseRefCounted {
public:
///
// True if this object is currently attached to a valid frame.
///
/*--cef()--*/
virtual bool IsValid() = 0;
///
// Execute undo in this frame.
///
/*--cef()--*/
virtual void Undo() = 0;
///
// Execute redo in this frame.
///
/*--cef()--*/
virtual void Redo() = 0;
///
// Execute cut in this frame.
///
/*--cef()--*/
virtual void Cut() = 0;
///
// Execute copy in this frame.
///
/*--cef()--*/
virtual void Copy() = 0;
///
// Execute paste in this frame.
///
/*--cef()--*/
virtual void Paste() = 0;
///
// Execute delete in this frame.
///
/*--cef(capi_name=del)--*/
virtual void Delete() = 0;
///
// Execute select all in this frame.
///
/*--cef()--*/
virtual void SelectAll() = 0;
///
// Save this frame's HTML source to a temporary file and open it in the
// default text viewing application. This method can only be called from the
// browser process.
///
/*--cef()--*/
virtual void ViewSource() = 0;
///
// Retrieve this frame's HTML source as a string sent to the specified
// visitor.
///
/*--cef()--*/
virtual void GetSource(CefRefPtr<CefStringVisitor> visitor) = 0;
///
// Retrieve this frame's display text as a string sent to the specified
// visitor.
///
/*--cef()--*/
virtual void GetText(CefRefPtr<CefStringVisitor> visitor) = 0;
///
// Load the request represented by the |request| object.
///
/*--cef()--*/
virtual void LoadRequest(CefRefPtr<CefRequest> request) = 0;
///
// Load the specified |url|.
///
/*--cef()--*/
virtual void LoadURL(const CefString& url) = 0;
///
// Load the contents of |string_val| with the specified dummy |url|. |url|
// should have a standard scheme (for example, http scheme) or behaviors like
// link clicks and web security restrictions may not behave as expected.
///
/*--cef()--*/
virtual void LoadString(const CefString& string_val,
const CefString& url) = 0;
///
// 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;
///
// Returns true if this is the main (top-level) frame.
///
/*--cef()--*/
virtual bool IsMain() = 0;
///
// Returns true if this is the focused frame.
///
/*--cef()--*/
virtual bool IsFocused() = 0;
///
// Returns the name for this frame. If the frame has an assigned name (for
// example, set via the iframe "name" attribute) then that value will be
// returned. Otherwise a unique name will be constructed based on the frame
// parent hierarchy. The main (top-level) frame will always have an empty name
// value.
///
/*--cef()--*/
virtual CefString GetName() = 0;
///
// Returns the globally unique identifier for this frame or < 0 if the
// underlying frame does not yet exist.
///
/*--cef()--*/
virtual int64 GetIdentifier() = 0;
///
// Returns the parent of this frame or NULL if this is the main (top-level)
// frame.
///
/*--cef()--*/
virtual CefRefPtr<CefFrame> GetParent() = 0;
///
// Returns the URL currently loaded in this frame.
///
/*--cef()--*/
virtual CefString GetURL() = 0;
///
// Returns the browser that this frame belongs to.
///
/*--cef()--*/
virtual CefRefPtr<CefBrowser> GetBrowser() = 0;
///
// Get the V8 context associated with the frame. This method can only be
// called from the render process.
///
/*--cef()--*/
virtual CefRefPtr<CefV8Context> GetV8Context() = 0;
///
// Visit the DOM document. This method can only be called from the render
// process.
///
/*--cef()--*/
virtual void VisitDOM(CefRefPtr<CefDOMVisitor> visitor) = 0;
};
1.9 CefApp應用程序類
CefApp是應用程序類,網頁嵌入程序都要實現這樣一個應用程序類。提供了一些簡單的接口:命令行參數修改,主題修改,獲取進程句柄等。還有很多的子類,例如SimpleApp、ClientApp等,clientapp又有很多子類ClientAppBrowser、ClientAppRenderer、ClientAppOther,表示不同的應用類型:應用程序類定義如下:
class CefApp : public virtual CefBaseRefCounted {
public:
///對命令行參數修改
// Provides an opportunity to view and/or modify command-line arguments before
// processing by CEF and Chromium. The |process_type| value will be empty for
// the browser process. Do not keep a reference to the CefCommandLine object
// passed to this method. The CefSettings.command_line_args_disabled value
// can be used to start with an empty command-line object. Any values
// specified in CefSettings that equate to command-line arguments will be set
// before this method is called. Be cautious when using this method to modify
// command-line arguments for non-browser processes as this may result in
// undefined behavior including crashes.
///
/*--cef(optional_param=process_type)--*/
virtual void OnBeforeCommandLineProcessing(
const CefString& process_type,
CefRefPtr<CefCommandLine> command_line) {}
///
// Provides an opportunity to register custom schemes. Do not keep a reference
// to the |registrar| object. This method is called on the main thread for
// each process and the registered schemes should be the same across all
// processes.
///
/*--cef()--*/注冊自定義主題
virtual void OnRegisterCustomSchemes(
CefRawPtr<CefSchemeRegistrar> registrar) {}
///
// Return the handler for resource bundle events. If
// CefSettings.pack_loading_disabled is true a handler must be returned. If no
// handler is returned resources will be loaded from pack files. This method
// is called by the browser and render processes on multiple threads.
///
/*--cef()--*/
virtual CefRefPtr<CefResourceBundleHandler> GetResourceBundleHandler() {
return NULL;
}
///
// Return the handler for functionality specific to the browser process. This
// method is called on multiple threads in the browser process.
///
/*--cef()--*/
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() {
return NULL;
}
///
// Return the handler for functionality specific to the render process. This
// method is called on the render process main thread.
///
/*--cef()--*/
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() {
return NULL;
}
};
1.10 CEF引用計數
CEF創建對象形式如CefRefPtr<SimpleApp> app(new SimpleApp);或者CefRefPtr<SimpleApp> app = new SimpleApp();創建的指針引用計數由CefRefPtr管理,CefRefPtr通過調用AddRef()和Release()方法自動管理引用計數。CefRefPtr定義如下:
using CefRefPtr = scoped_refptr<T>;
template <class T>
class scoped_refptr {
public:
typedef T element_type;
scoped_refptr() : ptr_(NULL) {}
scoped_refptr(T* p) : ptr_(p) {
if (ptr_)
ptr_->AddRef();
}
scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
if (ptr_)
ptr_->AddRef();
}
template <typename U>
scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
if (ptr_)
ptr_->AddRef();
}
~scoped_refptr() {
if (ptr_)
ptr_->Release();
}
T* get() const { return ptr_; }
// Allow scoped_refptr<C> to be used in boolean expression
// and comparison operations.
operator T*() const { return ptr_; }
T* operator->() const {
assert(ptr_ != NULL);
return ptr_;
}
scoped_refptr<T>& operator=(T* p) {
// AddRef first so that self assignment should work
if (p)
p->AddRef();
T* old_ptr = ptr_;
ptr_ = p;
if (old_ptr)
old_ptr->Release();
return *this;
}
scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
return *this = r.ptr_;
}
template <typename U>
scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
return *this = r.get();
}
void swap(T** pp) {
T* p = ptr_;
ptr_ = *pp;
*pp = p;
}
void swap(scoped_refptr<T>& r) { swap(&r.ptr_); }
protected:
T* ptr_;
};
1.11 CEF自定義字符串
1.11.1 為什么自定義字符串類型
libcef包和宿主程序可能使用不同的運行時,對堆管理的方式也不同。所有的對象,包括字符串,需要確保和申請堆內存使用相同的運行時環境。所以需要自定義字符串類型。
1.11.2 字符串操作函數CefString
CEF提供了一批C語言的方法來操作字符串(通過#define的方式來適應不同的字符編碼)
cef_string_set 對制定的字符串變量賦值(支持深拷貝或淺拷貝)。
cef_string_clear 清空字符串。
cef_string_cmp 比較兩個字符串。
1.11.3 CEF與String的轉換
CefString支持與std::string(UTF8)、std::wstring(wide)類型的相互轉換。也可以用來包裹一個cef_string_t結構來對其進行賦值。
(1)string轉CefString
單字節
std::string str = “Some UTF8 string”;
CefString cef_str(str);
cef_str = str;
cef_str.FromString(str);
寬字節
std::wstring str = “Some wide string”;
CefString cef_str(str);
cef_str = str;
cef_str.FromWString(str);
ASCII碼
const char* cstr = “Some ASCII string”;
CefString cef_str;
cef_str.FromASCII(cstr);
(2)CefString轉string
單字節
str = cef_str;
str = cef_str.ToString();
寬字節
str = cef_str;
str = cef_str.ToWString();
2 Cef常用接口類介紹
Cef是一個網頁嵌入外殼,要獲取網頁的響應,攔截網頁中的事件消息,CEF提供了一系列的接口回調類,提供各種事件回調函數,例如攔截獲取鍵盤、鼠標、加載、顯示、右鍵菜單、提示消息、狀態變化、窗口打開關閉等,CEF都可以從網頁上攔截捕捉消息事件,並通過回調函數傳給應用程序進行處理。
2.1 CefClient
CefClient提供了獲取各種handler的接口,例如上下文菜單handler、對話框handler、顯示狀態handler,下載事件handler、拖動事件handler、查找事件handler、鍵盤handler、生命周期事件handler、加載頁面事件handler、離屏render進程handler、請求事件handler等。但是只是返回事件的handle。每個handler的具體的回調函數不在CefClient類中,需要繼承各個handler類,才可以實現回調。CefClient類只有一個回調函數OnProcessMessageReceived用來處理進程間的通訊消息。CefClient的定義如下:
class CefClient : public virtual CefBaseRefCounted {
public:
///
// Return the handler for context menus. If no handler is provided the default
// implementation will be used.
///
/*--cef()--*/
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() {
return NULL;
}
///
// Return the handler for dialogs. If no handler is provided the default
// implementation will be used.
///
/*--cef()--*/
virtual CefRefPtr<CefDialogHandler> GetDialogHandler() { return NULL; }
///
// Return the handler for browser display state events.
///
/*--cef()--*/
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() { return NULL; }
///
// Return the handler for download events. If no handler is returned downloads
// will not be allowed.
///
/*--cef()--*/
virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() { return NULL; }
///
// Return the handler for drag events.
///
/*--cef()--*/
virtual CefRefPtr<CefDragHandler> GetDragHandler() { return NULL; }
///
// Return the handler for find result events.
///
/*--cef()--*/
virtual CefRefPtr<CefFindHandler> GetFindHandler() { return NULL; }
///
// Return the handler for focus events.
///
/*--cef()--*/
virtual CefRefPtr<CefFocusHandler> GetFocusHandler() { return NULL; }
///
// Return the handler for JavaScript dialogs. If no handler is provided the
// default implementation will be used.
///
/*--cef()--*/
virtual CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() { return NULL; }
///
// Return the handler for keyboard events.
///
/*--cef()--*/
virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() { return NULL; }
///
// Return the handler for browser life span events.
///
/*--cef()--*/
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() { return NULL; }
///
// Return the handler for browser load status events.
///
/*--cef()--*/
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() { return NULL; }
///
// Return the handler for off-screen rendering events.
///
/*--cef()--*/
virtual CefRefPtr<CefRenderHandler> GetRenderHandler() { return NULL; }
///
// Return the handler for browser request events.
///
/*--cef()--*/
virtual CefRefPtr<CefRequestHandler> GetRequestHandler() { return NULL; }
///
// Called when a new message is received from a different process. Return true
// if the message was handled or false otherwise. Do not keep a reference to
// or attempt to access the message outside of this callback.
///
/*--cef()--*/
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
return false;
}
};
2.2 CefContextMenuHandler右鍵菜單處理類
CefContextMenuHandler是網頁上的右鍵菜單事件回調函數類,提供OnBeforeContextMenu回調函數,在右鍵菜單彈出之前修改或者禁用右鍵菜單。右鍵菜單按鈕響應回調RunContextMenu、右鍵菜單命令回調OnContextMenuCommand菜單禁用回調函數OnContextMenuDismissed等。可以攔截右鍵菜單響應,進行自定義的處理。
class CefContextMenuHandler : public virtual CefBaseRefCounted {
public:
typedef cef_event_flags_t EventFlags;
///
// Called before a context menu is displayed. |params| provides information
// about the context menu state. |model| initially contains the default
// context menu. The |model| can be cleared to show no context menu or
// modified to show a custom menu. Do not keep references to |params| or
// |model| outside of this callback.
///
/*--cef()--*/
virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model) {}
///
// Called to allow custom display of the context menu. |params| provides
// information about the context menu state. |model| contains the context menu
// model resulting from OnBeforeContextMenu. For custom display return true
// and execute |callback| either synchronously or asynchronously with the
// selected command ID. For default display return false. Do not keep
// references to |params| or |model| outside of this callback.
///
/*--cef()--*/
virtual bool RunContextMenu(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model,
CefRefPtr<CefRunContextMenuCallback> callback) {
return false;
}
///
// Called to execute a command selected from the context menu. Return true if
// the command was handled or false for the default implementation. See
// cef_menu_id_t for the command ids that have default implementations. All
// user-defined command ids should be between MENU_ID_USER_FIRST and
// MENU_ID_USER_LAST. |params| will have the same values as what was passed to
// OnBeforeContextMenu(). Do not keep a reference to |params| outside of this
// callback.
///
/*--cef()--*/
virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
int command_id,
EventFlags event_flags) {
return false;
}
///
// Called when the context menu is dismissed irregardless of whether the menu
// was empty or a command was selected.
///
/*--cef()--*/
virtual void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame) {}
};
2.3 CefDisplayHandler網頁顯示處理類
CefDisplayHandler提供了一些頁面顯示回調函數,例如網址發送變化OnAddressChange,網頁標題OnTitleChange發生變化,網頁圖標發生變化OnFaviconURLChange,全屏變化OnFullscreenModeChange,顯示提示消息OnTooltip,狀態欄消息顯示OnStatusMessage,控制台消息回調OnConsoleMessage,設置了自動調整大小回調OnAutoResize,加載進程變化回調OnLoadingProgressChange,CefDisplayHandler類定義如下
class CefDisplayHandler : public virtual CefBaseRefCounted {
public:
///
// Called when a frame's address has changed.
///
/*--cef()--*/
virtual void OnAddressChange(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& url) {}
///
// Called when the page title changes.
///
/*--cef(optional_param=title)--*/
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) {}
///
// Called when the page icon changes.
///
/*--cef(optional_param=icon_urls)--*/
virtual void OnFaviconURLChange(CefRefPtr<CefBrowser> browser,
const std::vector<CefString>& icon_urls) {}
///
// Called when web content in the page has toggled fullscreen mode. If
// |fullscreen| is true the content will automatically be sized to fill the
// browser content area. If |fullscreen| is false the content will
// automatically return to its original size and position. The client is
// responsible for resizing the browser if desired.
///
/*--cef()--*/
virtual void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
bool fullscreen) {}
///
// Called when the browser is about to display a tooltip. |text| contains the
// text that will be displayed in the tooltip. To handle the display of the
// tooltip yourself return true. Otherwise, you can optionally modify |text|
// and then return false to allow the browser to display the tooltip.
// When window rendering is disabled the application is responsible for
// drawing tooltips and the return value is ignored.
///
/*--cef(optional_param=text)--*/
virtual bool OnTooltip(CefRefPtr<CefBrowser> browser, CefString& text) {
return false;
}
///
// Called when the browser receives a status message. |value| contains the
// text that will be displayed in the status message.
///
/*--cef(optional_param=value)--*/
virtual void OnStatusMessage(CefRefPtr<CefBrowser> browser,
const CefString& value) {}
///
// Called to display a console message. Return true to stop the message from
// being output to the console.
///
/*--cef(optional_param=message,optional_param=source)--*/
virtual bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
cef_log_severity_t level,
const CefString& message,
const CefString& source,
int line) {
return false;
}
///
// Called when auto-resize is enabled via CefBrowserHost::SetAutoResizeEnabled
// and the contents have auto-resized. |new_size| will be the desired size in
// view coordinates. Return true if the resize was handled or false for
// default handling.
///
/*--cef()--*/
virtual bool OnAutoResize(CefRefPtr<CefBrowser> browser,
const CefSize& new_size) {
return false;
}
///
// Called when the overall page loading progress has changed. |progress|
// ranges from 0.0 to 1.0.
///
/*--cef()--*/
virtual void OnLoadingProgressChange(CefRefPtr<CefBrowser> browser,
double progress) {}
};
2.4 CefDownloadHandler網頁下載處理類
CefDownloadHandler網頁上下載文件類,提供開始從網頁下載文件回調函數OnBeforeDownload,下載文件進度更新回調函數OnDownloadUpdated。
class CefDownloadHandler : public virtual CefBaseRefCounted {
public:
///
// Called before a download begins. |suggested_name| is the suggested name for
// the download file. By default the download will be canceled. Execute
// |callback| either asynchronously or in this method to continue the download
// if desired. Do not keep a reference to |download_item| outside of this
// method.
///
/*--cef()--*/
virtual void OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback) = 0;
///
// Called when a download's status or progress information has been updated.
// This may be called multiple times before and after OnBeforeDownload().
// Execute |callback| either asynchronously or in this method to cancel the
// download if desired. Do not keep a reference to |download_item| outside of
// this method.
///
/*--cef()--*/
virtual void OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
CefRefPtr<CefDownloadItemCallback> callback) {}
};
2.5 CefDragHandler鼠標拖動到網頁處理類
CefDragHandler處理鼠標拖動事件,提供鼠標拖動進入網頁回調函數OnDragEnter,網頁中可以拖動放入的區域發生變化回調函數OnDraggableRegionsChanged。
// Implement this interface to handle events related to dragging. The methods of
// this class will be called on the UI thread.
///
/*--cef(source=client)--*/
class CefDragHandler : public virtual CefBaseRefCounted {
public:
typedef cef_drag_operations_mask_t DragOperationsMask;
///
// Called when an external drag event enters the browser window. |dragData|
// contains the drag event data and |mask| represents the type of drag
// operation. Return false for default drag handling behavior or true to
// cancel the drag event.
///
/*--cef()--*/
virtual bool OnDragEnter(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDragData> dragData,
DragOperationsMask mask) {
return false;
}
///
// Called whenever draggable regions for the browser window change. These can
// be specified using the '-webkit-app-region: drag/no-drag' CSS-property. If
// draggable regions are never defined in a document this method will also
// never be called. If the last draggable region is removed from a document
// this method will be called with an empty vector.
///
/*--cef()--*/
virtual void OnDraggableRegionsChanged(
CefRefPtr<CefBrowser> browser,
const std::vector<CefDraggableRegion>& regions) {}
};
2.6 CefKeyboardHandler鍵盤事件響應處理類
CefKeyboardHandler處理鍵盤響應事件,提供鍵盤按鍵響應回調函數。攔截鍵盤消息。
// Implement this interface to handle events related to keyboard input. The
// methods of this class will be called on the UI thread.
///
/*--cef(source=client)--*/
class CefKeyboardHandler : public virtual CefBaseRefCounted {
public:
///
// Called before a keyboard event is sent to the renderer. |event| contains
// information about the keyboard event. |os_event| is the operating system
// event message, if any. Return true if the event was handled or false
// otherwise. If the event will be handled in OnKeyEvent() as a keyboard
// shortcut set |is_keyboard_shortcut| to true and return false.
///
/*--cef()--*/
virtual bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
const CefKeyEvent& event,
CefEventHandle os_event,
bool* is_keyboard_shortcut) {
return false;
}
///
// Called after the renderer and JavaScript in the page has had a chance to
// handle the event. |event| contains information about the keyboard event.
// |os_event| is the operating system event message, if any. Return true if
// the keyboard event was handled or false otherwise.
///
/*--cef()--*/
virtual bool OnKeyEvent(CefRefPtr<CefBrowser> browser,
const CefKeyEvent& event,
CefEventHandle os_event) {
return false;
}
};
2.7 CefLifeSpanHandler生命周期處理類
CefLifeSpanHandler是生命周期處理類,新打開一個網頁或者關閉一個網頁時,會觸發回調函數。OnBeforePopup這個只能在創建一個新的彈出式網頁時,才會觸發,如果是在一個網頁中打開一個子網頁,回調函數是攔截不到消息的。OnAfterCreated網頁創建完成后的回調函數。browser銷毀之前會觸發回調函數OnBeforeClose。還有一個關閉回調函數DoClose有點復雜,當調用CefBrowserHost::*CloseBrowser()函數關閉browser,或者browser是CEF創建的頂層窗口的子窗口,當頂層窗口關閉時,也會觸發關閉DoClose回調函數。點擊網頁的關閉按鈕后,網頁不會立刻關閉,而是會調用兩次CloseBrowser()或TryCloseBrowser(),提供了一個讓CEF處理JS的onbeforeunload事件和選擇性取消關閉網頁的機會。CefLifeSpanHandler類定義如下:
// Implement this interface to handle events related to browser life span. The
// methods of this class will be called on the UI thread unless otherwise
// indicated.
///
/*--cef(source=client)--*/
class CefLifeSpanHandler : public virtual CefBaseRefCounted {
public:
typedef cef_window_open_disposition_t WindowOpenDisposition;
///
// Called on the UI thread before a new popup browser is created. The
// |browser| and |frame| values represent the source of the popup request. The
// |target_url| and |target_frame_name| values indicate where the popup
// browser should navigate and may be empty if not specified with the request.
// The |target_disposition| value indicates where the user intended to open
// the popup (e.g. current tab, new tab, etc). The |user_gesture| value will
// be true if the popup was opened via explicit user gesture (e.g. clicking a
// link) or false if the popup opened automatically (e.g. via the
// DomContentLoaded event). The |popupFeatures| structure contains additional
// information about the requested popup window. To allow creation of the
// popup browser optionally modify |windowInfo|, |client|, |settings| and
// |no_javascript_access| and return false. To cancel creation of the popup
// browser return true. The |client| and |settings| values will default to the
// source browser's values. If the |no_javascript_access| value is set to
// false the new browser will not be scriptable and may not be hosted in the
// same renderer process as the source browser. Any modifications to
// |windowInfo| will be ignored if the parent browser is wrapped in a
// CefBrowserView. Popup browser creation will be canceled if the parent
// browser is destroyed before the popup browser creation completes (indicated
// by a call to OnAfterCreated for the popup browser).
///
/*--cef(optional_param=target_url,optional_param=target_frame_name)--*/
virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
const CefString& target_frame_name,
WindowOpenDisposition target_disposition,
bool user_gesture,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings,
bool* no_javascript_access) {
return false;
}
///
// Called after a new browser is created. This callback will be the first
// notification that references |browser|.
///
/*--cef()--*/
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) {}
///
// Called when a browser has recieved a request to close. This may result
// directly from a call to CefBrowserHost::*CloseBrowser() or indirectly if
// the browser is parented to a top-level window created by CEF and the user
// attempts to close that window (by clicking the 'X', for example). The
// DoClose() method will be called after the JavaScript 'onunload' event has
// been fired.
//
// An application should handle top-level owner window close notifications by
// calling CefBrowserHost::TryCloseBrowser() or
// CefBrowserHost::CloseBrowser(false) instead of allowing the window to close
// immediately (see the examples below). This gives CEF an opportunity to
// process the 'onbeforeunload' event and optionally cancel the close before
// DoClose() is called.
//
// When windowed rendering is enabled CEF will internally create a window or
// view to host the browser. In that case returning false from DoClose() will
// send the standard close notification to the browser's top-level owner
// window (e.g. WM_CLOSE on Windows, performClose: on OS X, "delete_event" on
// Linux or CefWindowDelegate::CanClose() callback from Views). If the
// browser's host window/view has already been destroyed (via view hierarchy
// tear-down, for example) then DoClose() will not be called for that browser
// since is no longer possible to cancel the close.
//
// When windowed rendering is disabled returning false from DoClose() will
// cause the browser object to be destroyed immediately.
//
// If the browser's top-level owner window requires a non-standard close
// notification then send that notification from DoClose() and return true.
//
// The CefLifeSpanHandler::OnBeforeClose() method will be called after
// DoClose() (if DoClose() is called) and immediately before the browser
// object is destroyed. The application should only exit after OnBeforeClose()
// has been called for all existing browsers.
//
// The below examples describe what should happen during window close when the
// browser is parented to an application-provided top-level window.
//
// Example 1: Using CefBrowserHost::TryCloseBrowser(). This is recommended for
// clients using standard close handling and windows created on the browser
// process UI thread.
// 1. User clicks the window close button which sends a close notification to
// the application's top-level window.
// 2. Application's top-level window receives the close notification and
// calls TryCloseBrowser() (which internally calls CloseBrowser(false)).
// TryCloseBrowser() returns false so the client cancels the window close.
// 3. JavaScript 'onbeforeunload' handler executes and shows the close
// confirmation dialog (which can be overridden via
// CefJSDialogHandler::OnBeforeUnloadDialog()).
// 4. User approves the close.
// 5. JavaScript 'onunload' handler executes.
// 6. CEF sends a close notification to the application's top-level window
// (because DoClose() returned false by default).
// 7. Application's top-level window receives the close notification and
// calls TryCloseBrowser(). TryCloseBrowser() returns true so the client
// allows the window close.
// 8. Application's top-level window is destroyed.
// 9. Application's OnBeforeClose() handler is called and the browser object
// is destroyed.
// 10. Application exits by calling CefQuitMessageLoop() if no other browsers
// exist.
//
// Example 2: Using CefBrowserHost::CloseBrowser(false) and implementing the
// DoClose() callback. This is recommended for clients using non-standard
// close handling or windows that were not created on the browser process UI
// thread.
// 1. User clicks the window close button which sends a close notification to
// the application's top-level window.
// 2. Application's top-level window receives the close notification and:
// A. Calls CefBrowserHost::CloseBrowser(false).
// B. Cancels the window close.
// 3. JavaScript 'onbeforeunload' handler executes and shows the close
// confirmation dialog (which can be overridden via
// CefJSDialogHandler::OnBeforeUnloadDialog()).
// 4. User approves the close.
// 5. JavaScript 'onunload' handler executes.
// 6. Application's DoClose() handler is called. Application will:
// A. Set a flag to indicate that the next close attempt will be allowed.
// B. Return false.
// 7. CEF sends an close notification to the application's top-level window.
// 8. Application's top-level window receives the close notification and
// allows the window to close based on the flag from #6B.
// 9. Application's top-level window is destroyed.
// 10. Application's OnBeforeClose() handler is called and the browser object
// is destroyed.
// 11. Application exits by calling CefQuitMessageLoop() if no other browsers
// exist.
///
/*--cef()--*/
virtual bool DoClose(CefRefPtr<CefBrowser> browser) { return false; }
///
// Called just before a browser is destroyed. Release all references to the
// browser object and do not attempt to execute any methods on the browser
// object after this callback returns. This callback will be the last
// notification that references |browser|. See DoClose() documentation for
// additional usage information.
///
/*--cef()--*/
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) {}
};
2.7.1 DoClose標准關閉處理
當窗口創建是在browser進程的UI線程創建時,采用標准的關閉處理,使用CefBrowserHost::TryCloseBrowser()。不實現DoClose回調,默認返回false。具體步驟:
(1) 點擊窗口的關閉按鈕,發送一個關閉通知給頂層窗口。
(2) 頂層窗口接收到關閉通知,調用TryCloseBrowser()函數,返回false;
(3) JS的onbeforeunload處理句柄執行顯示關閉確認對話框。
(4) 用戶點擊按鈕同意關閉;
(5) JS的onunload處理句柄執行;
(6) CEF發送一個close通知給頂層窗口;
(7) 定鞥窗口接收到關閉通知,調用TryCloseBrowser,返回true,表示允許關閉。
(8) 頂層窗口銷毀
(9) 程序的OnBeforeClose處理回調函數執行,browser銷毀.
(10)如果不存在其他browser,則調用CefQuitMessageLoop退出程序。
2.7.2 DoClose非標准關閉處理
當窗口不是在browser進程的UI線程中創建時,采用非標准的關閉處理,使用函數CefBrowserHost::CloseBrowser(false),並且實現DoClose函數。
(1) 用戶點擊窗口的關閉按鈕,發送一個關閉通知給頂層窗口。
(2) 頂層窗口接收到關閉通知,調用CefBrowserHost::CloseBrowser(false)函數,取消關閉;
(3) JS的onbeforeunload處理句柄執行顯示關閉確認對話框。
(4) 用戶點擊按鈕同意關閉;
(5) JS的onunload處理句柄執行;
(6) 程序的DoClose()回調函數被調用,設置一個flag表明下次關閉嘗試會被允許,返回false;
(7) CEF發送一個close通知給頂層窗口;
(8) 頂層窗口接收到關閉通知,根據之前設置的flag判斷是否關閉窗口。
(9) 頂層窗口銷毀;
(10)程序的OnBeforeClose處理回調函數執行,browser銷毀.
(11)如果不存在其他browser,則調用CefQuitMessageLoop退出程序。
2.8 CefLoadHandler網頁加載處理類
在一個網頁中加載內容,或者在網頁中打開一個子frame,都可以攔截到iframe打開時的消息以及url等信息。可以攔截子網頁url
(1) 開始加載OnLoadStart,navigation執行網之后,開始加載內容之前,回調此函數,多frame的進程會同時加載。同頁面巡航不會調用。
(2) 加載結束OnLoadEnd,加載結束時回調,sub-frame在主frame加載結束后, 會繼續開始加載或繼續進行加載,同頁面巡航不會調用。
(3) 加載錯誤OnLoadError,navigation失敗或者取消是回調。
(4) 加載狀態發生變化OnLoadingStateChange,加載初始化和加載結束時各調用一次,在OnLoadStart之前調用一次,OnLoadEnd或OnLoadError之后調用一次。
// Implement this interface to handle events related to browser load status. The
// methods of this class will be called on the browser process UI thread or
// render process main thread (TID_RENDERER).
///
/*--cef(source=client)--*/
class CefLoadHandler : public virtual CefBaseRefCounted {
public:
typedef cef_errorcode_t ErrorCode;
typedef cef_transition_type_t TransitionType;
///
// Called when the loading state has changed. This callback will be executed
// twice -- once when loading is initiated either programmatically or by user
// action, and once when loading is terminated due to completion, cancellation
// of failure. It will be called before any calls to OnLoadStart and after all
// calls to OnLoadError and/or OnLoadEnd.
///
/*--cef()--*/
virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) {}
///
// Called after a navigation has been committed and before the browser begins
// loading contents in the frame. The |frame| value will never be empty --
// call the IsMain() method to check if this frame is the main frame.
// |transition_type| provides information about the source of the navigation
// and an accurate value is only available in the browser process. Multiple
// frames may be loading at the same time. Sub-frames may start or continue
// loading after the main frame load has ended. This method will not be called
// for same page navigations (fragments, history state, etc.) or for
// navigations that fail or are canceled before commit. For notification of
// overall browser load status use OnLoadingStateChange instead.
///
/*--cef()--*/
virtual void OnLoadStart(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
TransitionType transition_type) {}
///
// Called when the browser is done loading a frame. The |frame| value will
// never be empty -- call the IsMain() method to check if this frame is the
// main frame. Multiple frames may be loading at the same time. Sub-frames may
// start or continue loading after the main frame load has ended. This method
// will not be called for same page navigations (fragments, history state,
// etc.) or for navigations that fail or are canceled before commit. For
// notification of overall browser load status use OnLoadingStateChange
// instead.
///
/*--cef()--*/
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) {}
///
// Called when a navigation fails or is canceled. This method may be called
// by itself if before commit or in combination with OnLoadStart/OnLoadEnd if
// after commit. |errorCode| is the error code number, |errorText| is the
// error text and |failedUrl| is the URL that failed to load.
// See net\base\net_error_list.h for complete descriptions of the error codes.
///
/*--cef(optional_param=errorText)--*/
virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) {}
};
2.9 CefRequestHandler網絡請求處理類
當打開一個網頁, CefRequestHandler的OnBeforeBrowser可以攔截網絡請求,只有在新打開網頁的時候,才會觸發,如果網頁已經打開,在網頁內部點擊查詢按鈕,查詢內容,雖然也有request請求,但是OnBeforeBrowser攔截不到獲取請求的URL,post請求的參數都可以獲取到。OnResourceRedirect還可以攔截重定向請求。CefLoadHandler也可以攔截request請求,而且頁面加載中調用很多的GET和POST請求都可以攔截到。測試發現CefRequestHandler頁面內部的加載變化是獲取不到的,只有打開頁面的請求能獲取到。而另外一個函數OnBeforeResourceLoad則可以攔截所有的請求,在瀏覽器中F12顯示的所有請求,包括圖片下載等請求都能一一獲取。所以CefLoadHandler攔截的請求更詳細一些,點擊查詢查詢,OnLoadStart和OnLoadEnd 攔截不到,但是OnLoadingStateChange 可以攔截的到請求。
OnBeforeBrowser |
打開新的網頁可以攔截,頁面內容變化,或者頁面內部調用請求攔截不到。 |
OnBeforeResourceLoad |
攔截一切請求,最詳細。 |
OnResourceResponse |
攔截一切請求,最詳細。 |
|
|
OnLoadStart和OnLoadEnd |
新打開頁面可以攔截 |
OnLoadingStateChange |
打開新頁面, 頁面內容重新加載,查詢,按鈕響應可以攔截。像一些圖片加載,CSS加載是攔截不到的。第二詳細。 |
各回調函的調用的先后順序是
OnLoadingStateChange->OnBeforeBrowser->OnLoadStart->OnLoadEnd->OnLoadingStateChange。
// Implement this interface to handle events related to browser requests. The
// methods of this class will be called on the thread indicated.
///
/*--cef(source=client)--*/
class CefRequestHandler : public virtual CefBaseRefCounted {
public:
typedef cef_return_value_t ReturnValue;
typedef cef_termination_status_t TerminationStatus;
typedef cef_urlrequest_status_t URLRequestStatus;
typedef cef_window_open_disposition_t WindowOpenDisposition;
typedef std::vector<CefRefPtr<CefX509Certificate>> X509CertificateList;
///
// Called on the UI thread before browser navigation. Return true to cancel
// the navigation or false to allow the navigation to proceed. The |request|
// object cannot be modified in this callback.
// CefLoadHandler::OnLoadingStateChange will be called twice in all cases.
// If the navigation is allowed CefLoadHandler::OnLoadStart and
// CefLoadHandler::OnLoadEnd will be called. If the navigation is canceled
// CefLoadHandler::OnLoadError will be called with an |errorCode| value of
// ERR_ABORTED. The |user_gesture| value will be true if the browser
// navigated via explicit user gesture (e.g. clicking a link) or false if it
// navigated automatically (e.g. via the DomContentLoaded event).
///
/*--cef()--*/
(1)OnBeforeBrowse,在瀏覽器巡航前調用。
virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
bool user_gesture,
bool is_redirect) {
return false;
}
///
// Called on the UI thread before OnBeforeBrowse in certain limited cases
// where navigating a new or different browser might be desirable. This
// includes user-initiated navigation that might open in a special way (e.g.
// links clicked via middle-click or ctrl + left-click) and certain types of
// cross-origin navigation initiated from the renderer process (e.g.
// navigating the top-level frame to/from a file URL). The |browser| and
// |frame| values represent the source of the navigation. The
// |target_disposition| value indicates where the user intended to navigate
// the browser based on standard Chromium behaviors (e.g. current tab,
// new tab, etc). The |user_gesture| value will be true if the browser
// navigated via explicit user gesture (e.g. clicking a link) or false if it
// navigated automatically (e.g. via the DomContentLoaded event). Return true
// to cancel the navigation or false to allow the navigation to proceed in the
// source browser's top-level frame.
///
/*--cef()--*/
(2)OnOpenURLFromTab,以特殊的方式打開的網頁,例如鼠標中間按鈕,快捷鍵等,一些很少的應用場景。
virtual bool OnOpenURLFromTab(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
WindowOpenDisposition target_disposition,
bool user_gesture) {
return false;
}
///
// Called on the IO thread before a resource request is loaded. The |request|
// object may be modified. Return RV_CONTINUE to continue the request
// immediately. Return RV_CONTINUE_ASYNC and call CefRequestCallback::
// Continue() at a later time to continue or cancel the request
// asynchronously. Return RV_CANCEL to cancel the request immediately.
//
///
/*--cef(default_retval=RV_CONTINUE)--*/
(3)OnBeforeResourceLoad網頁開始加載資源時調用,可以攔截所有的請求,最為詳細。
virtual ReturnValue OnBeforeResourceLoad(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefRequestCallback> callback) {
return RV_CONTINUE;
}
};
///
// Called on the IO thread before a resource is loaded. To allow the resource
// to load normally return NULL. To specify a handler for the resource return
// a CefResourceHandler object. The |request| object should not be modified in
// this callback.
///
/*--cef()--*/
virtual CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
return NULL;
}
///
// Called on the IO thread when a resource load is redirected. The |request|
// parameter will contain the old URL and other request-related information.
// The |response| parameter will contain the response that resulted in the
// redirect. The |new_url| parameter will contain the new URL and can be
// changed if desired. The |request| object cannot be modified in this
// callback.
///
/*--cef()--*/
(4)OnResourceRedirect重定向請求攔截
virtual void OnResourceRedirect(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
CefString& new_url) {}
///
// Called on the IO thread when a resource response is received. To allow the
// resource to load normally return false. To redirect or retry the resource
// modify |request| (url, headers or post body) and return true. The
// |response| object cannot be modified in this callback.
///
/*--cef()--*/
(5)OnResourceResponse請求響應后的回調函數
virtual bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) {
return false;
}
///
// Called on the IO thread to optionally filter resource response content.
// |request| and |response| represent the request and response respectively
// and cannot be modified in this callback.
///
/*--cef()--*/
virtual CefRefPtr<CefResponseFilter> GetResourceResponseFilter(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) {
return NULL;
}
///
// Called on the IO thread when a resource load has completed. |request| and
// |response| represent the request and response respectively and cannot be
// modified in this callback. |status| indicates the load completion status.
// |received_content_length| is the number of response bytes actually read.
///
/*--cef()--*/
(6)OnResourceLoadComplete資源加載結束時的回調
virtual void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) {}
///
// Called on the IO thread when the browser needs credentials from the user.
// |isProxy| indicates whether the host is a proxy server. |host| contains the
// hostname and |port| contains the port number. |realm| is the realm of the
// challenge and may be empty. |scheme| is the authentication scheme used,
// such as "basic" or "digest", and will be empty if the source of the request
// is an FTP server. Return true to continue the request and call
// CefAuthCallback::Continue() either in this method or at a later time when
// the authentication information is available. Return false to cancel the
// request immediately.
///
/*--cef(optional_param=realm,optional_param=scheme)--*/
virtual bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
bool isProxy,
const CefString& host,
int port,
const CefString& realm,
const CefString& scheme,
CefRefPtr<CefAuthCallback> callback) {
return false;
}
///
// Called on the IO thread before sending a network request with a "Cookie"
// request header. Return true to allow cookies to be included in the network
// request or false to block cookies. The |request| object should not be
// modified in this callback.
///
/*--cef()--*/
virtual bool CanGetCookies(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
return true;
}
///
// Called on the IO thread when receiving a network request with a
// "Set-Cookie" response header value represented by |cookie|. Return true to
// allow the cookie to be stored or false to block the cookie. The |request|
// object should not be modified in this callback.
///
/*--cef()--*/
virtual bool CanSetCookie(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
const CefCookie& cookie) {
return true;
}
///
// Called on the IO thread when JavaScript requests a specific storage quota
// size via the webkitStorageInfo.requestQuota function. |origin_url| is the
// origin of the page making the request. |new_size| is the requested quota
// size in bytes. Return true to continue the request and call
// CefRequestCallback::Continue() either in this method or at a later time to
// grant or deny the request. Return false to cancel the request immediately.
///
/*--cef()--*/
virtual bool OnQuotaRequest(CefRefPtr<CefBrowser> browser,
const CefString& origin_url,
int64 new_size,
CefRefPtr<CefRequestCallback> callback) {
return false;
}
///
// Called on the UI thread to handle requests for URLs with an unknown
// protocol component. Set |allow_os_execution| to true to attempt execution
// via the registered OS protocol handler, if any.
// SECURITY WARNING: YOU SHOULD USE THIS METHOD TO ENFORCE RESTRICTIONS BASED
// ON SCHEME, HOST OR OTHER URL ANALYSIS BEFORE ALLOWING OS EXECUTION.
///
/*--cef()--*/
virtual void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
const CefString& url,
bool& allow_os_execution) {}
///
// Called on the UI thread to handle requests for URLs with an invalid
// SSL certificate. Return true and call CefRequestCallback::Continue() either
// in this method or at a later time to continue or cancel the request. Return
// false to cancel the request immediately. If
// CefSettings.ignore_certificate_errors is set all invalid certificates will
// be accepted without calling this method.
///
/*--cef()--*/
virtual bool OnCertificateError(CefRefPtr<CefBrowser> browser,
cef_errorcode_t cert_error,
const CefString& request_url,
CefRefPtr<CefSSLInfo> ssl_info,
CefRefPtr<CefRequestCallback> callback) {
return false;
}
///
// Called on the UI thread when a client certificate is being requested for
// authentication. Return false to use the default behavior and automatically
// select the first certificate available. Return true and call
// CefSelectClientCertificateCallback::Select either in this method or at a
// later time to select a certificate. Do not call Select or call it with NULL
// to continue without using any certificate. |isProxy| indicates whether the
// host is an HTTPS proxy or the origin server. |host| and |port| contains the
// hostname and port of the SSL server. |certificates| is the list of
// certificates to choose from; this list has already been pruned by Chromium
// so that it only contains certificates from issuers that the server trusts.
///
/*--cef()--*/
virtual bool OnSelectClientCertificate(
CefRefPtr<CefBrowser> browser,
bool isProxy,
const CefString& host,
int port,
const X509CertificateList& certificates,
CefRefPtr<CefSelectClientCertificateCallback> callback) {
return false;
}
///
// Called on the browser process UI thread when a plugin has crashed.
// |plugin_path| is the path of the plugin that crashed.
///
/*--cef()--*/
virtual void OnPluginCrashed(CefRefPtr<CefBrowser> browser,
const CefString& plugin_path) {}
///
// Called on the browser process UI thread when the render view associated
// with |browser| is ready to receive/handle IPC messages in the render
// process.
///
/*--cef()--*/
virtual void OnRenderViewReady(CefRefPtr<CefBrowser> browser) {}
///
// Called on the browser process UI thread when the render process
// terminates unexpectedly. |status| indicates how the process
// terminated.
///
/*--cef()--*/
virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) {}
};
3 CEF高級應用
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
CefV8Handler
public:
public:
CefString
name CefRefPtr CefV8Value object CefV8ValueList arguments CefRefPtr CefV8Value retval CefString exception override
private:
std map std pair std string std pair CefRefPtr CefV8Context CefRefPtr CefV8Value CallbackMap
CallbackMap callback_map_
public:
CCEFV8HandlerEx
};
//CEFV8HandlerEx.cpp
//JS調用C++函數的回調
CCEFV8HandlerEx CefString name CefRefPtr CefV8Value object CefV8ValueList arguments CefRefPtr CefV8Value retval CefString exception
{
name
arguments
CefString strParam1 arguments
CefString strParam2 arguments
TCHAR szBuffer
szBuffer
szBuffer
strParam1
strParam2
szBuffer
MB_OK
retval
CefV8Value
retval
CefV8Value
name L
CefString strParam1 arguments
TCHAR szBuffer
szBuffer
szBuffer
strParam1
szBuffer
MB_OK
}
3.2 CefV8Value類實現定JavaScript數據類型
上面的實例中我們已經用到CefV8Value類的CreateString("My Value!")創建字符串和CreateFunction創建了函數。這個類還可以創建, null, bool, int, double, date string等數據類型,都有對應的函數。static CefRefPtr<CefV8Value> CreateArray(int length)創建JavaScript數組。下面詳細講解下給JavaScript提供創建對象。accessor就是給JavaScript提供調用的set和get方法設置或獲取屬性。interceptor是攔截器,攔截set和get方法。
///
// Create a new CefV8Value object of type object with optional accessor and/or
// interceptor. This method should only be called from within the scope of a
// CefRenderProcessHandler, CefV8Handler or CefV8Accessor callback, or in
// combination with calling Enter() and Exit() on a stored CefV8Context
// reference.
///
/*--cef(optional_param=accessor, optional_param=interceptor)--*/
static CefRefPtr<CefV8Value> CreateObject(
CefRefPtr<CefV8Accessor> accessor,
CefRefPtr<CefV8Interceptor> interceptor);
3.2.1 一般對象
如果不需要給JavaScript提供get和set方法,可以直接創建對象。
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(NULL);
給對象設置屬性名稱為myval,屬性值為My String;字符串類型。
virtual bool SetValue(int index, CefRefPtr<CefV8Value> value) = 0;
obj->SetValue("myval", CefV8Value::CreateString("My String!"));
3.2.2 CEF實現帶access的JavaScript對象
如果要提供set和get方法,需要提供access實現類。
(1)實現access類
class MyV8Accessor : public CefV8Accessor {
public:
MyV8Accessor() {}
virtual bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE {
if (name == "myval") {
// Return the value.
retval = CefV8Value::CreateString(myval_);
return true;
}
// Value does not exist.
return false;
}
virtual bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) OVERRIDE {
if (name == "myval") {
if (value->IsString()) {
// Store the value.
myval_ = value->GetStringValue();
} else {
// Throw an exception.
exception = "Invalid value type";
}
return true;
}
// Value does not exist.
return false;
}
// Variable used for storing the value.
CefString myval_;
// Provide the reference counting implementation for this class.
IMPLEMENT_REFCOUNTING(MyV8Accessor);
};
(2)使用SetValue函數設置對象屬性采用access方式設置和獲取數據。把對象設置到窗口對象中。
virtual bool SetValue(const CefString& key,
AccessControl settings,
PropertyAttribute attribute) = 0;
typedef enum {
V8_ACCESS_CONTROL_DEFAULT = 0,
V8_ACCESS_CONTROL_ALL_CAN_READ = 1,
V8_ACCESS_CONTROL_ALL_CAN_WRITE = 1 << 1,
V8_ACCESS_CONTROL_PROHIBITS_OVERWRITING = 1 << 2
} cef_v8_accesscontrol_t;
///
// V8 property attribute values.
///
typedef enum {
V8_PROPERTY_ATTRIBUTE_NONE = 0, // Writeable, Enumerable,
// Configurable
V8_PROPERTY_ATTRIBUTE_READONLY = 1 << 0, // Not writeable
V8_PROPERTY_ATTRIBUTE_DONTENUM = 1 << 1, // Not enumerable
V8_PROPERTY_ATTRIBUTE_DONTDELETE = 1 << 2 // Not configurable
} cef_v8_propertyattribute_t;
obj->SetValue("myval", V8_ACCESS_CONTROL_DEFAULT,
V8_PROPERTY_ATTRIBUTE_NONE);
(3)JavaScript中調用set和get方法,就可以設置和獲取屬性值。
3.2.3 CEF實現帶攔截器CefV8Interceptor的JavaScript對象
攔截器CefV8Interceptor和Access的區別是,除了可以用字符串來映射屬性,還可以用index索引來映射屬性。其定義如下
// Interface that should be implemented to handle V8 interceptor calls. The
// methods of this class will be called on the thread associated with the V8
// interceptor. Interceptor's named property handlers (with first argument of
// type CefString) are called when object is indexed by string. Indexed property
// handlers (with first argument of type int) are called when object is indexed
// by integer.
///
/*--cef(source=client,no_debugct_check)--*/
class CefV8Interceptor : public virtual CefBaseRefCounted {
public:
///
// Handle retrieval of the interceptor value identified by |name|. |object| is
// the receiver ('this' object) of the interceptor. If retrieval succeeds, set
// |retval| to the return value. If the requested value does not exist, don't
// set either |retval| or |exception|. If retrieval fails, set |exception| to
// the exception that will be thrown. If the property has an associated
// accessor, it will be called only if you don't set |retval|.
// Return true if interceptor retrieval was handled, false otherwise.
///
/*--cef(capi_name=get_byname)--*/
virtual bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) = 0;
///
// Handle retrieval of the interceptor value identified by |index|. |object|
// is the receiver ('this' object) of the interceptor. If retrieval succeeds,
// set |retval| to the return value. If the requested value does not exist,
// don't set either |retval| or |exception|. If retrieval fails, set
// |exception| to the exception that will be thrown.
// Return true if interceptor retrieval was handled, false otherwise.
///
/*--cef(capi_name=get_byindex,index_param=index)--*/
virtual bool Get(int index,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) = 0;
///
// Handle assignment of the interceptor value identified by |name|. |object|
// is the receiver ('this' object) of the interceptor. |value| is the new
// value being assigned to the interceptor. If assignment fails, set
// |exception| to the exception that will be thrown. This setter will always
// be called, even when the property has an associated accessor.
// Return true if interceptor assignment was handled, false otherwise.
///
/*--cef(capi_name=set_byname)--*/
virtual bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) = 0;
///
// Handle assignment of the interceptor value identified by |index|. |object|
// is the receiver ('this' object) of the interceptor. |value| is the new
// value being assigned to the interceptor. If assignment fails, set
// |exception| to the exception that will be thrown.
// Return true if interceptor assignment was handled, false otherwise.
///
/*--cef(capi_name=set_byindex,index_param=index)--*/
virtual bool Set(int index,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) = 0;
};
實例步驟如下
(1)實現Interceptor類
class Interceptor : public CefV8Interceptor {
public:
Interceptor() {}
virtual bool Get(const CefString& name,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE {
return true;
}
virtual bool Get(int index,
const CefRefPtr<CefV8Value> object,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE {
return true;
}
virtual bool Set(const CefString& name,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) OVERRIDE {
return true;
}
virtual bool Set(int index,
const CefRefPtr<CefV8Value> object,
const CefRefPtr<CefV8Value> value,
CefString& exception) OVERRIDE {
return true;
}
IMPLEMENT_REFCOUNTING(Interceptor);
};
(2)創建Interceptor對象和JavaScript對象
CefRefPtr<CefV8Interceptor> interceptor = new Interceptor();
PERF_ITERATIONS_START()
CefRefPtr<CefV8Value> value = CefV8Value::CreateObject(nullptr, interceptor);
(3)SetValue()函設置到窗口對象中(待驗證)
(4)JavaScript調用按照字符串名稱或者索引來設置獲取值。
3.3 CEF進程間通訊
3.3.1 進程間通訊函數
CEF有Browser進程和Renderer進程,進程之間可以相互通信。
發送消息Browser進程和Renderer進程都是一樣的,使用CefBrowser::SendProcessMessage() ,SendProcessMessage第一個參數是CefProcessId,是一個枚舉類型,給Browser進程發送,就用PID_BROWSER,給Render進程發送,就用PID_RENDERER。
typedef enum {
///
// Browser process.
///
PID_BROWSER,
///
// Renderer process.
///
PID_RENDERER,
} cef_process_id_t;
Render進程這邊, 重寫CefRenderProcessHandler::OnProcessMessageReceived()
Browser進程這邊,重寫CefClient::OnProcessMessageReceived()這個方法來處理跨進程消息。
3.3.2 進程通訊實例
(1)renderer進程發送消息
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("login_msg");
// Retrieve the argument list object.
CefRefPtr<CefListValue> args = msg->GetArgumentList();
// Populate the argument values.
args->SetSize(2);
args->SetString(0, strUser);
args->SetString(1, strPassword);
// Send the process message to the browser process.
CefV8Context::GetCurrentContext()->GetBrowser()->SendProcessMessage(PID_BROWSER, msg);
(2)Browser進程這邊,重寫CefClient::OnProcessMessageReceived()這個方法來處理跨進程消息。接收到消息之后再回復消息。
bool ClientHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
{
const std::string& messageName = message->GetName();
if (messageName == "login_msg")
{
// extract message
CefRefPtr<CefListValue> args = message->GetArgumentList();
CefString strUser = args->GetString(0);
CefString strPassword = args->GetString(1);
TCHAR szLog[256] = { 0 };
_stprintf_s(szLog, 256, _T("BrowserProcess, user - %s, password - %s\r\n"), strUser.c_str(), strPassword.c_str());
OutputDebugString(szLog);
//send reply to render process
CefRefPtr<CefProcessMessage> outMsg = CefProcessMessage::Create("login_reply");
// Retrieve the argument list object.
CefRefPtr<CefListValue> replyArgs = outMsg->GetArgumentList();
// Populate the argument values.
replyArgs->SetSize(1);
replyArgs->SetInt(0, 0);
// Send the process message to the renderer process.
browser->SendProcessMessage(PID_RENDERER, outMsg);
return true;
}
return false;
}
(3)Render進程這邊, 重寫CefRenderProcessHandler:: OnProcessMessageReceived()方法來處理來自Browser進程的消息。
bool ClientAppRenderer::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
{
const std::string& messageName = message->GetName();
if (messageName == "login_reply")
{
// extract message
CefRefPtr<CefListValue> args = message->GetArgumentList();
int status = args->GetInt(0);
OutputDebugString(status == 0 ? _T("Renderer process, login ok\r\n") : _T("Renderer process, login failed\r\n"));
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('Got Login Reply from Browser process')", frame->GetURL(), 0);
return true;
}
return false;
}
3.3.3 CEF指定frame通訊
一個網頁有多個frame,有一個mainframe和多個subframe,發送消息時,將frame的id放入參數中一起發過去,接收時消息時,獲取frameid,就可以實現指定frame通訊。因為frameid是一個int64類型的數據,所以發送時將它分解為兩個int32
的高低位數據,接收數據時,再將兩個int32的數據拼接成一個int64數據。
#define MAKE_INT64(int_low, int_high) \
((int64) (((int) (int_low)) | ((int64) ((int) (int_high))) << 32))
#define LOW_INT(int64_val) ((int) (int64_val))
#define HIGH_INT(int64_val) ((int) (((int64) (int64_val) >> 32) & 0xFFFFFFFFL))
// Sending the frame ID.
const int64 frame_id = frame->GetIdentifier();
args->SetInt(0, LOW_INT(frame_id));
args->SetInt(1, HIGH_INT(frame_id));
// Receiving the frame ID.
const int64 frame_id = MAKE_INT64(args->GetInt(0), args->GetInt(1));
CefRefPtr<CefFrame> frame = browser->GetFrame(frame_id);