CEF入門必看嗦枢!CEF官方教程(Wiki)翻譯&校對(duì)&注語

翻譯自CEF官方英文教程绽左,若有疏忽歡迎指正。歡迎評(píng)論區(qū)討論~

Chromium Embedded Framework

介紹

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镶柱、MacOSLinux

線程

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ū)挘┥撞DJ(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.hcef_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.cccefclient/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)拟淮。

  1. 定期調(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è)試此模式。
  2. 設(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.hcefsimple/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.hcefsimple/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é)中描述的生命周期通知。要使用離屏渲染:

  1. 實(shí)現(xiàn)CefRenderHandler接口淘捡。除非另有說明藕各,否則所有方法都是必需的。
  2. 在將 CefWindowInfo 結(jié)構(gòu)傳遞給 CefBrowserHost::CreateBrowser() 之前調(diào)用 CefWindowInfo::SetAsWindowless()焦除。如果沒有父窗口被傳遞給 SetAsWindowless 一些功能如上下文菜單可能不可用座韵。
  3. CefRenderHandler::GetViewRect() 方法將被調(diào)用以檢索所需的視圖矩形。
  4. CefRenderHandler::OnPaint() 方法將被調(diào)用以提供無效區(qū)域和更新的像素緩沖區(qū)踢京。cefclient 應(yīng)用程序使用 OpenGL 繪制緩沖區(qū),但您的應(yīng)用程序可以使用您喜歡的任何技術(shù)宦棺。
  5. 要調(diào)整瀏覽器大小瓣距,請(qǐng)調(diào)用 CefBrowserHost::WasResized()。這將導(dǎo)致調(diào)用 GetViewRect() 以檢索新大小代咸,然后調(diào)用 OnPaint()蹈丸。
  6. 調(diào)用 CefBrowserHost::SendXXX() 方法通知瀏覽器鼠標(biāo)、鍵盤和焦點(diǎn)事件呐芥。
  7. 調(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í)施CefSchemeHandlerFactoryCefResourceHandler類來處理請(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ì)象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浴麻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子囤攀,更是在濱河造成了極大的恐慌软免,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焚挠,死亡現(xiàn)場(chǎng)離奇詭異膏萧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門向抢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來认境,“玉大人,你說我怎么就攤上這事挟鸠〔嫘牛” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵艘希,是天一觀的道長(zhǎng)硼身。 經(jīng)常有香客問我,道長(zhǎng)覆享,這世上最難降的妖魔是什么佳遂? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮撒顿,結(jié)果婚禮上丑罪,老公的妹妹穿的比我還像新娘。我一直安慰自己凤壁,他們只是感情好吩屹,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拧抖,像睡著了一般煤搜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唧席,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天擦盾,我揣著相機(jī)與錄音,去河邊找鬼淌哟。 笑死迹卢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绞绒。 我是一名探鬼主播婶希,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼榕暇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蓬衡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彤枢,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤狰晚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后缴啡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壁晒,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年业栅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秒咐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谬晕。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖携取,靈堂內(nèi)的尸體忽然破棺而出攒钳,到底是詐尸還是另有隱情,我是刑警寧澤雷滋,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布不撑,位于F島的核電站,受9級(jí)特大地震影響晤斩,放射性物質(zhì)發(fā)生泄漏焕檬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一澳泵、第九天 我趴在偏房一處隱蔽的房頂上張望实愚。 院中可真熱鬧,春花似錦兔辅、人聲如沸爆侣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兔仰。三九已至,卻和暖如春蕉鸳,著一層夾襖步出監(jiān)牢的瞬間乎赴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工潮尝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榕吼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓勉失,卻偏偏與公主長(zhǎng)得像羹蚣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乱凿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容