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

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.        …

第一次写:有不对的可以告诉我!吐舌头

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页