一、基础
1、CEF是什么?
全称Chromium Embedded Framework,是为第三方应用提供可嵌入浏览器支持;
Cef是chromium项目的一个分支,抽离webkit和内核层,提供content层上的API接口,把复杂的底层接口进行封装,应用在客户端的内嵌网页;
2、应用场景:
- 嵌入一个兼容HTML5的浏览器控件到一个已经存在的本地应用。如:IDEA Web渲染插件
- 创建一个轻量化的壳浏览器,用以托管主要用Web技术开发的应用。如:electron
- 有些应用有独立的绘制框架,使用CEF对Web内容做离线渲染。
在离屏渲染模式下,CEF不会创建原生浏览器窗口。CEF为宿主程序提供无效的区域和像素缓存区,而宿主程序负责通知鼠标键盘以及焦点事件给CEF。离屏渲染目前不支持混合加速,所以性能上可能无法和非离屏渲染相比。离屏浏览器将收到和窗口浏览器同样的事件通知
- 使用CEF做自动化Web测试。
二、概念
1. 多进程(Processes)
浏览器进程(Browser Process):
- 窗口创建、绘制
- 网络访问
- ......
渲染进程(Renderer Process):
- 通过Blink引擎渲染HTML
- JavaScript执行(V8引擎)
- ......
默认的进程模型中,会为每个标签页创建一个新的Render进程。其他进程按需创建,例如管理插件的进程以及处理合成加速的进程等都是按需创建。
- CEF3的进程之间可以通过IPC进行通信。Browser和Render进程可以通过发送异步消息进行双向通信。甚至在Render进程可以注册在Browser进程响应的异步JavaScript API。 更多细节,请参考Inter-Process Communication一节。
通过设置命令行的
--single-process,CEF3就可以支持用于调试目的的单进程运行模型
2. 多线程
在CEF3中,每个进程都会运行多个线程。完整的线程类型表请参照cef_thread_id_t。例如,在Browser进程中包含如下主要的线程:
- TID_UI 线程是浏览器的主线程。如果应用程序在调用调用CefInitialize()时,传递CefSettings.multi_threaded_message_loop=false,这个线程也是应用程序的主线程。
- TID_IO 线程主要负责处理IPC消息以及网络通信。
- TID_FILE 线程负责与文件系统交互。
3.引用计数(Reference Counting)
所有的框架类从CefBase继承,实例指针由CefRefPtr管理,CefRefPtr通过调用AddRef()和Release()方法自动管理引用计数
4.处理启动消息(Process Startup Messages)
为了给所有的Render进程提供一样的启动信息,请在Browser进程实现CefBrowserProcessHander::OnRenderProcessThreadCreated()方法。在这里传入的信息会在Render进程的CefRenderProcessHandler::OnRenderThreadCreated()方法里接受。
5.处理运行时消息(Process Runtime Messages)
在进程生命周期内,任何时候你都可以通过CefProcessMessage类传递进程间消息。这些信息和特定的CefBrowser实例绑定在一起
- 通过CefBrowser::SendProcessMessage()方法发送。进程间消息可以包含任意的状态信息
- 用户可以通过CefProcessMessage::GetArgumentList()获取。
6.异步JavaScript绑定(Asynchronous JavaScript Bindings)
JavaScript被集成在Render进程,但是需要频繁和Browser进程交互。 JavaScript API应该被设计成可使用闭包异步执行。
CEF提供了在Render进程执行的JavaScript和在Browser进程执行的C++代码之间同步通信的转发器。
- Render进程发送异步进程间通信到Browser进程。
- Browser进程接收到进程间消息,并处理。
- Browser进程处理完毕后,发送一个异步进程间消息给Render进程,返回结果。
- Render进程接收到进程间消息,则调用最开始保存的JavaScript注册的回调函数处理之。
7.网络层(Network Layer)
默认情况下,CEF3的网络请求会被宿主程序手工处理。然而CEF3也暴露了一系列网络相关的函数用以处理网络请求
- 自定义请求 如: 通过CefFrame::LoadURL()方法可简单加载一个url
- 发送浏览器无关请求
- 请求响应
- Scheme响应:允许为特定的(sheme+domain)请求注册特定的请求响应。
- 请求拦截:允许处理任意的网络请求
- 其他回调 CefRequestHander接口还提供了其他回调函数以定制其他网络相关事件。包括授权、cookie处理、外部协议处理、证书错误等。
- Proxy Resolution
CEF3使用类似Google Chrome一样的方式,通过命令行参数传递代理配置。
三. 应用程序结构(Application Structure)
每个CEF3应用程序都是相同的结构
- 提供入口函数,用于初始化CEF、运行子进程执行逻辑或者CEF消息循环。
- 提供CefApp实现,提供了指定进程的回调访问。
- 提供CefClient实现,用于处理Browser实例相关的回调。如渲染进程中发生的各种V8事件、下载事件,显示事件等。
- 执行CefBrowserHost::CreateBrowser()创建一个Browser实例,使用CefLifeSpanHandler管理Browser对象生命周期。
3.1.CefSettings
CefSettings结构体允许定义全局的CEF配置,经常用到的配置项如下:
- single_process 设置为true时,Browser和Renderer使用一个进程。
- browser_subprocess_path 设置用于启动子进程单独执行器的路径。
- cache_path 设置磁盘上用于存放缓存数据的位置。
- locale 此设置项将传递给Blink。如果此项为空,将使用默认值“en-US”。
- log_file 此项设置的文件夹和文件名将用于输出debug日志。
- log_severity 此项设置日志级别。
- resources_dir_path 此项设置资源文件夹的位置。
- locales_dir_path 此项设置locale文件夹位置。
- remote_debugging_port 此项可以设置1024-65535之间的值,用于在指定端口开启远程调试。
3.2 CefBrowser和CefFrame
CefBrowser和CefFrame对象被用来发送命令给浏览器以及在回调函数里获取状态信息。
每个CefBrowser对象包含一个主CefFrame对象,主CefFrame对象代表页面的顶层frame;同时每个CefBrowser对象可以包含零个或多个的CefFrame对象,分别代表不同的子Frame。
例如,一个浏览器加载了两个iframe,则该CefBrowser对象拥有三个CefFrame对象(顶层frame和两个iframe)。
下面的代码在浏览器的主frame里加载一个URL:
browser->GetMainFrame()->LoadURL(some_url);
下面的代码执行浏览器的回退操作:
browser->GoBack();
CefBrowser和CefFrame对象在Browser进程和Render进程都有对等的代理对象。
在Browser进程里,Host(宿主)行为控制可以通过CefBrowser::GetHost()方法控制
3.3.CefApp
浏览器进程和渲染进程中会分别调用GetBrowserProcessHandler和GetRendererProcessHandler获取对应的handler;
class CefBrowserProcessHandler : public virtual CefBaseRefCounted {
public:
// Cookie处理定制化
virtual void GetCookieableSchemes(std::vector<CefString>& schemes,
bool& include_defaults) {}
// 在CEF上下文初始化后,在浏览器进程UI线程中进行调用。
virtual void OnContextInitialized() {}
// 可定制化处理子进程启动时的命令行参数
virtual void OnBeforeChildProcessLaunch(
CefRefPtr<CefCommandLine> command_line) {}
// 打印处理
virtual CefRefPtr<CefPrintHandler> GetPrintHandler() { return nullptr; }
// 自定义消息循环的时候,消息循环的频率
virtual void OnScheduleMessagePumpWork(int64 delay_ms) {}
// 获取默认的CefClient
virtual CefRefPtr<CefClient> GetDefaultClient() { return nullptr; }
};
3.4.CefClient
CefClient提供访问特定Browser实例的回调接口。
在CefClient中各种回调的事件,本质上发生的地方是渲染进程。因为每当一个浏览器实例(不是浏览器进程)创建的时候,会有一个对应的渲染进程创建(也可能由于配置,而共用一个,这里先认为默认多个一对一)。渲染进程中发生的各种V8事件、下载事件,显示事件等触发后,会通过进程间通讯给到浏览器进程,然后在浏览器进程中找到与之相关的CefClient,然后从CefClient中找到对应的Handler,回调Handler对应的方法。
以下为几个重要的回调:
- 比如处理Browser的生命周期,右键菜单,对话框,通知显示, 拖曳事件,焦点事件,键盘事件等等。如果没有对某个特定的处理接口进行实现会造成什么影响,请查看cef_client.h文件中相关说明。
- OnProcessMessageReceived在Browser收到Render进程的消息时被调用。更多细节,请参考Inter-Process Communication一节。
class CefClient : public virtual CefBaseRefCounted {
public:
virtual CefRefPtr<CefAudioHandler> GetAudioHandler() { return nullptr; }
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() {
return nullptr;
}
virtual CefRefPtr<CefDialogHandler> GetDialogHandler() { return nullptr; }
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() { return nullptr; }
virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() { return nullptr; }
virtual CefRefPtr<CefDragHandler> GetDragHandler() { return nullptr; }
// ...... 还有很多的Handler
}
3.5. Browser生命周期(Browser Life Span)
Browser生命周期从执行 CefBrowserHost::CreateBrowser() 或者 CefBrowserHost::CreateBrowserSync() 开始。
- OnAfterCreated 当Browser对象创建后
- DoCloase
- OnBeforeClose
参考: 初识CEF 、 Chromium Embedded Framework、IDEA Web渲染插件开发、cef使用
欢迎关注我的前端自检清单,我和你一起成长