原文地址:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF_Close.mdgit
=============================================================================github
我來講windows下CEF3.2171的關閉流程,裏面會引用一部分官方庫的文檔和我的的僞代碼,爲了輔助理解—— 如下是截取自cef_life_span_handler.h的頭文件文檔,因此一部分文檔他仍是寫在頭文件裏的,根據他的流程,能很快的去梳理相關邏輯web
// The CefLifeSpanHandler::OnBeforeClose() method will be called immediately // before the browser object is destroyed. The application should only exit // after OnBeforeClose() has been called for all existing browsers. // // If the browser represents a modal window and a custom modal loop // implementation was provided in CefLifeSpanHandler::RunModal() this callback // should be used to restore the opener window to a usable state. // // By way of example consider what should happen during window close when the // browser is parented to an application-provided top-level OS window. // 1. User clicks the window close button which sends an OS close // notification (e.g. WM_CLOSE on Windows, performClose: on OS-X and // "delete_event" on Linux). // 2. Application's top-level window receives the close notification and: // A. Calls CefBrowserHost::CloseBrowser(false). // B. Cancels the window close. // 3. JavaScript 'onbeforeunload' handler executes and shows the close // confirmation dialog (which can be overridden via // CefJSDialogHandler::OnBeforeUnloadDialog()). // 4. User approves the close. // 5. JavaScript 'onunload' handler executes. // 6. Application's DoClose() handler is called. Application will: // A. Set a flag to indicate that the next close attempt will be allowed. // B. Return false. // 7. CEF sends an OS close notification. // 8. Application's top-level window receives the OS close notification and // allows the window to close based on the flag from #6B. // 9. Browser OS window is destroyed. // 10. Application's CefLifeSpanHandler::OnBeforeClose() handler is called and // the browser object is destroyed. // 11. Application exits by calling CefQuitMessageLoop() if no other browsers // exist.
pre1. 若是你要管理CefBrowser的生命週期,意味者你必須實現相關 CefLifeSpanHandler接口,在OnAfterCreated裏管理和獲取CefBrowser的每個browser,在DoClose和OnBeforeClose裏管理關閉 pre2. 這裏要注意整個流程對應開發者來講不是線性代碼,都是基於消息、事件投遞、接口切面的一種編程習慣,你們要對異步接口的開發有心理準備,你實現的一個接口都是等待框架或者別人調用。編程
case WM_CLOSE: if (client && !client->IsClosing()) { CefRefPtr<CefBrowser> browser = client->GetBrowser(); if (browser.get()) { // Notify the browser window that we would like to close it. This // will result in a call to ClientHandler::DoClose() if the // JavaScript 'onbeforeunload' event handler allows it. browser->GetHost()->CloseBrowser(false); // Cancel the close. return 0; } } // Allow the close. break;
這是單browser關閉windows
browser->GetHost()->CloseBrowser(false);
一樣能夠替換成多browser關閉app
void ClientHandler::CloseAllBrowsers(bool force_close) { if (!CefCurrentlyOn(TID_UI)) { // Execute on the UI thread. CefPostTask(TID_UI, base::Bind(&ClientHandler::CloseAllBrowsers, this, force_close)); return; } if (!popup_browsers_.empty()) { // Request that any popup browsers close. BrowserList::const_iterator it = popup_browsers_.begin(); for (; it != popup_browsers_.end(); ++it) (*it)->GetHost()->CloseBrowser(force_close); } if (browser_.get()) { // Request that the main browser close. browser_->GetHost()->CloseBrowser(force_close); } }
intermittent. 最終會觸發一個或多個CloseBrowser => CefBrowserHostImpl::CloseBrowser => CefBrowserHostImpl::CloseContents框架
void CefBrowserHostImpl::CloseBrowser(bool force_close) { if (CEF_CURRENTLY_ON_UIT()) { // Exit early if a close attempt is already pending and this method is // called again from somewhere other than WindowDestroyed(). if (destruction_state_ >= DESTRUCTION_STATE_PENDING && (IsWindowless() || !window_destroyed_)) { if (force_close && destruction_state_ == DESTRUCTION_STATE_PENDING) { // Upgrade the destruction state. destruction_state_ = DESTRUCTION_STATE_ACCEPTED; } return; } if (destruction_state_ < DESTRUCTION_STATE_ACCEPTED) { destruction_state_ = (force_close ? DESTRUCTION_STATE_ACCEPTED : DESTRUCTION_STATE_PENDING); } content::WebContents* contents = web_contents(); if (contents && contents->NeedToFireBeforeUnload()) { // Will result in a call to BeforeUnloadFired() and, if the close isn't // canceled, CloseContents(). contents->DispatchBeforeUnload(false); } else { CloseContents(contents); } } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::CloseBrowser, this, force_close)); } }
咱們跳過其餘細節( 我也沒關注:) ),和相關CEF_POST_TASK把函數投遞到UI線程上去處理的線程類型,最後會進入 CloseContentsless
void CefBrowserHostImpl::CloseContents(content::WebContents* source) { if (destruction_state_ == DESTRUCTION_STATE_COMPLETED) return; bool close_browser = true; // If this method is called in response to something other than // WindowDestroyed() ask the user if the browser should close. if (client_.get() && (IsWindowless() || !window_destroyed_)) { CefRefPtr<CefLifeSpanHandler> handler = client_->GetLifeSpanHandler(); if (handler.get()) { close_browser = !handler->DoClose(this); // CefLifeSpanHandler::DoClose } } if (close_browser) { if (destruction_state_ != DESTRUCTION_STATE_ACCEPTED) destruction_state_ = DESTRUCTION_STATE_ACCEPTED; if (!IsWindowless() && !window_destroyed_) { // A window exists so try to close it using the platform method. Will // result in a call to WindowDestroyed() if/when the window is destroyed // via the platform window destruction mechanism. PlatformCloseWindow(); } else { // Keep a reference to the browser while it's in the process of being // destroyed. CefRefPtr<CefBrowserHostImpl> browser(this); // No window exists. Destroy the browser immediately. DestroyBrowser(); // CefLifeSpanHandler::OnBeforeClose if (!IsWindowless()) { // Release the reference added in PlatformCreateWindow(). Release(); } } } else if (destruction_state_ != DESTRUCTION_STATE_NONE) { destruction_state_ = DESTRUCTION_STATE_NONE; } }
在CloseContents裏發現了咱們的DoClose接口,其實這個函數裏還有咱們要處理的OnBeforeClose接口,DestroyBrowser包含了他異步
void CefBrowserHostImpl::DestroyBrowser() { CEF_REQUIRE_UIT(); destruction_state_ = DESTRUCTION_STATE_COMPLETED; if (client_.get()) { CefRefPtr<CefLifeSpanHandler> handler = client_->GetLifeSpanHandler(); if (handler.get()) { // Notify the handler that the window is about to be closed. handler->OnBeforeClose(this); //`CefLifeSpanHandler::OnBeforeClose } } // 省略 }
到這裏咱們要有這樣的調用棧初步概念(以後會修改..)ide
=> 表示同步調用 ->異步調用 [os]window close => [我的]browser(s) close => [cef]CefBrowserHostImpl::CloseBrowser => [cef]CefBrowserHostImpl::CloseContents => [實現cef接口]CefLifeSpanHandler::DoClose => [cef]CefLifeSpanHandler::DestroyBrowser => [實現cef接口]CefLifeSpanHandler::OnBeforeClose => [os]阻止window close
3-5. js和交互級的操做,略,其中有一點js onunload讓你有機會在js層面去通知關閉,好比通知後臺的native 線程去作自我清理動做,特別是cefQuery持久化之後的和JS交互的native線程 6.DoClose接口被調用 A. 容許設置一些狀態表示browser正在關閉 B. "return false;" 咱們不須要緊急關閉(好像是預留接口,或者其餘平臺用) 7.cef 發送一個系統級別的關閉動做發現以前的調用棧已經須要修改了,由於這裏異步操做) CloseContents => PlatformCloseWindow => CefBrowserHostImpl::PlatformCloseWindow =>PostMessage WM_CLOSE
void CefBrowserHostImpl::PlatformCloseWindow() { if (window_info_.window != NULL) { HWND frameWnd = GetAncestor(window_info_.window, GA_ROOT); PostMessage(frameWnd, WM_CLOSE, 0, 0); } }
PS: host公共代碼在這個文件browser_host_impl.cc裏,平臺特殊接口在browser_host_impl_xxx.cc裏 PlatformCloseWindow在browser_host_impl_win.cc裏
容許關閉"if (client && !client->IsClosing()) " 以前我代碼裏IsClosing已經容許(
case WM_CLOSE: if (client && !client->IsClosing()) { // 此次不會進入IF分支
8.系統窗口已銷燬
LRESULT CALLBACK CefBrowserHostImpl::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { // 略 case WM_DESTROY: if (browser) { // Clear the user data pointer. gfx::SetWindowUserData(hwnd, NULL); // Force the browser to be destroyed and release the reference added in // PlatformCreateWindow(). browser->WindowDestroyed(); } return 0; // 略 }
cef居然在等系統羣發銷燬消息而後處理的代碼,看到這裏才發現CefBrowserHostImpl這個類是CefBrowser和系統整合的橋樑,不一樣平臺對 CefBrowserHostImpl 的實現也不同
9.框架發起銷燬操做 10. 調用OnBeforeClose WM_DESTROY => CefBrowserHostImpl::WindowDestroyed => ClientHandler::OnBeforeClose
void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) { CEF_REQUIRE_UI_THREAD(); message_router_->OnBeforeClose(browser); if (GetBrowserId() == browser->GetIdentifier()) { { base::AutoLock lock_scope(lock_); // Free the browser pointer so that the browser can be destroyed browser_ = NULL; } if (osr_handler_.get()) { osr_handler_->OnBeforeClose(browser); osr_handler_ = NULL; } } else if (browser->IsPopup()) { // Remove from the browser popup list. BrowserList::iterator bit = popup_browsers_.begin(); for (; bit != popup_browsers_.end(); ++bit) { if ((*bit)->IsSame(browser)) { popup_browsers_.erase(bit); break; } } } if (--browser_count_ == 0) { // All browser windows have closed. // Remove and delete message router handlers. MessageHandlerSet::const_iterator it = message_handler_set_.begin(); for (; it != message_handler_set_.end(); ++it) { message_router_->RemoveHandler(*(it)); delete *(it); } message_handler_set_.clear(); message_router_ = NULL; // Quit the application message loop. //AppQuitMessageLoop(); XCefAppManage::Instance()->QuitMessageLoop(); } }
我判斷到 "--browser_count_ == 0" , 開始發起真實關閉。
void XCefAppManage::QuitMessageLoop() { if (GetCefSettings().multi_threaded_message_loop) { // Running in multi-threaded message loop mode. Need to execute // PostQuitMessage on the main application thread. if (NULL == message_wnd__) { message_wnd__ = ::FindWindow(XWinUtil::GetMessageWindowClassName(XWinUtil::GetParentProcessID()), NULL); } DCHECK(message_wnd__); PostMessage(message_wnd__, WM_COMMAND, ID_QUIT, 0); } else { CefQuitMessageLoop(); } }
若是CefRunMessageLoop建立的消息循環對應CefQuitMessageLoop去終結,其餘方式的處理就千奇百怪了,我根據cefclient的例子用一個隱藏窗口來結束系統的消息循環
PostMessage(message_wnd__, WM_COMMAND, ID_QUIT, 0);
處理很是簡單,調用了原生API PostQuitMessage退出循環
case ID_QUIT: PostQuitMessage(0); return 0;
PS: 若是須要把CEF混入其餘框架的話,不一樣框架的處理都須要適配,切記。 12. CefShutDown() 銷燬Cef資源,若是有資源沒關閉,好比browser的計數不爲0,debug下會check assert。 最後我再來修改一下以前的調用棧
=> 表示同步調用 ->異步調用 [os]on window close WM_CLOSE => [我的]browser(s) close => [cef]CefBrowserHostImpl::CloseBrowser => [cef]CefBrowserHostImpl::CloseContents => [實現cef接口]CefLifeSpanHandler::DoClose => [cef]CefBrowserHostImpl::PlatformCloseWindow => [os] post window close -> WM_CLOSE [os]阻止window close post被接收 [os] 第二次on window close WM_CLOSE => [我的] 容許 window close ->WM_DESTROY [os] 系統羣發WM_DESTROY [cef] CefBrowserHostImpl::WndProc => [cef & os] 每一個browser proc內的 WM_CLOSE 處理 [cef]CefLifeSpanHandler::DestroyBrowser => [實現cef接口]CefLifeSpanHandler::OnBeforeClose => 發起總關閉(我的使用XCefAppManage::QuitMessageLoop)-> 退出消息循環 CefShutDown
這就一些對CEF CLOSE的我的理解的心得
原文鏈接:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF_Close.md