CEF完整嵌入DUI窗体(四) --将浏览器的回调通知应用层
来源:互联网 发布:navicat linux 注册码 编辑:程序博客网 时间:2024/06/06 20:25
上一节主要讲解了一些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中设置浏览器控件的显示与否,这也是我在实际项目中用到最多的两个回调接口;
- CEF完整嵌入DUI窗体(四) --将浏览器的回调通知应用层
- CEF完整嵌入DUI窗体(三) --基本浏览器功能
- CEF完整嵌入DUI窗体(一) --Cef3简介
- CEF完整嵌入DUI窗体(五) --JS调用C++注册的函数
- CEF完整嵌入DUI窗体(二) --在程序中初始化Cef
- MFC嵌入浏览器框架CEF
- CEF方面的研究(四) CEF嵌入MFC对话框关闭崩溃问题解决办法
- CEF方面的研究(三) 将CEF嵌入MFC对话框程序
- Dui缓慢飘出窗体的实现
- 关于MFC将一个窗体嵌入另一个窗体的方法
- 如何将Chromium Embedded Framework (CEF) 嵌入到你自己的程序中
- 如何将Chromium Embedded Framework (CEF) 嵌入到你自己的程序中
- MFC应用程序中嵌入一个谷歌cef浏览器
- MFC应用程序中嵌入一个谷歌cef浏览器
- CEF 嵌入到MFC的流程,包括启动CEF以及CEF程序的关闭流程。
- 将窗体嵌入到Panel
- android 摄像头对焦,zoom的通知事件回调,告诉java应用层已经对焦完成
- vb:将窗体嵌入桌面的一段代码
- 3
- 嵌入式每日学习心得2017.07.10
- python构建restful服务
- HDU 4370 0,1规划转换成最短路问题
- 数据库的约束
- CEF完整嵌入DUI窗体(四) --将浏览器的回调通知应用层
- 基于讯飞开放平台的安卓语音开发——语音听写(语音→文本)
- Docker学习
- CentOS_6.5安装GitLab_7
- Webpack2/3配置ExtractTextPlugin和Autoprefixer
- 记录开发HIS系统体温单的思路历程
- JS-9-拷贝
- AES128加解密(SBOX+Rijndael)两种方法
- 如何测试网页的登录页面