上一节主要讲解了一些Cef3浏览器的基本功能,这一节我们讲解下如何将Cef的一些主要回调接口再次向应用层抛出,因为我们在最上层使用浏览器的时候也需要知道什么时候浏览器创建完成,什么时候url加载完毕,还在上层对浏览器窗口做一些操作,这种回调的思想我们也借鉴google回调封装的经验;
我们也按照功能来区分接口集,然后封装成相关的接口类,我们只关心生命周期,显示和加载的相关回调接口;
//IDisplayHandleSolt 回调类
class IDisplayHandleSolt : public CSoltBaseClass {
public:
//地址改变
virtual void OnAddressChange(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& url) {}
//标题改变
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) {}
//页面图标改变
virtual void OnFaviconURLChange(CefRefPtr<CefBrowser> browser,
const std::vector<CefString>& icon_urls) {}
//页面全屏时调用
virtual void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
bool fullscreen) {}
//浏览器显示工具提示
virtual void OnTooltip(CefRefPtr<CefBrowser> browser,
CefString& text) {}
//接收到状态信息时
virtual void OnStatusMessage(CefRefPtr<CefBrowser> browser,
const CefString& value) {}
//调用显示控制台信息
virtual void OnConsoleMessage(CefRefPtr<CefBrowser> browser,
const CefString& message,
const CefString& source,
int line) {}
virtual ~IDisplayHandleSolt() {}
};
//CefLoadHandler回调类
class ILoadHandleSlot : public virtual CSoltBaseClass {
public:
//加载出错
virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefLoadHandler::ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) {}
//加载状态改变
virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) {}
//加载开始
virtual void OnLoadStart(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame) {}
//加载完成
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) {}
virtual ~ILoadHandleSlot() {}
};
//CefLifeSpanHandler回调类
class ILifeSpanHandleSlot : public virtual CSoltBaseClass {
public:
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) {}
virtual void DoClose(CefRefPtr<CefBrowser> browser) {}
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) {}
virtual ~ILifeSpanHandleSlot() {}
};
我们在这里并没有将类定义为纯虚接口,因为派生类可以选择性的多态实现自己感兴趣的接口,所以每个接口类定义一个virtual析构函数,那么我们在哪里将派生类的指针动态的绑定到自己创建的浏览器控件上呢?我们同样按照google封装cef的方案,定义一个类来管理所有的回调指针,负责将相关的接口集类指针传递给内部:
class ISetHandleSolt {
public:
//浏览器回调实例指针设置函数
virtual reeiss_cef::ILoadHandleSlot* GetILoadHandleSlot(std::wstring browser_name) { return NULL; }
virtual reeiss_cef::ILifeSpanHandleSlot* GetILifeSpanHandleSlot(std::wstring browser_name) { return NULL; }
virtual reeiss_cef::IDisplayHandleSolt* GetIDisplayHandleSolt(std::wstring browser_name){ return NULL; }
virtual ~ISetHandleSolt () {}
}
我们在每个DuiLib类中需要用到浏览器控件时,继承这个类,在控件创建时调用这个几个接口将定义的name传递给应用层以让其区分创建的浏览器控件,我们将DisplayHandleSolt ,ILoadHandleSlot ,ILifeSpanFileDialogSolt 这几个接口集的类指针保存在CBrowserClient类中,在这里我们不能将派生类的this指针裸着放在这里,因为派生类析构之后Cef控件内部在不知道的情况下依然回调从而引起奔溃,那我们使用智能指针吗?this指针在对象析构的时候并不能传递到智能指针的引用计数,所以我们自己写一个工具来对this指针进行封装,以便析构之后能及时的通知到我们内部:
我们让用到浏览器控件的Dui窗体类析构函数中通知工具已经析构,如果手动在各个使用类的析构函数中添加这个通知操作,那简直是对封装二字的侮辱。那我们继续使用继承多态来实现这个功能,定义一个基类,在析构函数中调用工具类的回调接口
typedef boost::function<void (bool)> FeedBackFunction;
class CSoltBaseClass{
public:
void SetFeedBackFunction(FeedBackFunction feed_back) {
feed_back_ = feed_back;
}
virtual ~CSoltBaseClass() {
feed_back_(false);
}
private:
FeedBackFunction feed_back_;
};
下边是工具类的声明和定义:
class CSoltBaseClass;
class IDisplayHandleSolt;
class ILoadHandleSlot;
class ILifeSpanHandleSlot;
class CSmartCountTool {
public:
enum display_type {
display
};
enum load_type {
load
};
enum life_span_type {
life_span
};
enum life_span_file_dialog_type {
life_span_file_dialog
};
CSmartCountTool();
//设置回调指针
void SetSolt(CSoltBaseClass *solt);
//回调基类中 析构函数会调用这个回调
void FeedBack(bool can_use);
//查询指针是否可用
bool CanUse();
//返回不同类型指针 通过不同类型只重载不同的函数
IDisplayHandleSolt* GetSoltPtr(display_type);
ILoadHandleSlot* GetSoltPtr(load_type);
ILifeSpanHandleSlot* GetSoltPtr(life_span_type);
ILifeSpanFileDialogSolt* GetSoltPtr(life_span_file_dialog_type);
private:
bool can_use_;
CSoltBaseClass *solt_;
};
//下边是定义
CSmartCountTool::CSmartCountTool() : can_use_(true) {
}
void CSmartCountTool::SetSolt(CSoltBaseClass *solt) {
solt_ = solt;
if (solt_) {
solt_->SetFeedBackFunction(boost::bind(&CSmartCountTool::FeedBack, this, _1));
}
}
void CSmartCountTool::FeedBack(bool can_use) {
can_use_ = can_use;
}
bool CSmartCountTool::CanUse() {
return solt_ && can_use_;
}
IDisplayHandleSolt* CSmartCountTool::GetSoltPtr(display_type) {
return dynamic_cast<IDisplayHandleSolt*>(solt_);
}
ILoadHandleSlot* CSmartCountTool::GetSoltPtr(load_type) {
return dynamic_cast<ILoadHandleSlot*>(solt_);
}
ILifeSpanHandleSlot* CSmartCountTool::GetSoltPtr(life_span_type) {
return dynamic_cast<ILifeSpanHandleSlot*>(solt_);
}
ILifeSpanFileDialogSolt* CSmartCountTool::GetSoltPtr(life_span_file_dialog_type) {
return dynamic_cast<ILifeSpanFileDialogSolt*>(solt_);
}
那么我们什么时候将继承回调接口集的派生类指针传递给CBrowserClient类呢? 第三节在介绍创建浏览器的时候我们看到创建浏览器的的接口有关于这几个指正的传递:
//这里设置了我们需要的向上层抛出的回调接口,之后会讲解
life_handle_->SetSolt(life_handle);
load_handle_->SetSolt(load_handle);
display_handle_->SetSolt(display_handle);
filedialog_handle_->SetSolt(filedialog_handle);
之后通过工具类的CanUse()接口来判断该派生类的指针是否可用,我们在CBrowserClient类的相关回调接口中再调用传进来的接口指针:
//加载错误
void CBrowserClient::OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) {
CEF_REQUIRE_UI_THREAD();
if (load_handle_->CanUse()) {
load_handle_->GetSoltPtr(CSmartCountTool::load)->OnLoadError(browser, frame, errorCode, errorText, failedUrl);
}
}
//加载状态改变
void CBrowserClient::OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) {
CEF_REQUIRE_UI_THREAD();
if (load_handle_->CanUse()) {
load_handle_->GetSoltPtr(CSmartCountTool::load)->OnLoadingStateChange(browser, isLoading, canGoBack, canGoForward);
}
}
//加载开始
void CBrowserClient::OnLoadStart(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame) {
CEF_REQUIRE_UI_THREAD();
if (load_handle_->CanUse()) {
load_handle_->GetSoltPtr(CSmartCountTool::load)->OnLoadStart(browser, frame);
}
}
//加载完成
void CBrowserClient::OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) {
CEF_REQUIRE_UI_THREAD();
browser_loaded_ = true;
if (load_handle_->CanUse()) {
load_handle_->GetSoltPtr(CSmartCountTool::load)->OnLoadEnd(browser, frame, httpStatusCode);
}
}
//地址改变
void CBrowserClient::OnAddressChange(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& url) {
CEF_REQUIRE_UI_THREAD();
if (display_handle_->CanUse()) {
display_handle_->GetSoltPtr(CSmartCountTool::display)->OnAddressChange(browser, frame, url);
}
}
//标题改变
void CBrowserClient::OnTitleChange(CefRefPtr<CefBrowser> browser,
const CefString& title) {
CEF_REQUIRE_UI_THREAD();
if (display_handle_->CanUse()) {
display_handle_->GetSoltPtr(CSmartCountTool::display)->OnTitleChange(browser, title);
}
}
//页面图标改变
void CBrowserClient::OnFaviconURLChange(CefRefPtr<CefBrowser> browser,
const std::vector<CefString>& icon_urls) {
CEF_REQUIRE_UI_THREAD();
if (display_handle_->CanUse()) {
display_handle_->GetSoltPtr(CSmartCountTool::display)->OnFaviconURLChange(browser, icon_urls);
}
}
//页面全屏时调用
void CBrowserClient::OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
bool fullscreen) {
CEF_REQUIRE_UI_THREAD();
if (display_handle_->CanUse()) {
display_handle_->GetSoltPtr(CSmartCountTool::display)->OnFullscreenModeChange(browser, fullscreen);
}
}
//浏览器显示工具提示
bool CBrowserClient::OnTooltip(CefRefPtr<CefBrowser> browser,
CefString& text) {
CEF_REQUIRE_UI_THREAD();
if (display_handle_->CanUse()) {
display_handle_->GetSoltPtr(CSmartCountTool::display)->OnTooltip(browser, text);
}
return true;
}
//接收到状态信息时
void CBrowserClient::OnStatusMessage(CefRefPtr<CefBrowser> browser,
const CefString& value) {
CEF_REQUIRE_UI_THREAD();
if (display_handle_->CanUse()) {
display_handle_->GetSoltPtr(CSmartCountTool::display)->OnStatusMessage(browser, value);
}
}
//调用显示控制台信息
bool CBrowserClient::OnConsoleMessage(CefRefPtr<CefBrowser> browser,
const CefString& message,
const CefString& source,
int line) {
CEF_REQUIRE_UI_THREAD();
if (display_handle_->CanUse()) {
display_handle_->GetSoltPtr(CSmartCountTool::display)->OnConsoleMessage(browser, message, source, line);
}
return true;
}
//浏览器创建完成
void CBrowserClient::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
base::AutoLock lock_scope(lock_);
if (!browser_) {
browser_ = browser;
}
if (life_handle_->CanUse()) {
life_handle_->GetSoltPtr(CSmartCountTool::life_span)->OnAfterCreated(browser);
}
}
//关闭浏览器
bool CBrowserClient::DoClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
if (life_handle_->CanUse()) {
life_handle_->GetSoltPtr(CSmartCountTool::life_span)->DoClose(browser);
}
}
//关闭浏览器
void CBrowserClient::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
CEF_REQUIRE_UI_THREAD();
base::AutoLock lock_scope(lock_);
if (life_handle_->CanUse()) {
life_handle_->GetSoltPtr(CSmartCountTool::life_span)->OnBeforeClose(browser);
}
browser_ = NULL;
}
到这里,我们就可以将相关的回调接口再次向上层抛出,以实现对应用层的通知。现学现用,借鉴了Cef的封装方式,对回调接口做了一个类似的封装;我们在最上层需要使用这些回调接口,例如在OnAfterCreated中导航url,在OnLoadEnd中设置浏览器控件的显示与否,这也是我在实际项目中用到最多的两个回调接口;