CEF使用

介绍

主页: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 object
,const CefV8ValueList& arguments
,CefRefPtr& retval
,CefString& exception);
private:
// Map of message callbacks.
typedef std::map<std::pair<std::string, int>,
std::pair<CefRefPtr, CefRefPtr > >
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 object //JavaScript调用者对象
,const CefV8ValueList& arguments //JavaScript传递的参数
,CefRefPtr& 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 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;

}

在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 context = it->second.first;
CefRefPtr 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();
 	}
 }

下载

https://cef-builds.spotifycdn.com/index.html

https://cef-builds.spotifycdn.com/cef_binary_98.1.19%2Bg57be9e2%2Bchromium-98.0.4758.80_windows64.tar.bz2

https://zhuanlan.zhihu.com/p/344306287

https://www.zhihu.com/column/c_1333096419650269184

https://zhuanlan.zhihu.com/p/346779059

https://zhuanlan.zhihu.com/p/365414447

主要的几个文件夹:

cmake:该目录下存放了配置和构建以Windows作为编译环境的cmake配置文件,具体内容可以自行查看。Debug和Release:这两个文件夹中,打开会看到已经编译好的CEF核心库文件include:libcef本身提供的头文件以及wrapper会使用到的头文件。libcef_dll:存放了libcef_dll_wrapper源码。Resources:CEF作为内核的浏览器运行时需要用到的资源文件。tests:存放了利用libcef、以及wrapper作为库来编写的浏览器Demo。其中,cefsimple编译出来的是一个简单的浏览器,而cefclient编译出来的是一个展示了cef许多API功能的exe。

编译


# To perform a Windows build using a 32-bit CEF binary distribution:
#   Using the Visual Studio 2019 IDE:
#     > cmake -G "Visual Studio 16" -A Win32 ..
#     Open buildcef.sln in Visual Studio and select Build > Build Solution.
#
#   Using Ninja with Visual Studio 2019 command-line tools:
#     (this path may be different depending on your Visual Studio installation)
#     > "C:Program Files (x86)Microsoft Visual Studio2019ProfessionalVCAuxiliaryBuildvcvars32.bat"
#     > cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Debug ..
#     > ninja cefclient cefsimple

也可以使用cmake

libcef和js交互

https://blog.csdn.net/weixin_40850689/article/details/118636580

Cef与JS交互:http://blog.csdn.net/foruok/article/details/50573612Cef与JS交互二:http://www.cnblogs.com/guolixiucai/p/4943748.html

Duilib嵌入cef3,实现浏览器功能

弹出式链接重定向到当前窗口

在开发过程中,需求可能只允许一个窗口,这是就要将链接重定向到现在的窗口上

首先需要继承类:CefLifeSpanHandler,重写方法:OnBeforePopup。这个方法在创建新的弹出式浏览器IO线程之前调用,可以捕获到当前的url,然后GetBrowser()->GetMainFrame()->LoadURL(url);就可以在本窗口打开新的url。

补充,多标签页也可以通过这个方法实现,也可以吧捕获到的URL穿个本地默认的浏览器打开。

JS与c++交互

Cef3最有用的一个功能,就是js和c++交互。

C++调用JS比较简单,cef提供了函数,c++可以直接让界面执行js方法代码如下:

CefRefPtr frame

frame->ExecuteJavaScript(“alert(“c++呼叫JS “)”,frame->GetURL(), 0);

c++执行上面两行代码时,浏览器就会弹框,显示内容“c++呼叫JS”,当然也可以执行其他的js,比如一些对表的填充操作。

JS调用C++比较复杂,浏览器在创建时需要通过CefRenderProcessHandler类中的两个方法在上下文中注册JS方法,在浏览器调用注册的JS方法时,回调到类CefV8Handler中的Execute方法,进行处理。具体实现代码:


void SimpleApp::OnWebKitInitialized()
 
{
 
//JS注册部分
 
std::string extensionCode =
 
  "var app;"
 
        "if (!app)"
 
        "    app = {};"
 
        "(function(){"
 
        "    app.GetId = function() {"
 
        "        native function GetId();"
 
        "        return GetId();"
 
        "    };"
 
        "})();";
    
    // JvaScript里调用app.jsInvokeCPlusPlus时,就会去通过CefRegisterExtension注册的CefV8Handler列表里查找

// 找到"v8/app"对应的CCEFV8HandlerEx,就调用它的Execute方法

// 假设v8Handler_是SimpleApp的一个成员变量

//v8Handler_ = newCCEFV8HandlerEx();

 

bool bRet =CefRegisterExtension("v8/app", extensionCode, v8Handler_);

}

void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser>browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context>context)

{undefined

OutputDebugString(_T("OnContextCreated,create window binding
"));

 

// Retrieve the context'swindow object.

CefRefPtr<CefV8Value>object = context->GetGlobal();



 

CefRefPtr<CefV8Value>funcw = CefV8Value::CreateFunction("register",v8Handler_);//第一个参数和SetValue参数保持一致,否则无法调用

// Add the"register" function to the "window" object.

   object->SetValue("register", funcw,V8_PROPERTY_ATTRIBUTE_NONE);

}

OnWebKitInitialized()注册了一个js对象app,app有一个方法GetId();

OnContextCreated注册了一个js方法:register;

浏览器调用了js方法:app.Getid()或register()后:


bool CCEFV8HandlerEx::Execute(const CefString& name  /*JavaScript调用的C++方法名字*/,
CefRefPtr<CefV8Value> object /*JavaScript调用者对象*/,
 const CefV8ValueList&arguments /*JavaScript传递的参数*/,       CefRefPtr<CefV8Value>& retval /*返回给JS的值设置给这个对象*/,
CefString& exception/*通知异常信息给JavaScript*/)
 
{
 
if(name.compare(CefString("register")) ==0) //测试
 
{
 
      if (arguments.size() == 2&& arguments[0]->IsString() &&arguments[1]->IsFunction())
 
      {
 
      string strUrl =arguments[0]->GetStringValue();
 
      CefRefPtr<CefV8Value>callback = arguments[1];
 
 
 
      CefV8ValueListarguments11;
 
            arguments11.push_back(CefV8Value::CreateString("{"eid":"","message":"请登录","status":308}"));
 
            // Execute thecallback.
 
            callback->ExecuteFunction(NULL,arguments11);
 
      }
 
      return true;
 
}
 
      else
 
      {
 
            OutputDebugString(_T("failed!
"));
 
      }
 
// Function does not exist.
 
return false;
 
}

在Execute方法中 执行c++代码,这样就实现了JS调用c++代码;

C++也可以通过ExecuteFunction(NULL, callback);回调参数给JS方法。

参考:

https://github.com/fanfeilong/cefutil

CEF General Usage(CEF3预览)

JavaScript和Cpp交互示例

https://github.com/karllen/cef3-duilib-YDDemo

https://blog.csdn.net/mfcing/category_3041287.html

Cef功能开发经验总结

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
我今天也不想吃肉的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容