从CEF3官方Demo源码分析并实现第一个Qt版的SimpleCef

本文详细解读了CEF3的多进程架构,展示了如何在Qt环境下实现首个简单的Cef应用,涉及进程、线程、引用计数及进程间通信。通过CefApp和CefClient接口定制,教你搭建独立的渲染进程和浏览器进程结构。

从CEF3官方Demo源码分析并实现第一个Qt版的SimpleCef

首先需要知道几个重要的概念

  • 进程(Processes)

CEF3是多进程架构的。Browser被定义为主进程,负责窗口管理,界面绘制和网络交互。Blink的渲染和Js的执行被放在一个独立的Render 进程中;除此之外,Render进程还负责Js Binding和对Dom节点的访问。 默认的进程模型中,会为每个标签页创建一个新的Render进程。其他进程按需创建,例如管理插件的进程以及处理合成加速的进程等都是按需创建。

默认情况下,主应用程序会被多次启动运行各自独立的进程。这是通过传递不同的命令行参数给CefExecuteProcess函数做到的。如果主应用程序很大,加载时间比较长,或者不能在非浏览器进程里使用,则宿主程序可使用独立的可执行文件去运行这些进程。这可以通过配置CefSettings.browser_subprocess_path变量做到。

  • 线程(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函数组支持简易的线程间异步消息传递。

  • 引用计数(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 implementation.
};

// References a MyClass instance
CefRefPtr<MyClass> my_class = new MyClass();

多进程架构

  • 背景

在某种程度上,web浏览器当前状态就像一个与过去的多任务操作系统合作的单独的用户。正如在一个这样的操作系统中的错误程序会让整个系统挂掉,所以一个错误的web页面也可以让一个现代浏览器挂掉。仅仅需要一个浏览器或插件的bug,就能让整个浏览器和所有正在运行的标签页停止运行。

现代操作系统更加鲁棒,因为他们把应用程序分成了彼此隔离的独立线程。一个程序中的crash通常不会影响其他程序或整个操作系统,每个用户对用户数据的访问也是有限制的。

我们为浏览器的标签页使用独立的进程,以此保护整个应用程序免受渲染引擎中的bug和故障的伤害。我们也会限制每个渲染引擎进程的相互访问,以及他们与系统其他部分的访问。某些程度上,这为web浏览提供了内存保护,为操作系统提供了访问控制。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zuf2XwVI-1626783337517)(Qt%E5%B5%8C%E5%85%A5%E6%B5%8F%E8%A7%88%E5%99%A8.assets/arch.png)]

  • CEF结构 (网页嵌入应用程序代码构成)

每个CEF3应用程序都是相同的结构

  • 提供入口函数,用于初始化CEF、运行子进程执行逻辑或者CEF消息循环。

  • 提供CefApp实现,用于处理进程相关的回调。

  • 提供CefClient实现,用于处理Browser实例相关的回调。

  • 执行CefBrowserHost::CreateBrowser()创建一个Browser实例,使用CefLifeSpanHandler管理Browser对象生命周期。

  • CEF进程和窗口之间的结构关系

    CEF3使用多个进程运行。处理窗口创建、绘制和网络访问的主要进程称为浏览器进程。这通常与宿主应用程序的进程相同,大多数应用程序的逻辑将在浏览器进程中运行。使用Blink引擎渲染HTML和JavaScript执行在单独的渲染进程中发生。一些应用程序逻辑(如JavaScript绑定和DOM访问)也将在渲染进程中运行。默认进程模型将为每个唯一源地址(scheme+domain)运行一个新的渲染进程。其他进程将根据需要生成,例如处理Flash等插件的插件进程和处理加速合成的GPU进程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PpXhnSQU-1626783337520)(file://D:\Framework\Qt+Cef\doc\WBCefView%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%B7%AF.assets\v2-b5b4346429dad6c78a01dc16d774b499_720w.jpg?lastModify=1626780285)]

  • Renderer进程的实现结构

renderer程序继承CefApp和CefRenderProcessHandler类,在main函数中初始化。通过CefSettings.browser_subprocess_path配置render可执行程序路径。browser进程就会去启动这个进程去渲染网页。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCgCfXS2-1626783337521)(file://D:\Framework\Qt+Cef\doc\WBCefView%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%B7%AF.assets\v2-87cb5b9eb0ee8b6a7438394d80ef09e5_r.jpg?lastModify=1626780298)]

  • browser进程的实现结构

browserapp要继承CefApp和CefBrowserProcessHandler类。实现browserapp的定义。同时要新建clienthandler类实现图中的回调函数接口类,用来处理拦截响应请求、管理生命周期、下载、显示加载、右键菜单等。在mian函数中初始化、启动消息循环。调用CefBrowserHost的静态方法创建browser窗口对象,在render进程的Frame中加载渲染内容。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cmAyHUjr-1626783337524)(file://D:\Framework\Qt+Cef\doc\WBCefView%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%B7%AF.assets\v2-06ae6746434179a9cf12e53174bf44ea_720w.jpg?lastModify=1626780310)]

入口函数

#include "widget.h"
#include "simple_app.h"
#include "simple_handler.h"

#include <QApplication>

void QCefInitSettings(CefSettings & settings)
{
   
   
    //设置缓存地址
//    std::string cache_path = "WebullView.cache";
//    CefString(&settings.cache_path) = CefString(cache_path);

    //开启多线程消息循环后 浏览器进程的UI线程是另外的线程 就不要CefRunMessageLoop()来阻塞浏览器的UI线程
    settings.multi_threaded_message_loop = true;
    //日志设置
    settings.log_severity = LOGSEVERITY_DISABLE;

    //单一进程模型仅调试使用,
    //线程模型建议使用默认的,默认的进程模型(多进程)中,会为每个标签页创建一个新的Render进程。
    //settings.single_process = true;

    //不启用沙盒
    settings.no_sandbox = true;

}

int QCefInit(int& argc, char** argv)
{
   
   
    HINSTANCE hInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));

    CefMainArgs mainArgs(hInstance);
    CefRefPtr<SimpleApp> app(new SimpleApp); //CefApp实现,用于处理进程相关的回调。

    CefSettings settings;
    QCefInitSettings(settings);

    //单一执行体
    int exit_code = CefExecuteProcess(mainArgs, app.get(), nullptr);
    if (exit_code >= 0) {
   
   
        return exit_code;
    }



    CefInitialize(mainArgs, settings, app, nullptr);

    return -1;
}

void CefQuit()
{
   
   
    CefShutdown();
}

int main(int argc, char *argv[])
{
   
   
    QApplication a(argc, argv);
    int result = 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZLOZL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值