背景
想要完成一个浏览器客户端的基本功能,除了可以加载出网页页面,实现页面跳转意外,还需要很多其他的功能。例如网页的前进、后退、浏览器的刷新、调出控制台窗口、设置 cookie 等。后续还会尝试实现页面调用打印时,弹出自定义的打印窗口、支持视频播放等功能。
我们的程序是基于 cef.sln 中的 cefsimple 项目现有的功能基础上进行实现的。所以读者想进行测试,可以直接将功能代码注入到 cef.sln 中的 cefsimple 项目中进行测试。
具体实现
CefBrowser 类表示浏览器窗口,类中实现了 GoBack()、GoForward()、Reload()等方法。可以实现网页的后退、前进、刷新等功能。
获取浏览器窗口类
在 simple_handler.h 文件中有一个浏览器窗口列表变量,里面存储了所有创建了的 CefBrowser,所以我们想要获取浏览器窗口类,就需要在 simple_handler.h 文件中添加一个 getBrowser()函数,返回一个 CefBrowser。
//simple_handler.h
CefRefPtr<CefBrowser> getBrowser();
复制代码
//simple_handler.cpp
//如果集合不为空,获取集合中的第一个CefBrowser元素。
CefRefPtr<CefBrowser> SimpleHandler::getBrowser()
{
if(!browser_list_.empty())
{
return browser_list_.front();
}
return NULL;
}
复制代码
1.前进
直接通过浏览器窗口的 SimpleHandler 调用我们创建好的 getBrowser(),判断指针是否为空,不为空调用 GoForward()函数即可。
void CefBrowserWidget::forward()
{
if(m_cefHandler->getBrowser())
{
m_cefHandler->getBrowser()->GoForward();
}
}
复制代码
2.后退
同前进。方法改为调用 GoBack()函数。
void CefBrowserWidget::back()
{
if(m_cefHandler->getBrowser())
{
m_cefHandler->getBrowser()->GoBack();
}
}
复制代码
3.刷新
同前进。方法改为调用 Reload()函数。还可以调用 StopLoad()函数停止刷新,调用 IsLoading()函数判断是否正在刷新。
void CefBrowserWidget::back()
{
if(m_cefHandler->getBrowser())
{
m_cefHandler->getBrowser()->Reload();
}
}
复制代码
还可以实现通过按下快捷键 F5 实现页面刷新。
4.显示控制台窗口
这个功能是用来支持网页开发程序员来调试页面用的,所以也是一个必不可少的功能。
void CefBrowserWidget::back()
{
if(m_cefHandler->getBrowser())
{
CefWindowInfo windowInfo;
CefBrowserSettings settings;
windowInfo.SetAsPopup(NULL, "Dev Tools");
m_cefHandler->getBrowser()->GetHost()
->ShowDevTools(windowInfo, m_cefHandler, settings, CefPoint());
}
}
复制代码
5.响应快捷键
响应快捷键首先要修改 SimpleHandler 类,让 SimpleHandler 继承 CefKeyboardHandler 类,并重新实现 OnPreKeyEvent 方法。
除了可以响应单个快捷键,还可以响应组合按键。
//simple_handler.h
class SimpleHandler : public QObject,
public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler,
public CefKeyboardHandler //继承CefKeyboardHandler类
{
……
virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() OVERRIDE{return this;} //一定要有
……
// CefKeyboardHandler methods:
// 重载OnPreKeyEvent
virtual bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
const CefKeyEvent &event,
MSG *os_event,
bool *is_keyboard_shortcut) override;
……
}
复制代码
//simple_handler.cpp
bool SimpleHandler::OnPreKeyEvent(CefRefPtr<CefBrowser> browser, const CefKeyEvent &event,
MSG *os_event, bool *is_keyboard_shortcut)
{
if (event.type == KEYEVENT_RAWKEYDOWN)
{
switch (event.windows_key_code)
{
case VK_F5: //按下按键F5刷新页面
browser->Reload();
return true;
case 'H': //按下Ctrl+Alt+H组合按键调用控制台窗口
if(event.modifiers == (EVENTFLAG_CONTROL_DOWN|EVENTFLAG_ALT_DOWN))
{
CefWindowInfo windowInfo;
CefBrowserSettings settings;
windowInfo.SetAsPopup(NULL, "Dev Tools");
browser->GetHost()->ShowDevTools(windowInfo, this, settings, CefPoint());
return true;
}
}
}
return false;
}
复制代码
6.设置 cookie
在这一步时,很多没有开发过浏览器相关功能的小伙伴,可能不是很理解 cookie 这个东西是干什么的,那就更谈不上知道怎么用它了。
首先 cookie 是什么?cookie 的中文翻译是曲奇,小甜饼的意思。但是我们这里说的 cookie 是一个代名词,跟饼干没什么关系,它其实就是一些数据信息,类型为“小型文本文件”,存储于电脑上的文本文件中。
其次,我们为什么要用到 cookie?想象一个场景,当我们打开一个网站时,首先需要进行注册或登录。当我们登录过这个网站后,那么我们再次打开网站时,发现就不需要再次登录了,而是直接进入了首页,并且登录信息保留的是我们之前登录过的账号。例如掘金,csdn,知乎等网站都是这样。那浏览器是如何记得这些信息的,就是通过 cookie。这些 cookie 是服务器创建后返回给游览器的。游览器只进行了保存。
一般情况下,cookie 是以键值对进行表示的(key-value),例如 name=test,这个就表示 cookie 的名字是 name,cookie 携带的值是 test。
cookie 的时效性。cookie 是有时效性的,通过设置,可以控制 cookie 多久之后失效,失效之后的 cookie 值就不能再自动进入网站首页了。
在控制台弹出的窗口中,我们可以看到当前页面的 cookie 信息。
cookie 中包含 name、value、domain、path、expires 等信息。这些信息就是我们设置 cookie 时需要设置的。
cookie 中常用属性的含义:
Name:这个代表这一条 cookie 的名字。
Value:这个代表这一条 cooke 的值。
Path:这个定义了 Web 站点上可以访问该 Cookie 的目录。这里默认是空
Expires:这个值表示 cookie 的过期时间,也就是有效期的值,cookie 在这个有效期之前都是有效的。
Size:这个表示 cookie 的大小。\
设置 cookie 之前要先设置 CefSettings,这个在 main 方法中,可以参考之前的文章。
// Init CEF
CefSettings settings;
……
settings.persist_session_cookies = true;
CefInitialize(main_args, settings, app.get(), nullptr);
复制代码
struct Coockie // 示例
{
QString name; // 名称
QString value; // 值
QString domain; // 域名
QString path; // 路径
};
复制代码
自定义一个结构体用来传递 cookie 的参数。
void CefBrowserWidget::setCookie(QString url, Coockie cookie)
{
CefRefPtr<CefCookieManager> manager = CefCookieManager::GetGlobalManager(NULL);
CefCookie cefCookie;
CefString(&cefCookie.name).FromString(cookie.name.toStdString());
CefString(&cefCookie.value).FromString(cookie.value.toStdString());
CefString(&cefCookie.domain).FromString(cookie.domain.toStdString());
CefString(&cefCookie.path).FromString(cookie.path.toStdString());
cefCookie.has_expires = true;
std::wstring httpDomain = url.toStdWString();
manager->SetCookie(CefString(httpDomain.c_str()), cefCookie, NULL);
}
复制代码
设置完 cookie 之后在调用加载。
设置多个 cookie 时,调用多次设置 cookie 的方法就可以了,只需修改每次的传参就行。
CefCookie 是一个结构体,创建 CefCookie 结构体并存入对应变量的值。CefCookieManager 是 CEF 管理 cookie 的类。
可以调用 setCookie()函数设置 cookie。
DeleteCookies()函数可以删除与指定参数匹配的所有 cookie。
FlushStore()函数可以将备份存储(如果有)刷新到磁盘。
7.加载为指定 url
当我们通过某一个 url 创建了一个 browser 之后,想要在这个 browser 中显示一个新的 url。
void CefBrowserWidget::load(QString url)
{
if(m_cefHandler->getBrowser())
{
CefRefPtr<CefFrame> frame = m_cefHandler->getBrowser()->GetMainFrame();
if (frame)
{
frame->LoadURL(url.toStdWString());
}
}
}
复制代码
8.实现浏览器窗口的大小随窗体变化(这种适用于将 cef 浏览器窗口作为 child 嵌入到 QWidget 中显示的情况)。
重写 QWidget 的 resizeEvent()函数,当 QWidget 大小发生变化时就会响应这个消息。在函数中获取浏览器窗口的 HWND,然后获取当前 QWidget 窗体的大小,通过调用系统函数::MoveWindow()更新浏览器窗口的大小。
void CefBrowserWidget::resizeEvent(QResizeEvent *event)
{
if(m_cefHandler->GetInstance())
{
HWND wnd = m_cefHandler->getBrowserWindowHandle();
if(wnd)
{
QRect qtRect = this->rect();
::MoveWindow(wnd, qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height(), true);
}
}
return QWidget::resizeEvent(event);
}
复制代码
后续还可以在这些功能的基础上开发浏览器的下载功能(接口类是有的,可以重载下载接口类做操作),还可以实现 cef 支持视频播放的功能(现在编译好的 cef 是不支持视频播放的,需要自己做一些处理)。
评论