欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CEF(Chromium Embedded Framework)使用说明书

程序员文章站 2022-07-01 12:49:32
...

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

 CEF(Chromium Embedded Framework)使用说明书

 

 

如图所示,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,。

 CEF(Chromium Embedded Framework)使用说明书

 

 

1.3.2         Renderer进程的实现结构

renderer程序继承CefApp和CefRenderProcessHandler类,在main函数中初始化。通过CefSettings.browser_subprocess_path配置render可执行程序路径。browser进程就会去启动这个进程去渲染网页。

 CEF(Chromium Embedded Framework)使用说明书

 

 

1.3.3         browser进程的实现结构

browserapp要继承CefApp和CefBrowserProcessHandler类。实现browserapp的定义。同时要新建clienthandler类实现图中的回调函数接口类,用来处理拦截响应请求、管理生命周期、下载、显示加载、右键菜单等。在mian函数中初始化、启动消息循环。调用CefBrowserHost的静态方法创建browser窗口对象,在render进程的Frame中加载渲染内容。

 CEF(Chromium Embedded Framework)使用说明书

 

 

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结构来对其进行赋值。

1stringCefString

单字节

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)CefStringstring

单字节

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
 
class CCEFV8HandlerEx : 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++函数的回调
bool CCEFV8HandlerEx::Execute(const CefString& 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;
}

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);

相关标签: CEF网页嵌入