CEF3介绍:
CEF全称Chromium Embedded Framework,是一个基于Google Chromium 的开源项目。Google Chromium项目主要是为Google Chrome应用开发的,而CEF的目标则是为第三方应用提供可嵌入浏览器支持。CEF隔离底层Chromium和Blink的复杂代码,并提供一套产品级稳定的API,发布跟踪具体Chromium版本的分支,以及二进制包。CEF的大部分特性都提供了丰富的默认实现,让使用者做尽量少的定制即可满足需求。在本文发布的时候,世界上已经有很多公司和机构采用CEF,CEF的安装量超过了100万。[CEF wikipedia]页面上有使用CEF的公司和机构的不完全的列表。CEF的典型应用场景包括:
- 嵌入一个兼容HTML5的浏览器控件到一个已经存在的本地应用。
- 创建一个轻量化的壳浏览器,用以托管主要用Web技术开发的应用。
- 有些应用有独立的绘制框架,使用CEF对Web内容做离线渲染。
- 使用CEF做自动化Web测试。
CEF3是基于Chomuim Content API多进程构架的下一代CEF,拥有下列优势:
- 改进的性能和稳定性(JavaScript和插件在一个独立的进程内执行)。
- 支持Retina显示器。
- 支持WebGL和3D CSS的GPU加速。
- 类似WebRTC和语音输入这样的前卫特性。
- 通过DevTools远程调试协议以及ChromeDriver2提供更好的自动化UI测试。
- 更快获得当前以及未来的Web特性和标准的能力。
(摘取自https://github.com/fanfeilong/cefutil/blob/master/doc/CEF%20General%20Usage-zh-cn.md#request-interception ps:cef3官网的翻译文档)
CEF3 版本介绍:
cef_binary_3.2171.1972_windows32:VS2005-2010可直接编译版本,也不存在3.2171.1901的外边框问题,但是输入法点位有问题。(下载链接)
cef_binary_3.2623.1401_windows32,cef3最后一个支持XP系统的版本。(下载链接:cef3.2623.1395编译后文件)
其他编译版本支持MP3等可加群:231250586
CEF3 使用介绍:
CEF3的各个类的介绍我这就不写了,其他博客已经介绍的非常详细了,我这就直接开始说使用过程。先贴两个类:
simple_handler.h
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_ #define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_ #include "include/cef_client.h" #include "include/cef_base.h" #include "include/cef_browser.h" #include "include\base\cef_lock.h" #include "include/cef_web_plugin.h" #include "cef_cookie.h" #include <list> class SimpleHandler : public CefClient, public CefDisplayHandler, public CefDownloadHandler, public CefLifeSpanHandler, public CefLoadHandler, public CefContextMenuHandler, public CefCookieVisitor, public CefWebPluginInfoVisitor { public: SimpleHandler(); ~SimpleHandler(); // Provide access to the single global instance of this object. static SimpleHandler* GetInstance(); // CefClient methods: virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE { return this; } virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE { return this; } virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE { return this; } virtual CefRefPtr<CefContextMenuHandler>GetContextMenuHandler() { return this; } virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler(){ 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; /*在创建新的弹出式浏览器IO线程之前调用;*/ /*本程序主要阻止弹出,在本浏览器中打开;*/ virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& target_url, const CefString& target_frame_name, WindowOpenDisposition target_disposition, bool user_gesture, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr<CefClient>& client, CefBrowserSettings& settings, bool* no_javascript_access) OVERRIDE; // CefLoadHandler methods: /*浏览器加载错误时调用;*/ virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) OVERRIDE; /*浏览器完成加载时调用;*/ virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) OVERRIDE; // CefDownloadHandler methods: /*在下载开始之前调用;*/ virtual void OnBeforeDownload( CefRefPtr<CefBrowser>browser, CefRefPtr<CefDownloadItem>download_item, const CefString&suggested_name, CefRefPtr<CefBeforeDownloadCallback>callback) OVERRIDE; /*在下载状态或进度信息更新时调用;*/ virtual void OnDownloadUpdated( CefRefPtr<CefBrowser>browser, CefRefPtr<CefDownloadItem>download_item, CefRefPtr<CefDownloadItemCallback>callback) OVERRIDE; // CefContextMenuHandler methods: /*在显示上下文菜单之前调用;*/ virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model) OVERRIDE; /*调用来执行从上下文菜单中选择的命;*/ virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, int command_id, EventFlags event_flags) OVERRIDE; // CefCookieVisitor methods: virtual bool Visit(const CefCookie& cookie, int count, int total, bool& deleteCookie)OVERRIDE { return false; }; // Request that all existing browser windows close. // void CloseAllBrowsers(bool force_close); bool IsClosing() const { return is_closing_; } CefRefPtr<CefBrowser> GetBrowser(){return m_browser;} public: CefRefPtr<CefBrowser> m_browser; bool is_closing_; // Include the default reference counting implementation. protected: // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(SimpleHandler); //由于CEF采用多线程架构,有必要使用锁和闭包来保证在多不同线程安全的传递数据。IMPLEMENT_LOCKING定义提供了Lock()和Unlock()方法以及AutoLock对象来保证不同代码块同步访问 IMPLEMENT_LOCKING(SimpleHandler); //My members. public: //My variable RECT m_ClientRc;//浏览器窗口大小; int m_BrowserId;//浏览器ID唯一标识符; CString m_name; bool m_end; CefWindowHandle m_main_wnd;//浏览器的父窗口句柄; //My method void InvokeDefaultBrowser(std::wstring url);//使用默认浏览器打开url; int JugeIsAdvertisementWindow(CefString strUrl);//判断url类型; void SetMainHwnd(CefWindowHandle hwnd);//获取浏览器的父窗口句柄; void CloseHostBrowser(CefRefPtr<CefBrowser>browser, bool force_close) ; //void SetDlg(AccountDlg *Dlg); }; #endif // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
simple_handler.cpp
// 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 "simple_handler.h" #include <sstream> #include <string> #include "include/base/cef_bind.h" #include "include/cef_app.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_helpers.h" #include "client_switches.h" #define MENU_ID_VIEW_DELETHC MENU_ID_USER_FIRST + 200 #define MENU_ID_VIEW_GOTSY MENU_ID_USER_FIRST + 202 #define WM_AFTER_BROSWER_CREATE WM_USER+0x1001 #define URL_Redirect 1 #define URL_DYZS 2 #define URL_OUTLIST 3 namespace { SimpleHandler* g_instance = NULL; } // namespace SimpleHandler::SimpleHandler() : is_closing_(false), m_browser(NULL) { DCHECK(!g_instance); g_instance = this; m_main_wnd = NULL; m_end=false; memset(&m_ClientRc,0,sizeof(RECT)); } SimpleHandler::~SimpleHandler() { g_instance = NULL; } void SimpleHandler::SetMainHwnd(CefWindowHandle hwnd) { //AutoLock lock_scope(this); m_main_wnd = hwnd; } // static SimpleHandler* SimpleHandler::GetInstance() { return g_instance; } void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) { CEF_REQUIRE_UI_THREAD(); if (!m_browser.get()) { m_browser = browser; m_BrowserId = browser->GetIdentifier(); } else if (browser->IsPopup()) { } } bool SimpleHandler::DoClose(CefRefPtr<CefBrowser> browser) { CEF_REQUIRE_UI_THREAD(); if(m_browser) is_closing_ = true; // Allow the close. For windowed browsers this will result in the OS close // event being sent. return false; } void SimpleHandler::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model) { // //在这里,我添加了自己想要的菜单; cef_context_menu_type_flags_t flag = params->GetTypeFlags(); model->Clear(); if (flag & CM_TYPEFLAG_PAGE){ //普通页面的右键消息; model->AddItem(MENU_ID_BACK, L"后退"); model->AddItem(MENU_ID_FORWARD, L"前进"); model->AddSeparator(); model->AddItem(MENU_ID_RELOAD, L"刷新"); model->AddItem(MENU_ID_VIEW_DELETHC, L"清理缓存"); } } bool SimpleHandler::OnContextMenuCommand(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefContextMenuParams> params, int command_id, EventFlags event_flags) { //CefString strLinkURL; CefString strURLLink; CefString* strTargetURL = NULL; int nBuffLength = 0; switch (command_id) { case MENU_ID_VIEW_DELETHC: if (true) { //外部程序实现 } default: break; } return false; } void SimpleHandler::CloseHostBrowser(CefRefPtr<CefBrowser>browser, bool force_close) { if (!CefCurrentlyOn(TID_UI)) { // Execute on the UI thread. CefPostTask(TID_UI, base::Bind(&SimpleHandler::CloseHostBrowser, this, browser, force_close)); return; } int nID = browser->GetIdentifier(); browser->GetHost()->CloseBrowser(force_close); } void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) { CEF_REQUIRE_UI_THREAD(); if (m_browser == NULL) { return; } if(m_browser->IsSame(browser)) m_browser = NULL; } 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); } int SimpleHandler::JugeIsAdvertisementWindow(CefString strUrl) { if ((-1 != url.find(L"http")) || (-1 != url.find(L"https"))) { return URL_Redirect; } return true; } void SimpleHandler::InvokeDefaultBrowser(std::wstring url) { ::ShellExecuteW(NULL,L"open",url.c_str(), NULL, NULL, SW_NORMAL); } bool SimpleHandler::OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& target_url, const CefString& target_frame_name, WindowOpenDisposition target_disposition, bool user_gesture, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr<CefClient>& client, CefBrowserSettings& settings, bool* no_javascript_access) { std::wstring urlFind = target_url; int iURL_type = JugeIsAdvertisementWindow(target_url); if (iURL_type == URL_Redirect)//重定向 { std::wstring url = target_url; this->GetBrowser()->GetMainFrame()->LoadURL(url); return true; } return false; } //文件下载方法重载 void SimpleHandler::OnBeforeDownload(CefRefPtr<CefBrowser>browser,CefRefPtr<CefDownloadItem> download_item,const CefString& suggested_name,CefRefPtr<CefBeforeDownloadCallback>callback) { callback->Continue( download_item ->GetURL(),true); } void SimpleHandler::OnDownloadUpdated(CefRefPtr<CefBrowser>browser,CefRefPtr<CefDownloadItem> download_item,CefRefPtr<CefDownloadItemCallback>callback) { // callback->Cancel(); CefString strUrl = download_item->GetURL(); // ShellExecute(NULL, L"open", strUrl.c_str(), NULL, NULL, SW_SHOW); if(download_item->IsComplete()) { //MessageBox.Show("下载成功"); if(browser->IsPopup()&&!browser->HasDocument()) { //browser->GetHost()->ParentWindowWillClose(); //browser->GetHost()->CloseBrowser(true); } } } void SimpleHandler::OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) { m_end = true; } void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) { CEF_REQUIRE_UI_THREAD(); CString strSendData(title.ToWString().c_str()); //获取页面标题 }
simple_app.h
#pragma once #include "stdafx.h" #include "include\cef_app.h" //#include "client_app.h" class SimpleApp : public CefApp, public CefBrowserProcessHandler, public CefRenderProcessHandler { public: SimpleApp(); ~SimpleApp(); virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() { return this; } // CefApp methods: virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE; /*提供了查看和修改之前的命令行参数的机会*/ virtual void OnBeforeCommandLineProcessing(const CefString& process_type, CefRefPtr<CefCommandLine> command_line) OVERRIDE; // CefBrowserProcessHandler methods: virtual void OnContextInitialized() OVERRIDE; //CefRenderProcessHandler methods /*在WebKit初始化之后调用;*/ /*注册JS方法;*/ virtual void OnWebKitInitialized() OVERRIDE; /*在创建了一个对象的V8上下文之后立即调用;*/ /*注册JS方法;*/ virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE; private: CefRefPtr<CCEFV8HandlerEx> v8Handler_; // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(SimpleApp); private: };
simple_app.cpp
#include "simple_app.h" SimpleApp::SimpleApp() { v8Handler_ = new CCEFV8HandlerEx; } SimpleApp::~SimpleApp() { //v8Handler_->Release(); } CefRefPtr<CefBrowserProcessHandler> SimpleApp::GetBrowserProcessHandler() { return this; } void SimpleApp::OnBeforeCommandLineProcessing(const CefString & process_type, CefRefPtr<CefCommandLine> command_line) { //设置中文语言环境 command_line->AppendSwitchWithValue("--lang", "zh-CN");//设置中文 //command_line->AppendSwitch("--enable-system-flash");//加载系统flash // command_line->AppendSwitchWithValue("--ppapi-flash-version", "27.0.0.187");//加载文件夹内的flash // // //加载flash插件 // command_line->AppendSwitchWithValue("--ppapi-flash-path", "plugins\\pepflashplayer32_27_0_0_187.dll"); } void SimpleApp::OnContextInitialized() { Sleep(0); } void SimpleApp::OnWebKitInitialized() { //JS注册部分 std::string extensionCode = "var app;" "if (!app)" " app = {};" "(function() {" " app.GetId = function() {" " native function GetId();" " return GetId();" " };" "})();"; // JavaScript里调用app.jsInvokeCPlusPlus时,就会去通过CefRegisterExtension注册的CefV8Handler列表里查找 // 找到"v8/app"对应的CCEFV8HandlerEx,就调用它的Execute方法 // 假设v8Handler_是SimpleApp的一个成员变量 //v8Handler_ = new CCEFV8HandlerEx(); bool bRet = CefRegisterExtension("v8/cloudAccountClient", extensionCode, v8Handler_); } void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) { OutputDebugString(_T("ClientAppRenderer::OnContextCreated, create window binding\r\n")); // Retrieve the context's window 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); // Create the "NativeLogin" function. CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("NativeLogin", v8Handler_); // Add the "NativeLogin" function to the "window" object. object->SetValue("NativeLogin", func, V8_PROPERTY_ATTRIBUTE_NONE); }
现在开始讲具体的用法:
初始化cef:
在duilib窗口初始化InitWindow()的时候初始化cef也可以和duilib之前初始化:
通过函数bool CefInitialize(constCefMainArgs& args,
const CefSettings&settings,
CefRefPtr<CefApp>application,
void* windows_sandbox_info);
进行初始化,用户可以通过设置 settings来改变缓存路径,日志路径,单/多进程模式等等;代码如下:
void*sandbox_info = NULL;//Windows下可为null CefMainArgsmain_args(AfxGetInstanceHandle()); CefRefPtr<SimpleApp>app(new SimpleApp);// SimpleApp 继承了cefapp CefSettingssettings; settings.no_sandbox= true; settings.multi_threaded_message_loop= true; //使用主程序消息循环 //settings.single_process= true; //使用单进程模式 TCHARszSpecialPath[MAX_PATH]; memset(szSpecialPath,'\0', sizeof(szSpecialPath)); //缓存路径; CStringpath =L"C:\\Users\\Administrator\\AppData\\Cache"; _tcscpy(szSpecialPath,path); CefString(&settings.cache_path).FromString(szSpecialPath,sizeof(szSpecialPath) / 2, true); CefString(&settings.log_file).FromString(szSpecialPath,sizeof(szSpecialPath) / 2, true);//日志 CefInitialize(main_args,settings, app.get(), sandbox_info);
创建cef:
在duilib窗口初始化InitWindow()的时候创建cef:
通过函数bool CreateBrowser(constCefWindowInfo& windowInfo,
CefRefPtr<CefClient> client,
constCefString& url,
constCefBrowserSettings& settings,
CefRefPtr<CefRequestContext>request_context);
CefWindowInfo windowInfo设置浏览器的窗口信息,如大小和父窗口句柄;
CefString url 创建的浏览器打开的连接;
CefBrowserSettings& settings, 初始化传入参数,CefRefPtr<CefRequestContext>request_context浏览器上下文,问NULL内部会自己创建一个合适的Context;
创建代码:
CefWindowInfowindow_info; RECTrt; GetWindowRect(m_hWnd,&rt); window_info.SetAsChild(m_hWnd,rt);//设置浏览器窗口大小和父窗口句柄 CefRefPtr<SimpleHandler>m_handler; m_handler= new SimpleHandler(); m_handler->SetMainHwnd(m_hWnd); m_handler->m_ClientRc=rt; CefBrowserSettingsbrowserSettings; CefString(&browserSettings.default_encoding).FromASCII("UTF-8"); browserSettings.universal_access_from_file_urls= STATE_ENABLED; std::stringurl; url= "www.baidu.com"; CefBrowserHost::CreateBrowser(window_info,m_handler.get(), url, browserSettings, NULL);
使cef窗口跟随duilib窗口大小的改变而改变:
HWND hWnd=GetHWND();//duilib 的窗口句柄 RECT rect; HWND cefB_hwnd = ::FindWindowEx(hWnd, NULL, L"CefBrowserWindow", NULL); //cef的窗口句柄 switch( uMsg ) { case WM_SIZE: { if (cefB_hwnd) { GetClientRect(hWnd, &rect);//获取窗口大小 int nWidth = rect.right - rect.left - 2; int nHeight = rect.bottom - rect.top - 31; ::MoveWindow(cefB_hwnd,rect.left+1,rect.top+30, nWidth, nHeight, TRUE);//改变cef窗口大小 //::MoveWindow(cefB_hwnd,1, 29, LOWORD(lParam)-2, HIWORD(lParam), TRUE); } break; }
部分功能使用介绍:
1. Flash(命令行设置)设置:
通过cef命令行参数设置路径:
/*提供了查看和修改之前的命令行参数的机会*/
virtual voidOnBeforeCommandLineProcessing(const CefString& process_type,
CefRefPtr<CefCommandLine>command_line) OVERRIDE;
重写 cefapp类中的OnBeforeCommandLineProcessing函数设置命令行,
代码如下:
void SimpleApp::OnBeforeCommandLineProcessing(const CefString &process_type, CefRefPtr<CefCommandLine> command_line) { //设置中文语言环境 command_line->AppendSwitchWithValue("--lang","zh-CN"); //command_line->AppendSwitch("--disable-web-security");//关闭同源策略 //command_line->AppendSwitchWithValue("--ppapi-flash-version","27.0.0.183"); //加载flash插件 //command_line->AppendSwitchWithValue("--ppapi-flash-path","plugins\\pepflashplayer.dll"); }
2. 右键菜单修改
如上图,修改右键菜单选项,需要重写CefContextMenuHandler类中的OnBeforeContextMenu和OnContextMenuCommand两个方法来实现;
OnBeforeContextMenu:右键后触发,可以清空,添加,删除右键菜单内的内容重写例子如下代码:
cef_context_menu_type_flags_tflag = params->GetTypeFlags(); model->Clear(); if (flag & CM_TYPEFLAG_PAGE){ //普通页面的右键消息; model->AddItem(MENU_ID_BACK,L"后退"); model->AddItem(MENU_ID_FORWARD, L"前进"); model->AddSeparator(); model->AddItem(MENU_ID_RELOAD, L"刷新"); model->AddItem(MENU_ID_VIEW_DELETHC,L"清理缓存");}
OnContextMenuCommand处理右键菜单选中的命令,配合OnBeforeContextMenu可以实现自己想要的命令效果,如后面实现的清理缓存功能。
3. 基于cef的http请求
基于CEF开发应用时,可能会有URL请求处理的需求,比如HTTP下载或上传,此时可以利用CEF提供的类库来完成,而不必自己实现或引入其它第三方的类库缓存清理方法(来源网络)
可以用过继承修改CefURLRequestClient类进行封装实现,也可以直接调用该类,具体网上有很好的例子:
https://blog.csdn.net/foruok/article/details/50679694
4. 弹出式链接重定向到当前窗口
在开发过程中,需求可能只允许一个窗口,这是就要将链接重定向到现在的窗口上
首先需要继承类:CefLifeSpanHandler,重写方法:OnBeforePopup。这个方法在创建新的弹出式浏览器IO线程之前调用,可以捕获到当前的url,然后GetBrowser()->GetMainFrame()->LoadURL(url);就可以在本窗口打开新的url。
补充,多标签页也可以通过这个方法实现,也可以吧捕获到的URL穿个本地默认的浏览器打开。
5. JS与c++交互
Cef3最有用的一个功能,就是js和c++交互。
C++调用JS比较简单,cef提供了函数,c++可以直接让界面执行js方法代码如下:
CefRefPtr<CefFrame> 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)
{
OutputDebugString(_T("OnContextCreated,create window binding\r\n"));
// 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!\r\n")); } // Function does not exist. return false; }
在Execute方法中 执行c++代码,这样就实现了JS调用c++代码;
C++也可以通过ExecuteFunction(NULL, callback);回调参数给JS方法。
6. 清理缓存:
浏览器想要清理缓存,没有相关接口,只能通过删除缓存的文件来实现清理缓存的功能。
二、 调试和问题:
Cef3存的的坑还是很多的,本人还没踩全,在开发过程中遇到一些,现在说几点:
1. 判断cef是否启动:若是多进程的情况下,可以通过任务管理器来判断
2. Cef3如果是启动时崩溃,请看资源目录是否文件都齐全;在退出时崩溃,程序退出前,在释放了所有的Browser后就直接调用退出操作。
3. 开了多进程时,Render进程无法断点调试,可在初始化的时候用单进程模式:settings.single_process = true;
4. Cef加载的很慢时,查看是否开启了IE代理(网络正常的情况下)
5. 还有就是版本的问题CEF(2272)之前的版本存在输入法点位问题,cef3.2171.1901版本在改变窗口大小时会出现白色边框(亲测)
6. 页面上的一些请求需要自己post
7. Cef3有下载接口但是需要自己实现下载页面
8. …
第一次写:有不对的可以告诉我!