开发环境:
VS2015+Qt5.9
关于CEF
CEF全称是Chromium Embedded Framework,它是Chromium的Content API的封装库,基于Google Chromium 的开源项目,而Google Chromium项目主要是为Google Chrome应用开发的,而CEF的目标则是为第三方应用提供可嵌入浏览器支持
主要组成分为
Chromium:基础,网络堆栈,线程,消息机制,log,进程控制,生成Web browser。
WebKit:提供DOM解析,布局,事件处理,渲染,HTML5JS的API。
V8:JS引擎。
Skia:2D图形库。
Angle:3D图形转换,和DirectX有关。
目前CEF分为CEF1,CEF2,CEF3,其中前者使用的是单进程架构,后两者是多进程架构。
在开发使用中一般都是基于CEF3开发。
CEF3是基于Chomuim Content API多进程构架的下一代CEF,拥有下列优势:
1.改进的性能和稳定性(JavaScript和插件在一个独立的进程内执行)。
2.支持Retina显示器。
3.支持WebGL和3D CSS的GPU加速。
4.类似WebRTC和语音输入这样的前卫特性。
5.通过DevTools远程调试协议以及ChromeDriver2提供更好的自动化UI测试。
6.更快获得当前以及未来的Web特性和标准的能力。
CEF使用(QCef封装)
单一执行体(Single Executable)
以windows平台为例子
在使用CEF3的时候,需要在主进程中启动CEF
CefEnableHighDPISupport();
//入口函数(Entry-Point Function)
HINSTANCE hInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
CefMainArgs main_args(hInstance);
void* sandbox_info = nullptr; //沙盒信息为空
CefRefPtr<MyRenderProcessHandler> app(new MyRenderProcessHandler);//处理进程相关的回调。
int exit_code = CefExecuteProcess(main_args, app.get(), sandbox_info);
if (exit_code >= 0) {
// 子流程已经完成,返回
return exit_code;
}
CefSettings settings;
settings.no_sandbox = true; //关闭沙盒模式
settings.multi_threaded_message_loop = true; //多线程消息循环
settings.log_severity = LOGSEVERITY_DISABLE;//日志
//settings.windowless_rendering_enabled = true; //开启离屏模式
CefInitialize(main_args, settings, app_r, sandbox_info);
//启动CEF消息循环,运行后会阻塞到CefQuitMessageLoop()被唤醒
CefRunMessageLoop();
//关闭CEF
CefShutdown();
CEF3常用类和接口
一:CefClient:回调管理类
CefClient提供访问browser-instance-specific的回调接口,单实例CefClient可以共数任意数量的浏览器进程。
比如处理Browser的生命周期,右键菜单,对话框,通知显示, 拖曳事件,焦点事件,
键盘事件等等。如果没有对某个特定的处理接口进行实现会造成什么影响,请查看
cef_client.h文件中相关说明。
举例代码说明(目前小编用到的开发代码)
class CefBrowserHandler: public QObject,
public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefFocusHandler,
public CefLoadHandler//,
//public CefRenderHandler //离屏渲染(关于离屏渲染,后续介绍)
{
Q_OBJECT
public:
CefBrowserHandler();
public:
// 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<CefFocusHandler> GetFocusHandler() OVERRIDE { return this; }
//virtual CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE { return this; }
// CefLifeSpanHandler methods:
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
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 OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward) OVERRIDE;
virtual void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, TransitionType transition_type) OVERRIDE;
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) OVERRIDE;
// CefFocusHandler methods:
virtual void OnGotFocus(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message);
// Member accessors.
void CloseAllBrowsers(bool forceClose);
CefRefPtr<CefBrowser> GetBrower() { return m_Browser; }
bool IsClosing() { return m_bIsClosing; }
// CefRenderHandler methods:
/*virtual bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;
virtual void OnPaint(CefRefPtr<CefBrowser> browser,
PaintElementType type,
const RectList& dirtyRects,
const void* buffer,
int width,
int height) OVERRIDE;
virtual bool GetScreenPoint(CefRefPtr<CefBrowser> browser,
int viewX,
int viewY,
int& screenX,
int& screenY) OVERRIDE;
virtual void OnCursorChange(CefRefPtr<CefBrowser> browser,
CefCursorHandle cursor,
CursorType type,
const CefCursorInfo& custom_cursor_info) OVERRIDE;*/
signals:
void loadStarted();
void loadFinished(bool loadSuccess);
void browserCreated();
void urlChanged(const QString& url);
void titleChanged(const QString& title);
void loadingStateChanged(bool isLoading, bool canGoBack, bool canGoForward);
void webViewGotFocus();
void recvRenderMsg(const QString& msg);
private:
CefRefPtr<CefBrowser> m_Browser;
int m_BrowserId;
int m_BrowserCount;
bool m_bIsClosing;
typedef std::list<CefRefPtr<CefBrowser>> BrowserList;
BrowserList _browserList;
IMPLEMENT_REFCOUNTING(CefBrowserHandler);
IMPLEMENT_LOCKING(CefBrowserHandler);
};
1.Browser的生命周期从执行 CefBrowserHost::CreateBrowser() 或者CefBrowserHost::CreateBrowserSync()开始
创建代码,以windows平台为例子
//自定义此结构体来管理浏览器行为
CefBrowserSettings cSettings;
cSettings.file_access_from_file_urls = STATE_ENABLED; //url文件能否访问其他url文件
cSettings.universal_access_from_file_urls = STATE_ENABLED; //url文件能否访问所有url
cSettings.javascript_access_clipboard = STATE_ENABLED; //js是否可以访问剪贴板
cSettings.plugins = STATE_DISABLED; //是否加载插件
//cSettings.windowless_frame_rate = 60; //设置帧率,默认值是30
//根据窗口id来确定浏览器渲染位置
CefWindowInfo info;
CefWindowHandle hWnd = (CefWindowHandle)this->winId();
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = this->width()*qApp->devicePixelRatio();
rect.bottom = this->height()*qApp->devicePixelRatio();
info.SetAsChild(hWnd, rect);
QString url = "www.baidu.com";
//初始化CefClient子类,其中CefLifeSpanHandler类提供了管理Browser生命周期所必需的回调
CefRefPtr<CefBrowserHandler> browserHandler = GetBrowserHandler(browserHandlerIndex);
//创建浏览器
CefBrowserHost::CreateBrowser(info, browserHandler.get(), CefString(url.toStdWString()), _settings, NULL);
2.当Browser对象创建后OnAfterCreated() 方法立即执行,宿主程序可以用这个方法来保持对Browser对象的引用。
void CefBrowserHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
//保护成员不受多线程上的访问
AutoLock lock_scope(this);
if (!_browser.get()) {
// 创建浏览器成功后弹出浏览器主窗口
_browser = browser;
_browserId = browser->GetIdentifier();
//发送信号,处理某些业务逻辑
emit browserCreated();
}
else if (browser->IsPopup()) {
// 如果浏览器窗口已经弹出,加入list,方便后续管理(保证只能在CEF UI线程访问)
_browserList.push_back(browser);
}
//记录浏览器个数
++_browserCount;
}
3.Browser的生命周期从执行CefBrowserHost::CloseBrowser()销毁Browser对象结束
Browser对象的关闭事件来源于他的父窗口的关闭方法(比如,在父窗口上点击X控钮。)。父窗口需要调用 CloseBrowser(false) 并且等待操作系统的第二个关闭事件来决定是否允许关闭。
void CefBrowserHandler::CloseAllBrowsers(bool forceClose)
{
qInfo() << "CloseAllBrowsers::" << __FUNCTION__;
if (!CefCurrentlyOn(TID_UI)) {
// 在UI线程中执行
CefPostTask(TID_UI, base::Bind(&CefBrowserHandler::CloseAllBrowsers, this, forceClose));
return;
}
if (_browser == NULL)
return;
if (!_browserList.empty()) {
//关闭浏览器列表存在的浏览器
BrowserList::const_iterator it = _browserList.begin();
for (; it != _browserList.end(); ++it)
(*it)->GetHost()->CloseBrowser(forceClose);
return;
}
if (_browser.get()) {
// 请求关闭浏览器
_browser->GetHost()->CloseBrowser(forceClose);
}
}
DoClose方法设置m_blsClosing 标志位为true,并返回false以再次发送操作系统的关闭事件。
bool CefBrowserHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
qInfo() << "DoClose::" << __FUNCTION__;
CEF_REQUIRE_UI_THREAD();
//保护成员不受多线程上的访问
AutoLock lock_scope(this);
if (browser->GetIdentifier() == _browserId) {
_isClosing = true;
}
//再次发送操作系统的关闭事件
return false;
}
当操作系统捕捉到第二次关闭事件,它才会允许父窗口真正关闭。该动作会先触发OnBeforeClose()回调,请在该回调里释放所有对浏览器对象的引用
void CefBrowserHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
qInfo() << "OnBeforeClose::" << __FUNCTION__;
CEF_REQUIRE_UI_THREAD();
//保护成员不受多线程上的访问
AutoLock lock_scope(this);
if (_browserId == browser->GetIdentifier()) {
_browser = nullptr;
}
else if (browser->IsPopup()) {
//清空浏览器列表并关闭销毁浏览器对象
BrowserList::iterator bit = _browserList.begin();
for (; bit != _browserList.end(); ++bit) {
if ((*bit)->IsSame(browser)) {
_browserList.erase(bit);
break;
}
}
}
if (--_browserCount == 0) {
// 当所有浏览器窗口已关闭退出时应用程序消息循环.
CefQuitMessageLoop();
}
}
二:CefApp
CefApp接口提供了不同进程的可定制回调函数,包含与进程,命令行参数,代理,资源管理相关的回调类,一些功能是由所有进程共享的,有些必须实现浏览器的过程中,必须在渲染过程中执行,可以让开发者定制属于自己的逻辑。
其中重要的回调函数如下:
1.OnBeforeCommandLineProcessing 提供了以编程方式设置命令行参数的机会。
2.OnRegisterCustomSchemes 提供了注册自定义schemes的机会。
3.CefBrowserProcessHandler 返回定制Browser进程的Handler,该Handler包括了OnContextInitialized这样的回调。
4.CefRenderProcessHandler 返回定制Render进程的Handler,该Handler包含了JavaScript相关的一些回调以及消息处理的回调。
举例代码说明(小编所用到的回调类)
//CefBrowserProcessHandler
class CefAppHandler : public CefApp,
public CefBrowserProcessHandler
{
public:
CefAppHandler();
public:
// CefApp methods:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE {
return this;
}
private:
IMPLEMENT_REFCOUNTING(CefAppHandler);
};
//CefRenderProcessHandler
class MyRenderProcessHandler :public CefApp,
public CefRenderProcessHandler
{
public:
MyRenderProcessHandler();
CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE
{
return this;
}
void OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context);
private:
IMPLEMENT_REFCOUNTING(MyRenderProcessHandler);
};
详细定制逻辑功能不详说了,因功能而异实现对应逻辑。
三:CefBrowser和CefFrame
CefBrowser管理renderer进程中执行浏览相关的类,包括网页前进后退,历史导航,加载字符串和请求,发送编辑命令,提取text/html内容,来源检索,加载请求等。
CefFrame用于加载特定url,在该运行环境下执行JavaScript代码等。
CefBrowser和CefFrame对象被用来发送命令给浏览器以及在回调函数里获取状态信息。每个CefBrowser对象包含一个主CefFrame对象,主CefFrame对象代表页面的顶层frame。
同时每个CefBrowser对象可以包含零个或多个的CefFrame对象,分别代表不同的子Frame,例如,一个浏览器加载了两个iframe,则该CefBrowser对象拥有三个CefFrame对象(顶层frame和两个iframe)。
常见的网页操作举例代码如下:
//在浏览器的主frame加载一个特定url
browser->GetMainFrame()->LoadURL("www.baidu.com");
//浏览器页面回退
browser->GoBack();
//浏览器页面向前
browser->GoForward();
//浏览器页面刷新
browser->Reload();
//浏览器页面停止
browser->StopLoad();
//浏览器窗口的原生句柄
CefWindowHandle window_handle = browser->GetHost()->GetWindowHandle();
//从主frame里获取HTML内容
class Visitor : public CefStringVisitor
{
public:
Visitor() {}
virtual void Visit(const CefString& string) OVERRIDE {
// xxx(); 某些逻辑操作
}
IMPLEMENT_REFCOUNTING(Visitor);
};
browser->GetMainFrame()->GetSource(new Visitor());
四:V8引擎
v8 引擎用于高效解析和执行 JavaScript。
v8 引擎设计了高效的垃圾回收机制,以保证快速的对象内存分配、短暂的垃圾回收暂停、无内存碎片。
CEF3提供支持V8Extension的接口,但是这有两个限制
第一,v8 extension仅在Renderer进程使用。
第二,仅在沙箱模型关闭时使用。
V8引擎的功能比较多,目前小编只用到了CefV8Handler类与JavaScript交互功能
CefV8Handler 是一个纯接口类,只有一个方法,你可以继承它,并提供相应的实现
class MyV8Handler :public CefV8Handler
{
public:
MyV8Handler();
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) OVERRIDE;
void SendMsgToBrowser(CefString str);
public:
IMPLEMENT_REFCOUNTING(MyV8Handler);
};
//与js进行通信交互,还需要用到CefV8Context类和CEF 类型:CefV8Value
CefRefPtr<CefV8Context> context
CefRefPtr<CefV8Value> object = context->GetGlobal();
// 创建MyV8Handler对象
CefRefPtr<MyV8Handler> handler = new MyV8Handler();
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction("ReceivedMsgFromJS", handler);
object->SetValue("ReceivedMsgFromJS", func, V8_PROPERTY_ATTRIBUTE_NONE);
//发送消息
void QCefWebView::SendMsgToPage(const QString &msg)
{
CefRefPtr<CefFrame> frame =GetBrowser(browserHandlerIndex)->GetMainFrame();
QString datastr;
datastr="SigSendMessageToJS('";
datastr+=msg;
datastr+="')";
CefString code;
code.FromString(datastr.toStdString());
frame->ExecuteJavaScript(code, frame->GetURL(), 0);
}
bool MyV8Handler::Execute(const CefString &name, CefRefPtr<CefV8Value> object, const CefV8ValueList &arguments, CefRefPtr<CefV8Value> &retval, CefString &exception)
{
if (name == "ReceivedMsgFromJS") {
if (arguments.size() == 1) {
CefString strFromWeb = arguments.at(0)->GetStringValue();
std::string stdstr=strFromWeb.ToString();
QString str=QString::fromLocal8Bit(strFromWeb.ToString().c_str());
SendMsgToBrowser(strFromWeb);
retval = CefV8Value::CreateString(strFromWeb);
}
else {
retval = CefV8Value::CreateInt(0);
}
return true;
}
return false;
}
void MyV8Handler::SendMsgToBrowser(CefString str)
{
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("vvMsg");
CefRefPtr<CefListValue> args = msg->GetArgumentList();
args->SetSize(1);
args->SetString(0, str);
CefV8Context::GetCurrentContext()->GetBrowser()->SendProcessMessage(PID_BROWSER, msg);
}
//接收消息
//OnProcessMessageReceived在Render进程收到进程间消息时被调用
bool CefBrowserHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message)
{
const std::string& messageName = message->GetName();
if (messageName == "vvMsg") //messageName要和MyV8Handler类SendMsgToBrowser函数中定义的一样
{
CefRefPtr<CefListValue> args = message->GetArgumentList();
CefString string0 = args->GetString(0);
// QString res=QString::fromLocal8Bit(string0.ToString().c_str()); 中文会乱码
QString res = QString::fromStdWString(string0.c_str());
emit recvRenderMsg(res);
return true;
}
return false;
}
五:其他类
常见的都在以上代码中已定义,其他的可以查询文档详解,不例举了。
CEF官方文档
目前还在深入研究学习CEF,文章有诸多漏洞,望见谅,望指正,感谢~
CEF官网地址
CEF官方论坛
参考文档:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF%20General%20Usage-zh-cn.md#off-screen-rendering
内附小编浏览器效果
完整项目工程地址:GitHub
今天的文章支持flash的浏览器_支持flash的浏览器分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/81890.html