CEF架构
1、CEF架构的概述
1.1、背景:
CEF:chromium embedded Framework是由Marshall Greenblatt在08年成立的一个开源项目,目的是开发一个基于Google Chromium项目的Web browser控制器。CEF目前支持大多数编程语言和操作系统,并可以轻松的整合新的和已存在的应用。设计的目的也是为了效率/性能和方便使用。基础的框架包括了借助原生库的C/C++接口,这样将主机的应用与chromium和WebKit隔离开来。它提供了浏览器控制和主机应用程序(包括支持自定义插件、协议、JavaScript对象和JavaScript扩展)之间紧密的集成。主机应用程序可以有选择地控制资源加载、导航、菜单、打印等,当利用了相同的性能和Google Chrome浏览器具备的HTML5技术。
1.2、依赖/相关:
CEF项目依靠一堆其它由第三方维护的项目,主要又以下:
- Chromium: 基础,网络堆栈,线程,消息机制,log,进程控制,生成Web browser。
- WebKit: 提供DOM解析,布局,事件处理,渲染,HTML5JS的API。
- **V8: ** JS引擎。 Skia:2D图形库。
- Angle: 3D图形转换,和DirectX有关。
1.3、版本
3个版本:
- CEF1: 单进程工具调用chromium Webkit API 。
- CEF2: 多进程工具建立在Chromium browser 。
- CEF3: ** 多进程工具调用Chromium**。
1.4、Content API
通用API用法:
所有版本的CEF都公开一个简单好用的API(让用户从Chromium和Webkit复杂的代码中分隔开来),详细如下:
- 调用**CefInitialize()**对CEF初始化。
- 调用CefRunMessageLoop() or **CefDoMessageLoopWork()**来在UI消息上处理事务。
- 调用CreateBrowser() or CreateBrowserSync()并传递一个CefClient事例来创造一个browser窗口。
- 在程序退出前调用**CefShutdown()**来关闭CEF。
1.5、CEF1
CEF1使用单进程架构,并直接将Chromium和Webkit整合到客户端应用程序中。单进程的优点包括了建设内存的使用和进一步与客户端应用的耦合。缺点有某些类型的加载内容性能低,和由于同进程中运行Flash插件的崩溃问题。
CEF1 API用法:
-
CefApp,此接口用来传递到CefInitialize(),和允许应用程序定制全局,如资源加载,代
理。 -
CefClient,此接口用来传递到CefCreateBrowser() or CefCreateBrowserSync(),和充当单独CEF Browser事例和客户端应用程序的连接,也负责请求和显示处理的接口 。
-
CefBrowser,公开由浏览器提供的功能。包括前进后退导航,来源检索,加载请求等,一个CefBrowser可能有一到多个子类的CefFrame对象
线程注意事项 。 -
CEF1包括UI,IO和FILE线程。 UI线程创建Browser窗口,用来所有与WebKit和V8的交互,IO线程用来处理模式和网络请求。 FILE线程用于应用程序缓存和其他各种活动。
-
当使用CEF1时,你应该记住以下线程考虑:
-
不要在UI线程上执行会造成阻塞的操作。这将导致严重的性能问题。
-
如果CefInitialize()通过值为false的CefSettings.multi_threaded_message_loop被调用,那么UI线程将和主线程一样。
-
所有WebKit和V8互为作用必须在UI线程上进行。因此,有些CEF API函数只能在UI线程上调用。有相同限制的功能会被相应地记录并关联到CEF的头文件里。
-
CefPostTask方法可以用来动态同步地进行不同线程里的任务。
-
实施细则 :
-
CEF1主要有以下实现类:
- CefContext 代表全局CEF内容。单个CefContext对象是由**CefInitialize()创建,由CefShutdown()**销毁 。
- CefProcess 由CefContext创建和管理的CEF线程。
- BrowserWebKitInit 管理全局WebKit环境作为Chromium WebKit API暴露。
- WebViewHost 提供本地窗口“wrapper”工具给WebView。这个类扩展了,提供和在某些平台上弹出部件(如选择菜单)一样的功能,WebWidgetHost。
- CefBrowserImpl 实现CefBrowser的接口,创建WebViewHost,并提供了但个Browser实例的粘合代码。
- BrowserWebViewDelegate 实现WebKit接口,提供CefBrowserImpl和底层的WebView之间
的通信。
-
-
-
1.6、CEF2已经被取消了
1.7、 CEF3
CEF3借助Chromium Content API使用和Chromium Web 浏览器一样的多进程架构,相比于单进程的CEF1,他具有更多的优势。
- 同时支持单进程和多进程模式。
- 更多的代码分析通过Chromium浏览器 。
- 改进的性能和由于代码路径的支持而减少的损坏 .
- 新特性的快速获取 。
- 大多数情况下,CEF3和Chromium浏览器拥有相同的性能和稳定特征。
- API用法 :
- CEF3的初始化需要考虑多进程。在Windows和Linux上的所有进程享有相同的可执行文件,或选择使用单独的可执行文件的子进程(借助CefSettings.browser_subprocess_path的值)。在Mac OS-X的所有的子进程都必须绑推一个分开的app使用。这是必要的因为在Mac OS-X的子进程必须有一个不同的Info.plist文件。
- CefExecuteProcess()函数是负责执行子过程的逻辑。它检查“类型”的命令行标志来决定目前正在执行的进程类型。如果被browser进程调用(识别为没有“类型”的命令行值)它会立即返回一个为-1值。如果被调用且认为是次级进程,它会阻塞直到该进程应该退出,然后返回进程的退出代码。如果执行browser进程,应用程序会继续在标准进程初始化,被描述为“通用API的用法”部分。将cef_app.h理解为一个完成的的功能初始化和用法的描述。将Wcefclient_win.cpp wWinMain功能视作一个窗口例程。
- “单进程”命令行标志或者CefSettings.single_process的值可用在一个单一的进程中运行CEF3。这是有用于调试目的,但不建议用于生产。
- 下面是主要CEF3接口和它们的用途的概述:
- CefApp:此接口用来传递到CefInitialize(),和允许应用程序定制全局,如资源加载,代理。一些功能是由所有进程共享的,有些必须实现浏览器的过程中,必须在渲染过程中执行。见详情头文件的意见。
- CefClient:此接口用来传递到CefCreateBrowser() or CefCreateBrowserSync(),和充当单独CEF Browser事例和客户端应用程序的连接,也负责请求和显示处理的接口。请求处理,显示处理等额外的接口,通过这个接口暴露。
- CefBrowser:公开由浏览器提供的功能。包括前进后退导航,来源检索,加载请求等,一个CefBrowser可能有一到多个子类的CefFrame对象。在一个特定的过程或一个特定的线程必须调用一些方法,所以仔细阅读文档。
- CefBrowserHost -:公开有关运行browser进程中唯一可用的browser窗口的功能。例如,检索本地父窗口句柄,或销毁browser窗口。
- CefRenderProcessHandler : 公开WebKit和V8对渲染进程中应用程序的集成能力。通过CefApp返回此对象的一个实例。
- 实施细则CEF3主要有以下实现类:
- CefMainDelegate 实现通用进程的引导逻辑。
- CefContentClient 实现所有进程中共同的Content API回调。
- CefContext 代表全局CEF内容在browser进程中。单个CefContext对象由**CefInitialize()创
建并由CefShutdown()**销毁。 - CefBrowserMainParts 实现browser进程中的引导逻辑。
- CefContentBrowserClient 实现browser进程的ContentAPI回调。
- CefBrowserHostImpl 实现在browser进程中CefBrowser和CefBrowserHost的接口。提供粘合代码和工具的借口来和RenderViewHost通信。
- CefContentRendererClient 实现渲染进程中的ContentAPI回调。
- CefBrowserImpl 实现渲染进程中的CefBrowser接口。提供粘合代码和工具的借口来和RenderView通信。
- 实施细则CEF3主要有以下实现类:
- 下面是主要CEF3接口和它们的用途的概述:
- cef 是一个基于google chromiun的简单的框架。 它主要是作为一个内嵌浏览器嵌入到客户端应用程序中。可以再 http://cefbuilds.com 下载最新的编译版本。
- API用法 :
2、总体框架预览
- CEF 使用了多进程。主进程是“browser”进程。 而子进程是由rederes, plugins, GPU, 等组件创建。
- 在CEF的所有进程中,都可以有多线程。CEF提供了函数和接口在不同的线程中来传递任务。
- 一些回调方法和函数只能在特定线程和进程中使用。在使用API之前请确保仔细阅读注释。
3、源代码
-
cefsimple 工程初始化CEF并创建了一个简单的浏览器窗口。
-
系统在入口点函数中(main或者wWinMain)函数中开启browser进程
-
入口点函数:
-
创建SimpleApp的实例,在这个类中保存process-level callbacks。
-
初始化CEF并开启消息循环。
-
-
当CEF初始化完毕以后, **SimpleApp::OnContextInitialized()**会被调用。在这个方法中:
-
创建一个单例的SimpleHandler。
-
由**CefBrowserHost::CreateBrowserSync()**创建一个浏览器窗口
-
-
所有的浏览器共享同一个在SimpleHandler。SimpleHandler负责定制浏览器的行为并保存browser-related callbacks(loading状态,标题行为等)。
-
当浏览器窗口被关闭的时候, SimpleHandler::OnBeforeClose() 被调用。当所有浏览器窗口被关闭,CEF消息循环退出。
-
-
可以看下如下代码:
int APIENTRY wWinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance , LPTSTR lpCmdLine , int nCmdShow) { CefMainArgs args(hInstance); CefRefPtr<MySimpleApp> app(new MySimpleApp); int exitCode = CefExecuteProcess(args, app, NULL); if(exitCode >= 0) return exitCode; CefSettings settings; CefInitialize(args, settings, app, NULL); CefRunMessageLoop(); CefShutdown(); return 0; }
-
此时进程开启,但是没有任何窗口, 如果我们需要建立窗口需要如下:
CefWindowInfo winInfo; winInfo.SetAsPopup(NULL, "myCefSimple"); CefBrowserSettings browser_settings; CefRefPtr<CefClient> client(new MySimpleClient()); std::string url = "file:///D:/project/github/cptf/resource/binding.html"; CefBrowserHost::CreateBrowser(winInfo , client.get() , url , browser_settings , NULL);
-
这样就显示出来窗口了。但是我们发现在关闭的时候进程没有关掉, 所以我们要做如下动作:
class MySimpleClient : public CefClient , public CefLifeSpanHandler void MySimpleClient::OnBeforeClose(CefRefPtr<CefBrowser> browser) { CEF_REQUIRE_UI_THREAD(); CefQuitMessageLoop(); }
4、总结
- CefApp和CefClient是CEF中最重要的两个类。CefExecuteProcess 和 CefInitialize 是建立CefApp的两个最重要方法。
- 而CreateBrowser 是创建浏览器窗口的最重要方法。
- Cef支持各种语言和多种操作系统。在设计的时候充分考虑了性能和易用性。
- cef核心功能提供了c和c++的接口。
- cef提供了和主程序之间的通信能力(利用 custom plugins, protocols,javascrpit object 和 javascript extensions)。
- 主应用程序可以选择性的使用控制 资源的加载,切换, context menus, printing等。
5、依赖
chromium
webkit
v8
skia
angle
6、线程注意事项
CEF线程有如下几种:
typedef enum {
TID_UI,
TID_DB,
TID_FILE,
TID_FILE_USER_BLOCKING,
TID_PROCESS_LAUNCHER,
TID_CACHE,
TID_IO,
TID_RENDERER,
} cef_thread_id_t;
- 在使用线程的时候需要注意如下几点:
- 千万不要阻塞UI线程。
- UI线程会任务是主线程,当 CefSettings.multi_threaded_message_loop = false的时候。
- 所有的webkit和V8的交互必须用 TID_RENDERER线程。
- CefPostTask 方法可以再不同线程中进行异步调用。
7、接口
- CefApp:此接口用来传递到CefInitialize(),和允许应用程序定制全局,如资源加载,代理。这些功能是由所有进程共享的,有些必须实现浏览器的过程中,必须在渲染过程中执行。见详情头文件的注释。
- CefClient:此接口用来传递到CefCreateBrowser() or CefCreateBrowserSync(),和充当单独CEF Browser事例和客户端应用程序的连接,也负责请求和显示处理的接口。请求处理,显示处理等额外的接口,通过这个接口暴露。
- CefBrowser:公开由浏览器提供的功能。包括前进后退导航,来源检索,加载请求等,一个CefBrowser可能有一到多个子类的CefFrame对象。在一个特定的过程或一个特定的线程必须调用一些方法,所以仔细阅读文档。
- CefBrowserHost :公开有关运行browser进程中唯一可用的browser窗口的功能。例如,检索本地父窗口句柄,或销毁browser窗口。
- CefRenderProcessHandler :公开WebKit和V8对渲染进程中应用程序的集成能力。通过CefApp返回此对象的一个实例。
8、进程注意事项
CEF3使用了很多不同的进程:
- Broser process:这个进程可以认为是应该程序的主进程,当调用**CefInitialize()**的时候建立
- Render process:web容器(webkit和v8)在此进程中执行
- plugin process:插件(如 Flash)
- GPU process:GPU渲染进程
- Utility process: 各种其他任务在这个进程中跑。
-
CefBrowser 和 CefFrame 在browser和render进程中都存在,并且传递一系列callbacks。
-
CefProcessMessage能够在browser进中利用CefBrowser::SendProcessMessage 函数发出, 并且在CefClient::OnProcessMessageRecieved 和CefRenderProcessHandler::OnProcessMessageRecieved 接收。
9、重要的细节
CEF3 有如下几个比较重要的类:
- CefMainDelegate :用于普通进程的逻辑过程
- CefContentClient:在普通进程 展现Content Api的回调。
- CefContext:在browser进程中,展现全局的CEF上下文。 一个单独的CefContext对象时由CefInitialize()建立,有CefShutdown销毁。
- CefBrowserMainParts:browser 进程的逻辑。
- CefContentBrowserClient:在browser 进程展现Content Api的回调。
- CefBrowserHostImpl:是CefBrowserHost 的实现。
- CefContentRendererClient:在render 进程中展现Content Api的回调。
- CefBrowserImpl:CefBrowser的实现者。