这是一个翻译文档,持续更新中
这是一个翻译文档,持续更新中
CEF General Usage(CEF3预览)
介绍
CEF全称Chromium Embedded Framework,是一个基于Google Chromium 的开源项
目。Google Chromium项目主要是为Google Chrome应用开发的,而CEF的目标则是为第
三方应用提供可嵌入浏览器支持。CEF隔离底层Chromium和Blink的复杂代码,并提供一套产
品级稳定的API,发布跟踪具体Chromium版本的分支,以及二进制包。CEF的大部分特性都提
供了丰富的默认实现,让使用者做尽量少的定制即可满足需求。在本文发布的时候,世界上已经
有很多公司和机构采用CEF,CEF的安装量超过了100万。[CEF wikipedia]页面上有使用CEF
的公司和机构的不完全的列表。CEF的典型应用场景包括:
嵌入一个兼容HTML5的浏览器控件到一个已经存在的本地应用。
创建一个轻量化的壳浏览器,用以托管主要用Web技术开发的应用。
有些应用有独立的绘制框架,使用CEF对Web内容做离线渲染。
使用CEF做自动化Web测试。
CEF3是基于Chomuim Content API多进程构架的下一代CEF,拥有下列优势:
改进的性能和稳定性(JavaScript和插件在一个独立的进程内执行)。
支持Retina显示器。
支持WebGL和3D CSS的GPU加速。
类似WebRTC和语音输入这样的前卫特性。
通过DevTools远程调试协议以及ChromeDriver2提供更好的自动化UI测试。
更快获得当前以及未来的Web特性和标准的能力。
本文档介绍CEF3开发中涉及到的一般概念。
开始
使用二进制包
从源码编译(Building from Source Code)
示例应用程序(Sample Application)
重要概念(Important Concepts)
C++ 封装(C++ Wrapper)
进程(Processes)
线程(Threads)
引用计数(Reference Counting)
字符串(Strings)
命令行参数(Command Line Arguments)
应用程序布局(Application Layout)
应用程序结构(Application Structure)
Windows操作系统(Windows)
Linux操作系统(Linux)
Mac X平台(Mac OS X)
单一执行体(Single Executable)
分离子进程执行体(Separate Sub-Process Executable)
集成消息循环(Message Loop Integration)
CefSettings
CefBrowser和CefFrame
CefApp
CefClient
Browser生命周期(Browser Life Span)
离屏渲染(Off-Screen Rendering)
投递任务(Posting Tasks)
进程间通信(Inter-Process Communication (IPC))
处理启动消息(Process Startup Messages)
处理运行时消息(Process Runtime Messages)
异步JavaScript绑定(Asynchronous JavaScript Bindings)
通用消息转发(Generic Message Router)
自定义实现(Custom Implementation)
同步请求(Synchronous Requests)
网络层(Network Layer)
自定义请求(Custom Requests)
浏览器无关请求(Browser-Independent Requests)
请求响应(Request Handling)
Scheme响应(Scheme Handler)
请求拦截(Request Interception)
其他回调(Other Callbacks)
Proxy Resolution
使用二进制包
CEF3的二进制包可以在这个页面下载。其中包含了在特定平台(Windows,Mac OS X 以及
Linux)编译特定版本CEF3所需的全部文件。不同平台拥有共同的结构:
cefclient
Debug
include
libcef_dll
Release
Resources
tools
每个二进制包包含一个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二进制包创建简单应用程序的细节。
从源码编译(Building from Source Code)
CEF可以从源码编译,用户可以使用本地编译系统或者像TeamCity这样的自动化编译系统编
译。首先你需要使用svn或者git下载Chromium和CEF的源码。由于Chromium源码很大,只
建议在内存大于4GB的现代机器上编译。编译Chromium和CEF的细节请参
考BranchesAndBuilding页面。
示例应用程序(Sample Application)
cefclient是一个完整的CEF客户端应用程序示例,并且它的源码包含在CEF每个二进制发布包
中。使用CEF创建一个新的应用程序,最简单的方法是先从cefclient应用程序开始,删除你不
需要的部分。本文档中许多示例都是来源于cefclient应用程序。
重要概念(Important Concepts)
在开发基于CEF3的应用程序前,有一些重要的基础概念应该被理解。
C++ 封装(C++ Wrapper)
libcef 动态链接库导出 C API 使得使用者不用关心CEF运行库和基础代码。
libcef_dll_wrapper 工程把 C API 封装成 C++ API同时包含在客户端应用程序工程中,与
cefclient一样,源代码作为CEF二进制发布包的一部分共同发布。C/C++ API的转换层代码是
由转换工具自动生成。UsingTheCAPI 页面描述了如何使用C API。
进程(Processes)
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。
线程(Threads)
在CEF3中,每个进程都会运行多个线程。完整的线程类型表请参照cef_thread_id_t。例如,
在Browser进程中包含如下主要的线程:
TID_UI 线程是浏览器的主线程。如果应用程序在调用调用CefInitialize()时,传递
CefSettings.multi_threaded_message_loop=false,这个线程也是应用程序的主线
程。
TID_IO 线程主要负责处理IPC消息以及网络通信。
TID_FILE 线程负责与文件系统交互。
由于CEF采用多线程架构,有必要使用锁和闭包来保证数据的线程安全语义。
IMPLEMENT_LOCKING定义提供了Lock()和Unlock()方法以及AutoLock对象来保证不同代码
块同步访问数据。CefPostTask函数组支持简易的线程间异步消息传递。更多信息,请参
考Posting Tasks章节。
可以通过CefCurrentlyOn()方法判断当前所在的线程环境,cefclient工程使用下面的定义来确
保方法在期望的线程中被执行。
#define REQUIRE_UI_THREAD() ASSERT(CefCurrentlyOn(TID_UI));
#define REQUIRE_IO_THREAD() ASSERT(CefCurrentlyOn(TID_IO));
#define REQUIRE_FILE_THREAD() ASSERT(CefCurrentlyOn(TID_FILE));
引用计数(Reference Counting)
所有的框架类从CefBase继承,实例指针由CefRefPtr管理,CefRefPtr通过调用AddRef()和
Release()方法自动管理引用计数。框架类的实现方式如下:
class MyClass : public CefBase {
public:
// Various class methods here...
private:
// Various class members here...
IMPLEMENT_REFCOUNTING(MyClass); // Provides atomic refcounting implementati
on.
};
// References a MyClass instance
CefRefPtr my_class = new MyClass();
字符串(Strings)
CEF为字符串定义了自己的数据结构。主要是出于以下原因:
- libcef包和宿主程序可能使用不同的运行时,对堆管理的方式也不同。所有的对象,包括字符
串,需要确保和申请堆内存使用相同的运行时环境。
- libcef包可以编译为支持不同的字符串类型(UTF8,UTF16以及WIDE)。默认采用的是
UTF16,默认字符集可以通过更改cef_string.h文件中的定义,然后重新编译来修改。当使用宽
字节集的时候,切记字符的长度由当前使用的平台决定。
UTF16字符串结构体示例如下:
typedef struct _cef_string_utf16_t {
char16* str; // Pointer to the string
size_t length; // String length
void (*dtor)(char16* str); // Destructor for freeing the string on the corr
ect heap
} cef_string_utf16_t;
通过typedef来设置常用的字符编码。
typedef char16 cef_char_t;
typedef cef_string_utf16_t cef_string_t;
CEF提供了一批C语言的方法来操作字符串(通过#define的方式来适应不同的字符编码)
cef_string_set 对制定的字符串变量赋值(支持深拷贝或浅拷贝)。
cef_string_clear 清空字符串。
cef_string_cmp 比较两个字符串。
CEF也提供了字符串不同编码之间相互转换的方法。具体函数列表请查阅cef_string.h和
cef_string_types.h文件。
在C++中,通常使用CefString类来管理CEF的字符串。CefString支持与std::string(UTF8)、
std::wstring(wide)类型的相互转换。也可以用来包裹一个cef_string_t结构来对其进行赋值。
和std::string的相互转换:
std::string str = “Some UTF8 string”;
// Equivalent ways of assigning |str| to |cef_str|. Conversion from UTF8 will
occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromString(str);
// Equivalent ways of assigning |cef_str| to |str|. Conversion to UTF8 will oc
cur if necessary.
str = cef_str;
str = cef_str.ToString();
和std::wstring的相互转换:
std::wstring str = “Some wide string”;
// Equivalent ways of assigning |str| to |cef_str|. Conversion from wide will
occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromWString(str);
// Equivalent ways of assigning |cef_str| to |str|. Conversion to wide will oc
cur if necessary.
str = cef_str;
str = cef_str.ToWString();
如果是ASCII编码,使用FromASCII进行赋值:
const char* cstr = “Some ASCII string”;
CefString cef_str;
cef_str.FromASCII(cstr);
一些结构体(比如CefSettings)含有cef_string_t类型的成员,CefString支持直接赋值给这些成
员。
CefSettings settings;
const char* path = “/path/to/log.txt”;
// Equivalent assignments.
CefString(&settings.log_file).FromASCII(path);
cef_string_from_ascii(path, strlen(path), &settings.log_file);
命令行参数(Command Line Arguments)
在CEF3和Chromium中许多特性可以使用命令行参数进行配置。这些参数采
用 --some-argument[=optional-param] 形式,并通过CefExecuteProcess()和CefMainArgs
结构(参考下面的应用资源布局章节)传递给CEF。在传递CefSettings结构给CefInitialize()
之前,我们可以设置CefSettings.command_line_args_disabled为true来禁用对命令行参数
的处理。如果想指定命令行参数传入主应用程序,实现
CefApp::OnBeforeCommandLineProcessing()方法。更多关于如何查找已支持的命令行选
项的信息,请查看client_switches.cpp文件的注释。
应用程序布局(Application Layout)
应用资源布局依赖于平台,有很大的不同。比如,在Mac OS X上,你的资源布局必须遵循特定
的app bundles结构;Window与Linux则更灵活,允许你定制CEF库文件与资源文件所在的位
置。为了获取到特定可以正常工作的示例,你可以从工程的下载页面下载到一个client压缩包。
每个平台对应的README.txt文件详细说明了哪些文件是可选的,哪些文件是必须的。
Windows操作系统(Windows)
在Windows平台上,默认的资源布局将libcef库文件、相关资源与可执行文件放置在同级目
录,文件夹结构大致如下:
Application/
cefclient.exe <= cefclient application executable
libcef.dll <= main CEF library
icudt.dll <= ICU unicode support library
ffmpegsumo.dll <= HTML5 audio/video support library
libEGL.dll, libGLESv2.dll, … <= accelerated compositing support libraries
cef.pak, devtools_resources.pak <= non-localized resources and strings
locales/
en-US.pak, … <= locale-specific resources and strings
使用结构体CefSettings可以定制CEF库文件、资源文件的位置(查看README.txt文件或者本
文中CefSettings部分获取更详细的信息)。虽然在Windows平台上,cefclient项目将资源文
件以二进制形式编译进cefclient.rc文件,但是改为从文件系统加载资源也很容易。
Linux操作系统(Linux)
在Linux平台上,默认的资源布局将libcef库文件、相关资源与可执行文件放置在同级目录。注
意:在你编译的版本与发行版本应用程序中,libcef.so的位置是有差异的,此文件的位置取决
于编译可执行程序时,编译器rpath的值。比如,编译选项为“-Wl,-rpath,.”(“.”意思是当前文
件夹),这样libcef.so与可执行文件处于同级目录。libcef.so文件的路径可以通过环境变量中
的“ LD_LIBRARY_PATH ”指定。
Application/
cefclient <= cefclient application executable
libcef.so <= main CEF library
ffmpegsumo.so <-- HTML5 audio/video support library
cef.pak, devtools_resources.pak <= non-localized resources and strings
locales/
en-US.pak, … <= locale-specific resources and strings
files/
binding.html, … <= cefclient application resources
使用结构体CefSettings可以定制CEF库文件、资源文件(查看README.txt文件或者本文中
CefSettings部分获取更详细的信息)。
Mac X平台(Mac OS X)
在Mac X平台上,app bundles委托给了Chromium实现,因此不是很灵活。文件夹结构大致
如下:
cefclient.app/
Contents/
Frameworks/
Chromium Embedded Framework.framework/
Libraries/
ffmpegsumo.so <= HTML5 audio/video support library
libcef.dylib <= main CEF library
Resources/
cef.pak, devtools_resources.pak <= non-localized resources
and strings
*.png, *.tiff <= Blink image and cursor resources
en.lproj/, … <= locale-specific resources and strings
libplugin_carbon_interpose.dylib <= plugin support library
cefclient Helper.app/
Contents/
Info.plist
MacOS/
cefclient Helper <= helper executable
Pkginfo
cefclient Helper EH.app/
Contents/
Info.plist
MacOS/
cefclient Helper EH <= helper executable
Pkginfo
cefclient Helper NP.app/
Contents/
Info.plist
MacOS/
cefclient Helper NP <= helper executable
Pkginfo
Info.plist
MacOS/
cefclient <= cefclient application executable
Pkginfo
Resources/
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。