初识CEF框架基础

1,803 阅读5分钟

CEF 的进程模型

CEF 使用的是 chromium 内核,下图是chromium浏览器的进程模型。方框代表进程,连接线代表IPC进程间通信。

chromium浏览器的进程模型

进程类型描述
Browser 进程宿主进程,有且只有1个,负责处理窗口的创建、绘制、网络交互等绝大多数主要逻辑
Render 进程网页渲染进程,blink 渲染和执行 js 代码(blink 是谷歌的排版引擎)
模型会为每个唯一源(协议+域)创建至少一个 render 进程
1、如果打开了多个子窗口,访问相同的源,它们会共享 render 进程
2、如果每个子窗口访问的源都不一样,则会为每个源都创建一个 render 进程
3、如果窗口中改变了访问源,则进程模型会将原有 render 进程杀掉,创建新的 render 进程
GPU 加速进程按需创建,最多只有1个,只有 GPU 硬件加速打开的时候才被创建
NPAPI 插件进程按需创建,每种类型的插件只会有一个进程,每个插件进程可以被多个 Render 进程共享
Pepper 插件进程同 NPAPI 插件进程,不同的是为 Pepper 插件而创建的进程
其它进程进程模型会根据需要创建其它的进程,我们不太需要关注这些进程

CEF 的使用中,我们主要实现 Browser 进程和 Render 进程。其它进程可以交由框架去处理。

CEF 的多线程模型

为了保证用户的的高响应度,CEF采用了多线程模型,保证程序的实时响应,如下图

CEF多线程模型

1、Broswer 进程收到用户请求,首先由UI线程处理,将相应的任务转交给 IO 线程,IO线程处理后将任务传递给 Render 进程。 2、Render 进程的 IO 线程经过简单的解释后交给渲染线程,渲染线程接收请求加载网页并渲染,渲染完成后再给由 IO 线程通知 Browser 进程。 3、Browser 进程将收到结果并将结果绘制出来。

CEF C++类的组织构架

先看几个关键类的层次结构:

CEF 类层次关系图

  • CefV8Context 类: V8 引擎上下文,用于执行 JS 代码,和维护 JS 环境中的所有资源
  • CefFrame 类: 表示浏览器窗口中的一个 frame,HTML 页面可以由很多 frame 组成,每个 frame 都有自己的 URL 或者一段 HTML 代码,可通过该类获取页面的源码、文本、URL、V8 执行上下文、访问页面中的 DOM等。
  • CefBrowser 类: 该类代表一个浏览器对象,管理浏览器的 CefFrame 列表、前进、后退、刷新等。
  • CefClient 类: 响应页面的各种事件,如 CefBrowser 生命周期回调、右键菜单、对话框、状态通知显示、下载事件、拖曳事件、焦点事件、键盘事件,离屏渲染事件等
  • CefBrowserHost 类: 表示一个 CefBrowser 所处的进程,所以它可以被 browser 进程中的任意线程调用,但绝不能在其他进程中使用。相比较于 CefBrowser 类,CefBrowserHost 提供了更多功能接口,如窗体句柄、窗体事件、DevTools、文件对话框等。
  • CefBrowserView 类: 表示一个视图,视图需要吸附在一个窗体 (CefWindow) 上。
  • CefWindow 类: 表示一个窗体控件,可以控制窗体的显示与隐藏、大小、全屏等。一个窗体可以添加多个 CefBrowserView 视图。

C++ 调用 JS

由 CEF 类层次关系图可以看出来,C++ 层想要执行 JS 代码,需要在V8引擎的上下文中执行。 CefFrame 层封装了一个接口ExecuteJavaScript,用于执行 JS 代码,示例如下:

CefRefPtr<CefBrowser> browser = ...;
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('Hello World')", ...);

ExecuteJavaScript 缺点:函数返回值为空,函数中也没有其他形式获取 JS 的返回值

JS 调用 C++

C++ 想被 JS 层调用,必须将自己映射到 JS 环境中,这里只涉及到一个类:CefV8Value,如下图所示: CefV8Value

CefV8Value可以理解成 Qt 的 QVariant、boost 中的boost::any。可以将 C++ 任意类型数据赋值给它。 JS 调用 C++ 的本质,就是 C++ 将自己的打包成 CefV8Value中,并注册到 JS 环境中。这样一来,JS 层就可以直接对其进行读写调用了。

示例1:绑定 C++ 变量到 JS 层的window 对象上

void CefRenderProcessHandler::OnContextCreated(
    CefRefPtr<CefBrowser> browser,  
    CefRefPtr<CefFrame> frame,  
    CefRefPtr<CefV8Context> context)
{
  CefRefPtr<CefV8Value> object = context->GetGlobal();// 获取到js的window对象句柄
  CefRefPtr<CefV8Value> str = CefV8Value::CreateString("Hello World");
  object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
}

示例2:感知 C++ 变量被 JS 读写

class MyV8Accessor : public CefV8Accessor {  
public:
  MyV8Accessor() {}  
  virtual bool Get(const CefString& name,  
                   const CefRefPtr<CefV8Value> object,  
                   CefRefPtr<CefV8Value>& retval,  
                   CefString& exception)
  {
      retval = CefV8Value::CreateString(m_myval);  
      return true;
  }

  virtual bool Set(const CefString& name,  
                   const CefRefPtr<CefV8Value> object,  
                   const CefRefPtr<CefV8Value> value,  
                   CefString& exception)
  {
      if (value.IsString()) {
        m_myval = value.GetStringValue();
      } else {
        exception = "Invalid value type";
      }
      return true;
    }
  }

private:
  CefString m_myval;  
};

CefRefPtr<CefV8Accessor> accessor = new MyV8Accessor();
CefRefPtr<CefV8Value> obj = CefV8Value::CreateObject(accessor);

示例3:绑定 C++ 函数到 JS 层

class MyV8Handler : public CefV8Handler {  
public:
  virtual bool Execute(const CefString& name,  
                       CefRefPtr<CefV8Value> object,  
                       const CefV8ValueList& arguments,  
                       CefRefPtr<CefV8Value>& retval,  
                       CefString& exception)
{
      retval = CefV8Value::CreateString("My Value!");
      return true;
};

CefRefPtr<CefV8Handler> hander = new MyV8Handler();
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("myfunc", handler);

示例4(不推荐):将预定义的代码,注册到 JS 环境中(优先考虑直接将代码写在 JS 里)

void MyRenderProcessHandler::OnWebKitInitialized() {  
    std::string extensionCode =   
    "var test;"  
    "if (!test)"  
    "  test = {};"  
    "(function() {"  
    "  test.myval = 'My Value!';"  
    "})();";  
  
  CefRegisterExtension("v8/test", extensionCode, NULL);  
}  

CEF 库文件说明

  • libcef.dll: CEF核心库
  • chrome_elf.dll: 崩溃报告
  • icudtl.dat: Unicode支持数据
  • snapshot_blob.bin: V8快照数据

下方文件,负责HTML5加速,如2D画布,3D CSS和WebGL等:

  • d3dcompiler_47.dll
  • libEGL.dll
  • libGLESv2.dll

下方文件包含CEF,Chromium和Blink使用的非本地化资源。没有这些文件,任何Web组件可能会显示不正确。

  • resources.pak
  • chrome_100_percent.pak
  • chrome_200_percent.pak