此处针对的是一个CefClient管理多个CefBrowser的情景。
情景实现步骤:进入百度首页=》新闻=》随便点击一个新闻(此时会创建一个新CefBrowser,cef默认是popup,我们修改为WS_CHILD,实现多标签页,具体实现下一章介绍)
cefsample的实例中我们知道了cefclient的生命周期需要自己管理,并且在退出的时候调用CefBrowserHost::CloseBrowser(false)或者CefBrowserHost::TryCloseBrowser(),随后触发DoClose(注意:DoClose调用之后OnBeforeClose并不是一定被调用,这个和CefClient的生命周期有关)
在Cef多标签浏览器的时候我们实现了CloseAllBrowsers,OnAfterCreated,OnBeforeClose实现CefClient生命周期管理。代码如下(有问题代码):
void CBrowserHandler::CloseAllBrowsers()
{
if (!CefCurrentlyOn(TID_UI))
{
// Execute on the UI thread.
// bind ref cef_closure_task
CefPostTask(TID_UI, base::Bind(&CBrowserHandler::CloseAllBrowsers, this));
return;
}
if (m_browser_list.empty())
return;
std::vector<CefRefPtr<CefBrowser> >::const_iterator it = m_browser_list.begin();
for (it; it != m_browser_list.end(); ++it)
(*it)->GetHost()->TryCloseBrowser();// CloseBrowser(force_close);
}
bool CBrowserHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
m_is_closing = (m_browser_list.size() <= 0);
return false;
}
void CBrowserHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
// Remove from the list of existing browsers.
std::vector<CefRefPtr<CefBrowser> >::iterator bit = m_browser_list.begin();
for (; bit != m_browser_list.end(); ++bit) {
if ((*bit)->IsSame(browser)) {
m_browser_list.erase(bit);
break;
}
}
}
测试结果:多标签的时候程序关闭不了,原因:m_is_closing 始终为false。N个标签的时候虽然系统调用了N次DoClose,但是OnBeforeClose却一次也没有调用,所以m_is_closing 的条件始终不会成立。
修改代码,把删除操作放在DoClose里面管理,代码如下:
void CBrowserHandler::CloseAllBrowsers()
{
if (!CefCurrentlyOn(TID_UI))
{
// Execute on the UI thread.
// bind ref cef_closure_task
CefPostTask(TID_UI, base::Bind(&CBrowserHandler::CloseAllBrowsers, this));
return;
}
if (m_browser_list.empty())
return;
std::vector<CefRefPtr<CefBrowser> >::const_iterator it = m_browser_list.begin();
for (it; it != m_browser_list.end(); ++it)
(*it)->GetHost()->TryCloseBrowser();// CloseBrowser(force_close);
}
bool CBrowserHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
std::vector<CefRefPtr<CefBrowser> >::iterator bit = m_browser_list.begin();
for (; bit != m_browser_list.end(); ++bit) {
if ((*bit)->IsSame(browser)) {
m_browser_list.erase(bit);
break;
}
}
m_is_closing = (m_browser_list.size() <= 0);
return false;
}
void CBrowserHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
}
运行结果:
为什么会迭代器无效呢?调试发现
for (it; it != m_browser_list.end();++it)
(*it)->GetHost()->TryCloseBrowser();// CloseBrowser(force_close);
for关闭浏览器的的时候会触发DoClose,此时DoClose里面删除了元素导致迭代器失效。此时当再次操作迭代器it的时候就会触发异常了。所以修改CloseAllBrowsers代码如下:
void CBrowserHandler::CloseAllBrowsers()
{
if (!CefCurrentlyOn(TID_UI))
{
// Execute on the UI thread.
// bind ref cef_closure_task
CefPostTask(TID_UI, base::Bind(&CBrowserHandler::CloseAllBrowsers, this));
return;
}
if (m_browser_list.empty())
return;
//建立一个副本操作
std::vector<CefRefPtr<CefBrowser> > temp_browser_list = m_browser_list;
std::vector<CefRefPtr<CefBrowser> >::const_iterator it = temp_browser_list.begin();
for (it; it != temp_browser_list.end(); ++it)
(*it)->GetHost()->TryCloseBrowser();// CloseBrowser(force_close);
}
运行结果和预期一致。但是调试的过程发现一个新问题?理论上调用一次CloseAllBrowsers就应该关闭所有的CefClient,即:for N次TryCloseBrowser触发N次DoClose。但是结果只调用了一次,而且是触发的最后一个调用对象的DoClose(DoClose会再次触发WM_CLOSE)(对象只有第一次调用TryCloseBrowser才会触发DoClose,后面再次调用都不会再触发DoClose)。(由于cef源码没有编译,所以这里暂时不知道为什么会这样,如果知道的大神请告诉一下)。
作者:cqclark
来源:CSDN
原文:https://blog.csdn.net/cqclark/article/details/49121027
版权声明:本文为博主原创文章,转载请附上博文链接!
如果浏览器是其他窗口的父窗口,那么这个关闭事件会引起父窗口的系统函数调用。那父窗口需要调用 CloseBrowser(false) 并等待第二个系统调用的关闭事件来指示浏览进程允许关闭。
如果关闭通过Javascript事件或DoClose()回调函数处理,那第二个系统关闭事件就不会被发送。
IsClosing()测试是否关闭,如果是第一次的系统关闭事件就返回false,每二次返回true;
所以最终修改代码如下:
void CBrowserHandler::CloseAllBrowsers()
{
if (!CefCurrentlyOn(TID_UI))
{
// Execute on the UI thread.
// bind ref cef_closure_task
CefPostTask(TID_UI, base::Bind(&CBrowserHandler::CloseAllBrowsers, this));
return;
}
if (m_browser_list.empty())
return;
//一次只能关闭一个CefBrowser=》DoClose,即使关闭多个也只会调用一次DoClose
std::vector<CefRefPtr<CefBrowser> >::const_iterator it = m_browser_list.begin();
if (it != m_browser_list.end())
(*it)->GetHost()->TryCloseBrowser();// CloseBrowser(force_close);
}
bool CBrowserHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
std::vector<CefRefPtr<CefBrowser> >::iterator bit = m_browser_list.begin();
for (; bit != m_browser_list.end(); ++bit) {
if ((*bit)->IsSame(browser)) {
m_browser_list.erase(bit);
break;
}
}
m_is_closing = (m_browser_list.size() <= 0);
return false;
}
void CBrowserHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
}
此时多标签浏览器可以正常关闭并且没有进程驻留