标签:css 兼容 base via 详细说明 important disable instance upd
CEF全称Chromium Embedded Framework,是一个基于Google Chromium 的开源项目。Google Chromium项目主要是为Google Chrome应用开发的,而CEF的目标则是为第三方应用提供可嵌入浏览器支持。CEF隔离底层Chromium和Blink的复杂代码,并提供一套产品级稳定的API,发布跟踪具体Chromium版本的分支,以及二进制包。CEF的大部分特性都提供了丰富的默认实现,让使用者做尽量少的定制即可满足需求。在本文发布的时候,世界上已经有很多公司和机构采用CEF,CEF的安装量超过了100万。[CEF wikipedia]页面上有使用CEF的公司和机构的不完全的列表。CEF的典型应用场景包括:
CEF3是基于Chomuim Content API多进程构架的下一代CEF,拥有下列优势:
本文档介绍CEF3开发中涉及到的一般概念。
CEF3的二进制包可以在这个页面下载。其中包含了在特定平台(Windows,Mac OS X 以及 Linux)编译特定版本CEF3所需的全部文件。不同平台拥有共同的结构:
每个二进制包包含一个README.txt文件和一个LICENSE.txt文件,README.txt用以描述平台相关的细节,而LICENSE.txt包含CEF的BSD版权说明。如果你发布了基于CEF的应用,则应该在应用程序的某个地方包含该版权声明。例如,你可以在"关于”和“授权"页面列出该版权声明,或者单独一个文档包含该版权声明。“关于”和“授权”信息也可以分别在CEF浏览器的"about:license"和"about:credits"页面查看。
基于CEF二进制包的应用程序可以使用每个平台上的经典编译工具。包括Windows平台上的Visual Studio,Mac OSX平台上的Xcode,以及Linux平台上的gcc/make编译工具链。CEF项目的下载页面包含了这些平台上编译特定版本CEF所需的编译工具的版本信息。在Linux上编译CEF时需要特别注意依赖工具链。
Tutorial Wiki页面有更多关于如何使用CEF3二进制包创建简单应用程序的细节。
CEF可以从源码编译,用户可以使用本地编译系统或者像TeamCity这样的自动化编译系统编译。首先你需要使用svn或者git下载Chromium和CEF的源码。由于Chromium源码很大,只建议在内存大于4GB的现代机器上编译。编译Chromium和CEF的细节请参考BranchesAndBuilding页面。
cefclient是一个完整的CEF客户端应用程序示例,并且它的源码包含在CEF每个二进制发布包中。使用CEF创建一个新的应用程序,最简单的方法是先从cefclient应用程序开始,删除你不需要的部分。本文档中许多示例都是来源于cefclient应用程序。
在开发基于CEF3的应用程序前,有一些重要的基础概念应该被理解。
libcef 动态链接库导出 C API 使得使用者不用关心CEF运行库和基础代码。libcef_dll_wrapper 工程把 C API 封装成 C++ API同时包含在客户端应用程序工程中,与cefclient一样,源代码作为CEF二进制发布包的一部分共同发布。C/C++ API的转换层代码是由转换工具自动生成。UsingTheCAPI 页面描述了如何使用C API。
CEF3是多进程架构的。Browser被定义为主进程,负责窗口管理,界面绘制和网络交互。Blink的渲染和Js的执行被放在一个独立的Render 进程中;除此之外,Render进程还负责Js Binding和对Dom节点的访问。 默认的进程模型中,会为每个标签页创建一个新的Render进程。其他进程按需创建,例如管理插件的进程以及处理合成加速的进程等都是按需创建。
默认情况下,主应用程序会被多次启动运行各自独立的进程。这是通过传递不同的命令行参数给CefExecuteProcess函数做到的。如果主应用程序很大,加载时间比较长,或者不能在非浏览器进程里使用,则宿主程序可使用独立的可执行文件去运行这些进程。这可以通过配置CefSettings.browser_subprocess_path变量做到。更多细节请参考Application Structure一节。
CEF3的进程之间可以通过IPC进行通信。Browser和Render进程可以通过发送异步消息进行双向通信。甚至在Render进程可以注册在Browser进程响应的异步JavaScript API。 更多细节,请参考Inter-Process Communication一节。
通过设置命令行的--single-process
,CEF3就可以支持用于调试目的的单进程运行模型。支持的平台为:Windows,Mac OS X 和Linux。
在CEF3中,每个进程都会运行多个线程。完整的线程类型表请参照cef_thread_id_t。例如,在Browser进程中包含如下主要的线程:
由于CEF采用多线程架构,有必要使用锁和闭包来保证数据的线程安全语义。IMPLEMENT_LOCKING定义提供了Lock()和Unlock()方法以及AutoLock对象来保证不同代码块同步访问数据。CefPostTask函数组支持简易的线程间异步消息传递。更多信息,请参考Posting Tasks章节。
可以通过CefCurrentlyOn()方法判断当前所在的线程环境,cefclient工程使用下面的定义来确保方法在期望的线程中被执行。
1 #define REQUIRE_UI_THREAD() ASSERT(CefCurrentlyOn(TID_UI)); 2 #define REQUIRE_IO_THREAD() ASSERT(CefCurrentlyOn(TID_IO)); 3 #define REQUIRE_FILE_THREAD() ASSERT(CefCurrentlyOn(TID_FILE));
所有的框架类从CefBase继承,实例指针由CefRefPtr管理,CefRefPtr通过调用AddRef()和Release()方法自动管理引用计数。框架类的实现方式如下:
1 class MyClass : public CefBase { 2 public: 3 // Various class methods here... 4 5 private: 6 // Various class members here... 7 8 IMPLEMENT_REFCOUNTING(MyClass); // Provides atomic refcounting implementation. 9 }; 10 11 // References a MyClass instance 12 CefRefPtr<MyClass> my_class = new MyClass();
CEF为字符串定义了自己的数据结构。主要是出于以下原因:
UTF16字符串结构体示例如下:
1 typedef struct _cef_string_utf16_t { 2 char16* str; // Pointer to the string 3 size_t length; // String length 4 void (*dtor)(char16* str); // Destructor for freeing the string on the correct heap 5 } cef_string_utf16_t;
通过typedef来设置常用的字符编码。
1 typedef char16 cef_char_t; 2 typedef cef_string_utf16_t cef_string_t;
CEF提供了一批C语言的方法来操作字符串(通过#define的方式来适应不同的字符编码)
CEF也提供了字符串不同编码之间相互转换的方法。具体函数列表请查阅cef_string.h和cef_string_types.h文件。
在C++中,通常使用CefString类来管理CEF的字符串。CefString支持与std::string(UTF8)、std::wstring(wide)类型的相互转换。也可以用来包裹一个cef_string_t结构来对其进行赋值。
和std::string的相互转换:
1 std::string str = “Some UTF8 string”; 2 3 // Equivalent ways of assigning |str| to |cef_str|. Conversion from UTF8 will occur if necessary. 4 CefString cef_str(str); 5 cef_str = str; 6 cef_str.FromString(str); 7 8 // Equivalent ways of assigning |cef_str| to |str|. Conversion to UTF8 will occur if necessary. 9 str = cef_str; 10 str = cef_str.ToString();
和std::wstring的相互转换:
1 std::wstring str = “Some wide string”; 2 3 // Equivalent ways of assigning |str| to |cef_str|. Conversion from wide will occur if necessary. 4 CefString cef_str(str); 5 cef_str = str; 6 cef_str.FromWString(str); 7 8 // Equivalent ways of assigning |cef_str| to |str|. Conversion to wide will occur if necessary. 9 str = cef_str; 10 str = cef_str.ToWString();
如果是ASCII编码,使用FromASCII进行赋值:
1 const char* cstr = “Some ASCII string”; 2 CefString cef_str; 3 cef_str.FromASCII(cstr);
一些结构体(比如CefSettings)含有cef_string_t类型的成员,CefString支持直接赋值给这些成员。
1 CefSettings settings; 2 const char* path = “/path/to/log.txt”; 3 4 // Equivalent assignments. 5 CefString(&settings.log_file).FromASCII(path); 6 cef_string_from_ascii(path, strlen(path), &settings.log_file);
在CEF3和Chromium中许多特性可以使用命令行参数进行配置。这些参数采用--some-argument[=optional-param]
形式,并通过CefExecuteProcess()和CefMainArgs结构(参考下面的应用资源布局章节)传递给CEF。在传递CefSettings结构给CefInitialize()之前,我们可以设置CefSettings.command_line_args_disabled为true来禁用对命令行参数的处理。如果想指定命令行参数传入主应用程序,实现CefApp::OnBeforeCommandLineProcessing()方法。更多关于如何查找已支持的命令行选项的信息,请查看client_switches.cpp文件的注释。
应用资源布局依赖于平台,有很大的不同。比如,在Mac OS X上,你的资源布局必须遵循特定的app bundles结构;Window与Linux则更灵活,允许你定制CEF库文件与资源文件所在的位置。为了获取到特定可以正常工作的示例,你可以从工程的下载页面下载到一个client压缩包。每个平台对应的README.txt文件详细说明了哪些文件是可选的,哪些文件是必须的。
在Windows平台上,默认的资源布局将libcef库文件、相关资源与可执行文件放置在同级目录,文件夹结构大致如下:
1 Application/ 2 cefclient.exe <= cefclient application executable 3 libcef.dll <= main CEF library 4 icudt.dll <= ICU unicode support library 5 ffmpegsumo.dll <= HTML5 audio/video support library 6 libEGL.dll, libGLESv2.dll, … <= accelerated compositing support libraries 7 cef.pak, devtools_resources.pak <= non-localized resources and strings 8 locales/ 9 en-US.pak, … <= locale-specific resources and strings
使用结构体CefSettings可以定制CEF库文件、资源文件的位置(查看README.txt文件或者本文中CefSettings部分获取更详细的信息)。虽然在Windows平台上,cefclient项目将资源文件以二进制形式编译进cefclient.rc文件,但是改为从文件系统加载资源也很容易。
在Linux平台上,默认的资源布局将libcef库文件、相关资源与可执行文件放置在同级目录。注意:在你编译的版本与发行版本应用程序中,libcef.so的位置是有差异的,此文件的位置取决于编译可执行程序时,编译器rpath的值。比如,编译选项为“-Wl,-rpath,.”(“.”意思是当前文件夹),这样libcef.so与可执行文件处于同级目录。libcef.so文件的路径可以通过环境变量中的“LD_LIBRARY_PATH
”指定。
1 Application/ 2 cefclient <= cefclient application executable 3 libcef.so <= main CEF library 4 ffmpegsumo.so <-- HTML5 audio/video support library 5 cef.pak, devtools_resources.pak <= non-localized resources and strings 6 locales/ 7 en-US.pak, … <= locale-specific resources and strings 8 files/ 9 binding.html, … <= cefclient application resources
使用结构体CefSettings可以定制CEF库文件、资源文件(查看README.txt文件或者本文中CefSettings部分获取更详细的信息)。
在Mac X平台上,app bundles委托给了Chromium实现,因此不是很灵活。文件夹结构大致如下:
1 cefclient.app/ 2 Contents/ 3 Frameworks/ 4 Chromium Embedded Framework.framework/ 5 Libraries/ 6 ffmpegsumo.so <= HTML5 audio/video support library 7 libcef.dylib <= main CEF library 8 Resources/ 9 cef.pak, devtools_resources.pak <= non-localized resources and strings 10 *.png, *.tiff <= Blink image and cursor resources 11 en.lproj/, … <= locale-specific resources and strings 12 libplugin_carbon_interpose.dylib <= plugin support library 13 cefclient Helper.app/ 14 Contents/ 15 Info.plist 16 MacOS/ 17 cefclient Helper <= helper executable 18 Pkginfo 19 cefclient Helper EH.app/ 20 Contents/ 21 Info.plist 22 MacOS/ 23 cefclient Helper EH <= helper executable 24 Pkginfo 25 cefclient Helper NP.app/ 26 Contents/ 27 Info.plist 28 MacOS/ 29 cefclient Helper NP <= helper executable 30 Pkginfo 31 Info.plist 32 MacOS/ 33 cefclient <= cefclient application executable 34 Pkginfo 35 Resources/ 36 binding.html, … <= cefclient application resources
列表中的“Chromium Embedded Framework.framework”,这个未受版本管控的框架包含了所有的CEF库文件、资源文件。使用install_name_tool与@executable_path,将cefclient,cefclient helper等可执行文件,连接到了libcef.dylib上。
应用程序cefclient helper用来执行不同特点、独立的进程(Renderer,plugin等),这些进程需要独立的资源布局与Info.plist等文件,它们没有显示停靠图标。用来启动插件进程的EH Helper清除了MH_NO_HEAP_EXECUTION标志位,这样就允许一个可执行堆。只能用来启动NaCL插件进程的NP Helper,清除了MH_PIE标志位,这样就禁用了ASLR。这些都是tools文件夹下面,用来构建进程脚本的一部分。为了理清脚本的依赖关系,更好的做法是检查发行版本中的Xcode工程或者原始文件cefclient.gyp。
每个CEF3应用程序都是相同的结构
像本文中进程章节描述的那样,一个CEF3应用程序会运行多个进程,这些进程能够使用同一个执行器或者为子进程定制的、单独的执行器。进程的执行从入口函数开始,示例cefclient_win.cc、cefclient_gtk.cc、cefclient_mac.mm分别对应Windows、Linux和Mac OS-X平台下的实现。
当执行子进程时,CEF将使用命令行参数指定配置信息,这些命令行参数必须通过CefMainArgs结构体传入到CefExecuteProcess函数。CefMainArgs的定义与平台相关,在Linux、Mac OS X平台下,它接收main函数传入的argc和argv参数值。
CefMainArgs main_args(argc, argv);
在Windows平台下,它接收wWinMain函数传入的参数:实例句柄(HINSTANCE),这个实例能够通过函数GetModuleHandle(NULL)获取。
CefMainArgs main_args(hInstance);
当以单一执行体运行时,根据不同的进程类型,入口函数有差异。Windows、Linux平台支持单一执行体架构,Mac OS X平台则不行。
1 int main(int argc, char* argv[]) { 2 // Structure for passing command-line arguments. 3 // The definition of this structure is platform-specific. 4 CefMainArgs main_args(argc, argv); 5 6 // Optional implementation of the CefApp interface. 7 CefRefPtr<MyApp> app(new MyApp); 8 9 // Execute the sub-process logic, if any. This will either return immediately for the browser 10 // process or block until the sub-process should exit. 11 int exit_code = CefExecuteProcess(main_args, app.get()); 12 if (exit_code >= 0) { 13 // The sub-process terminated, exit now. 14 return exit_code; 15 } 16 17 // Populate this structure to customize CEF behavior. 18 CefSettings settings; 19 20 // Initialize CEF in the main process. 21 CefInitialize(main_args, settings, app.get()); 22 23 // Run the CEF message loop. This will block until CefQuitMessageLoop() is called. 24 CefRunMessageLoop(); 25 26 // Shut down CEF. 27 CefShutdown(); 28 29 return 0; 30 }
当使用独立的子进程执行体时,你需要2个分开的可执行工程和2个分开的入口函数。
主程序的入口函数:
1 // Program entry-point function. 2 // 程序入口函数 3 int main(int argc, char* argv[]) { 4 // Structure for passing command-line arguments. 5 // The definition of this structure is platform-specific. 6 // 传递命令行参数的结构体。 7 // 这个结构体的定义与平台相关。 8 CefMainArgs main_args(argc, argv); 9 10 // Optional implementation of the CefApp interface. 11 // 可选择性地实现CefApp接口 12 CefRefPtr<MyApp> app(new MyApp); 13 14 // Populate this structure to customize CEF behavior. 15 // 填充这个结构体,用于定制CEF的行为。 16 CefSettings settings; 17 18 // Specify the path for the sub-process executable. 19 // 指定子进程的执行路径 20 CefString(&settings.browser_subprocess_path).FromASCII(“/path/to/subprocess”); 21 22 // Initialize CEF in the main process. 23 // 在主进程中初始化CEF 24 CefInitialize(main_args, settings, app.get()); 25 26 // Run the CEF message loop. This will block until CefQuitMessageLoop() is called. 27 // 执行消息循环,此时会堵塞,直到CefQuitMessageLoop()函数被调用。 28 CefRunMessageLoop(); 29 30 // Shut down CEF. 31 // 关闭CEF 32 CefShutdown(); 33 34 return 0; 35 }
子进程程序的入口函数:
1 // Program entry-point function. 2 // 程序入口函数 3 int main(int argc, char* argv[]) { 4 // Structure for passing command-line arguments. 5 // The definition of this structure is platform-specific. 6 // 传递命令行参数的结构体。 7 // 这个结构体的定义与平台相关。 8 CefMainArgs main_args(argc, argv); 9 10 // Optional implementation of the CefApp interface. 11 // 可选择性地实现CefApp接口 12 CefRefPtr<MyApp> app(new MyApp); 13 14 // Execute the sub-process logic. This will block until the sub-process should exit. 15 // 执行子进程逻辑,此时会堵塞直到子进程退出。 16 return CefExecuteProcess(main_args, app.get()); 17 }
CEF可以不用它自己提供的消息循环,而与已经存在的程序中消息环境集成在一起,有两种方式可以做到:
周期性执行CefDoMessageLoopWork()函数,替代调用CefRunMessageLoop()。CefDoMessageLoopWork()的每一次调用,都将执行一次CEF消息循环的单次迭代。需要注意的是,此方法调用次数太少时,CEF消息循环会饿死,将极大的影响Browser的性能,调用次数太频繁又将影响CPU使用率。
设置CefSettings.multi_threaded_message_loop=true(Windows平台下有效),这个设置项将导致CEF在单独的线程上运行Browser的界面,而不是在主线程上,这种场景下CefDoMessageLoopWork()或者CefRunMessageLoop()都不需要调用,CefInitialze()、CefShutdown()仍然在主线程中调用。你需要提供主程序线程通信的机制(查看cefclient_win.cpp中提供的消息窗口实例)。在Windows平台下,你可以通过命令行参数--multi-threaded-message-loop
测试上述消息模型。
CefSettings结构体允许定义全局的CEF配置,经常用到的配置项如下:
CefBrowser和CefFrame对象被用来发送命令给浏览器以及在回调函数里获取状态信息。每个CefBrowser对象包含一个主CefFrame对象,主CefFrame对象代表页面的顶层frame;同时每个CefBrowser对象可以包含零个或多个的CefFrame对象,分别代表不同的子Frame。例如,一个浏览器加载了两个iframe,则该CefBrowser对象拥有三个CefFrame对象(顶层frame和两个iframe)。
下面的代码在浏览器的主frame里加载一个URL:
browser->GetMainFrame()->LoadURL(some_url);
下面的代码执行浏览器的回退操作:
browser->GoBack();
下面的代码从主frame里获取HTML内容:
1 // Implementation of the CefStringVisitor interface. 2 class Visitor : public CefStringVisitor { 3 public: 4 Visitor() {} 5 6 // Called asynchronously when the HTML contents are available. 7 virtual void Visit(const CefString& string) OVERRIDE { 8 // Do something with |string|... 9 } 10 11 IMPLEMENT_REFCOUNTING(Visitor); 12 }; 13 14 browser->GetMainFrame()->GetSource(new Visitor());
CefBrowser和CefFrame对象在Browser进程和Render进程都有对等的代理对象。在Browser进程里,Host(宿主)行为控制可以通过CefBrowser::GetHost()方法控制。例如,浏览器窗口的原生句柄可以用下面的代码获取:
// CefWindowHandle is defined as HWND on Windows, NSView* on Mac OS X // and GtkWidget* on Linux. CefWindowHandle window_handle = browser->GetHost()->GetWindowHandle();
其他方法包括历史导航,加载字符串和请求,发送编辑命令,提取text/html内容等。请参考支持函数相关的文档或者CefBrowser的头文件注释。
CefApp接口提供了不同进程的可定制回调函数。毕竟重要的回调函数如下:
CefApp子类的例子:
1 // MyApp implements CefApp and the process-specific interfaces. 2 class MyApp : public CefApp, 3 public CefBrowserProcessHandler, 4 public CefRenderProcessHandler { 5 public: 6 MyApp() {} 7 8 // CefApp methods. Important to return |this| for the handler callbacks. 9 virtual void OnBeforeCommandLineProcessing( 10 const CefString& process_type, 11 CefRefPtr<CefCommandLine> command_line) { 12 // Programmatically configure command-line arguments... 13 } 14 virtual void OnRegisterCustomSchemes( 15 CefRefPtr<CefSchemeRegistrar> registrar) OVERRIDE { 16 // Register custom schemes... 17 } 18 virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() 19 OVERRIDE { return this; } 20 virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() 21 OVERRIDE { return this; } 22 23 // CefBrowserProcessHandler methods. 24 virtual void OnContextInitialized() OVERRIDE { 25 // The browser process UI thread has been initialized... 26 } 27 virtual void OnRenderProcessThreadCreated(CefRefPtr<CefListValue> extra_info) 28 OVERRIDE { 29 // Send startup information to a new render process... 30 } 31 32 // CefRenderProcessHandler methods. 33 virtual void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) 34 OVERRIDE { 35 // The render process main thread has been initialized... 36 // Receive startup information in the new render process... 37 } 38 virtual void OnWebKitInitialized(CefRefPtr<ClientApp> app) OVERRIDE { 39 // WebKit has been initialized, register V8 extensions... 40 } 41 virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE { 42 // Browser created in this render process... 43 } 44 virtual void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) OVERRIDE { 45 // Browser destroyed in this render process... 46 } 47 virtual bool OnBeforeNavigation(CefRefPtr<CefBrowser> browser, 48 CefRefPtr<CefFrame> frame, 49 CefRefPtr<CefRequest> request, 50 NavigationType navigation_type, 51 bool is_redirect) OVERRIDE { 52 // Allow or block different types of navigation... 53 } 54 virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, 55 CefRefPtr<CefFrame> frame, 56 CefRefPtr<CefV8Context> context) OVERRIDE { 57 // JavaScript context created, add V8 bindings here... 58 } 59 virtual void OnContextReleased(CefRefPtr<CefBrowser> browser, 60 CefRefPtr<CefFrame> frame, 61 CefRefPtr<CefV8Context> context) OVERRIDE { 62 // JavaScript context released, release V8 references here... 63 } 64 virtual bool OnProcessMessageReceived( 65 CefRefPtr<CefBrowser> browser, 66 CefProcessId source_process, 67 CefRefPtr<CefProcessMessage> message) OVERRIDE { 68 // Handle IPC messages from the browser process... 69 } 70 71 IMPLEMENT_REFCOUNTING(MyApp); 72 };
CefClient提供访问Browser实例的回调接口。一个CefClient实现可以在任意数量的Browser进程中共享。以下为几个重要的回调:
CefClient子类的例子:
1 // MyHandler implements CefClient and a number of other interfaces. 2 class MyHandler : public CefClient, 3 public CefContextMenuHandler, 4 public CefDisplayHandler, 5 public CefDownloadHandler, 6 public CefDragHandler, 7 public CefGeolocationHandler, 8 public CefKeyboardHandler, 9 public CefLifeSpanHandler, 10 public CefLoadHandler, 11 public CefRequestHandler { 12 public: 13 MyHandler(); 14 15 // CefClient methods. Important to return |this| for the handler callbacks. 16 virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() OVERRIDE { 17 return this; 18 } 19 virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE { 20 return this; 21 } 22 virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() OVERRIDE { 23 return this; 24 } 25 virtual CefRefPtr<CefDragHandler> GetDragHandler() OVERRIDE { 26 return this; 27 } 28 virtual CefRefPtr<CefGeolocationHandler> GetGeolocationHandler() OVERRIDE { 29 return this; 30 } 31 virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() OVERRIDE { 32 return this; 33 } 34 virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE { 35 return this; 36 } 37 virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE { 38 return this; 39 } 40 virtual CefRefPtr<CefRequestHandler> GetRequestHandler() OVERRIDE { 41 return this; 42 } 43 virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, 44 CefProcessId source_process, 45 CefRefPtr<CefProcessMessage> message) 46 OVERRIDE { 47 // Handle IPC messages from the render process... 48 } 49 50 // CefContextMenuHandler methods 51 virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, 52 CefRefPtr<CefFrame> frame, 53 CefRefPtr<CefContextMenuParams> params, 54 CefRefPtr<CefMenuModel> model) OVERRIDE { 55 // Customize the context menu... 56 } 57 virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser, 58 CefRefPtr<CefFrame> frame, 59 CefRefPtr<CefContextMenuParams> params, 60 int command_id, 61 EventFlags event_flags) OVERRIDE { 62 // Handle a context menu command... 63 } 64 65 // CefDisplayHandler methods 66 virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser, 67 bool isLoading, 68 bool canGoBack, 69 bool canGoForward) OVERRIDE { 70 // Update UI for browser state... 71 } 72 virtual void OnAddressChange(CefRefPtr<CefBrowser> browser, 73 CefRefPtr<CefFrame> frame, 74 const CefString& url) OVERRIDE { 75 // Update the URL in the address bar... 76 } 77 virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, 78 const CefString& title) OVERRIDE { 79 // Update the browser window title... 80 } 81 virtual bool OnConsoleMessage(CefRefPtr<CefBrowser> browser, 82 const CefString& message, 83 const CefString& source, 84 int line) OVERRIDE { 85 // Log a console message... 86 } 87 88 // CefDownloadHandler methods 89 virtual void OnBeforeDownload( 90 CefRefPtr<CefBrowser> browser, 91 CefRefPtr<CefDownloadItem> download_item, 92 const CefString& suggested_name, 93 CefRefPtr<CefBeforeDownloadCallback> callback) OVERRIDE { 94 // Specify a file path or cancel the download... 95 } 96 virtual void OnDownloadUpdated( 97 CefRefPtr<CefBrowser> browser, 98 CefRefPtr<CefDownloadItem> download_item, 99 CefRefPtr<CefDownloadItemCallback> callback) OVERRIDE { 100 // Update the download status... 101 } 102 103 // CefDragHandler methods 104 virtual bool OnDragEnter(CefRefPtr<CefBrowser> browser, 105 CefRefPtr<CefDragData> dragData, 106 DragOperationsMask mask) OVERRIDE { 107 // Allow or deny drag events... 108 } 109 110 // CefGeolocationHandler methods 111 virtual void OnRequestGeolocationPermission( 112 CefRefPtr<CefBrowser> browser, 113 const CefString& requesting_url, 114 int request_id, 115 CefRefPtr<CefGeolocationCallback> callback) OVERRIDE { 116 // Allow or deny geolocation API access... 117 } 118 119 // CefKeyboardHandler methods 120 virtual bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser, 121 const CefKeyEvent& event, 122 CefEventHandle os_event, 123 bool* is_keyboard_shortcut) OVERRIDE { 124 // Perform custom handling of key events... 125 } 126 127 // CefLifeSpanHandler methods 128 virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser, 129 CefRefPtr<CefFrame> frame, 130 const CefString& target_url, 131 const CefString& target_frame_name, 132 const CefPopupFeatures& popupFeatures, 133 CefWindowInfo& windowInfo, 134 CefRefPtr<CefClient>& client, 135 CefBrowserSettings& settings, 136 bool* no_javascript_access) OVERRIDE { 137 // Allow or block popup windows, customize popup window creation... 138 } 139 virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE { 140 // Browser window created successfully... 141 } 142 virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE { 143 // Allow or block browser window close... 144 } 145 virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE { 146 // Browser window is closed, perform cleanup... 147 } 148 149 // CefLoadHandler methods 150 virtual void OnLoadStart(CefRefPtr<CefBrowser> browser, 151 CefRefPtr<CefFrame> frame) OVERRIDE { 152 // A frame has started loading content... 153 } 154 virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser, 155 CefRefPtr<CefFrame> frame, 156 int httpStatusCode) OVERRIDE { 157 // A frame has finished loading content... 158 } 159 virtual void OnLoadError(CefRefPtr<CefBrowser> browser, 160 CefRefPtr<CefFrame> frame, 161 ErrorCode errorCode, 162 const CefString& errorText, 163 const CefString& failedUrl) OVERRIDE { 164 // A frame has failed to load content... 165 } 166 virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, 167 TerminationStatus status) OVERRIDE { 168 // A render process has crashed... 169 } 170 171 // CefRequestHandler methods 172 virtual CefRefPtr<CefResourceHandler> GetResourceHandler( 173 CefRefPtr<CefBrowser> browser, 174 CefRefPtr<CefFrame> frame, 175 CefRefPtr<CefRequest> request) OVERRIDE { 176 // Optionally intercept resource requests... 177 } 178 virtual bool OnQuotaRequest(CefRefPtr<CefBrowser> browser, 179 const CefString& origin_url, 180 int64 new_size, 181 CefRefPtr<CefQuotaCallback> callback) OVERRIDE { 182 // Allow or block quota requests... 183 } 184 virtual void OnProtocolExecution(CefRefPtr<CefBrowser> browser, 185 const CefString& url, 186 bool& allow_os_execution) OVERRIDE { 187 // Handle execution of external protocols... 188 } 189 190 IMPLEMENT_REFCOUNTING(MyHandler); 191 };
Browser生命周期从执行 CefBrowserHost::CreateBrowser() 或者 CefBrowserHost::CreateBrowserSync() 开始。可以在CefBrowserProcessHandler::OnContextInitialized() 回调或者特殊平台例如windows的WM_CREATE 中方便的执行业务逻辑。
// Information about the window that will be created including parenting, size, etc. // The definition of this structure is platform-specific.
1 // 定义的结构体与平台相关 2 3 CefWindowInfo info; 4 // On Windows for example... 5 info.SetAsChild(parent_hwnd, client_rect); 6 7 // Customize this structure to control browser behavior. 8 CefBrowserSettings settings; 9 10 // CefClient implementation. 11 CefRefPtr<MyClient> client(new MyClient); 12 13 // Create the browser asynchronously. Initially loads the Google URL. 14 CefBrowserHost::CreateBrowser(info, client.get(), “http://www.google.com”, settings); 15 16 The CefLifeSpanHandler class provides the callbacks necessary for managing browser life span. Below is an extract of the relevant methods and members. 17 18 CefLifeSpanHandler 类提供管理 Browser生命周期必需的回调。以下为相关方法和成员。 19 20 class MyClient : public CefClient, 21 public CefLifeSpanHandler, 22 ... { 23 // CefClient methods. 24 virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE { 25 return this; 26 } 27 28 // CefLifeSpanHandler methods. 29 void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE; 30 bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE; 31 void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE; 32 33 // Member accessors. 34 CefRefPtr<CefBrowser> GetBrower() { return m_Browser; } 35 bool IsClosing() { return m_bIsClosing; } 36 37 private: 38 CefRefPtr<CefBrowser> m_Browser; 39 int m_BrowserId; 40 int m_BrowserCount; 41 bool m_bIsClosing; 42 43 IMPLEMENT_REFCOUNTING(MyHandler); 44 IMPLEMENT_LOCKING(MyHandler); 45 };
当Browser对象创建后OnAfterCreated() 方法立即执行。宿主程序可以用这个方法来保持对Browser对象的引用。
1 void MyClient::OnAfterCreated(CefRefPtr<CefBrowser> browser) { 2 // Must be executed on the UI thread. 3 REQUIRE_UI_THREAD(); 4 // Protect data members from access on multiple threads. 5 AutoLock lock_scope(this); 6 7 if (!m_Browser.get()) { 8 // Keep a reference to the main browser. 9 m_Browser = browser; 10 m_BrowserId = browser->GetIdentifier(); 11 } 12 13 // Keep track of how many browsers currently exist. 14 m_BrowserCount++; 15 }
执行CefBrowserHost::CloseBrowser()销毁Browser对象。
// Notify the browser window that we would like to close it. This will result in a call to // MyHandler::DoClose() if the JavaScript ‘onbeforeunload‘ event handler allows it. browser->GetHost()->CloseBrowser(false);
Browser对象的关闭事件来源于他的父窗口的关闭方法(比如,在父窗口上点击X控钮。)。父窗口需要调用 CloseBrowser(false) 并且等待操作系统的第二个关闭事件来决定是否允许关闭。如果在JavaScript ‘onbeforeunload‘事件处理或者 DoClose()回调中取消了关闭操作,则操作系统的第二个关闭事件可能不会发送。注意一下面示例中对IsCloseing()的判断-它在第一个关闭事件中返回false,在第二个关闭事件中返回true(当 DoCloase 被调用后)。
Windows平台下,在父窗口的WndProc里处理WM_ClOSE消息:
1 case WM_CLOSE: 2 if (g_handler.get() && !g_handler->IsClosing()) { 3 CefRefPtr<CefBrowser> browser = g_handler->GetBrowser(); 4 if (browser.get()) { 5 // Notify the browser window that we would like to close it. This will result in a call to 6 // MyHandler::DoClose() if the JavaScript ‘onbeforeunload‘ event handler allows it. 7 browser->GetHost()->CloseBrowser(false); 8 9 // Cancel the close. 10 return 0; 11 } 12 } 13 14 // Allow the close. 15 break; 16 17 case WM_DESTROY: 18 // Quitting CEF is handled in MyHandler::OnBeforeClose(). 19 return 0; 20 }
Linux平台下,处理delete_event
信号:
1 gboolean delete_event(GtkWidget* widget, GdkEvent* event, 2 GtkWindow* window) { 3 if (g_handler.get() && !g_handler->IsClosing()) { 4 CefRefPtr<CefBrowser> browser = g_handler->GetBrowser(); 5 if (browser.get()) { 6 // Notify the browser window that we would like to close it. This will result in a call to 7 // MyHandler::DoClose() if the JavaScript ‘onbeforeunload‘ event handler allows it. 8 browser->GetHost()->CloseBrowser(false); 9 10 // Cancel the close. 11 return TRUE; 12 } 13 } 14 15 // Allow the close. 16 return FALSE; 17 }
MacOS X平台下,处理windowShouldClose选择器:
1 // Called when the window is about to close. Perform the self-destruction 2 // sequence by getting rid of the window. By returning YES, we allow the window 3 // to be removed from the screen. 4 - (BOOL)windowShouldClose:(id)window { 5 if (g_handler.get() && !g_handler->IsClosing()) { 6 CefRefPtr<CefBrowser> browser = g_handler->GetBrowser(); 7 if (browser.get()) { 8 // Notify the browser window that we would like to close it. This will result in a call to 9 // MyHandler::DoClose() if the JavaScript ‘onbeforeunload‘ event handler allows it. 10 browser->GetHost()->CloseBrowser(false); 11 12 // Cancel the close. 13 return NO; 14 } 15 } 16 17 // Try to make the window go away. 18 [window autorelease]; 19 20 // Clean ourselves up after clearing the stack of anything that might have the 21 // window on it. 22 [self performSelectorOnMainThread:@selector(cleanup:) 23 withObject:window 24 waitUntilDone:NO]; 25 26 // Allow the close. 27 return YES; 28 }
DoClose方法设置m_blsClosing 标志位为true,并返回false以再次发送操作系统的关闭事件。
1 bool MyClient::DoClose(CefRefPtr<CefBrowser> browser) { 2 // Must be executed on the UI thread. 3 REQUIRE_UI_THREAD(); 4 // Protect data members from access on multiple threads. 5 AutoLock lock_scope(this); 6 7 // Closing the main window requires special handling. See the DoClose() 8 // documentation in the CEF header for a detailed description of this 9 // process. 10 if (m_BrowserId == browser->GetIdentifier()) { 11 // Notify the browser that the parent window is about to close. 12 browser->GetHost()->ParentWindowWillClose(); 13 14 // Set a flag to indicate that the window close should be allowed. 15 m_bIsClosing = true; 16 } 17 18 // Allow the close. For windowed browsers this will result in the OS close 19 // event being sent. 20 return false; 21 }
当操作系统捕捉到第二次关闭事件,它才会允许父窗口真正关闭。该动作会先触发OnBeforeClose()回调,请在该回调里释放所有对浏览器对象的引用。
1 void MyHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) { 2 // Must be executed on the UI thread. 3 REQUIRE_UI_THREAD(); 4 // Protect data members from access on multiple threads. 5 AutoLock lock_scope(this); 6 7 if (m_BrowserId == browser->GetIdentifier()) { 8 // Free the browser pointer so that the browser can be destroyed. 9 m_Browser = NULL; 10 } 11 12 if (--m_BrowserCount == 0) { 13 // All browser windows have closed. Quit the application message loop. 14 CefQuitMessageLoop(); 15 } 16 }
完整的流程,请参考cefclient例子里对不同平台的处理。
在离屏渲染模式下,CEF不会创建原生浏览器窗口。CEF为宿主程序提供无效的区域和像素缓存区,而宿主程序负责通知鼠标键盘以及焦点事件给CEF。离屏渲染目前不支持混合加速,所以性能上可能无法和非离屏渲染相比。离屏浏览器将收到和窗口浏览器同样的事件通知,例如前一节介绍的生命周期事件。下面介绍如何使用离屏渲染:
使用命令行参数--off-screen-rendering-enabled
运行cefclient,可以测试离屏渲染的效果。
任务(Task)可以通过CefPostTask在一个进程内的不同的线程之间投递。CefPostTask有一系列的重载方法,详细内容请参考cef_task.h头文件。任务将会在被投递线程的消息循环里异步执行。例如,为了在UI线程上执行MyObject::MyMethod方法,并传递两个参数,代码如下:
CefPostTask(TID_UI, NewCefRunnableMethod(object, &MyObject::MyMethod, param1, param2));
为了在IO线程在执行MyFunction方法,同时传递两个参数,代码如下:
CefPostTask(TID_IO, NewCefRunnableFunction(MyFunction, param1, param2));
参考cef_runnable.h头文件以了解更多关于NewCefRunnable模板方法的细节。
如果宿主程序需要保留一个运行循环的引用,则可以使用CefTaskRunner类。例如,获取UI线程的任务运行器(task runner),代码如下:
CefRefPtr<CefTaskRunner> task_runner = CefTaskRunner::GetForThread(TID_UI);
由于CEF3运行在多进程环境下,所以需要提供一个进程间通信机制。CefBrowser和CefFrame对象在Borwser和Render进程里都有代理对象。CefBrowser和CefFrame对象都有一个唯一ID值绑定,便于在两个进程间定位匹配的代理对象。
为了给所有的Render进程提供一样的启动信息,请在Browser进程实现CefBrowserProcessHander::OnRenderProcessThreadCreated()方法。在这里传入的信息会在Render进程的CefRenderProcessHandler::OnRenderThreadCreated()方法里接受。
在进程生命周期内,任何时候你都可以通过CefProcessMessage类传递进程间消息。这些信息和特定的CefBrowser实例绑定在一起,用户可以通过CefBrowser::SendProcessMessage()方法发送。进程间消息可以包含任意的状态信息,用户可以通过CefProcessMessage::GetArgumentList()获取。
// Create the message object. CefRefPtr<CefProcessMessage> msg= CefProcessMessage::Create(“my_message”); // Retrieve the argument list object. CefRefPtr<CefListValue> args = msg>GetArgumentList(); // Populate the argument values. args->SetString(0, “my string”); args->SetInt(0, 10); // Send the process message to the render process. // Use PID_BROWSER instead when sending a message to the browser process. browser->SendProcessMessage(PID_RENDERER, msg);
一个从Browser进程发送到Render进程的消息将会在CefRenderProcessHandler::OnProcessMessageReceived()方法里被接收。一个从Render进程发送到Browser进程的消息将会在CefClient::OnProcessMessageReceived()方法里被接收。
bool MyHandler::OnProcessMessageReceived( CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) { // Check the message name. const std::string& message_name = message->GetName(); if (message_name == “my_message”) { // Handle the message here... return true; } return false; }
我们可以调用CefFrame::GerIdentifier()获取CefFrame的ID,并通过进程间消息发送给另一个进程,然后在接收端通过CefBrowser::GetFrame()找到对应的CefFrame。通过这种方式可以将进程间消息和特定的CefFrame联系在一起。
// Helper macros for splitting and combining the int64 frame ID value. #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);
JavaScript被集成在Render进程,但是需要频繁和Browser进程交互。 JavaScript API应该被设计成可使用闭包异步执行。
从1574版本开始,CEF提供了在Render进程执行的JavaScript和在Browser进程执行的C++代码之间同步通信的转发器。应用程序通过C++回调函数(OnBeforeBrowse, OnProcessMessageRecieved, OnContextCreated等)传递数据。Render进程支持通用的JavaScript回调函数注册机制,Browser进程则支持应用程序注册特定的Handler进行处理。
下面的代码示例在JavaScript端扩展window对象,添加cefQuery函数:
// Create and send a new query. var request_id = window.cefQuery({ request: ‘my_request‘, persistent: false, onSuccess: function(response) {}, onFailure: function(error_code, error_message) {} }); // Optionally cancel the query. window.cefQueryCancel(request_id);
对应的C++ Handler代码如下:
1 class Callback : public CefBase { 2 public: 3 /// 4 // Notify the associated JavaScript onSuccess callback that the query has 5 // completed successfully with the specified |response|. 6 /// 7 virtual void Success(const CefString& response) =0; 8 9 /// 10 // Notify the associated JavaScript onFailure callback that the query has 11 // failed with the specified |error_code| and |error_message|. 12 /// 13 virtual void Failure(int error_code, const CefString& error_message) =0; 14 }; 15 16 class Handler { 17 public: 18 /// 19 // Executed when a new query is received. |query_id| uniquely identifies the 20 // query for the life span of the router. Return true to handle the query 21 // or false to propagate the query to other registered handlers, if any. If 22 // no handlers return true from this method then the query will be 23 // automatically canceled with an error code of -1 delivered to the 24 // JavaScript onFailure callback. If this method returns true then a 25 // Callback method must be executed either in this method or asynchronously 26 // to complete the query. 27 /// 28 virtual bool OnQuery(CefRefPtr<CefBrowser> browser, 29 CefRefPtr<CefFrame> frame, 30 int64 query_id, 31 const CefString& request, 32 bool persistent, 33 CefRefPtr<Callback> callback) { 34 return false; 35 } 36 37 /// 38 // Executed when a query has been canceled either explicitly using the 39 // JavaScript cancel function or implicitly due to browser destruction, 40 // navigation or renderer process termination. It will only be called for 41 // the single handler that returned true from OnQuery for the same 42 // |query_id|. No references to the associated Callback object should be 43 // kept after this method is called, nor should any Callback methods be 44 // executed. 45 /// 46 virtual void OnQueryCanceled(CefRefPtr<CefBrowser> browser, 47 CefRefPtr<CefFrame> frame, 48 int64 query_id) {} 49 };
完整的用法请参考wrapper/cef_message_router.h
一个CEF应用程序也可以提供自己的异步JavaScript绑定。典型的实现如下:
// In JavaScript register the callback function. app.setMessageCallback(‘binding_test‘, function(name, args) { document.getElementById(‘result‘).value = "Response: "+args[0]; });
// Map of message callbacks. typedef std::map<std::pair<std::string, int>, std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value> > > CallbackMap; CallbackMap callback_map_; // In the CefV8Handler::Execute implementation for “setMessageCallback”. if (arguments.size() == 2 && arguments[0]->IsString() && arguments[1]->IsFunction()) { std::string message_name = arguments[0]->GetStringValue(); CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext(); int browser_id = context->GetBrowser()->GetIdentifier(); callback_map_.insert( std::make_pair(std::make_pair(message_name, browser_id), std::make_pair(context, arguments[1]))); }
Render进程发送异步进程间通信到Browser进程。
Browser进程接收到进程间消息,并处理。
Browser进程处理完毕后,发送一个异步进程间消息给Render进程,返回结果。
Render进程接收到进程间消息,则调用最开始保存的JavaScript注册的回调函数处理之。
// Execute the registered JavaScript callback if any. if (!callback_map_.empty()) { const CefString& message_name = message->GetName(); CallbackMap::const_iterator it = callback_map_.find( std::make_pair(message_name.ToString(), browser->GetIdentifier())); if (it != callback_map_.end()) { // Keep a local reference to the objects. The callback may remove itself // from the callback map. CefRefPtr<CefV8Context> context = it->second.first; CefRefPtr<CefV8Value> callback = it->second.second; // Enter the context. context->Enter(); CefV8ValueList arguments; // First argument is the message name. arguments.push_back(CefV8Value::CreateString(message_name)); // Second argument is the list of message arguments. CefRefPtr<CefListValue> list = message->GetArgumentList(); CefRefPtr<CefV8Value> args = CefV8Value::CreateArray(list->GetSize()); SetList(list, args); // Helper function to convert CefListValue to CefV8Value. arguments.push_back(args); // Execute the callback. CefRefPtr<CefV8Value> retval = callback->ExecuteFunction(NULL, arguments); if (retval.get()) { if (retval->IsBool()) handled = retval->GetBoolValue(); } // Exit the context. context->Exit(); } }
void MyHandler::OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) { // Remove any JavaScript callbacks registered for the context that has been released. if (!callback_map_.empty()) { CallbackMap::iterator it = callback_map_.begin(); for (; it != callback_map_.end();) { if (it->second.first->IsSame(context)) callback_map_.erase(it++); else ++it; } } }
某些特殊场景下,也许会需要在Browser进程和Render进程做进程间同步通信。这应该被尽可能避免,因为这会对Render进程的性能造成负面影响。然而如果你一定要做进程间同步通信,可以考虑使用XMLHttpRequest,XMLHttpRequest在等待Browser进程的网络响应的时候会等待。Browser进程可以通过自定义scheme Handler或者网络交互处理XMLHttpRequest。更多细节,请参考Network Layer一节。
默认情况下,CEF3的网络请求会被宿主程序手工处理。然而CEF3也暴露了一系列网络相关的函数用以处理网络请求。
网络相关的回调函数可在不同线程被调用,因此要注意相关文档的说明,并对自己的数据进行线程安全保护。
通过CefFrame::LoadURL()方法可简单加载一个url:
browser->GetMainFrame()->LoadURL(some_url);
如果希望发送更复杂的请求,则可以调用CefFrame::LoadRequest()方法。该方法接受一个CefRequest对象作为唯一的参数。
// Create a CefRequest object. CefRefPtr<CefRequest> request = CefRequest::Create(); // Set the request URL. request->SetURL(some_url); // Set the request method. Supported methods include GET, POST, HEAD, DELETE and PUT. request->SetMethod(“POST”); // Optionally specify custom headers. CefRequest::HeaderMap headerMap; headerMap.insert( std::make_pair("X-My-Header", "My Header Value")); request->SetHeaderMap(headerMap); // Optionally specify upload content. // The default “Content-Type” header value is "application/x-www-form-urlencoded". // Set “Content-Type” via the HeaderMap if a different value is desired. const std::string& upload_data = “arg1=val1&arg2=val2”; CefRefPtr<CefPostData> postData = CefPostData::Create(); CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create(); element->SetToBytes(upload_data.size(), upload_data.c_str()); postData->AddElement(element); request->SetPostData(postData);
应用程序可以通过CefURLRequest类发送和浏览器无关的网络请求。并实现CefURLRequestClient接口处理响应。CefURLRequest可以在Browser和Render进程被使用。
class MyRequestClient : public CefURLRequestClient { public: MyRequestClient() : upload_total_(0), download_total_(0) {} virtual void OnRequestComplete(CefRefPtr<CefURLRequest> request) OVERRIDE { CefURLRequest::Status status = request->GetRequestStatus(); CefURLRequest::ErrorCode error_code = request->GetRequestError(); CefRefPtr<CefResponse> response = request->GetResponse(); // Do something with the response... } virtual void OnUploadProgress(CefRefPtr<CefURLRequest> request, uint64 current, uint64 total) OVERRIDE { upload_total_ = total; } virtual void OnDownloadProgress(CefRefPtr<CefURLRequest> request, uint64 current, uint64 total) OVERRIDE { download_total_ = total; } virtual void OnDownloadData(CefRefPtr<CefURLRequest> request, const void* data, size_t data_length) OVERRIDE { download_data_ += std::string(static_cast<const char*>(data), data_length); } private: uint64 upload_total_; uint64 download_total_; std::string download_data_; private: IMPLEMENT_REFCOUNTING(MyRequestClient); };
下面的代码发送一个请求:
// Set up the CefRequest object. CefRefPtr<CefRequest> request = CefRequest::Create(); // Populate |request| as shown above... // Create the client instance. CefRefPtr<MyRequestClient> client = new MyRequestClient(); // Start the request. MyRequestClient callbacks will be executed asynchronously. CefRefPtr<CefURLRequest> url_request = CefURLRequest::Create(request, client.get()); // To cancel the request: url_request->Cancel();
可以通过CefRequest::SetFlags定制请求的行为,这些标志位包括:
例如,为了跳过缓存并不报告下载数据,代码如下:
request->SetFlags(UR_FLAG_SKIP_CACHE | UR_FLAG_NO_DOWNLOAD_DATA);
CEF3 支持两种方式处理网络请求。一种是实现scheme Handler,这种方式允许为特定的(sheme+domain)请求注册特定的请求响应。另一种是请求拦截,允许处理任意的网络请求。
注册自定义scheme(有别于HTTP
,HTTPS
等)可以让CEF按希望的方式处理请求。例如,如果你希望特定的shceme被当策划那个HTTP一样处理,则应该注册一个standard
的scheme。如果你的自定义shceme可被跨域执行,则应该考虑使用使用HTTP scheme代替自定义scheme以避免潜在问题。如果你希望使用自定义scheme,实现CefApp::OnRegisterCustomSchemes回调。
void MyApp::OnRegisterCustomSchemes(CefRefPtr<CefSchemeRegistrar> registrar) { // Register "client" as a standard scheme. registrar->AddCustomScheme("client", true, false, false); }
通过CefRegisterSchemeHandlerFactory方法注册一个scheme响应,最好在CefBrowserProcessHandler::OnContextInitialized()方法里调用。例如,你可以注册一个"client://myapp/"的请求:
CefRegisterSchemeHandlerFactory("client", “myapp”, new MySchemeHandlerFactory());
scheme Handler类可以被用在内置shcme(HTTP,HTTPS等),也可以被用在自定义scheme上。当使用内置shceme,选择一个对你的应用程序来说唯一的域名。实现CefSchemeHandlerFactory和CefResoureHandler类去处理请求并返回响应数据。可以参考cefclient/sheme_test.h的例子。
1 // Implementation of the factory for creating client request handlers. 2 class MySchemeHandlerFactory : public CefSchemeHandlerFactory { 3 public: 4 virtual CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser, 5 CefRefPtr<CefFrame> frame, 6 const CefString& scheme_name, 7 CefRefPtr<CefRequest> request) 8 OVERRIDE { 9 // Return a new resource handler instance to handle the request. 10 return new MyResourceHandler(); 11 } 12 13 IMPLEMENT_REFCOUNTING(MySchemeHandlerFactory); 14 }; 15 16 // Implementation of the resource handler for client requests. 17 class MyResourceHandler : public CefResourceHandler { 18 public: 19 MyResourceHandler() {} 20 21 virtual bool ProcessRequest(CefRefPtr<CefRequest> request, 22 CefRefPtr<CefCallback> callback) 23 OVERRIDE { 24 // Evaluate |request| to determine proper handling... 25 // Execute |callback| once header information is available. 26 // Return true to handle the request. 27 return true; 28 } 29 30 virtual void GetResponseHeaders(CefRefPtr<CefResponse> response, 31 int64& response_length, 32 CefString& redirectUrl) OVERRIDE { 33 // Populate the response headers. 34 response->SetMimeType("text/html"); 35 response->SetStatus(200); 36 37 // Specify the resulting response length. 38 response_length = ...; 39 } 40 41 virtual void Cancel() OVERRIDE { 42 // Cancel the response... 43 } 44 45 virtual bool ReadResponse(void* data_out, 46 int bytes_to_read, 47 int& bytes_read, 48 CefRefPtr<CefCallback> callback) 49 OVERRIDE { 50 // Read up to |bytes_to_read| data into |data_out| and set |bytes_read|. 51 // If data isn‘t immediately available set bytes_read=0 and execute 52 // |callback| asynchronously. 53 // Return true to continue the request or false to complete the request. 54 return …; 55 } 56 57 private: 58 IMPLEMENT_REFCOUNTING(MyResourceHandler); 59 };
如果响应数据类型是已知的,则CefStreamResourceHandler类提供了CefResourceHandler类的默认实现。
// CefStreamResourceHandler is part of the libcef_dll_wrapper project. #include “include/wrapper/cef_stream_resource_handler.h” const std::string& html_content = “<html><body>Hello!</body></html>”; // Create a stream reader for |html_content|. CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData( static_cast<void*>(const_cast<char*>(html_content.c_str())), html_content.size()); // Constructor for HTTP status code 200 and no custom response headers. // There’s also a version of the constructor for custom status code and response headers. return new CefStreamResourceHandler("text/html", stream);
CefRequestHandler::GetResourceHandler()方法支持拦截任意请求。参考client_handler.cpp。
CefRefPtr<CefResourceHandler> MyHandler::GetResourceHandler( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request) { // Evaluate |request| to determine proper handling... if (...) return new MyResourceHandler(); // Return NULL for default handling of the request. return NULL; }
CefRequestHander接口还提供了其他回调函数以定制其他网络相关事件。包括授权、cookie处理、外部协议处理、证书错误等。
CEF3使用类似Google Chrome一样的方式,通过命令行参数传递代理配置。
--proxy-server=host:port Specify the HTTP/SOCKS4/SOCKS5 proxy server to use for requests. An individual proxy server is specified using the format: [<proxy-scheme>://]<proxy-host>[:<proxy-port>] Where <proxy-scheme> is the protocol of the proxy server, and is one of: "http", "socks", "socks4", "socks5". If the <proxy-scheme> is omitted, it defaults to "http". Also note that "socks" is equivalent to "socks5". Examples: --proxy-server="foopy:99" Use the HTTP proxy "foopy:99" to load all URLs. --proxy-server="socks://foobar:1080" Use the SOCKS v5 proxy "foobar:1080" to load all URLs. --proxy-server="sock4://foobar:1080" Use the SOCKS v4 proxy "foobar:1080" to load all URLs. --proxy-server="socks5://foobar:66" Use the SOCKS v5 proxy "foobar:66" to load all URLs. It is also possible to specify a separate proxy server for different URL types, by prefixing the proxy server specifier with a URL specifier: Example: --proxy-server="https=proxy1:80;http=socks4://baz:1080" Load https://* URLs using the HTTP proxy "proxy1:80". And load http://* URLs using the SOCKS v4 proxy "baz:1080". --no-proxy-server Disables the proxy server. --proxy-auto-detect Autodetect proxy configuration. --proxy-pac-url=URL Specify proxy autoconfiguration URL.
如果代理请求授权,CefRequestHandler::GetAuthCredentials()回调会被调用。如果isProxy参数为true,则需要返回用户名和密码。
bool MyHandler::GetAuthCredentials( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, bool isProxy, const CefString& host, int port, const CefString& realm, const CefString& scheme, CefRefPtr<CefAuthCallback> callback) { if (isProxy) { // Provide credentials for the proxy server connection. callback->Continue("myuser", "mypass"); return true; } return false; }
网络内容加载可能会因为代理而有延迟。为了更好的用户体验,可以考虑让你的应用程序先显示一个闪屏,等内容加载好了再通过meta refresh显示真实网页。可以指定--no-proxy-server
禁用代理并做相关测试。代理延迟也可以通过chrome浏览器重现,方式是使用命令行传参:chrome -url=...
.
标签:css 兼容 base via 详细说明 important disable instance upd
原文地址:http://www.cnblogs.com/chechen/p/6104943.html