此处针对的是一个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();
}

运行结果:

CEF CloseBrowser_多标签


为什么会迭代器无效呢?调试发现

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
那么这个关闭事件会引起父窗口的系统函数调用。那父窗口需要调用 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();
}

此时多标签浏览器可以正常关闭并且没有进程驻留

CEF CloseBrowser_CloseBrowser_02