介绍
主页:http://opensource.spotify.com/cefbuilds/index.html
CEF 全称是 Chromium Embedded Framework(Chromium 嵌入式框架),它主要目的是开发一个基于 Google Chromium 的 Webbrowser 控件。CEF 支持一系列的编程语言和操作系统,并且能很容易地整合到新的或已有的工程中去。
它的设计思想政治就是易用且兼顾性能。CEF 基本的框架包含 C/C++ 程序接口,通过本地库的接口来实现,而这个库则会隔离宿主程序和 Chromium & Webkit 的操作细节。它在浏览器控件和宿主程序之间提供紧密的整合,它支持用户插件,协议,JavaScript 对象以及 javascript 扩展,宿主程序可以随意地控件资源下载,导航,下下文内容和打印等,并且可以跟 Google Chrome 浏览器一起,支持高性能和 HTML5 技术。
CEF3文档:http://magpcss.org/ceforum/apidocs3/
CEF的典型应用场景包括:
嵌入一个兼容HTML5的浏览器控件到一个已经存在的本地应用。
创建一个轻量化的壳浏览器,用以托管主要用Web技术开发的应用。
有些应用有独立的绘制框架,使用CEF对Web内容做离线渲染。
使用CEF做自动化Web测试。
CEF3是基于Chomuim Content API多进程构架的下一代CEF,拥有下列优势:
改进的性能和稳定性(JavaScript和插件在一个独立的进程内执行)。
支持Retina显示器
支持WebGL和3D CSS的GPU加速
类似WebRTC和语音输入这样的前卫特性。
通过DevTools远程调试协议以及ChromeDriver2提供更好的自动化UI测试
更快获得当前以及未来的Web特性和标准的能力
二进制发布包目录结构大致如下:
[解压目录]
|-include -- 头文件目录
|-Debug -- 编译好的CEF库,Debug版
|-Release -- 编译好的CEF库,Release版
|-Resources -- CEF库需要的资源文件
|-libcef_dll -- CEF的C++包装类库源代码,需要手动编译
|-tests -- 包含了一些测试工程
|-CMakeLists.txt --CMake工程文件
使用CMake生成vs项目
cefsimple
cefsimple_win.cc
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include <windows.h>
#include "include/cef_sandbox_win.h"
#include "tests/cefsimple/simple_app.h"
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
// to the CMake command-line to disable use of the sandbox.
// Uncomment this line to manually enable sandbox support.
// #define CEF_USE_SANDBOX 1
#if defined(CEF_USE_SANDBOX)
// The cef_sandbox.lib static library is currently built with VS2013. It may not
// link successfully with other VS versions.
#pragma comment(lib, "cef_sandbox.lib")
#endif
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Enable High-DPI support on Windows 7 or newer.
CefEnableHighDPISupport();
void* sandbox_info = NULL;
#if defined(CEF_USE_SANDBOX)
// Manage the life span of the sandbox information object. This is necessary
// for sandbox support on Windows. See cef_sandbox_win.h for complete details.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, NULL, sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
// Specify CEF global settings here.
CefSettings settings;
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
// SimpleApp implements application-level callbacks for the browser process.
// It will create the first browser instance in OnContextInitialized() after
// CEF has initialized.
CefRefPtr<SimpleApp> app(new SimpleApp);
// Initialize CEF.
CefInitialize(main_args, settings, app.get(), sandbox_info);
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
return 0;
}
simple_app.h
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#include "include/cef_app.h"
// Implement application-level callbacks for the browser process.
class SimpleApp : public CefApp, public CefBrowserProcessHandler {
public:
SimpleApp();
// CefApp methods:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
OVERRIDE {
return this;
}
// CefBrowserProcessHandler methods:
virtual void OnContextInitialized() OVERRIDE;
private:
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(SimpleApp);
};
#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
simple_app.cc
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple/simple_app.h"
#include <string>
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/cefsimple/simple_handler.h"
namespace {
// When using the Views framework this object provides the delegate
// implementation for the CefWindow that hosts the Views-based browser.
class SimpleWindowDelegate : public CefWindowDelegate {
public:
explicit SimpleWindowDelegate(CefRefPtr<CefBrowserView> browser_view)
: browser_view_(browser_view) {}
void OnWindowCreated(CefRefPtr<CefWindow> window) OVERRIDE {
// Add the browser view and show the window.
window->AddChildView(browser_view_);
window->Show();
// Give keyboard focus to the browser view.
browser_view_->RequestFocus();
}
void OnWindowDestroyed(CefRefPtr<CefWindow> window) OVERRIDE {
browser_view_ = NULL;
}
bool CanClose(CefRefPtr<CefWindow> window) OVERRIDE {
// Allow the window to close if the browser says it's OK.
CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
if (browser)
return browser->GetHost()->TryCloseBrowser();
return true;
}
private:
CefRefPtr<CefBrowserView> browser_view_;
IMPLEMENT_REFCOUNTING(SimpleWindowDelegate);
DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate);
};
} // namespace
SimpleApp::SimpleApp() {}
void SimpleApp::OnContextInitialized() {
CEF_REQUIRE_UI_THREAD();
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::GetGlobalCommandLine();
#if defined(OS_WIN) || defined(OS_LINUX)
// Create the browser using the Views framework if "--use-views" is specified
// via the command-line. Otherwise, create the browser using the native
// platform framework. The Views framework is currently only supported on
// Windows and Linux.
const bool use_views = command_line->HasSwitch("use-views");
#else
const bool use_views = false;
#endif
// SimpleHandler implements browser-level callbacks.
CefRefPtr<SimpleHandler> handler(new SimpleHandler(use_views));
// Specify CEF browser settings here.
CefBrowserSettings browser_settings;
std::string url;
// Check if a "--url=" value was provided via the command-line. If so, use
// that instead of the default URL.
url = command_line->GetSwitchValue("url");
if (url.empty())
url = "http://www.google.com";
if (use_views) {
// Create the BrowserView.
CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
handler, url, browser_settings, NULL, NULL);
// Create the Window. It will show itself after creation.
CefWindow::CreateTopLevelWindow(new SimpleWindowDelegate(browser_view));
} else {
// Information used when creating the native window.
CefWindowInfo window_info;
#if defined(OS_WIN)
// On Windows we need to specify certain flags that will be passed to
// CreateWindowEx().
window_info.SetAsPopup(NULL, "cefsimple");
#endif
// Create the first browser window.
CefBrowserHost::CreateBrowser(window_info, handler, url, browser_settings,
NULL);
}
}
simple_handler.h
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#include "include/cef_client.h"
#include <list>
class SimpleHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler {
public:
explicit SimpleHandler(bool use_views);
~SimpleHandler();
// Provide access to the single global instance of this object.
static SimpleHandler* GetInstance();
// CefClient methods:
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE { return this; }
// CefDisplayHandler methods:
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) OVERRIDE;
// CefLifeSpanHandler methods:
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
// CefLoadHandler methods:
virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) OVERRIDE;
// Request that all existing browser windows close.
void CloseAllBrowsers(bool force_close);
bool IsClosing() const { return is_closing_; }
private:
// Platform-specific implementation.
void PlatformTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title);
// True if the application is using the Views framework.
const bool use_views_;
// List of existing browser windows. Only accessed on the CEF UI thread.
typedef std::list<CefRefPtr<CefBrowser>> BrowserList;
BrowserList browser_list_;
bool is_closing_;
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(SimpleHandler);
};
#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
simple_handler.cc
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple/simple_handler.h"
#include <sstream>
#include <string>
#include "include/base/cef_bind.h"
#include "include/cef_app.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
namespace {
SimpleHandler* g_instance = NULL;
} // namespace
SimpleHandler::SimpleHandler(bool use_views)
: use_views_(use_views), is_closing_(false) {
DCHECK(!g_instance);
g_instance = this;
}
SimpleHandler::~SimpleHandler() {
g_instance = NULL;
}
// static
SimpleHandler* SimpleHandler::GetInstance() {
return g_instance;
}
void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) {
CEF_REQUIRE_UI_THREAD();
if (use_views_) {
// Set the title of the window using the Views framework.
CefRefPtr<CefBrowserView> browser_view =
CefBrowserView::GetForBrowser(browser);
if (browser_view) {
CefRefPtr<CefWindow> window = browser_view->GetWindow();
if (window)
window->SetTitle(title);
}
} else {
// Set the title of the window using platform APIs.
PlatformTitleChange(browser, title);
}
}
void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Add to the list of existing browsers.
browser_list_.push_back(browser);
}
bool SimpleHandler::DoClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Closing the main window requires special handling. See the DoClose()
// documentation in the CEF header for a detailed destription of this
// process.
if (browser_list_.size() == 1) {
// 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;
}
void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
// Remove from the list of existing browsers.
BrowserList::iterator bit = browser_list_.begin();
for (; bit != browser_list_.end(); ++bit) {
if ((*bit)->IsSame(browser)) {
browser_list_.erase(bit);
break;
}
}
if (browser_list_.empty()) {
// All browser windows have closed. Quit the application message loop.
CefQuitMessageLoop();
}
}
void SimpleHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) {
CEF_REQUIRE_UI_THREAD();
// Don't display an error for downloaded files.
if (errorCode == ERR_ABORTED)
return;
// Display a load error message.
std::stringstream ss;
ss << "<html><body bgcolor=\"white\">"
"<h2>Failed to load URL "
<< std::string(failedUrl) << " with error " << std::string(errorText)
<< " (" << errorCode << ").</h2></body></html>";
frame->LoadString(ss.str(), failedUrl);
}
void SimpleHandler::CloseAllBrowsers(bool force_close) {
if (!CefCurrentlyOn(TID_UI)) {
// Execute on the UI thread.
CefPostTask(TID_UI, base::Bind(&SimpleHandler::CloseAllBrowsers, this,
force_close));
return;
}
if (browser_list_.empty())
return;
BrowserList::const_iterator it = browser_list_.begin();
for (; it != browser_list_.end(); ++it)
(*it)->GetHost()->CloseBrowser(force_close);
}
simple_handler_win.cc
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple/simple_handler.h"
#include <windows.h>
#include <string>
#include "include/cef_browser.h"
void SimpleHandler::PlatformTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) {
CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle();
SetWindowText(hwnd, std::wstring(title).c_str());
}
重要概念
C++ Wrapper
C++ 封装
libcef 动态链接库导出 C API 使得使用者不用关心CEF运行库和基础代码。libcef_dll_wrapper 工程把 C API 封装成 C++ API同时包含在客户端应用程序工程中,与cefclient一样,源代码作为CEF二进制发布包的一部份共同发布。C/C++ API的转换层代码是由转换工具自动生成。
Processes
进程
CEF3是多进程架构的。”browser”被定义为主进程,负责窗口管理,界面绘制和网络交互。Blink的渲染和Js的执行被放在一个独立的”render” 进程中;除此之外,render进程还负责Js Binding和对Dom节点的访问。 默认的进程模型中,会为每个标签页创建一个新的”render”进程。其他进程按需创建,象管理插件的进程和处理合成加速的进程。
默认情况下,主应用程序会被多次启动运行各自独立的进程。这是通过传递不同的命令行参数给CefExecuteProcess函数做到的。如果主应用程序很大,加载时间比较长,或者不能在非浏览器进程里使用,则宿主程序可使用独立的可执行文件去运行这些进程。这可以通过配置CefSettings.browser_subprocess_path变量做到。
CEF3的进程之间可以通过IPC进行通信。”Browser”和”Render”进程可以通过发送异步消息进行双向通信。甚至在Render进程可以注册在Browser进程响应的异步JavaScript API。
通过设置命令行的”–single-process”,CEF3就可以支持用于调试目的的单进程运行模型。支持的平台为:Windows,Mac OS X 和Linux。
Threads
线程
在CEF3中,每个进程都会运行多个线程。完整的线程类型表请参照cef_thread_id_t。例如,在browser进程中包含如下主要的线程:
TID_UI 线程是浏览器的主线程。如果应用程序在调用CefInitialize()时,传递CefSettings.multi_threaded_message_loop=false,这个线程也是应用程序的主线程。
TID_IO 线程主要负责处理IPC消息以及网络通信。
TID_FILE 线程负责与文件系统交互。
由于CEF采用多线程架构,有必要使用锁和闭包来保证在多不同线程安全的传递数据。IMPLEMENT_LOCKING定义提供了Lock()和Unlock()方法以及AutoLock对象来保证不同代码块同步访问数据。CefPostTask函数组支持简易的线程间异步消息传递。
Reference Counting
引用计数
所有的框架类从CefBase继承,实例指针由CefRefPtr管理,CefRefPtr通过调用AddRef()和Release()方法自动管理引用计数。
Strings
字符串
CEF为字符串定义了自己的数据结构。下面是这样做的理由:
libcef包和宿主程序可能使用不同的运行时,对堆管理的方式也不同。所有的对象,包括字符串,需要确保和申请堆内存使用相同的运行时环境。
libcef包可以编译为支持不同的字符串类型(UTF8,UTF16以及WIDE)。默认采用的是UTF16,默认字符集可以通过更改cef_string.h文件中的定义,然后重新编译来修改。当使用宽字节集的时候,切记字符的长度由当前使用的平台决定。
在C++中,通常使用CefString类来管理CEF的字符串。CefString支持与std::string(UTF8)、std::wstring(wide)类型的相互转换。也可以用来包裹一个cef_string_t结构来对其进行赋值。
Application Structure
应用程序结构
每个CEF3应用程序都是相同的结构
提供入口函数,用于初始化CEF、运行子进程执行逻辑或者CEF消息循环。
提供CefApp实现,用于处理进程相关的回调。
提供CefClient实现,用于处理browser实例相关的回调
执行CefBrowserHost::CreateBrowser()创建一个browser实例,使用CefLifeSpanHandler管理browser对象生命周期。
入口函数
当执行子进程是,CEF将使用命令行参数指定配置信息,这些命令行参数必须通过CefMainArgs结构体传入到CefExecuteProcess函数。CefMainArgs的定义与平台相关,在Linux、Mac OS X平台下,它接收main函数传入的argc和argv参数值。
CefMainArgs main_args(argc, argv);
在Windows平台下,它接收wWinMain函数传入的参数:实例句柄(HINSTANCE),这个实例能够通过函数GetModuleHandle(NULL)获取。
CefMainArgs main_args(hInstance);
单一执行体
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);
// Optional 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;
}
独立的子进程执行体
主程序的入口函数:
// 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);
// Optional implementation of the CefApp interface.
// 可选择性地实现CefApp接口
CefRefPtr<MyApp> app(new MyApp);
// Populate this structure to customize CEF behavior.
// 填充这个结构体,用于定制CEF的行为。
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.
// 在主进程中初始化CEF
CefInitialize(main_args, settings, app.get());
// Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
// 执行消息循环,此时会堵塞,直到CefQuitMessageLoop()函数被调用。
CefRunMessageLoop();
// Shut down CEF.
// 关闭CEF
CefShutdown();
return 0;
}
子进程程序的入口函数:
// 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);
// Optional implementation of the CefApp interface.
// 可选择性地实现CefApp接口
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());
}
集成消息循环
CEF可以不用它自己提供的消息循环,而与已经存在的程序中消息环境集成在一起,有两种方式可以做到:
周期性执行CefDoMessageLoopWork()函数,替代调用CefRunMessageLoop()。CefDoMessageLoopWork()的每一次调用,都将执行一次CEF消息循环的单次迭代。需要注意的是,此方法调用次数太少时,CEF消息循环会饿死,将极大的影响browser的性能,调用次数太频繁又将影响CPU使用率。
设置CefSettings.multi_threaded_message_loop=true(Windows平台下有效),这个设置项将导致CEF运行browser UI运行在单独的线程上,而不是在主线程上,这种场景下CefDoMessageLoopWork()或者CefRunMessageLoop()都不需要调用,CefInitialze()、CefShutdown()仍然在主线程中调用。你需要提供主程序线程通信的机制(查看cefclient_win.cpp中提供的消息窗口实例)。在Windows平台下,你可以通过命令行参数“--multi-threaded-message-loop”测试上述消息模型。
CefBrowser and CefFrame
CefBrowser和CefFrame对象被用来发送命令给浏览器以及在回调函数里获取状态信息。每个CefBrowser对象包含一个主CefFrame对象,主CefFrame对象代表页面的顶层frame;同时每个CefBrowser对象可以包含零个或多个的CefFrame对象,分别代表不同的子Frame。例如,一个浏览器加载了两个iframe,则该CefBrowser对象拥有三个CefFrame对象(顶层frame和两个iframe)。
下面的代码在浏览器的主frame里加载一个URL:
browser->GetMainFrame()->LoadURL(some_url);
下面的代码执行浏览器的回退操作:
browser->GoBack();
下面的代码从主frame里获取HTML内容:
// 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对象在Browser进程和Render进程都有对等的代理对象。在Browser进程里,Host(宿主)行为控制可以通过CefBrowser::GetHost()方法控制。例如,浏览器窗口的原生句柄可以用下面的代码获取:
// CefWindowHandle is defined as HWND on Windows, NSView* on Mac OS X
// and GtkWidget* on Linux.
CefWindowHandle window_handle = browser->GetHost()->GetWindowHandle();
CefApp
CefApp接口提供了不同进程的可定制回调函数。重要的回调函数如下:
OnBeforeCommandLineProcessing 提供了以编程方式设置命令行参数的机会,更多细节,请参考Command Line Arguments一节。
OnRegisterCustomSchemes 提供了注册自定义schemes的机会,更多细节,请参考Request Handling一节。
GetBrowserProcessHandler 返回定制Browser进程的Handler,该Handler包括了诸如OnContextInitialized的回调。
GetRenderProcessHandler 返回定制Render进程的Handler,该Handler包含了JavaScript相关的一些回调以及消息处理的回调。更多细节,请参考JavascriptIntegration和Inter-Process Communication两节。
CefApp子类的例子:
// MyApp implements CefApp and the process-specific interfaces.
class MyApp : public CefApp,
public CefBrowserProcessHandler,
public CefRenderProcessHandler {
public:
MyApp() {}
// CefApp methods. Important to return |this| for the handler callbacks.
virtual void OnBeforeCommandLineProcessing(
const CefString& process_type,
CefRefPtr<CefCommandLine> command_line) {
// Programmatically configure command-line arguments...
}
virtual void OnRegisterCustomSchemes(
CefRefPtr<CefSchemeRegistrar> registrar) OVERRIDE {
// Register custom schemes...
}
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
OVERRIDE { return this; }
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler()
OVERRIDE { return this; }
// CefBrowserProcessHandler methods.
virtual void OnContextInitialized() OVERRIDE {
// The browser process UI thread has been initialized...
}
virtual void OnRenderProcessThreadCreated(CefRefPtr<CefListValue> extra_info)
OVERRIDE {
// Send startup information to a new render process...
}
// CefRenderProcessHandler methods.
virtual void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info)
OVERRIDE {
// The render process main thread has been initialized...
// Receive startup information in the new render process...
}
virtual void OnWebKitInitialized(CefRefPtr<ClientApp> app) OVERRIDE {
// WebKit has been initialized, register V8 extensions...
}
virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE {
// Browser created in this render process...
}
virtual void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) OVERRIDE {
// Browser destroyed in this render process...
}
virtual bool OnBeforeNavigation(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
NavigationType navigation_type,
bool is_redirect) OVERRIDE {
// Allow or block different types of navigation...
}
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
// JavaScript context created, add V8 bindings here...
}
virtual void OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE {
// JavaScript context released, release V8 references here...
}
virtual bool OnProcessMessageReceived(
CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message) OVERRIDE {
// Handle IPC messages from the browser process...
}
IMPLEMENT_REFCOUNTING(MyApp);
};
CefClient
CefClient提供访问browser-instance-specific的回调接口。单实例CefClient可以共数任意数量的浏览器进程。以下为几个重要的回调:
比如处理browser的生命周期,右键菜单,对话框,通知显示, 拖曳事件,焦点事件,键盘事件等等。如果没有对某个特定的处理接口进行实现会造成什么影响,请查看cef_client.h文件中相关说明。
CefClient子类的例子:
// MyHandler implements CefClient and a number of other interfaces.
class MyHandler : public CefClient,
public CefContextMenuHandler,
public CefDisplayHandler,
public CefDownloadHandler,
public CefDragHandler,
public CefGeolocationHandler,
public CefKeyboardHandler,
public CefLifeSpanHandler,
public CefLoadHandler,
public CefRequestHandler {
public:
MyHandler();
// CefClient methods. Important to return |this| for the handler callbacks.
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefDragHandler> GetDragHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefGeolocationHandler> GetGeolocationHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE {
return this;
}
virtual CefRefPtr<CefRequestHandler> GetRequestHandler() OVERRIDE {
return this;
}
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
CefProcessId source_process,
CefRefPtr<CefProcessMessage> message)
OVERRIDE {
// Handle IPC messages from the render process...
}
// CefContextMenuHandler methods
virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
CefRefPtr<CefMenuModel> model) OVERRIDE {
// Customize the context menu...
}
virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefContextMenuParams> params,
int command_id,
EventFlags event_flags) OVERRIDE {
// Handle a context menu command...
}
// CefDisplayHandler methods
virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE {
// Update UI for browser state...
}
virtual void OnAddressChange(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& url) OVERRIDE {
// Update the URL in the address bar...
}
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) OVERRIDE {
// Update the browser window title...
}
virtual bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
const CefString& message,
const CefString& source,
int line) OVERRIDE {
// Log a console message...
}
// CefDownloadHandler methods
virtual void OnBeforeDownload(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
const CefString& suggested_name,
CefRefPtr<CefBeforeDownloadCallback> callback) OVERRIDE {
// Specify a file path or cancel the download...
}
virtual void OnDownloadUpdated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDownloadItem> download_item,
CefRefPtr<CefDownloadItemCallback> callback) OVERRIDE {
// Update the download status...
}
// CefDragHandler methods
virtual bool OnDragEnter(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefDragData> dragData,
DragOperationsMask mask) OVERRIDE {
// Allow or deny drag events...
}
// CefGeolocationHandler methods
virtual void OnRequestGeolocationPermission(
CefRefPtr<CefBrowser> browser,
const CefString& requesting_url,
int request_id,
CefRefPtr<CefGeolocationCallback> callback) OVERRIDE {
// Allow or deny geolocation API access...
}
// CefKeyboardHandler methods
virtual bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
const CefKeyEvent& event,
CefEventHandle os_event,
bool* is_keyboard_shortcut) OVERRIDE {
// Perform custom handling of key events...
}
// CefLifeSpanHandler methods
virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
const CefString& target_frame_name,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings,
bool* no_javascript_access) OVERRIDE {
// Allow or block popup windows, customize popup window creation...
}
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE {
// Browser window created successfully...
}
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE {
// Allow or block browser window close...
}
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE {
// Browser window is closed, perform cleanup...
}
// CefLoadHandler methods
virtual void OnLoadStart(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame) OVERRIDE {
// A frame has started loading content...
}
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) OVERRIDE {
// A frame has finished loading content...
}
virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) OVERRIDE {
// A frame has failed to load content...
}
virtual void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
TerminationStatus status) OVERRIDE {
// A render process has crashed...
}
// CefRequestHandler methods
virtual CefRefPtr<CefResourceHandler> GetResourceHandler(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) OVERRIDE {
// Optionally intercept resource requests...
}
virtual bool OnQuotaRequest(CefRefPtr<CefBrowser> browser,
const CefString& origin_url,
int64 new_size,
CefRefPtr<CefQuotaCallback> callback) OVERRIDE {
// Allow or block quota requests...
}
virtual void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
const CefString& url,
bool& allow_os_execution) OVERRIDE {
// Handle execution of external protocols...
}
IMPLEMENT_REFCOUNTING(MyHandler);
};
JavaScript和Cpp交互示例
一个CEF应用程序也可以提供自己的异步JavaScript绑定。
此处演示:
- JavaScript注册函数给Render进程,Render进程保存该JavaScript函数
- Render进程发消息通知Browser进程
- Browser进程处理后,回发消息给Render进程
- Render进程调用之前保存的JavaScript函数
步骤
- 首先在CefRenderProcessHandler的子类里覆写虚方法OnWebKitInitialized,并在该方法的实现里注册一个C++方法给JavaScript
//假设CefRenderProcessHandler的子类为CefRenderProcessHandlerImpl
void CefRenderProcessHandlerImpl::OnWebKitInitialized(){
std::string app_code =
//-----------------------------------
//声明JavaScript里要调用的Cpp方法
"var app;"
"if (!app)"
" app = {};"
"(function() {"
// Send message
" app.sendMessage = function(name, arguments) {"
" native function sendMessage();"
" return sendMessage(name, arguments);"
" };"
// Registered Javascript Function, which will be called by Cpp
" app.registerJavascriptFunction = function(name,callback) {"
" native function registerJavascriptFunction();"
" return registerJavascriptFunction(name,callback);"
" };"
"})();";
//------------------------------------
// Register app extension module
// JavaScript里调用app.registerJavascriptFunction时,就会去通过CefRegisterExtension注册的CefV8Handler列表里查找
// 找到"v8/app"对应的CefV8HandlerImpl,就调用它的Execute方法
// 假设m_v8Handler是CefRenderProcessHandlerImpl的一个成员变量
m_v8Handler = new CefV8HandlerImpl();
CefRegisterExtension("v8/app", app_code,m_v8Handler);
}
在CefV8Handler的子类的Execute方法里实现sendMessage和registerJavascriptFunction
// in CefV8HandlerImpl.h class CefV8HandlerImpl : publice CefV8Handler{ public: CefV8HandlerImpl(); ~CefV8HandlerImpl(); public: /** * CefV8Handler Methods, Which will be called when the V8 extension * is called in the Javascript environment */ virtual bool Execute(const CefString& name ,CefRefPtr<CefV8Value> object ,const CefV8ValueList& arguments ,CefRefPtr<CefV8Value>& retval ,CefString& exception); private: // Map of message callbacks. typedef std::map<std::pair<std::string, int>, std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value> > > CallbackMap; CallbackMap callback_map_; }
CefV8HandlerImpl::CefV8HandlerImpl() { } CefV8HandlerImpl::~CefV8HandlerImpl() { // Remove any JavaScript callbacks registered for the context that has been released. 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; } } } // in CefV8HandlerImpl.cpp bool CefV8HandlerImpl::Execute(const CefString& name //JavaScript调用的C++方法名字 ,CefRefPtr<CefV8Value> object //JavaScript调用者对象 ,const CefV8ValueList& arguments //JavaScript传递的参数 ,CefRefPtr<CefV8Value>& retval //需要返回给JavaScript的值设置给这个对象 ,CefString& exception) //通知异常信息给JavaScript { // In the CefV8Handler::Execute implementation for “registerJavascriptFunction”. bool handle=false; if(name=="registerJavascriptFunction"){ //保存JavaScript设置的回答函数 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]))); handle = true; // 此时可以发送一个信息给Browser进程,见第4个步骤 } if(name=="sendMessage"){ //处理SendMessage, handle = true; } // 如果没有处理,则发异常信息给js if(!handle){ exception="not implement function"; } return true; }1
在HTML的JavaScript里,通过上面注册的方法向Render进程注册一个回调函数。
// In JavaScript register the callback function.
app.setMessageCallback('binding_test', function(name, args) {
document.getElementById('result').value = "Response: "+args[0];
});
- Render进程发送异步进程间通信到Browser进程。
- Browser进程接收到进程间消息,并处理。
- Browser进程处理完毕后,发送一个异步进程间消息给Render进程,返回结果。
Render进程接收到进程间消息,则调用最开始保存的JavaScript注册的回调函数处理之。
// Execute the registered JavaScript callback if any. 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()) { // Keep a local reference to the objects. The callback may remove itself // from the callback map. CefRefPtr<CefV8Context> context = it->second.first; CefRefPtr<CefV8Value> callback = it->second.second; // Enter the context. context->Enter(); CefV8ValueList arguments; // First argument is the message name. arguments.push_back(CefV8Value::CreateString(message_name)); // Second argument is the list of message arguments. 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); // Execute the callback. CefRefPtr<CefV8Value> retval = callback->ExecuteFunction(NULL, arguments); if (retval.get()) { if (retval->IsBool()) handled = retval->GetBoolValue(); } // Exit the context. context->Exit(); } }
在CefRenderProcessHandlerImpl::OnContextReleased()里释放JavaScript注册的回调函数以及其他V8资源。
void CefRenderProcessHandlerImpl::OnContextReleased(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
if(m_v8Handler->Get()){
m_v8Handler->Release();
}
}