1.使用CEF(四)— 在QT中集成CEF(1)基本集成
2.探索chrome二进制大小的码版变迁和剪裁chromium的一些思路
3.浅析Selenium-WebDriver
4.å¦ä½å©ç¨CEF3å建ä¸ä¸ªç®åçåºç¨ç¨åº
5.Qt中嵌入web网页的几种实现方式
6.chromium+embedded+framework能卸载吗?
使用CEF(四)— 在QT中集成CEF(1)基本集成
在QT,一个强大的码版跨平台C++开发框架,上构建基于CEF(Chrome Embedded Framework)的码版应用程序,我们之前的码版文章已经展示了基础的样例,但这些示例主要依赖原生或功能有限的码版CEF视图。本文将重新开始,码版dd驱动源码通过VS,码版实现一个结合QT和原生窗体的码版集成项目。环境搭建
本文不依赖QtCreator,码版而是码版使用VS和Qt VS Tools来构建环境,前提是码版你已安装并熟悉QT。安装Qt VS Tools插件
在VS扩展中搜索并安装相应的码版QT插件,安装后别忘了重启VS。码版配置Qt环境
在Extensions - Qt VS Tools - Options中,码版配置Qt-VS编译设置。码版创建Qt项目
在配置完成后,通过VS创建项目,Qt模板将自动出现。我们创建名为QtCefDemo的项目,Qt创建向导随即启动。项目结构
创建完成后,项目的文件结构如下,展示了一个基本的VS IDE下的QT项目。集成CEF环境
为了集成CEF,我们首先需要编译并准备相应的go get 源码头文件、库文件和资源文件,包括libcef_dll_wrapper.lib(MDd和MD版本)。项目目录结构
按照步骤,你会看到项目的文件夹结构,包括CefFiles文件夹及其内部的头文件、库文件和资源。配置manifest文件和VS设置
为CEF项目配置manifest文件,以及头文件和库文件的路径,这里会根据Debug和Release环境自动调整。集成完成后的问题与解决方案
在集成过程中,可能会遇到运行时的库文件问题。需要手动或通过脚本将相关文件复制到输出目录。初始运行可能存在问题,但后续文章将深入探讨解决方法。源码和资源
项目源码可以在我的GitHub仓库找到,链接在此省略。此外,需要下载CefFiles文件夹,可以从网盘获取,链接同样省略。探索chrome二进制大小的变迁和剪裁chromium的一些思路
研究chromium源码的价值不仅在于学习,还在于商业应用,但随着版本升级,cef的android sugar 源码大小从MB增长至MB,对注重安装包大小的开发者来说,寻求减小chromium内核尺寸是一个挑战。本文通过对比历史版本,探究chrome二进制文件的变化,为裁剪chromium提供策略。
首先,对比不同版本chrome的Windows 位安装包,发现从MB增长到MB,我们挑选了变化显著的包进行详细分析(红色箭头标出)。解压后,逐版本对比安装包内的文件大小变化,以及各文件占总大小的百分比变化。
chrome.dll的体积持续增长,占总大小的比例也不断提升,但其他模块总体趋势向小型化发展。在chrome.dll模块分析中,发现至版本,chrome_child.dll的合并抑制了体积增长;至版本,notification_helper.exe等模块的合并导致显著增长。这说明模块合并对整体体积控制有积极作用,但同时也增加了去除特定功能的难度。
特别指出,3D模块的增长显著,删除支持3D相关的html贺卡源码文件可减小MB。snapshot技术优化带来体积减少,部分隐藏在chrome.dll中。资源相关的文件体积明显减小,如icudtl.dat,可通过裁剪减少到几十KB。
关于裁剪思路,虽然chromium编译中间产物有3w多个obj文件,但我们通过分析Top 文件,发现v8和third_party模块的体积较大。通过一级目录聚合,可以看出v8和third_party\blink的体积不容忽视。进一步细分,blink的core和bindings模块对二进制贡献较大,而v8的优化则需更细致的处理。
特别值得关注的是,perfetto的trace_processor模块和pdfium、libjxl、dawn、webrtc等第三方库对体积影响较大。考虑使用V8的V8Lite模式和裁剪jit、wasm模块,能有效减少V8体积。然而,这些基于编译中间产物的模块转化源码分析可能与最终dll大小存在偏差,一般能减小-%的体积。
总的来说,理解chromium源码和运行方式有助于优化,对开发者来说,这是一次从不同角度深入了解chromium的机会。欢迎交流和学习。
浅析Selenium-WebDriver
年,当时在ThoughtWorks工作的Jason Huggins开发了Selenium(Selenium RC)的第一版。年,Google工程师基于Selenium开发了WebDriver。年,Selenium和WebDriver合并,形成了Selenium2(Selenium WebDriver)。目前,Selenium WebDriver的模式已经升级到Selenium4,并有一个支线项目Selenium-Grid,能够与Selenium配合进行多任务运行(主要针对分布式执行,对于当前业务现状,使用到的可能性很小,本文不展开讲解)。
使用现状:虽然无法直接统计出每个公司的使用现状,但我们可以通过搜索趋势来侧面验证。通过Google Trends查询的结果显示,Selenium WebDriver主导的方案占据主流地位,而Selenium RC的方案正在逐步被淘汰。
Selenium RC:
组成部分:Selenium RC主要由客户端和服务器两部分组成。
工作原理:Selenium RC通过发送HTTP请求与服务器进行通信,服务器再将请求转发给浏览器执行。
缺点:Selenium RC的执行速度较慢,且需要各个浏览器厂商提供支持。
Selenium WebDriver:
组成部分:WebDriver主要由WebDriver接口和对应的浏览器驱动程序组成。
Web Driver:WebDriver提供了另一种与浏览器交互的方式,即利用浏览器原生的API,封装成一套面向对象的Selenium WebDriver API,直接操作浏览器页面里的元素,甚至操作浏览器本身(截屏、窗口大小、启动、关闭、安装插件、配置证书等)。由于使用的是浏览器的原生API,速度大大提高,但缺点是需要各个浏览器厂商各自提供。
各种编程语言编写的客户端:向remote server发起请求。
工作原理:底层通信包含以下两个过程:
Selenium -> ChromeDriver server:这个通信过程是基于HTTP协议。
例如,我们要打开一个浏览器页面,并访问www.google.com,先看下Selenium源码是怎么实现这个过程的。
首次建立连接的过程:
选择一个空闲的端口启动chromedriver。
具体发请求的接口:
最终的收口就是_request,发起一个blogs.com/uncleyong/p...
[8] cloud.tencent.com/devel...
[9] einverne.gitbook.io/sel...
å¦ä½å©ç¨CEF3å建ä¸ä¸ªç®åçåºç¨ç¨åº
å¼å§
é¦å ï¼æ ¹æ®èªèº«æ使ç¨çå¼åå¹³å°ï¼å¯ä»¥å» è¿é ä¸è½½å¯¹åºçåå¸çæ¬ãé对è¿ä¸ªæç¨ï¼æ们éè¦ä¸è½½æè æ´æ°ççæ¬ãå½åæ¯æçå¹³å°æWindows, LinuxåMac OS Xãæ¯ä¸ä¸ªçæ¬é½å å«äºå½å¨ç¹å®å¹³å°ä¸ç¼è¯ç¹å®çæ¬CEF3æ¶æéè¦çæææ件åèµæºãæ¨å¯ä»¥éè¿å å«å¨éè¾¹ç REDME.txt æ件æè å¨Wikiä¸GeneralUsage ä¸ç Getting Startedï¼äºè§£æ¯ä¸ªåå¸çæ¬çå ·ä½å 容åç»èã
ç¼è¯åå¸çæ¬ä¸ç项ç®
以CEFåå¸çæ¬ä¸ºåºç¡å¼åçåºç¨ç¨åºå¯ä»¥ä½¿ç¨æ åçå¹³å°ç¼è¯å·¥å ·è¿è¡ç¼è¯æ§è¡ãå æ¬ Windows å¹³å°ä¸ç Visual Studio, Mac OS X å¹³å°ä¸ç Xcode, 以å Linux å¹³å°ä¸ç gcc/makeãé对平å°çä¸åï¼é¡¹ç®çç¼è¯è¿ç¨ä¹æäºè®¸çä¸ååè¦æ±ã
Windows
Windows å¹³å°ä¸ç¼è¯ Cefsimple æ¥éª¤ï¼
1. ç¨å¯¹åºç Visual Studio çæ¬æå¼é¡¹ç®è§£å³æ¹æ¡ã举个ä¾åï¼å¦æä½ å®è£ çæ¯ Visual Studio ï¼ é£ä¹ï¼æå¼çå°±æ¯ cesimple.slnã
2. å¦æä½ ä¸è½½çæ¯ xçæ¬ï¼è¯·ç¡®ä¿ä½ éæ©çæ¯ xçå¼åå¹³å°ã
3. å¼å§ç¼è¯ã
4. å¦æç¼è¯éè¿ï¼é£ä¹ï¼å¨å½å解å³æ¹æ¡çç®å½ä¸ï¼å°åºç°éout/Debugå°ï¼æè éout/Releaseå°ï¼æ件夹ã
5. æ§è¡æä»¶å¤¹ä¸ cefsimple.exe, ç¡®ä¿è½æ£ç¡®è¿è¡ã
å è½½ä¸ä¸ªèªå®ä¹URL
cefsimple项ç®ä¸é»è®¤å è½½çURLæ¯ google.comï¼å½ç¶ï¼ä½ ä¹å¯ä»¥ç¨èªå®ä¹ç URL å»æ¿ä»£å®ï¼ææ¹ä¾¿çå°±æ¯éè¿å½ä»¤è¡æå®ã
# Load the local file éc:\example\example.htmlå°
cefsimple.exe --url=file://c:/example/example.html
é¤äºå½ä»¤è¡çæ¹æ³ï¼ä¹å¯ä»¥éè¿ç´æ¥ä¿®æ¹å¨ cefsimple/simple.cpp æ件ä¸ç代ç ï¼è¾¾å°ä½ çç®çã
# Load the local file éc:\example\example.htmlå°
â¦
if (url.empty())
url = file://c:/example/example.html;
åºç¨ç¨åºç»æ
ææç CEF åºç¨ç¨åºé½æä¸ä¸ä¸»è¦ç»æé¨åï¼
1. CEF çå¨æé¾æ¥åº ã(å¨ Windows å¹³å°ä¸å°±æ¯ libcef.dll)
2. æ¯æåºãï¼ICU, FFMPEGçï¼
3. èµæºã(html/js/css, stringsç)
4. 客æ·ç«¯æ§è¡æ件ã(æ¬æç¨ä¸å°±æ¯ cefsimple.exe.)
è¦ç¹ï¼å¿ çï¼
1. CEF 使ç¨çæ¯å¤è¿ç¨ãåºç¨ç¨åºä¸»è¿ç¨æ¯æµè§å¨è¿ç¨ï¼èå ¶ä»åè¿ç¨æ¯ç± renderer, plugins, GPUçå建ã
2. å¨ Windows å Linux å¹³å°ä¸çæ§è¡æ件å¯ä»¥è¢«ä¸»è¿ç¨ååè¿ç¨ä½¿ç¨ã
3. CEF ä¸ææè¿ç¨é½å¯ä»¥æ¯å¤çº¿ç¨çãCEFæä¾äºè®¸å¤åè½åæ¥å£å¨ä¸åç线ç¨ä¸ä¼ éä»»å¡ã
4. ä¸äºåè°æ¹æ³åå½æ°åªè½å¨ç¹å®çè¿ç¨æè 线ç¨ä¸ä½¿ç¨ãå¨ä½ 第ä¸æ¬¡ä½¿ç¨æ°çåè°æ¹æ³æè å½æ°ä¹åï¼è¯·ç¡®ä¿ä½ å·²ç»é è¯»äº API 头æ件ä¸æºç ï¼ç使ç¨è¦æ±ã
æµç¨åæ
cefsimple åºç¨ç¨åºé¦å åå§åCEFï¼ç¶åå建äºä¸ä¸ªç®åçå¼¹åºæµè§å¨çªå£ãå½å ³éäºææçæµè§å¨çªå£ï¼åºç¨ç¨åºå°±ä¼ç»æãç¨åºæ§è¡æµç¨å¦ä¸ï¼
1. ç³»ç»æ§è¡å ¥å£ç¹å½æ°(main or wWinMain)ï¼å¹¶å建æµè§å¨è¿ç¨ã
2. å ¥å£ç¹å½æ°ï¼
1. å建è½å¤å¤çè¿ç¨çº§å«çåè°æ¹æ³ç SimpleApp å®ä¾ã
2. åå§å CEFï¼è¿å ¥ CEF æ¶æ¯å¾ªç¯ã
3. åå§å CEF ä¹åï¼è°ç¨ SimpleApp::OnContextInitialized() ãè¿ä¸ªæ¹æ³ä¸ï¼
1. å建åä¾ç SimpleHandler ã
2. ç± CefBrowserHost::CreateBrowsersync() æ¹æ³å建ä¸ä¸ªæµè§å¨çªå£ã
4. ææçæµè§å¨å ±äº« SimpleHandler å®ä¾ï¼ æ¤å®ä¾è½å®å¶æµè§å¨è¡ä¸ºãå¤çæµè§å¨ç¸å ³åè°æ¹æ³(life span, loading state, title displayç)ã
5. å½ä¸ä¸ªæµè§å¨çªå£å ³éçæ¶åï¼è°ç¨ SimpleHandler::OnBeforeClose() ãå½ææçæµè§å¨çªå£å ¨é¨å ³éæ¶ï¼OnBeforeClose() å½æ°å°±ä¼æ§è¡è·³åº CEF æ¶æ¯å¾ªç¯çè¡ä¸ºï¼éåºåºç¨ç¨åºã
å ¥å£ç¹å½æ°
ç¨åºçè¿è¡å¼å§äºæµè§å¨è¿ç¨ä¸çå ¥å£ç¹å½æ°ãè¿ä¸ªå½æ°ä¼åå§å CEF 以åææè·æä½ç³»ç»æå ³ç对象ã
Windows
#include <windows.h>
#include "cefsimple/simple_app.h"
#include "include/cef_sandbox_win.h"
// Set to 0 to disable sandbox support.
#define CEF_ENABLE_SANDBOX 1
#if CEF_ENABLE_SANDBOX
// The cef_sandbox.lib static library is currently built with VS. It may not
// link successfully with other VS versions.
#pragma comment(lib, "cef_sandbox.lib")
#endif
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
void* sandbox_info = NULL;
#if CEF_ENABLE_SANDBOX
// Manage the life span of the sandbox information object. This is necessary
// for sandbox support on Windows. See cef_sandbox_win.h for complete details.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
// SimpleApp implements application-level callbacks. It will create the first
// browser instance in OnContextInitialized() after CEF has initialized.
CefRefPtr<SimpleApp> app(new SimpleApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, app.get(), sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
// Specify CEF global settings here.
CefSettings settings;
#if !CEF_ENABLE_SANDBOX
settings.no_sandbox = true;
#endif
// Initialize CEF.
CefInitialize(main_args, settings, app.get(), sandbox_info);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
SimpleApp
SimpleApp è´è´£å¤çè¿ç¨çº§å«çåè°æ¹æ³ãå®ä¼æé²åºä¸äºå¨å¤è¿ç¨ä¸å ±äº«æè 被ç¹å®è¿ç¨ä½¿ç¨çæ¥å£åæ¹æ³ãCefBrowserProcessHandler æ¥å£ï¼å¨æµè§å¨è¿ç¨ä¸è°ç¨ãè¿æä¸ä¸ªè¢«åç¦»åº CefBrowserProcessHandler æ¥å£ï¼ä¾å项ç®æ²¡æå±ç¤ºï¼åªä¼å¨æ¸²æè¿ç¨ä¸è¢«è°ç¨ãç±äº CefBrowserProcessHandler ä¸å å®ç°äº CefApp, åæ¶è¿æ CefBrowserProcessHandlerï¼æ以å®çè¿åå¼å¿ é¡»æ¯[this]ã
// simple_app.h
#include "include/cef_app.h"
class SimpleApp : public CefApp,
public CefBrowserProcessHandler {
public:
SimpleApp();
// CefApp methods:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
OVERRIDE { return this; }
// CefBrowserProcessHandler methods:
virtual void OnContextInitialized() OVERRIDE;
private:
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(SimpleApp);
};
// simple_app.cpp
#include "cefsimple/simple_app.h"
#include <string>
#include "cefsimple/simple_handler.h"
#include "cefsimple/util.h"
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
SimpleApp::SimpleApp() {
}
void SimpleApp::OnContextInitialized() {
REQUIRE_UI_THREAD();
// Information used when creating the native window.
CefWindowInfo window_info;
#if defined(OS_WIN)
// On Windows we need to specify certain flags that will be passed to
// CreateWindowEx().
window_info.SetAsPopup(NULL, "cefsimple");
#endif
// SimpleHandler implements browser-level callbacks.
CefRefPtr<SimpleHandler> handler(new SimpleHandler());
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
std::string url;
// Check if a "--url=" value was provided via the command-line. If so, use
// that instead of the default URL.
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::GetGlobalCommandLine();
url = command_line->GetSwitchValue("url");
if (url.empty())
url = "";
// Create the first browser window.
CefBrowserHost::CreateBrowserSync(window_info, handler.get(), url,
browser_settings, NULL);
}
SimpleHandler
SimpleHandler è´è´£å¤çæµè§å¨çº§å«çåè°æ¹æ³ãè¿äºåè°æ¹æ³ä¼å¨æµè§å¨è¿ç¨ä¸æ§è¡ãå¨è¿ä¸ªé¡¹ç®ä¸ï¼é对ææçæµè§å¨ä½¿ç¨ç¸åç CefClient å®ä¾ï¼ä½æ¯å¦æä½ æ¿æï¼å¯ä»¥å¨èªå·±çåºç¨ç¨åºä¸ä½¿ç¨ä¸åç CefClientå®ä¾çã
// simple_handler.h
#include "include/cef_client.h"
#include <list>
class SimpleHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler {
public:
SimpleHandler();
~SimpleHandler();
// Provide access to the single global instance of this object.
static SimpleHandler* GetInstance();
// CefClient methods:
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE {
return this;
}
// CefDisplayHandler methods:
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) OVERRIDE;
// CefLifeSpanHandler methods:
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
// CefLoadHandler methods:
virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) OVERRIDE;
// Request that all existing browser windows close.
void CloseAllBrowsers(bool force_close);
private:
// List of existing browser windows. Only accessed on the CEF UI thread.
typedef std::list<CefRefPtr<CefBrowser> > BrowserList;
BrowserList browser_list_;
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(SimpleHandler);
};
// simple_handler.cpp
#include "cefsimple/simple_handler.h"
#include <sstream>
#include <string>
#include "cefsimple/util.h"
#include "include/cef_app.h"
#include "include/cef_runnable.h"
namespace {
SimpleHandler* g_instance = NULL;
Qt中嵌入web网页的几种实现方式
Web网页的界面交互相较于Qt客户端拥有较大优势,能提供丰富且便捷的用户体验,使得在客户端中嵌入web网页成为可能。这能结合web的优势与客户端特性,丰富界面功能。本文将介绍几种Qt客户端中嵌入web网页的实现方式及步骤。基于Qt自带控件实现
Qt内置的控件用于访问网页,集成webkit内核与google引擎,类似简易版浏览器。此方案在Qt5.9.6中采用QWebEngineView。环境配置:Qt5.9.6 + VS
检查webenginewidgets模块是否配置成功。
开发流程:通过拖拽控件或直接new使用,代码示例与效果展示。
总结:实现简单,但浏览器内核不支持视频解码,需编译源码解决,成本较高。
嵌入Chrome.exe进程实现
此方法启动本地Chrome浏览器,实现与本地浏览器功能等同。通过启动浏览器进程并传入参数,再获取窗口句柄,实现嵌入。环境配置:Qt5.9.6 + VS
检测Chrome安装情况,获取Chrome.exe路径。
开发流程:启动Chrome.exe,获取窗口句柄,转化为QWindow,嵌入界面。
总结:功能完整,但浏览器状态与客户端状态耦合,用户行为影响嵌入界面显示。
基于Chrome的CEF3实现
Chromium Embedded Framework(CEF)提供嵌入式浏览器支持。此方案需下载编译包,参照官方文档,实现嵌入网页界面。环境配置:Qt5.9.6 + VS
下载并配置CEF与chromium源码。
开发流程:参考示例文档,配置工程,实现嵌入。
总结:功能完整,但受限于源码编译环境,实际使用效果受限。
基于微软的WebView2实现
Microsoft Edge WebView2 控件允许嵌入Web技术(HTML、CSS、JavaScript)到本机应用程序。此方案通过读取进程窗口句柄实现嵌入。环境配置:Qt5.9.6 + VS,安装WebView2运行包。
开发流程:下载示例文档,配置NuGet包,启动WebView2进程,读取窗口句柄,嵌入界面。
总结:实现简单,功能与用户体验较好,但无法国产化。
总结
根据需求选择不同方案:仅显示网页时,推荐基于Qt自带控件;需视频播放时,考虑基于CEF的实现;受限环境,可选用WebView2。嵌入Chrome.exe方案存在不可控因素,不适合作为常规方案。通过比较不同方法的优缺点,可选最优方案满足需求。chromium+embedded+framework能卸载吗?
Chromium Embedded Framework(CEF)本身是一个开源的框架,可以通过GitHub等开源平台获取其源代码。由于其是一个框架,因此不是一个独立的应用程序,而是被其他应用程序(例如Web浏览器)所使用。因此,CEF本身无法被卸载。
如果您想卸载使用CEF构建的应用程序,可以通过以下方式进行:
在操作系统中找到该应用程序的卸载程序,一般可以在“控制面板”或者应用程序的安装目录中找到。运行卸载程序后,该应用程序及其使用的CEF框架将被卸载。
如果无法通过卸载程序进行卸载,可以尝试使用第三方卸载工具,例如Revo Uninstaller等。
需要注意的是,在卸载使用CEF构建的应用程序之前,请确保该应用程序中的所有数据已经备份或者转移至其他地方,以免造成数据丢失。