翻譯自CEF官方英文教程绽左,若有疏忽歡迎指正。歡迎評(píng)論區(qū)討論~
介紹
Chromium Embedded Framework (CEF) 是一個(gè)基于Google Chromium的開源項(xiàng)目解孙。與主要專注于 Google Chrome 應(yīng)用程序開發(fā)的 Chromium 項(xiàng)目本身不同,CEF 專注于促進(jìn)第三方應(yīng)用程序中的嵌入式瀏覽器用例抛人。CEF 通過提供穩(wěn)定的 API弛姜、發(fā)布特定 Chromium 版本的代碼分支和二進(jìn)制發(fā)行版,將用戶與底層 Chromium 和 Blink 代碼的復(fù)雜性隔離開來妖枚。CEF 中的大多數(shù)功能都有默認(rèn)實(shí)現(xiàn)廷臼,這些實(shí)現(xiàn)提供豐富的功能,只需要用戶進(jìn)行很少的集成工作。截至本文發(fā)表時(shí)中剩,全球范圍內(nèi)已安裝超過 1 億個(gè) CEF 實(shí)例忌穿,這些實(shí)例嵌入到來自眾多公司和行業(yè)的產(chǎn)品中。使用 CEF 的公司和產(chǎn)品的部分列表可在CEF 維基百科頁面上找到结啼。CEF 的一些產(chǎn)品案例包括:
- 在現(xiàn)有本機(jī)應(yīng)用程序中嵌入符合 HTML5 的 Web 瀏覽器控件掠剑。
- 創(chuàng)建一個(gè)輕量級(jí)本機(jī)“外殼”應(yīng)用程序,該應(yīng)用程序托管主要使用 Web 技術(shù)開發(fā)的用戶界面郊愧。
- 在具有自己的自定義繪圖框架的應(yīng)用程序中“離屏”呈現(xiàn) Web 內(nèi)容朴译。
- 充當(dāng)對(duì)現(xiàn)有 Web 屬性和應(yīng)用程序進(jìn)行自動(dòng)測(cè)試的主機(jī)。
CEF3 是基于多進(jìn)程Chromium Content API的下一代 CEF 属铁。CEF3 的多進(jìn)程架構(gòu)的優(yōu)勢(shì)包括:
- 改進(jìn)的性能和穩(wěn)定性(JavaScript 和插件在單獨(dú)的進(jìn)程中運(yùn)行)眠寿。
- 支持 Retina 顯示屏。
- WebGL 和 3D CSS 的 GPU 加速焦蘑。
- 很酷的新功能盯拱,如 WebRTC(網(wǎng)絡(luò)攝像頭支持)和語音輸入。
- 通過 DevTools 遠(yuǎn)程調(diào)試協(xié)議和ChromeDriver2更好地自動(dòng)化 UI 測(cè)試例嘱。
- 更快地使用當(dāng)前和未來的 Web 功能和標(biāo)準(zhǔn)狡逢。
本文檔介紹了使用 CEF3 開發(fā)應(yīng)用程序時(shí)涉及的一般概念。
入門
使用二進(jìn)制發(fā)行版(二進(jìn)制庫(kù))
CEF3 的二進(jìn)制庫(kù)可從項(xiàng)目下載頁面獲得拼卵。它們包含在特定平臺(tái)(Windows奢浑、MacOS 或 Linux)上構(gòu)建特定版本的 CEF3 所需的所有文件。有關(guān)如何使用 CEF3 二進(jìn)制發(fā)行版創(chuàng)建簡(jiǎn)單應(yīng)用程序的詳細(xì)說明腋腮,請(qǐng)參閱Tutorial Wiki 頁面雀彼。
無論平臺(tái)如何,所有二進(jìn)制發(fā)行版都共享相同的通用結(jié)構(gòu):
- CMakeLists.txt提供CMake 配置即寡,用于構(gòu)建包含在二進(jìn)制發(fā)行版中的測(cè)試應(yīng)用程序徊哑。特定于平臺(tái)的構(gòu)建說明在此文件頂部作為注釋提供。
- Debug包含調(diào)試構(gòu)建 CEF 共享庫(kù) (libcef) 和在平臺(tái)上運(yùn)行所需的任何其他庫(kù)嘿悬。
- include包含所有必需的 CEF 頭文件实柠。
- libcef_dll包含所有使用 CEF C++ API 的應(yīng)用程序都必須鏈接的 libcef_dll_wrapper 靜態(tài)庫(kù)的源代碼。有關(guān)詳細(xì)信息善涨,請(qǐng)參閱“C++ Wrapper ”(C++包裝器)部分窒盐。
- Release包含 CEF 共享庫(kù) (libcef) 的發(fā)布構(gòu)建以及在平臺(tái)上運(yùn)行所需的任何其他庫(kù)。
- Resources包含使用 CEF 的應(yīng)用程序所需的資源(僅限 Windows 和 Linux)钢拧。這包括 .pak 文件(具有全局資源的二進(jìn)制文件)和可能的其他文件蟹漓,例如取決于平臺(tái)。
- tests/cefclient包含配置為使用二進(jìn)制發(fā)行版中的文件構(gòu)建的 cefclient 示例應(yīng)用程序源内。此應(yīng)用程序演示了廣泛的 CEF 功能葡粒。
- tests/cefsimple包含配置為使用二進(jìn)制發(fā)行版中的文件構(gòu)建的 cefsimple 示例應(yīng)用程序。此應(yīng)用程序演示了創(chuàng)建瀏覽器窗口所需的最少功能。
- tests/ceftests包含配置為使用二進(jìn)制發(fā)行版中的文件構(gòu)建的 ceftests 示例應(yīng)用程序嗽交。此應(yīng)用程序?yàn)?CEF API 和功能提供單元測(cè)試覆蓋率卿嘲。
每個(gè)二進(jìn)制發(fā)行版還包含一個(gè) README.txt 文件,該文件更詳細(xì)地描述了特定于平臺(tái)的發(fā)行版夫壁,以及一個(gè)包含 CEF 的 BSD 許可證的 LICENSE.txt 文件拾枣。在發(fā)布基于 CEF 的應(yīng)用程序時(shí),您應(yīng)該在應(yīng)用程序分發(fā)的某處包含許可文本盒让。例如梅肤,您可以將其列在應(yīng)用程序 UI 的“關(guān)于”或“致謝名單”頁面上,或者列在與應(yīng)用程序捆綁在一起的文檔中邑茄。通過分別加載“about:license”和“about:credits”姨蝴,還可以在 CEF3 瀏覽器窗口中獲取許可證和信用信息。
可以使用標(biāo)準(zhǔn)平臺(tái)構(gòu)建工具構(gòu)建基于 CEF 二進(jìn)制發(fā)行版的應(yīng)用程序肺缕。這包括 Windows 上的 Visual Studio左医、MacOS 上的 Xcode 和 Linux 上的 gcc/make。項(xiàng)目下載頁面包含有關(guān)特定二進(jìn)制版本所需的操作系統(tǒng)和構(gòu)建工具版本的信息同木。在 Linux 上構(gòu)建時(shí)炒辉,還要特別注意列出的包依賴項(xiàng)。
使用源代碼構(gòu)建
CEF 可以在本地從源代碼構(gòu)建泉手,也可以使用自動(dòng)構(gòu)建系統(tǒng)(如TeamCity )構(gòu)建。這需要通過 Git 下載 Chromium 和 CEF 源代碼偶器。Chromium 代碼庫(kù)非常大斩萌,僅建議在 RAM 超過 8GB 的機(jī)器上從源代碼構(gòu)建 Chromium。BranchesAndBuilding Wiki 頁面上提供了從源代碼構(gòu)建 Chromium 和 CEF 的詳細(xì)說明屏轰。
示例應(yīng)用程序
cefclient 示例應(yīng)用程序是 CEF 集成的完整工作示例颊郎,并以源代碼形式包含在每個(gè)二進(jìn)制發(fā)行版中。使用 CEF 創(chuàng)建新應(yīng)用程序的最簡(jiǎn)單方法是從 cefclient 應(yīng)用程序開始并刪除不需要的部分霎苗。本文檔中的許多示例都來自 cefclient 應(yīng)用程序姆吭。
重要概念
開發(fā)基于 CEF3 的應(yīng)用程序有一些重要的基本概念,在繼續(xù)之前應(yīng)該了解這些概念唁盏。
C++ 包裝器
libcef 共享庫(kù)導(dǎo)出一個(gè) C API内狸,將用戶與 CEF 運(yùn)行時(shí)和代碼庫(kù)隔離開來。作為二進(jìn)制版本的一部分以源代碼形式分發(fā)的 libcef_dll_wrapper 項(xiàng)目將這個(gè)導(dǎo)出的 C API 包裝在 C++ API 中厘擂,然后鏈接到客戶端應(yīng)用程序昆淡。此 C/C++ API 轉(zhuǎn)換層的代碼由轉(zhuǎn)換工具自動(dòng)生成。UsingTheCAPI頁面上描述了 C API 的直接使用刽严。
進(jìn)程
CEF3 使用多個(gè)進(jìn)程運(yùn)行昂灵。處理窗口創(chuàng)建、UI 和網(wǎng)絡(luò)訪問的主要進(jìn)程稱為“瀏覽器”進(jìn)程。這通常是與主應(yīng)用程序相同的進(jìn)程眨补,并且大部分應(yīng)用程序邏輯將在瀏覽器進(jìn)程中運(yùn)行管削。Blink 渲染和 JavaScript 執(zhí)行發(fā)生在單獨(dú)的“渲染”進(jìn)程中。一些應(yīng)用程序邏輯撑螺,例如 JavaScript 綁定和 DOM 訪問含思,也會(huì)在渲染進(jìn)程中運(yùn)行。默認(rèn)進(jìn)程模型將為每個(gè)唯一的來源(方案+域实蓬,即URL)生成一個(gè)新的渲染進(jìn)程茸俭。其他進(jìn)程將根據(jù)需要產(chǎn)生,例如處理加速合成的“gpu”進(jìn)程安皱。
默認(rèn)情況下调鬓,主應(yīng)用程序可執(zhí)行文件將被多次啟動(dòng)為系統(tǒng)中的獨(dú)立進(jìn)程。這是通過傳遞到 CefExecuteProcess 函數(shù)的命令行標(biāo)志來處理的酌伊。如果主應(yīng)用程序可執(zhí)行文件很大腾窝,加載時(shí)間很長(zhǎng),或者不適用于非瀏覽器進(jìn)程居砖,可以使用單獨(dú)的可執(zhí)行文件虹脯。這可以通過 CefSettings.browser_subprocess_path 變量進(jìn)行配置。有關(guān)詳細(xì)信息奏候,請(qǐng)參閱“應(yīng)用程序結(jié)構(gòu)”部分循集。
CEF3 生成的獨(dú)立進(jìn)程使用進(jìn)程間通信 (IPC) 進(jìn)行通信。在瀏覽器和渲染進(jìn)程中實(shí)現(xiàn)的應(yīng)用程序邏輯可以通過來回發(fā)送異步消息進(jìn)行通信蔗草。渲染進(jìn)程中的JavaScriptIntegration可以調(diào)用位于瀏覽器進(jìn)程中的異步 API咒彤。有關(guān)詳細(xì)信息,請(qǐng)參閱“進(jìn)程間通信”部分咒精。
針對(duì)特定平臺(tái)的調(diào)試提示也適用于Windows镶柱、MacOS和Linux。
線程
CEF3 中的每個(gè)進(jìn)程都運(yùn)行多個(gè)線程模叙。有關(guān)線程的完整列表歇拆,請(qǐng)參閱cef_thread_id_t枚舉。這些是一些常用的線程:
- TID_UI線程是瀏覽器進(jìn)程中的主線程范咨。如果使用 CefSettings.multi_threaded_message_loop 值為 false 調(diào)用 CefInitialize()故觅,則此線程將與主應(yīng)用程序線程相同。
- TID_IO線程用于瀏覽器進(jìn)程處理IPC和網(wǎng)絡(luò)消息渠啊。
- TID_FILE_*線程用于瀏覽器進(jìn)程與文件系統(tǒng)交互逻卖。阻塞操作只能在該線程或客戶端應(yīng)用程序創(chuàng)建的CefThread上執(zhí)行。
- TID_RENDERER線程是渲染器進(jìn)程中的主線程昭抒。所有 Blink 和 V8 交互都必須在此線程上進(jìn)行评也。
由于 CEF 的多線程特性炼杖,使用消息傳遞或鎖定來保護(hù)數(shù)據(jù)成員免受多線程訪問非常重要。CefPostTask 系列函數(shù)支持在線程之間輕松傳遞異步消息盗迟。有關(guān)詳細(xì)信息坤邪,請(qǐng)參閱“發(fā)布任務(wù)”部分。
可以使用 CefCurrentlyOn() 函數(shù)驗(yàn)證當(dāng)前所在線程罚缕。CEF 示例應(yīng)用程序使用以下定義來驗(yàn)證當(dāng)前是否在預(yù)期線程上執(zhí)行艇纺。這些定義包含在include/wrapper/cef_helpers.h頭文件中。
#define CEF_REQUIRE_UI_THREAD() DCHECK(CefCurrentlyOn(TID_UI));
#define CEF_REQUIRE_IO_THREAD() DCHECK(CefCurrentlyOn(TID_IO));
#define CEF_REQUIRE_FILE_BACKGROUND_THREAD() \
DCHECK(CefCurrentlyOn(TID_FILE_BACKGROUND));
#define CEF_REQUIRE_FILE_USER_VISIBLE_THREAD() \
DCHECK(CefCurrentlyOn(TID_FILE_USER_VISIBLE));
#define CEF_REQUIRE_FILE_USER_BLOCKING_THREAD() \
DCHECK(CefCurrentlyOn(TID_FILE_USER_BLOCKING));
#define CEF_REQUIRE_RENDERER_THREAD() DCHECK(CefCurrentlyOn(TID_RENDERER));
為了支持對(duì)代碼塊的同步訪問邮弹,CEF 通過include/base/cef_lock.h頭文件提供了 base::Lock 和 base::AutoLock 類型黔衡。例如:
譯者注語:如果編譯器支持C++11及以上標(biāo)準(zhǔn),可以使用std::mutex腌乡、std::lock_guard等類盟劫,效果類似。
// Include the necessary header.
#include "include/base/cef_lock.h"
// Class declaration.
class MyClass : public CefBaseRefCounted {
public:
MyClass() : value_(0) {}
// Method that may be called on multiple threads.
void IncrementValue();
private:
// Value that may be accessed on multiple theads.
int value_;
// Lock used to protect access to |value_|.
base::Lock lock_;
IMPLEMENT_REFCOUNTING(MyClass);
};
// Class implementation.
void MyClass::IncrementValue() {
// Acquire the lock for the scope of this method.
base::AutoLock lock_scope(lock_);
// |value_| can now be modified safely.
value_++;
}
引用計(jì)數(shù)
所有框架類都實(shí)現(xiàn)了 CefBase[RefCounted|Scoped] 接口与纽,所有實(shí)例指針都使用 CefRefPtr 智能指針實(shí)現(xiàn)進(jìn)行處理侣签,該實(shí)現(xiàn)通過調(diào)用 AddRef() 和 Release() 自動(dòng)處理引用計(jì)數(shù)。實(shí)現(xiàn)這些類的最簡(jiǎn)單方法如下:
class MyClass : public CefBaseRefCounted {
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();
字符串
CEF 定義了自己的數(shù)據(jù)結(jié)構(gòu)來表示字符串急迂。原因在于:
- libcef 庫(kù)和主應(yīng)用程序可能使用不同的運(yùn)行時(shí)來管理堆內(nèi)存影所。所有對(duì)象,包括字符串僚碎,都需要使用分配內(nèi)存的相同運(yùn)行時(shí)來釋放猴娩。
- 可以編譯 libcef 庫(kù)以支持不同的底層字符串類型(UTF8、UTF16 或?qū)挘┥撞DJ(rèn)值為 UTF16胀溺,但可以通過修改cef_string.h中的定義并重新編譯 CEF 來更改。選擇寬字符串類型時(shí)請(qǐng)記住皆看,大小會(huì)因平臺(tái)而異。
對(duì)于 UTF16背零,字符串結(jié)構(gòu)如下所示:
typedef struct _cef_string_utf16_t {
char16* str; // Pointer to the string
size_t length; // String length
void (*dtor)(char16* str); // Destructor for freeing the string on the correct heap
} cef_string_utf16_t;
然后將選定的字符串類型定義為通用類型:
typedef char16 cef_char_t;
typedef cef_string_utf16_t cef_string_t;
CEF 提供了許多 C API 函數(shù)來操作 CEF 字符串類型(通過#defines 映射到特定于類型的函數(shù))腰吟。例如:
- cef_string_set將在復(fù)制或不復(fù)制值的情況下將字符串值分配給結(jié)構(gòu)。
- cef_string_clear將清除字符串值徙瓶。
- cef_string_cmp將比較兩個(gè)字符串值毛雇。
CEF 還提供了在所有支持的字符串類型(ASCII、UTF8侦镇、UTF16 和寬)之間進(jìn)行轉(zhuǎn)換的函數(shù)灵疮。有關(guān)完整的函數(shù)列表,請(qǐng)參閱cef_string.h和cef_string_types.h標(biāo)頭壳繁。
CefString 類簡(jiǎn)化了 C++ 中 CEF 字符串的使用震捣。CefString 提供與 std::string (UTF8) 和 std::wstring (wide) 類型之間的自動(dòng)轉(zhuǎn)換荔棉。它還可以用于包裝現(xiàn)有的 cef_string_t 結(jié)構(gòu)以用于分配目的。
對(duì) std::string 的賦值:
std::string str = “Some UTF8 string”;
// Equivalent ways of assigning |str| to |cef_str|. Conversion from UTF8 will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromString(str);
// Equivalent ways of assigning |cef_str| to |str|. Conversion to UTF8 will occur if necessary.
str = cef_str;
str = cef_str.ToString();
對(duì) std::wstring 的賦值:
std::wstring str = “Some wide string”;
// Equivalent ways of assigning |str| to |cef_str|. Conversion from wide will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromWString(str);
// Equivalent ways of assigning |cef_str| to |str|. Conversion to wide will occur if necessary.
str = cef_str;
str = cef_str.ToWString();
如果您知道格式是 ASCII蒿赢,請(qǐng)使用 FromASCII() 方法:
const char* cstr = “Some ASCII string”;
CefString cef_str;
cef_str.FromASCII(cstr);
一些結(jié)構(gòu)如 CefSettings 有 cef_string_t 成員润樱。CefString 可用于簡(jiǎn)化對(duì)這些成員的分配:
譯者注語:這里是相當(dāng)于將settings.log_file暫時(shí)交給一個(gè)匿名的CefString控制,這個(gè)匿名的CefString對(duì)象通過調(diào)用FromASCII成員函數(shù)羡棵,間接地修改了settings.log_file壹若。
CefSettings settings;
const char* path = “/path/to/log.txt”;
// Equivalent assignments.
CefString(&settings.log_file).FromASCII(path);
cef_string_from_ascii(path, strlen(path), &settings.log_file);
命令行參數(shù)
CEF3 和 Chromium 中的許多功能都可以使用命令行參數(shù)進(jìn)行配置。這些參數(shù)采用“--some-argument[=optional-param]”的形式皂冰,并通過 CefExecuteProcess() 和 CefMainArgs 結(jié)構(gòu)(參見文章后面的“應(yīng)用程序結(jié)構(gòu)”部分)傳遞到 CEF店展。
- 若需要禁用命令行參數(shù)的處理,請(qǐng)?jiān)趯?CefSettings 結(jié)構(gòu)傳遞到 CefInitialize() 之前秃流,將CefSettings.command_line_args_disabled 設(shè)置為 true赂蕴。
- 若需要在主應(yīng)用程序中指定 CEF/Chromium 命令行參數(shù),請(qǐng)調(diào)用 CefApp::OnBeforeCommandLineProcessing() 方法剔应。
- 若需要將特定于應(yīng)用程序(非 CEF/Chromium)的命令行參數(shù)傳遞給子進(jìn)程睡腿,請(qǐng)調(diào)用 CefBrowserProcessHandler::OnBeforeChildProcessLaunch() 方法。
關(guān)于 CEF/Chromium 命令行開關(guān)的更多信息峻贮,請(qǐng)參閱shared/common/client_switches.cc中的注釋席怪。
應(yīng)用布局
應(yīng)用程序布局可能因平臺(tái)而異。例如纤控,在 MacOS 上挂捻,您的應(yīng)用程序布局必須遵循特定的應(yīng)用程序打包結(jié)構(gòu)。Windows 和 Linux 更加靈活船万,允許您自定義存儲(chǔ) CEF 庫(kù)和資源的位置刻撒。對(duì)于所需布局的完整工作示例,您可以從項(xiàng)目下載頁面下載“示例應(yīng)用程序” 耿导。一些發(fā)布的文件是必需的声怔,一些是可選的。每個(gè)文件的要求和附加信息可以在二進(jìn)制發(fā)行版的README.txt 文件中找到舱呻。
Windows
在 Windows 上醋火,默認(rèn)布局將 libcef 庫(kù)和相關(guān)資源放置在應(yīng)用程序可執(zhí)行文件旁邊。4692 分支的目錄結(jié)構(gòu)如下所示:
Application/
cefclient.exe <= cefclient application executable
libcef.dll <= main CEF library
icudtl.dat <= unicode support data
libEGL.dll, libGLESv2.dll, ... <= accelerated compositing support libraries
chrome_100_percent.pak, chrome_200_percent.pak, resources.pak <= non-localized resources and strings
snapshot_blob.bin, v8_context_snapshot.bin <= V8 initial snapshot
locales/
en-US.pak, ... <= locale-specific resources and strings
可以使用 CefSettings 結(jié)構(gòu)自定義 CEF 庫(kù)和資源文件的位置(有關(guān)詳細(xì)信息箱吕,請(qǐng)參閱 README.txt 文件或“CefSettings”部分)芥驳。Windows 上的 cefclient 應(yīng)用程序通過 cefclient/resources/win/cefclient.rc中的 BINARY 資源類型編譯資源,但應(yīng)用程序可以輕松地從本地文件系統(tǒng)加載資源茬高。
Linux
在 Linux 上兆旬,默認(rèn)布局將 libcef 庫(kù)和相關(guān)資源放在應(yīng)用程序可執(zhí)行文件旁邊。但是請(qǐng)注意怎栽,libcef.so 在客戶端發(fā)行版中的位置與您自己構(gòu)建的二進(jìn)制發(fā)行版中的位置存在差異丽猬。該位置取決于在構(gòu)建應(yīng)用程序可執(zhí)行文件時(shí)如何設(shè)置鏈接器 rpath 值宿饱。例如,值“-Wl,-rpath,”宝鼓。(“.”表示當(dāng)前目錄)將允許您將 libcef.so 放在應(yīng)用程序可執(zhí)行文件旁邊刑棵。也可以使用 LD_LIBRARY_PATH 環(huán)境變量指定 libcef.so 的路徑。4692 分支的目錄結(jié)構(gòu)如下所示:
Application/
cefclient <= cefclient application executable
chrome-sandbox <= sandbox support binary
libcef.so <= main CEF library
icudtl.dat <= unicode support data
chrome_100_percent.pak, chrome_200_percent.pak, resources.pak <= non-localized resources and strings
snapshot_blob.bin, v8_context_snapshot.bin <= V8 initial snapshot
locales/
en-US.pak, ... <= locale-specific resources and strings
files/
binding.html, ... <= cefclient application resources
可以使用 CefSettings 結(jié)構(gòu)自定義 CEF 庫(kù)和資源文件的位置(有關(guān)詳細(xì)信息愚铡,請(qǐng)參閱“CefSettings”部分的 README.txt 文件)蛉签。
MacOS
MacOS 上的應(yīng)用程序(應(yīng)用程序包)布局由 Chromium 實(shí)現(xiàn)強(qiáng)制執(zhí)行,因此不是很靈活沥寥。4692 分支的目錄結(jié)構(gòu)如下所示:
cefclient.app/
Contents/
Frameworks/
Chromium Embedded Framework.framework/
Chromium Embedded Framework <= main application library
Resources/
chrome_100_percent.pak, chrome_200_percent.pak, resources.pak, ... <= non-localized resources and strings
icudtl.dat <= unicode support data
snapshot_blob.bin, v8_context_snapshot.bin <= V8 initial snapshot
en.lproj/, ... <= locale-specific resources and strings
cefclient Helper.app/
Contents/
Info.plist
MacOS/
cefclient Helper <= helper executable
Pkginfo
Info.plist
MacOS/
cefclient <= cefclient application executable
Pkginfo
Resources/
binding.html, ... <= cefclient application resources
“Chromium Embedded Framework.framework”是一個(gè)未版本控制的框架碍舍,其中包含所有 CEF 二進(jìn)制文件和資源∫匮牛可執(zhí)行文件(cefclient片橡、cefclient Helper 等)按此處所述動(dòng)態(tài)加載 CEF 框架。
“cefclient Helper”應(yīng)用程序用于執(zhí)行具有不同特征的單獨(dú)進(jìn)程(渲染器淮野、插件等)捧书。它需要有一個(gè)單獨(dú)的應(yīng)用程序包和 Info.plist 文件,以便除其他外骤星,它不顯示途桑靠欄圖標(biāo)。
應(yīng)用結(jié)構(gòu)
每個(gè) CEF3 應(yīng)用程序都具有相同的通用結(jié)構(gòu)洞难。
- 提供初始化 CEF 并運(yùn)行子進(jìn)程可執(zhí)行邏輯或 CEF 消息循環(huán)的入口點(diǎn)函數(shù)舆吮。
- 提供CefApp的實(shí)現(xiàn)來處理特定于進(jìn)程的回調(diào)。
- 提供CefClient的實(shí)現(xiàn)來處理特定于瀏覽器實(shí)例的回調(diào)队贱。
- 調(diào)用 CefBrowserHost::CreateBrowser() 創(chuàng)建瀏覽器實(shí)例并使用 CefLifeSpanHandler管理瀏覽器生命周期色冀。
入口函數(shù)
如“進(jìn)程”部分所述,CEF3 應(yīng)用程序?qū)⑦\(yùn)行多個(gè)進(jìn)程柱嫌。這些進(jìn)程可以全部使用相同的可執(zhí)行文件锋恬,或者可以為子進(jìn)程指定一個(gè)單獨(dú)的可執(zhí)行文件。進(jìn)程的執(zhí)行從入口函數(shù)開始编丘。cefclient/cefclient_win.cc与学、cefclient/cefclient_gtk.cc和cefclient/cefclient_mac.mm分別提供了適用于 Windows、Linux 和 MacOS 的完整平臺(tái)特定示例瘪吏。
當(dāng)啟動(dòng)子進(jìn)程時(shí),CEF 將使用命令行指定配置信息蜗巧,這些信息必須通過 CefMainArgs 結(jié)構(gòu)傳遞到 CefExecuteProcess 函數(shù)中掌眠。CefMainArgs 的定義是特定于平臺(tái)的。在 Linux 和 MacOS 上幕屹,它接受傳遞給main() 函數(shù)的 argc 和 argv 值蓝丙。
CefMainArgs main_args(argc, argv);
在 Windows 上级遭,它接受傳遞到wWinMain() 函數(shù)中的實(shí)例句柄 (HINSTANCE) 。實(shí)例句柄也可以通過 GetModuleHandle(nullptr) 檢索渺尘。
CefMainArgs main_args(hInstance);
單個(gè)可執(zhí)行文件
當(dāng)作為單個(gè)可執(zhí)行文件運(yùn)行時(shí)挫鸽,需要入口函數(shù)來區(qū)分不同的進(jìn)程類型。Windows 和 Linux 支持單一可執(zhí)行文件結(jié)構(gòu)鸥跟,但 MacOS 不支持丢郊。
// Program entry-point function.
int main(int argc, char* argv[]) {
// Structure for passing command-line arguments.
// The definition of this structure is platform-specific.
CefMainArgs main_args(argc, argv);
// Implementation of the CefApp interface.
CefRefPtr<MyApp> app(new MyApp);
// Execute the sub-process logic, if any. This will either return immediately for the browser
// process or block until the sub-process should exit.
int exit_code = CefExecuteProcess(main_args, app.get());
if (exit_code >= 0) {
// The sub-process terminated, exit now.
return exit_code;
}
// Populate this structure to customize CEF behavior.
CefSettings settings;
// Initialize CEF in the main process.
CefInitialize(main_args, settings, app.get());
// Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
單獨(dú)的子進(jìn)程可執(zhí)行文件
使用單獨(dú)的子進(jìn)程可執(zhí)行文件時(shí),您需要兩個(gè)單獨(dú)的可執(zhí)行項(xiàng)目和兩個(gè)單獨(dú)的入口函數(shù)医咨。
主進(jìn)程應(yīng)用入口函數(shù):
// Program entry-point function.
int main(int argc, char* argv[]) {
// Load the CEF framework library at runtime instead of linking directly
// as required by the macOS sandbox implementation.
CefScopedLibraryLoader library_loader;
if (!library_loader.LoadInMain())
return 1;
// Structure for passing command-line arguments.
// The definition of this structure is platform-specific.
CefMainArgs main_args(argc, argv);
// Implementation of the CefApp interface.
CefRefPtr<MyApp> app(new MyApp);
// Populate this structure to customize CEF behavior.
CefSettings settings;
// Specify the path for the sub-process executable.
CefString(&settings.browser_subprocess_path).FromASCII(“/path/to/subprocess”);
// Initialize CEF in the main process.
CefInitialize(main_args, settings, app.get());
// Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
子進(jìn)程應(yīng)用入口函數(shù):
// Program entry-point function.
int main(int argc, char* argv[]) {
// Initialize the macOS sandbox for this helper process.
CefScopedSandboxContext sandbox_context;
if (!sandbox_context.Initialize(argc, argv))
return 1;
// Load the CEF framework library at runtime instead of linking directly
// as required by the macOS sandbox implementation.
CefScopedLibraryLoader library_loader;
if (!library_loader.LoadInHelper())
return 1;
// Structure for passing command-line arguments.
// The definition of this structure is platform-specific.
CefMainArgs main_args(argc, argv);
// Implementation of the CefApp interface.
CefRefPtr<MyApp> app(new MyApp);
// Execute the sub-process logic. This will block until the sub-process should exit.
return CefExecuteProcess(main_args, app.get());
}
消息循環(huán)集成
CEF 還可以與現(xiàn)有的應(yīng)用程序消息循環(huán)集成枫匾,而不是運(yùn)行自己的消息循環(huán)。有兩種方法可以做到這一點(diǎn)拟淮。
- 定期調(diào)用 CefDoMessageLoopWork() 而不是調(diào)用 CefRunMessageLoop()干茉。每次調(diào)用 CefDoMessageLoopWork() 都會(huì)執(zhí)行一次 CEF 消息循環(huán)迭代。應(yīng)謹(jǐn)慎使用此方法很泊。過于頻繁地調(diào)用該方法會(huì)使 CEF 消息循環(huán)耗盡并對(duì)瀏覽器性能產(chǎn)生負(fù)面影響角虫。過于頻繁地調(diào)用該方法會(huì)對(duì) CPU 使用率產(chǎn)生負(fù)面影響。有關(guān)高級(jí)用法的詳細(xì)信息委造,請(qǐng)參閱CefBrowserProcessHandler::OnScheduleMessagePumpWork戳鹅。您可以在 cefclient 中通過使用“--external-message-pump”命令行標(biāo)志運(yùn)行來測(cè)試此模式。
- 設(shè)置 CefSettings.multi_threaded_message_loop = true(僅限 Windows 和 Linux)争涌。這將導(dǎo)致 CEF 在與主應(yīng)用程序線程不同的線程上運(yùn)行瀏覽器 UI 線程粉楚。使用這種方法,既不需要調(diào)用 CefDoMessageLoopWork() 也不需要調(diào)用 CefRunMessageLoop()亮垫。CefInitialize() 和 CefShutdown() 仍應(yīng)在主應(yīng)用程序線程上調(diào)用模软。您將需要提供自己的機(jī)制來與主應(yīng)用程序線程通信(例如,請(qǐng)參閱 cefclient_win.cpp 中的消息窗口用法)饮潦。您可以在 Windows 或 Linux 上的 cefclient 中通過使用“--multi-threaded-message-loop”命令行標(biāo)志運(yùn)行來測(cè)試此模式燃异。
數(shù)據(jù)結(jié)構(gòu)CefSettings
CefSettings 結(jié)構(gòu)允許配置應(yīng)用程序范圍的 CEF 設(shè)置。一些常用的用以配置的成員變量包括:
- browser_subprocess_path將為子進(jìn)程啟動(dòng)的單獨(dú)可執(zhí)行文件的路徑继蜡。有關(guān)詳細(xì)信息回俐,請(qǐng)參閱“單獨(dú)的子流程可執(zhí)行文件”部分。
- multi_threaded_message_loop設(shè)置為 true 讓瀏覽器處理消息循環(huán)在單獨(dú)的線程中運(yùn)行稀并。有關(guān)詳細(xì)信息仅颇,請(qǐng)參閱“消息循環(huán)集成”部分。
- command_line_args_disabled設(shè)置為 true 以禁用使用標(biāo)準(zhǔn) CEF 和 Chromium 命令行參數(shù)配置瀏覽器進(jìn)程功能碘举。有關(guān)詳細(xì)信息忘瓦,請(qǐng)參閱“命令行參數(shù)”部分。
- cache_path緩存數(shù)據(jù)將存儲(chǔ)在磁盤上的位置引颈。如果為空耕皮,內(nèi)存緩存將用于某些功能境蜕,而臨時(shí)磁盤緩存將用于其他功能。如果指定了緩存路徑凌停,HTML5 數(shù)據(jù)庫(kù)(如 localStorage)將僅在會(huì)話中持久存在粱年。
- locale將傳遞給 Blink 的語言環(huán)境字符串。如果為空罚拟,將使用默認(rèn)區(qū)域設(shè)置“en-US”台诗。在 Linux 上忽略此值,其中區(qū)域設(shè)置是使用具有優(yōu)先順序的環(huán)境變量解析確定的:LANGUAGE舟舒、LC_ALL拉庶、LC_MESSAGES 和 LANG。也可以使用“l(fā)ang”命令行開關(guān)進(jìn)行配置秃励。
- log_file用于調(diào)試日志的目錄和文件名氏仗。如果為空,將使用默認(rèn)名稱“debug.log”并將文件寫入應(yīng)用程序目錄夺鲜。也可使用“日志文件”命令行開關(guān)進(jìn)行配置皆尔。
- log_severity日志嚴(yán)重性。只會(huì)記錄此嚴(yán)重級(jí)別或更高級(jí)別的消息币励。也可以使用值為“verbose”慷蠕、“info”、“warning”食呻、“error”流炕、“error-report”或“disable”的“l(fā)og-severity”命令行開關(guān)進(jìn)行配置。
- resources_dir_path資源目錄的完全限定路徑仅胞。如果此值為空每辟,則 cef.pak 和/或 devtools_resources.pak 文件必須位于 Windows/Linux 上的模塊目錄或 MacOS 上的應(yīng)用程序包資源目錄中。也可以使用“resources-dir-path”命令行開關(guān)進(jìn)行配置干旧。
- locales_dir_path locales 目錄的完全限定路徑渠欺。如果此值為空,則 locales 目錄必須位于模塊目錄中椎眯。在始終從應(yīng)用程序包資源目錄加載包文件的 MacOS 上忽略此值挠将。也可使用“l(fā)ocales-dir-path”命令行開關(guān)進(jìn)行配置。
- remote_debugging_port設(shè)置為 1024 到 65535 之間的值以在指定端口上啟用遠(yuǎn)程調(diào)試。例如,如果指定了 8080,則遠(yuǎn)程調(diào)試 URL 將為 http://localhost:8080∩蟹眨可以從任何 CEF 或 Chrome 瀏覽器窗口遠(yuǎn)程調(diào)試 CEF。也可使用“remote-debugging-port”命令行開關(guān)進(jìn)行配置爪飘。
CefBrowser 和 CefFrame
CefBrowser和CefFrame對(duì)象用于向?yàn)g覽器發(fā)送命令和在回調(diào)方法中檢索狀態(tài)信息哆档。每個(gè) CefBrowser 對(duì)象都有一個(gè)代表頂級(jí)框架的主 CefFrame 對(duì)象和代表子框架的零個(gè)或多個(gè) CefFrame 對(duì)象贺归。例如,加載兩個(gè) iframe 的瀏覽器將具有三個(gè) CefFrame 對(duì)象(頂級(jí)框架和兩個(gè) iframe)断箫。
在瀏覽器主框架中加載 URL:
browser->GetMainFrame()->LoadURL(some_url);
向后導(dǎo)航瀏覽器:
browser->GoBack();
要檢索主框架 HTML 內(nèi)容:
// Implementation of the CefStringVisitor interface.
class Visitor : public CefStringVisitor {
public:
Visitor() {}
// Called asynchronously when the HTML contents are available.
virtual void Visit(const CefString& string) override {
// Do something with |string|...
}
IMPLEMENT_REFCOUNTING(Visitor);
};
browser->GetMainFrame()->GetSource(new Visitor());
CefBrowser 和 CefFrame 對(duì)象同時(shí)存在于瀏覽器進(jìn)程和渲染進(jìn)程中拂酣。可以通過 CefBrowser::GetHost() 方法在瀏覽器進(jìn)程中控制主機(jī)行為仲义。例如婶熬,可以按如下方式檢索窗口瀏覽器的本地句柄:
// CefWindowHandle is defined as HWND on Windows, NSView* on MacOS
// and (usually) X11 Window on Linux.
CefWindowHandle window_handle = browser->GetHost()->GetWindowHandle();
其他方法可用于歷史導(dǎo)航、加載字符串和請(qǐng)求埃撵、發(fā)送編輯命令赵颅、檢索文本/html 內(nèi)容等。相關(guān)方法的完整列表暂刘,請(qǐng)參閱類文檔饺谬。
CefApp
CefApp接口提供對(duì)進(jìn)程特定回調(diào)的訪問。重要的回調(diào)包括:
- OnBeforeCommandLineProcessing提供了以編程方式設(shè)置命令行參數(shù)的機(jī)會(huì)谣拣。有關(guān)詳細(xì)信息募寨,請(qǐng)參閱“命令行參數(shù)”部分。
- OnRegisterCustomSchemes提供注冊(cè)自定義方案的機(jī)會(huì)森缠。有關(guān)詳細(xì)信息拔鹰,請(qǐng)參閱“請(qǐng)求處理”部分。
- GetBrowserProcessHandler返回特定于瀏覽器進(jìn)程的功能的句柄贵涵,包括 OnContextInitialized() 方法列肢。
- GetRenderProcessHandler返回特定于渲染進(jìn)程的功能的句柄。這包括與 JavaScript 相關(guān)的回調(diào)和處理消息宾茂。有關(guān)詳細(xì)信息瓷马,請(qǐng)參閱JavaScriptIntegration Wiki 頁面和“進(jìn)程間通信”部分。
可以在cefsimple/simple_app.h和cefsimple/simple_app.cc中看到一個(gè) CefApp 實(shí)現(xiàn)例程刻炒。
CefClient
CefClient接口提供對(duì)瀏覽器實(shí)例特定回調(diào)的訪問决采。單個(gè) CefClient 實(shí)例可以在任意數(shù)量的瀏覽器之間共享。重要的回調(diào)包括:
- 處理瀏覽器生命周期坟奥、上下文菜單树瞭、對(duì)話框、顯示通知爱谁、拖動(dòng)事件晒喷、焦點(diǎn)事件、鍵盤事件等访敌。大多數(shù)事件處理器(Handlers)都是可選的凉敲。請(qǐng)參閱類文檔了解不實(shí)現(xiàn)特定事件處理器的副作用。
- OnProcessMessageReceived當(dāng)從呈現(xiàn)進(jìn)程接收到 IPC 消息時(shí)調(diào)用。有關(guān)詳細(xì)信息爷抓,請(qǐng)參閱“進(jìn)程間通信”部分势决。
CefClient 實(shí)現(xiàn)例程可以在cefsimple/simple_handler.h和cefsimple/simple_handler.cc中看到。
瀏覽器壽命
瀏覽器的生命周期從調(diào)用 CefBrowserHost::CreateBrowser() 或 CefBrowserHost::CreateBrowserSync() 開始蓝撇。執(zhí)行此邏輯的方便位置包括 CefBrowserProcessHandler::OnContextInitialized() 回調(diào)或特定于平臺(tái)的消息處理程序果复,如 Windows 上的 WM_CREATE。
// Information about the window that will be created including parenting, size, etc.
// The definition of this structure is platform-specific.
CefWindowInfo info;
// On Windows for example...
info.SetAsChild(parent_hwnd, client_rect);
// Customize this structure to control browser behavior.
CefBrowserSettings settings;
// CefClient implementation.
CefRefPtr<MyClient> client(new MyClient);
// Create the browser asynchronously. Initially loads the Google URL.
CefBrowserHost::CreateBrowser(info, client.get(), “http://www.google.com”, settings, nullptr, nullptr);
CefLifeSpanHandler類提供管理瀏覽器生命周期所需的回調(diào)渤昌。以下是相關(guān)方法和成員的摘錄虽抄。
class MyClient : public CefClient,
public CefLifeSpanHandler,
... {
// CefClient methods.
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override {
return this;
}
// CefLifeSpanHandler methods.
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
bool DoClose(CefRefPtr<CefBrowser> browser) override;
void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
// Member accessors.
CefRefPtr<CefBrowser> GetBrower() const { return browser_; }
bool IsClosing() const { return is_closing_; }
private:
CefRefPtr<CefBrowser> browser_;
int browser_id_ = -1; // invalid value
int browser_count_ = 0;
bool is_closing_ = false;
IMPLEMENT_REFCOUNTING(MyClient);
};
創(chuàng)建瀏覽器對(duì)象后將立即調(diào)用 OnAfterCreated() 方法。主機(jī)應(yīng)用程序可以使用此方法來保留對(duì)主瀏覽器對(duì)象的引用独柑。
void MyClient::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
// Must be executed on the UI thread.
REQUIRE_UI_THREAD();
if (!browser_) {
// Keep a reference to the main browser.
browser_ = browser;
browser_id_ = browser->GetIdentifier();
}
// Keep track of how many browsers currently exist.
browser_count_++;
}
要銷毀瀏覽器調(diào)用 CefBrowserHost::CloseBrowser()迈窟。
// Notify the browser window that we would like to close it. This will result in a call to
// MyHandler::DoClose() if the JavaScript 'onbeforeunload' event handler allows it.
browser->GetHost()->CloseBrowser(false);
如果瀏覽器是另一個(gè)窗口的父窗口,則關(guān)閉事件可能源自該父窗口的 OS 函數(shù)(例如忌栅,通過單擊父窗口上的 X)车酣。然后父窗口需要調(diào)用 CloseBrowser(false) 并等待第二個(gè)操作系統(tǒng)關(guān)閉事件以指示瀏覽器已允許關(guān)閉。如果關(guān)閉被 JavaScript“onbeforeunload”事件處理程序或 DoClose() 回調(diào)取消索绪,則不會(huì)發(fā)送第二個(gè)操作系統(tǒng)關(guān)閉事件骇径。請(qǐng)注意以下示例中的 IsClosing() 檢查——它將為第一個(gè)操作系統(tǒng)關(guān)閉事件返回 false,為第二個(gè)事件返回 true(在調(diào)用 DoClose 之后)者春。
Windows 上父窗口 WndProc 中的處理:
case WM_CLOSE:
if (g_handler && !g_handler->IsClosing()) {
CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
if (browser) {
// Notify the browser window that we would like to close it. This will result in a call to
// MyHandler::DoClose() if the JavaScript 'onbeforeunload' event handler allows it.
browser->GetHost()->CloseBrowser(false);
// Cancel the close.
return 0;
}
}
// Allow the close.
break;
case WM_DESTROY:
// Quitting CEF is handled in MyHandler::OnBeforeClose().
return 0;
}
在 Linux 上處理“delete_event”信號(hào):
gboolean delete_event(GtkWidget* widget, GdkEvent* event,
GtkWindow* window) {
if (g_handler && !g_handler->IsClosing()) {
CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
if (browser) {
// Notify the browser window that we would like to close it. This will result in a call to
// MyHandler::DoClose() if the JavaScript 'onbeforeunload' event handler allows it.
browser->GetHost()->CloseBrowser(false);
// Cancel the close.
return TRUE;
}
}
// Allow the close.
return FALSE;
}
OS X 上的關(guān)機(jī)更復(fù)雜破衔。請(qǐng)參閱cefsimple/cefsimple_mac.mm中的注釋,以全面了解關(guān)閉在該平臺(tái)上的工作原理钱烟。
DoClose() 方法設(shè)置 is_closing_ 標(biāo)志并返回 false 以發(fā)送第二個(gè)操作系統(tǒng)關(guān)閉事件晰筛。
bool MyClient::DoClose(CefRefPtr<CefBrowser> browser) {
// Must be executed on the UI thread.
REQUIRE_UI_THREAD();
// Closing the main window requires special handling. See the DoClose()
// documentation in the CEF header for a detailed description of this
// process.
if (browser_id_ == browser->GetIdentifier()) {
// Set a flag to indicate that the window close should be allowed.
is_closing_ = true;
}
// Allow the close. For windowed browsers this will result in the OS close
// event being sent.
return false;
}
當(dāng) OS 函數(shù)接收到第二個(gè) OS 關(guān)閉事件時(shí),它允許父窗口實(shí)際關(guān)閉拴袭。這會(huì)導(dǎo)致調(diào)用 OnBeforeClose()读第。確保在 OnBeforeClose() 回調(diào)中釋放對(duì)瀏覽器對(duì)象的任何引用。
void MyHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
// Must be executed on the UI thread.
REQUIRE_UI_THREAD();
if (browser_id_ == browser->GetIdentifier()) {
// Free the browser pointer so that the browser can be destroyed.
browser_ = nullptr;
}
if (--browser_count_ == 0) {
// All browser windows have closed. Quit the application message loop.
CefQuitMessageLoop();
}
}
有關(guān)每個(gè)平臺(tái)上完整的工作示例拥刻,請(qǐng)參閱 cefclient 應(yīng)用程序怜瞒。
離屏渲染
對(duì)于離屏渲染,CEF 不會(huì)創(chuàng)建本機(jī)瀏覽器窗口般哼。相反吴汪,CEF 向主機(jī)應(yīng)用程序提供無效區(qū)域和像素緩沖區(qū),并且主機(jī)應(yīng)用程序向 CEF 通知鼠標(biāo)蒸眠、鍵盤和焦點(diǎn)等輸入事件漾橙。離屏渲染目前不支持加速合成,因此與窗口瀏覽器相比楞卡,性能可能會(huì)受到影響霜运。離屏瀏覽器將收到與窗口瀏覽器相同的通知脾歇,包括上一節(jié)中描述的生命周期通知。要使用離屏渲染:
- 實(shí)現(xiàn)CefRenderHandler接口淘捡。除非另有說明藕各,否則所有方法都是必需的。
- 在將 CefWindowInfo 結(jié)構(gòu)傳遞給 CefBrowserHost::CreateBrowser() 之前調(diào)用 CefWindowInfo::SetAsWindowless()焦除。如果沒有父窗口被傳遞給 SetAsWindowless 一些功能如上下文菜單可能不可用座韵。
- CefRenderHandler::GetViewRect() 方法將被調(diào)用以檢索所需的視圖矩形。
- CefRenderHandler::OnPaint() 方法將被調(diào)用以提供無效區(qū)域和更新的像素緩沖區(qū)踢京。cefclient 應(yīng)用程序使用 OpenGL 繪制緩沖區(qū),但您的應(yīng)用程序可以使用您喜歡的任何技術(shù)宦棺。
- 要調(diào)整瀏覽器大小瓣距,請(qǐng)調(diào)用 CefBrowserHost::WasResized()。這將導(dǎo)致調(diào)用 GetViewRect() 以檢索新大小代咸,然后調(diào)用 OnPaint()蹈丸。
- 調(diào)用 CefBrowserHost::SendXXX() 方法通知瀏覽器鼠標(biāo)、鍵盤和焦點(diǎn)事件呐芥。
- 調(diào)用 CefBrowserHost::CloseBrowser() 來銷毀瀏覽器逻杖。
使用“--off-screen-rendering-enabled”命令行標(biāo)志運(yùn)行 cefclient 作為一個(gè)工作示例。
發(fā)布任務(wù)
可以使用 CefPostTask 系列方法在單個(gè)進(jìn)程中的不同線程之間發(fā)布任務(wù)(完整列表請(qǐng)參見include/cef_task.h頭文件)思瘟。該任務(wù)將在目標(biāo)線程的消息循環(huán)中異步執(zhí)行荸百。
譯者注語:這里提供了一種線程間通信的方式,類似Windows平臺(tái)的SendMessage滨攻。項(xiàng)目中使用Windows消息循環(huán)或者Qt的消息循環(huán)機(jī)制即可够话,或者其他平臺(tái)相關(guān)的線程通信方式。
CEF 提供 base::Bind[Once|Repeating] 和 base::[Once|Repeating]Callback 模板化回調(diào)類光绕,用于將綁定方法女嘲、對(duì)象和參數(shù)傳遞給 CefPostTask。有關(guān)完整的 base::Bind 和 base::Callback 用法信息诞帐,請(qǐng)參閱Chromium 文檔欣尼。include/wrapper/cef_closure_task.h標(biāo)頭提供了將 base::[Once|Repeating]Closure 轉(zhuǎn)換為 CefTask 的助手。例如:
// Include the necessary headers.
#include “include/base/cef_callback.h”
#include “include/wrapper/cef_closure_task.h”
// To execute a bound function:
// Define a function.
void MyFunc(int arg) { /* do something with |arg| on the UI thread */ }
// Post a task that will execute MyFunc on the UI thread and pass an |arg|
// value of 5.
CefPostTask(TID_UI, base::BindOnce(&MyFunc, 5));
// To execute a bound method:
// Define a class.
class MyClass : public CefBaseRefCounted {
public:
MyClass() {}
void MyMethod(int arg) { /* do something with |arg| on the UI thread */ }
private:
IMPLEMENT_REFCOUNTING(MyClass);
};
// Create an instance of MyClass.
CefRefPtr<MyClass> instance = new MyClass();
// Post a task that will execute MyClass::MyMethod on the UI thread and pass
// an |arg| value of 5. |instance| will be kept alive until after the task
// completes.
CefPostTask(TID_UI, base::BindOnce(&MyClass::MyMethod, instance, 5));
如果主機(jī)應(yīng)用程序需要保留對(duì)運(yùn)行循環(huán)的引用停蕉,它可以使用 CefTaskRunner 類愕鼓。例如,獲取 UI 線程的任務(wù)運(yùn)行器:
CefRefPtr<CefTaskRunner> task_runner = CefTaskRunner::GetForThread(TID_UI);
進(jìn)程間通信 (IPC)
由于 CEF3 在多個(gè)進(jìn)程中運(yùn)行慧起,因此有必要提供在這些進(jìn)程之間進(jìn)行通信的機(jī)制拒啰。CefBrowser 和 CefFrame 對(duì)象存在于瀏覽器和渲染進(jìn)程中,這有助于促進(jìn)這一過程完慧。每個(gè) CefBrowser 和 CefFrame 對(duì)象也有一個(gè)與之關(guān)聯(lián)的唯一 ID 值谋旦,它將在進(jìn)程邊界的兩側(cè)匹配剩失。
處理啟動(dòng)消息
啟動(dòng)數(shù)據(jù)可以在創(chuàng)建時(shí)通過CefBrowserHost::CreateBrowser 的CefRefPtr<CefDictionaryValue> extra_info
參數(shù)與特定的 CefBrowser 實(shí)例相關(guān)聯(lián)。extra_info 數(shù)據(jù)將通過 CefRenderProcessHandler::OnBrowserCreated 回調(diào)傳遞給與該 CefBrowser 關(guān)聯(lián)的每個(gè)渲染器進(jìn)程册着。有關(guān)何時(shí)以及如何生成新渲染器進(jìn)程的更多信息拴孤,請(qǐng)參閱“進(jìn)程”部分。
處理運(yùn)行時(shí)消息
消息可以在運(yùn)行時(shí)使用CefProcessMessage類在進(jìn)程之間傳遞甲捏。這些消息與特定的 CefBrowser 和 CefFrame 實(shí)例相關(guān)聯(lián)演熟,并使用 CefFrame::SendProcessMessage() 方法發(fā)送。進(jìn)程消息應(yīng)包含通過 CefProcessMessage::GetArgumentList() 所需的任何狀態(tài)信息司顿。
// Create the message object.
CefRefPtr<CefProcessMessage> msg= CefProcessMessage::Create(“my_message”);
// Retrieve the argument list object.
CefRefPtr<CefListValue> args = msg>GetArgumentList();
// Populate the argument values.
args->SetString(0, “my string”);
args->SetInt(0, 10);
// Send the process message to the main frame in the render process.
// Use PID_BROWSER instead when sending a message to the browser process.
browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, msg);
從瀏覽器進(jìn)程發(fā)送到渲染進(jìn)程的消息將到達(dá) CefRenderProcessHandler::OnProcessMessageReceived()芒粹。從渲染進(jìn)程發(fā)送到瀏覽器進(jìn)程的消息將到達(dá) CefClient::OnProcessMessageReceived()。
bool MyHandler::OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) {
// Check the message name.
const std::string& message_name = message->GetName();
if (message_name == “my_message”) {
// Handle the message here...
return true;
}
return false;
}
異步 JavaScript 綁定
JavaScriptIntegration在渲染進(jìn)程中實(shí)現(xiàn)大溜,但經(jīng)常需要與瀏覽器進(jìn)程通信化漆。JavaScript API 本身應(yīng)該設(shè)計(jì)為使用閉包和承諾異步工作。
通用消息路由器
譯者注語:這里給出了一種前端(JavaScript)調(diào)用客戶端(C++)接口的方法钦奋,并且通用消息路由器是一種異步的方式座云。
CEF 為在渲染器進(jìn)程中運(yùn)行的 JavaScript 和在瀏覽器進(jìn)程中運(yùn)行的 C++ 之間的異步消息路由提供了通用實(shí)現(xiàn)。應(yīng)用程序通過將數(shù)據(jù)從標(biāo)準(zhǔn)的 CEF C++ 回調(diào)(OnBeforeBrowse付材、OnProcessMessageRecieved朦拖、OnContextCreated 等)傳遞給路由器來與之交互。渲染器端的路由器支持通用的 JavaScript 回調(diào)注冊(cè)和執(zhí)行厌衔,而瀏覽器端的路由器通過一個(gè)或多個(gè)應(yīng)用程序提供的處理程序?qū)嵗С痔囟ㄓ趹?yīng)用程序的邏輯璧帝。有關(guān)演示 CefMessageRouter 用法的獨(dú)立示例應(yīng)用程序,請(qǐng)參閱message_router 示例富寿。有關(guān)完整的使用文檔裸弦,請(qǐng)參閱include/wrapper/cef_message_router.h。
這里官方文檔沒有直接給出代碼示例作喘。因此理疙,譯者給出一段使用消息路由器進(jìn)行異步通信的方式,以作參考泞坦。這個(gè)示例分為兩部分:JavaScript 代碼(運(yùn)行在渲染進(jìn)程)和 C++ 代碼(運(yùn)行在瀏覽器進(jìn)程)窖贤。
JavaScript 代碼(運(yùn)行在渲染進(jìn)程):
// 注冊(cè)回調(diào)函數(shù)
window.cefQuery({
request: 'myRequest', // 這是一個(gè)自定義請(qǐng)求字符串,將傳遞給 C++ 代碼
onSuccess: function(response) {
// 當(dāng) C++ 代碼成功處理請(qǐng)求并返回響應(yīng)時(shí)贰锁,將執(zhí)行此函數(shù)
console.log('Received response:', response);
},
onFailure: function(error_code, error_message) {
// 如果請(qǐng)求失敗赃梧,將執(zhí)行此函數(shù)
console.error('Request failed. Error code:', error_code, ', message:', error_message);
}
});
C++ 代碼(運(yùn)行在瀏覽器進(jìn)程):
首先,創(chuàng)建一個(gè)自定義的請(qǐng)求處理器類豌熄,繼承自 CefMessageRouterBrowserSide::Handler授嘀。
class MyRequestHandler : public CefMessageRouterBrowserSide::Handler {
public:
// 處理來自渲染進(jìn)程的請(qǐng)求
virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) OVERRIDE {
if (request == "myRequest") {
// 當(dāng)收到 "myRequest" 請(qǐng)求時(shí),執(zhí)行相應(yīng)的處理
// 模擬異步操作
CefPostDelayedTask(TID_UI,
base::Bind(&MyRequestHandler::SendSuccessResponse, this, callback),
1000); // 延遲 1000 毫秒
return true; // 表示已處理請(qǐng)求
}
return false; // 表示未處理請(qǐng)求
}
private:
// 發(fā)送成功響應(yīng)
void SendSuccessResponse(CefRefPtr<Callback> callback) {
callback->Success("Hello from C++!"); // 將響應(yīng)發(fā)送回渲染進(jìn)程
}
IMPLEMENT_REFCOUNTING(MyRequestHandler);
};
然后锣险,在 CefClient 派生類中設(shè)置消息路由器蹄皱。
class MyClient : public CefClient, public CefLifeSpanHandler {
public:
MyClient() {
// 創(chuàng)建消息路由器
CefMessageRouterConfig config;
message_router_ = CefMessageRouterBrowserSide::Create(config);
// 添加自定義請(qǐng)求處理器
message_router_->AddHandler(new MyRequestHandler(), false);
}
// 省略其他 CefClient 方法的實(shí)現(xiàn)
private:
CefRefPtr<CefMessageRouterBrowserSide> message_router_;
IMPLEMENT_REFCOUNTING(MyClient);
};
這個(gè)簡(jiǎn)單的示例展示了如何使用 CEF 的消息路由器在 JavaScript 代碼(運(yùn)行在渲染進(jìn)程)和 C++ 代碼(運(yùn)行在瀏覽器進(jìn)程)之間進(jìn)行異步通信览闰。
自定義實(shí)現(xiàn)
譯者注語:這里展示了如何通過自定義實(shí)現(xiàn)來實(shí)現(xiàn)異步 JavaScript 綁定。這個(gè)示例沒有使用 CEF 的內(nèi)置消息路由器巷折,而是創(chuàng)建了一個(gè)自定義的通信機(jī)制压鉴。個(gè)人覺得有點(diǎn)難以理解,可以暫時(shí)不用太糾結(jié)這一段內(nèi)容锻拘。
基于 CEF 的應(yīng)用程序還可以提供其自定義的異步 JavaScript 綁定實(shí)現(xiàn)油吭。一個(gè)簡(jiǎn)單的實(shí)現(xiàn)可以如下所示:
1. 渲染過程中的 JavaScript 綁定被傳遞一個(gè)回調(diào)函數(shù)。
// 在 JavaScript 中注冊(cè)回調(diào)函數(shù)署拟。
app.setMessageCallback('binding_test', function(name, args) {
document.getElementById('result').value = "Response: "+args[0];
});
2. 渲染進(jìn)程保留對(duì)回調(diào)函數(shù)的引用婉宰。
// 消息回調(diào)映射。
typedef std::map<std::pair<std::string, int>,
std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value> > >
CallbackMap;
CallbackMap callback_map_;
// 在 CefV8Handler::Execute 實(shí)現(xiàn)中為“setMessageCallback”推穷。
if (arguments.size() == 2 && arguments[0]->IsString() &&
arguments[1]->IsFunction()) {
std::string message_name = arguments[0]->GetStringValue();
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
int browser_id = context->GetBrowser()->GetIdentifier();
callback_map_.insert(
std::make_pair(std::make_pair(message_name, browser_id),
std::make_pair(context, arguments[1])));
}
3. 渲染進(jìn)程向?yàn)g覽器進(jìn)程發(fā)送異步 IPC 消息心包,請(qǐng)求執(zhí)行工作。
4. 瀏覽器進(jìn)程收到IPC消息并執(zhí)行工作缨恒。
5. 工作完成后,瀏覽器進(jìn)程將異步 IPC 消息連同結(jié)果發(fā)送回渲染進(jìn)程轮听。
6. 渲染進(jìn)程收到IPC消息骗露,并根據(jù)結(jié)果執(zhí)行回調(diào)函數(shù)。
// 執(zhí)行注冊(cè)的 JavaScript 回調(diào)(如果有)血巍。
if (!callback_map_.empty()) {
const CefString& message_name = message->GetName();
CallbackMap::const_iterator it = callback_map_.find(
std::make_pair(message_name.ToString(),
browser->GetIdentifier()));
if (it != callback_map_.end()) {
// 保留對(duì)對(duì)象的本地引用萧锉。回調(diào)可能會(huì)從回調(diào)映射中移除自身述寡。
CefRefPtr<CefV8Context> context = it->second.first;
CefRefPtr<CefV8Value> callback = it->second.second;
// 進(jìn)入上下文柿隙。
context->Enter();
CefV8ValueList arguments;
// 第一個(gè)參數(shù)是消息名稱。
arguments.push_back(CefV8Value::CreateString(message_name));
// 第二個(gè)參數(shù)是消息參數(shù)列表鲫凶。
CefRefPtr<CefListValue> list = message->GetArgumentList();
CefRefPtr<CefV8Value> args = CefV8Value::CreateArray(list->GetSize());
SetList(list, args); // Helper function to convert CefListValue to CefV8Value.
arguments.push_back(args);
// 執(zhí)行回調(diào)禀崖。
CefRefPtr<CefV8Value> retval = callback->ExecuteFunction(nullptr, arguments);
if (retval.get()) {
if (retval->IsBool())
handled = retval->GetBoolValue();
}
// 退出上下文。
context->Exit();
}
}
7. 釋放與 CefRenderProcessHandler::OnContextReleased() 中的上下文關(guān)聯(lián)的任何 V8 引用螟炫。
void MyHandler::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// 刪除已釋放上下文的任何已注冊(cè)的 JavaScript 回調(diào)波附。
if (!callback_map_.empty()) {
CallbackMap::iterator it = callback_map_.begin();
for (; it != callback_map_.end();) {
if (it->second.first->IsSame(context))
callback_map_.erase(it++);
else
++it;
}
}
}
同步請(qǐng)求
在極少數(shù)情況下,可能需要在瀏覽器和渲染進(jìn)程之間實(shí)現(xiàn)同步通信昼钻。應(yīng)盡可能避免這種情況掸屡,因?yàn)樗鼤?huì)對(duì)渲染過程中的性能產(chǎn)生負(fù)面影響。但是然评,如果您必須進(jìn)行同步通信仅财,請(qǐng)考慮使用同步 XMLHttpRequests,這將在瀏覽器進(jìn)程網(wǎng)絡(luò)層中等待處理時(shí)阻塞渲染進(jìn)程碗淌。瀏覽器進(jìn)程可以使用自定義方案處理程序或網(wǎng)絡(luò)攔截來處理請(qǐng)求盏求。有關(guān)詳細(xì)信息抖锥,請(qǐng)參閱“網(wǎng)絡(luò)層”部分。
網(wǎng)絡(luò)層
默認(rèn)情況下风喇,CEF3 中的網(wǎng)絡(luò)請(qǐng)求將以對(duì)主機(jī)應(yīng)用程序透明的方式處理宁改。對(duì)于希望與網(wǎng)絡(luò)層建立更緊密關(guān)系的應(yīng)用程序,CEF3 公開了一系列與網(wǎng)絡(luò)相關(guān)的功能魂莫。
與網(wǎng)絡(luò)相關(guān)的回調(diào)可能發(fā)生在不同的線程上还蹲,因此請(qǐng)務(wù)必注意文檔并妥善保護(hù)您的數(shù)據(jù)成員(請(qǐng)參閱“線程”部分了解背景信息)。
自定義請(qǐng)求
在瀏覽器框架中加載 URL 的最簡(jiǎn)單方法是通過 CefFrame::LoadURL() 方法耙考。
browser->GetMainFrame()->LoadURL(some_url);
希望發(fā)送包含自定義請(qǐng)求標(biāo)頭或上傳數(shù)據(jù)的更復(fù)雜請(qǐng)求的應(yīng)用程序可以使用 CefFrame::LoadRequest() 方法谜喊。此方法接受CefRequest對(duì)象作為單個(gè)參數(shù)。
譯者注語:這里相當(dāng)于是提供了一種發(fā)送HTTP請(qǐng)求的方式倦始。
警告:LoadRequest 方法將因“錯(cuò)誤的 IPC 消息”原因 INVALID_INITIATOR_ORIGIN (213) 而失敗斗遏,除非您首先使用其他機(jī)制(LoadURL、鏈接點(diǎn)擊等)導(dǎo)航到請(qǐng)求源(方案 + 域)鞋邑。
// Create a CefRequest object.
CefRefPtr<CefRequest> request = CefRequest::Create();
// Set the request URL.
request->SetURL(some_url);
// Set the request method. Supported methods include GET, POST, HEAD, DELETE and PUT.
request->SetMethod(“POST”);
// Optionally specify custom headers.
CefRequest::HeaderMap headerMap;
headerMap.insert(
std::make_pair("X-My-Header", "My Header Value"));
request->SetHeaderMap(headerMap);
// Optionally specify upload content.
// The default “Content-Type” header value is "application/x-www-form-urlencoded".
// Set “Content-Type” via the HeaderMap if a different value is desired.
const std::string& upload_data = “arg1=val1&arg2=val2”;
CefRefPtr<CefPostData> postData = CefPostData::Create();
CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create();
element->SetToBytes(upload_data.size(), upload_data.c_str());
postData->AddElement(element);
request->SetPostData(postData);
獨(dú)立于瀏覽器的請(qǐng)求
應(yīng)用程序可以通過CefURLRequest類發(fā)送網(wǎng)絡(luò)請(qǐng)求诵次。實(shí)現(xiàn)CefURLRequestClient接口來處理結(jié)果響應(yīng)。
- 與特定 CefBrowser/CefFrame 無關(guān)的請(qǐng)求可以通過 CefURLRequest::Create 方法發(fā)送枚碗。這些類型的請(qǐng)求只能在瀏覽器進(jìn)程中使用逾一。
- 與特定 CefBrowser/CefFrame 關(guān)聯(lián)的請(qǐng)求可以通過 CefFrame::CreateURLRequest 方法發(fā)送。這些類型的請(qǐng)求可以在瀏覽器和渲染器進(jìn)程中使用肮雨。
有關(guān)其他使用限制遵堵,請(qǐng)參閱有關(guān)上述方法的文檔。
以下是 CefURLRequest 用法的示例:
class MyRequestClient : public CefURLRequestClient {
public:
MyRequestClient()
: upload_total_(0),
download_total_(0) {}
void OnRequestComplete(CefRefPtr<CefURLRequest> request) override {
CefURLRequest::Status status = request->GetRequestStatus();
CefURLRequest::ErrorCode error_code = request->GetRequestError();
CefRefPtr<CefResponse> response = request->GetResponse();
// Do something with the response...
}
void OnUploadProgress(CefRefPtr<CefURLRequest> request,
int64 current,
int64 total) override {
upload_total_ = total;
}
void OnDownloadProgress(CefRefPtr<CefURLRequest> request,
int64 current,
int64 total) override {
download_total_ = total;
}
void OnDownloadData(CefRefPtr<CefURLRequest> request,
const void* data,
size_t data_length) override {
download_data_ += std::string(static_cast<const char*>(data), data_length);
}
bool GetAuthCredentials(bool isProxy,
const CefString& host,
int port,
const CefString& realm,
const CefString& scheme,
CefRefPtr<CefAuthCallback> callback) override {
return false; // Not handled.
}
private:
int64 upload_total_;
int64 download_total_;
std::string download_data_;
private:
IMPLEMENT_REFCOUNTING(MyRequestClient);
};
發(fā)送請(qǐng)求:
// Set up the CefRequest object.
CefRefPtr<CefRequest> request = CefRequest::Create();
// Populate |request| as shown above...
// Create the client instance.
CefRefPtr<MyRequestClient> client = new MyRequestClient();
// Start the request. MyRequestClient callbacks will be executed asynchronously.
CefRefPtr<CefURLRequest> url_request = CefURLRequest::Create(request, client.get(), nullptr);
// To cancel the request: url_request->Cancel();
使用 CefURLRequest 發(fā)出的請(qǐng)求也可以通過 CefRequest::SetFlags() 方法指定自定義行為怨规。支持的位標(biāo)志包括:
- UR_FLAG_SKIP_CACHE如果設(shè)置緩存將在處理請(qǐng)求時(shí)被跳過陌宿。
- UR_FLAG_ALLOW_STORED_CREDENTIALS如果設(shè)置 cookie 可以隨請(qǐng)求一起發(fā)送并從響應(yīng)中保存。
- UR_FLAG_REPORT_UPLOAD_PROGRESS如果設(shè)置上傳進(jìn)度事件波丰,當(dāng)請(qǐng)求有主體時(shí)將生成壳坪。
- UR_FLAG_NO_DOWNLOAD_DATA如果設(shè)置 CefURLRequestClient::OnDownloadData 方法將不會(huì)被調(diào)用。
- UR_FLAG_NO_RETRY_ON_5XX如果設(shè)置 5XX 重定向錯(cuò)誤將傳播給觀察者而不是自動(dòng)重試掰烟。這目前僅適用于源自瀏覽器進(jìn)程的請(qǐng)求弥虐。
有關(guān)受支持標(biāo)志的完整列表,請(qǐng)參閱cef_urlrequest_flags_t 媚赖。
例如霜瘪,要跳過緩存而不報(bào)告下載數(shù)據(jù):
request->SetFlags(UR_FLAG_SKIP_CACHE | UR_FLAG_NO_DOWNLOAD_DATA);
請(qǐng)求處理
CEF3 支持兩種在應(yīng)用程序內(nèi)部處理網(wǎng)絡(luò)請(qǐng)求的方法。方案處理程序方法允許為針對(duì)特定來源(方案+域)的請(qǐng)求注冊(cè)處理程序惧磺。請(qǐng)求攔截方法允許根據(jù)應(yīng)用程序的判斷處理任意請(qǐng)求颖对。
警告:使用 HTTP 方案而不是自定義方案可以避免一系列潛在問題。
如果您選擇使用自定義方案(“HTTP”磨隘、“HTTPS”等以外的任何方案)缤底,您必須向 CEF 注冊(cè)它顾患,以便它按預(yù)期運(yùn)行。如果您希望自定義方案的行為類似于 HTTP(支持 POST 請(qǐng)求并強(qiáng)制實(shí)施HTTP 訪問控制 (CORS)限制)个唧,則應(yīng)將其注冊(cè)為“標(biāo)準(zhǔn)”方案江解。如果您計(jì)劃對(duì)其他方案執(zhí)行跨域請(qǐng)求或通過 XMLHttpRequest 向您的方案處理程序發(fā)送 POST 請(qǐng)求,那么您應(yīng)該使用 HTTP 方案而不是自定義方案來避免潛在問題徙歼。如果您希望使用自定義方案犁河,則通過 CefApp::OnRegisterCustomSchemes() 回調(diào)注冊(cè)屬性,該回調(diào)必須在所有進(jìn)程中實(shí)現(xiàn)魄梯。
void MyApp::OnRegisterCustomSchemes(CefRefPtr<CefSchemeRegistrar> registrar) {
// Register "client" as a standard scheme.
int options = CEF_SCHEME_OPTION_STANDARD;
registrar->AddCustomScheme("client", options);
}
有關(guān)受支持標(biāo)志的完整列表桨螺,請(qǐng)參閱cef_scheme_options_t 。
通用資源管理器
CEF 提供了一種通用實(shí)現(xiàn)酿秸,用于管理來自一個(gè)或多個(gè)數(shù)據(jù)源的資源請(qǐng)求灭翔。此用戶為不同的數(shù)據(jù)源注冊(cè)處理程序,例如磁盤上的目錄辣苏、zip 存檔或自定義實(shí)現(xiàn)肝箱,并且管理器處理請(qǐng)求。應(yīng)用程序通過從標(biāo)準(zhǔn) CEF C++ 回調(diào)(OnBeforeResourceLoad稀蟋、GetResourceHandler)傳遞數(shù)據(jù)來與路由器交互煌张。有關(guān)演示 CefResourceManager 用法的獨(dú)立示例應(yīng)用程序,請(qǐng)參閱resource_manager 示例糊治。有關(guān)完整的使用文檔唱矛,請(qǐng)參閱include/wrapper/cef_resource_manager.h罚舱。
方案處理器(Scheme Handler)
方案處理器通過 CefRegisterSchemeHandlerFactory() 函數(shù)注冊(cè)井辜。調(diào)用此函數(shù)的好地方是 CefBrowserProcessHandler::OnContextInitialized()。例如管闷,您可以為“client://myapp/”請(qǐng)求注冊(cè)一個(gè)處理程序:
CefRegisterSchemeHandlerFactory("client", “myapp”, new MySchemeHandlerFactory());
處理程序可以與內(nèi)置方案(HTTP粥脚、HTTPS 等)和自定義方案一起使用。使用內(nèi)置方案時(shí)包个,請(qǐng)選擇您的應(yīng)用程序唯一的域名(如“myapp”或“internal”)刷允。實(shí)施CefSchemeHandlerFactory和CefResourceHandler類來處理請(qǐng)求并提供響應(yīng)數(shù)據(jù)。如果使用自定義方案碧囊,請(qǐng)不要忘記實(shí)現(xiàn) CefApp::OnRegisterCustomSchemes 方法树灶,如上所述。有關(guān)演示 CefSchemeHandlerFactory 用法的獨(dú)立示例應(yīng)用程序糯而,請(qǐng)參閱scheme_handler 示例天通。有關(guān)完整的使用文檔,請(qǐng)參閱include/cef_scheme.h熄驼。
如果響應(yīng)數(shù)據(jù)在請(qǐng)求時(shí)是已知的像寒,則CefStreamResourceHandler類提供了 CefResourceHandler 的一個(gè)方便的默認(rèn)實(shí)現(xiàn)烘豹。
// CefStreamResourceHandler is part of the libcef_dll_wrapper project.
#include “include/wrapper/cef_stream_resource_handler.h”
const std::string& html_content = “<html><body>Hello!</body></html>”;
// Create a stream reader for |html_content|.
CefRefPtr<CefStreamReader> stream =
CefStreamReader::CreateForData(
static_cast<void*>(const_cast<char*>(html_content.c_str())),
html_content.size());
// Constructor for HTTP status code 200 and no custom response headers.
// There’s also a version of the constructor for custom status code and response headers.
return new CefStreamResourceHandler("text/html", stream);
請(qǐng)求攔截
CefRequestHandler::GetResourceHandler() 方法支持?jǐn)r截任意請(qǐng)求。它使用與方案處理程序方法相同的 CefResourceHandler 類诺祸。如果使用自定義方案携悯,請(qǐng)不要忘記實(shí)現(xiàn) CefApp::OnRegisterCustomSchemes 方法,如上所述筷笨。
CefRefPtr<CefResourceHandler> MyHandler::GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
// Evaluate |request| to determine proper handling...
if (...)
return new MyResourceHandler();
// Return nullptr for default handling of the request.
return nullptr;
}
響應(yīng)過濾
CefRequestHandler::GetResourceResponseFilter() 方法支持過濾請(qǐng)求響應(yīng)數(shù)據(jù)憔鬼。有關(guān)工作示例,請(qǐng)參閱cefclient/browser/response_filter_test.cc(可通過測(cè)試菜單 > 其他測(cè)試 > 響應(yīng)過濾從 cefclient 示例應(yīng)用程序訪問)奥秆。
其他回調(diào)
CefRequestHandler接口為各種網(wǎng)絡(luò)相關(guān)事件提供回調(diào)逊彭,包括身份驗(yàn)證、cookie 處理构订、外部協(xié)議處理侮叮、證書錯(cuò)誤等。
代理解析
代理設(shè)置在 CEF3 中使用與 Google Chrome 相同的命令行標(biāo)志進(jìn)行配置悼瘾。
--proxy-server=host:port
Specify the HTTP/SOCKS4/SOCKS5 proxy server to use for requests. An individual proxy
server is specified using the format:
[<proxy-scheme>://]<proxy-host>[:<proxy-port>]
Where <proxy-scheme> is the protocol of the proxy server, and is one of:
"http", "socks", "socks4", "socks5".
If the <proxy-scheme> is omitted, it defaults to "http". Also note that "socks" is equivalent to
"socks5".
Examples:
--proxy-server="foopy:99"
Use the HTTP proxy "foopy:99" to load all URLs.
--proxy-server="socks://foobar:1080"
Use the SOCKS v5 proxy "foobar:1080" to load all URLs.
--proxy-server="sock4://foobar:1080"
Use the SOCKS v4 proxy "foobar:1080" to load all URLs.
--proxy-server="socks5://foobar:66"
Use the SOCKS v5 proxy "foobar:66" to load all URLs.
It is also possible to specify a separate proxy server for different URL types, by prefixing
the proxy server specifier with a URL specifier:
Example:
--proxy-server="https=proxy1:80;http=socks4://baz:1080"
Load https://* URLs using the HTTP proxy "proxy1:80". And load http://*
URLs using the SOCKS v4 proxy "baz:1080".
--no-proxy-server
Disables the proxy server.
--proxy-auto-detect
Autodetect proxy configuration.
--proxy-pac-url=URL
Specify proxy autoconfiguration URL.
如果代理需要身份驗(yàn)證囊榜,CefRequestHandler::GetAuthCredentials() 回調(diào)將使用 |isProxy| 執(zhí)行。值為 true 以檢索用戶名和密碼亥宿。
bool MyHandler::GetAuthCredentials(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
bool isProxy,
const CefString& host,
int port,
const CefString& realm,
const CefString& scheme,
CefRefPtr<CefAuthCallback> callback) {
if (isProxy) {
// Provide credentials for the proxy server connection.
callback->Continue("myuser", "mypass");
return true;
}
return false;
}
由于網(wǎng)絡(luò)代理解析(例如卸勺,如果在 Windows 上選中“自動(dòng)檢測(cè)代理設(shè)置”),應(yīng)用程序啟動(dòng)期間的 Web 內(nèi)容加載可能會(huì)延遲烫扼。為了獲得最佳用戶體驗(yàn)曙求,請(qǐng)考慮將您的應(yīng)用程序設(shè)計(jì)為首先顯示靜態(tài)啟動(dòng)頁面,然后使用元刷新重定向到實(shí)際網(wǎng)站——重定向?qū)⒈蛔柚褂称螅钡酱斫馕鐾瓿晌蛴3鲇跍y(cè)試目的,可以使用“--no-proxy-server”命令行標(biāo)志禁用代理解析堰氓。通過從命令行運(yùn)行“chrome --url=...”挤渐,也可以在 Google Chrome 中復(fù)制代理解析延遲。
推薦閱讀:
菜鳥與 cef 的邂逅之旅(三):Cef3 中 C++ 與 JavaScript 的互相調(diào)用
使用CEF(三)— 從CEF官方Demo源碼入手解析CEF架構(gòu)與CefApp双絮、CefClient對(duì)象