diff options
author | ojan@google.com <ojan@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-02 00:44:47 +0000 |
---|---|---|
committer | ojan@google.com <ojan@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-02 00:44:47 +0000 |
commit | 04b4a6c37ac2250bc4e7230c24801d5cf9ffd8e7 (patch) | |
tree | 1e67976bfec449d5f6c4b209630491a4bfde3ffc /chrome/browser/browser.cc | |
parent | 9e43dae7106b6064ebc6580fe111568f1cbdd393 (diff) | |
download | chromium_src-04b4a6c37ac2250bc4e7230c24801d5cf9ffd8e7.zip chromium_src-04b4a6c37ac2250bc4e7230c24801d5cf9ffd8e7.tar.gz chromium_src-04b4a6c37ac2250bc4e7230c24801d5cf9ffd8e7.tar.bz2 |
A bunch of cleanups to beforeunload/unload handling.
1. Remove all the is_closing_browser plumbing. WebContents/TabContents/RenderViewHost/etc really shouldn't (and don't!) need to know anything about whether we're closing the browser or not.
2. Refactor the handling of beforeunload/unload state in browser.cc. I think this makes it considerably easier to reason about the correctness of it.
3. Added a couple TODOs for future cleanups that would have made this change a bit too large.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/browser.cc')
-rw-r--r-- | chrome/browser/browser.cc | 181 |
1 files changed, 105 insertions, 76 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index 02fe2e2..2768b65 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -209,7 +209,7 @@ Browser::Browser(const gfx::Rect& initial_bounds, : profile_(profile), window_(NULL), initial_show_command_(show_command), - is_processing_tab_unload_events_(false), + is_attempting_to_close_browser_(false), controller_(this), chrome_updater_factory_(this), method_factory_(this), @@ -743,6 +743,15 @@ void Browser::LoadingStateChanged(TabContents* source) { } void Browser::CloseContents(TabContents* source) { + if (is_attempting_to_close_browser_) { + // If we're trying to close the browser, just clear the state related to + // waiting for unload to fire. Don't actually try to close the tab as it + // will go down the slow shutdown path instead of the fast path of killing + // all the renderer processes. + UnloadFired(source); + return; + } + int index = tabstrip_model_.GetIndexOfTabContents(source); if (index == TabStripModel::kNoTab) { NOTREACHED() << "CloseContents called for tab not in our strip"; @@ -992,11 +1001,10 @@ void Browser::MoveToFront(bool should_activate) { } bool Browser::ShouldCloseWindow() { - if (is_processing_tab_unload_events_) { - return tabs_needing_before_unload_fired_.empty() && - tabs_needing_unload_fired_.empty(); + if (HasCompletedUnloadProcessing()) { + return true; } - is_processing_tab_unload_events_ = true; + is_attempting_to_close_browser_ = true; for (int i = 0; i < tab_count(); ++i) { if (tabstrip_model_.TabHasUnloadListener(i)) { @@ -1015,108 +1023,129 @@ bool Browser::ShouldCloseWindow() { if (tabs_needing_before_unload_fired_.empty()) return true; - ProcessPendingBeforeUnloadTabs(); + ProcessPendingTabs(); return false; } -void Browser::ProcessPendingBeforeUnloadTabs() { - DCHECK(is_processing_tab_unload_events_); - - TabContents* tab = tabs_needing_before_unload_fired_.back(); - tab->AsWebContents()->render_view_host()->AttemptToClosePage(true); -} - -void Browser::ProcessPendingUnloadTabs() { - DCHECK(is_processing_tab_unload_events_); +void Browser::ProcessPendingTabs() { + DCHECK(is_attempting_to_close_browser_); - TabContents* tab = tabs_needing_unload_fired_.back(); - tab->AsWebContents()->render_view_host()->OnProceedWithClosePage(true); -} - -void Browser::BeforeUnloadFired(TabContents* tab, bool proceed) { - DCHECK(is_processing_tab_unload_events_); - - if (!proceed) { - tabs_needing_before_unload_fired_.clear(); - tabs_needing_unload_fired_.clear(); - is_processing_tab_unload_events_ = false; + if (HasCompletedUnloadProcessing()) { + // We've finished all the unload events and can proceed to close the + // browser. + OnWindowClosing(); return; } - tabs_needing_unload_fired_.push_back(tab); - - for (UnloadListenerVector::iterator it = - tabs_needing_before_unload_fired_.begin(); - it != tabs_needing_before_unload_fired_.end(); - ++it) { - if (*it == tab) { - tabs_needing_before_unload_fired_.erase(it); - break; - } - } - - if (tabs_needing_before_unload_fired_.empty()) { + // Process beforeunload tabs first. When that queue is empty, process + // unload tabs. + // TODO(ojan): Move some of this logic down into TabContents and/or + // WebContents so we don't need to dig into RenderViewHost here. + if (!tabs_needing_before_unload_fired_.empty()) { + TabContents* tab = tabs_needing_before_unload_fired_.back(); + tab->AsWebContents()->render_view_host()->FirePageBeforeUnload(); + } else if (!tabs_needing_unload_fired_.empty()) { // We've finished firing all beforeunload events and can proceed with unload // events. // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting // somewhere around here so that we have accurate measurements of shutdown // time. - ProcessPendingUnloadTabs(); + // TODO(ojan): We can probably fire all the unload events in parallel and + // get a perf benefit from that in the cases where the tab hangs in it's + // unload handler or takes a long time to page in. + TabContents* tab = tabs_needing_unload_fired_.back(); + tab->AsWebContents()->render_view_host()->FirePageUnload(); } else { - ProcessPendingBeforeUnloadTabs(); + NOTREACHED(); } } -void Browser::UnloadFired(TabContents* tab) { - DCHECK(is_processing_tab_unload_events_); +bool Browser::HasCompletedUnloadProcessing() { + return is_attempting_to_close_browser_ && + tabs_needing_before_unload_fired_.empty() && + tabs_needing_unload_fired_.empty(); +} - for (UnloadListenerVector::iterator it = tabs_needing_unload_fired_.begin(); - it != tabs_needing_unload_fired_.end(); - ++it) { - if (*it == tab) { - tabs_needing_unload_fired_.erase(it); - break; - } +void Browser::CancelWindowClose() { + DCHECK(is_attempting_to_close_browser_); + // Only cancelling beforeunload should be able to cancel the window's close. + // So there had better be a tab that we think needs beforeunload fired. + DCHECK(!tabs_needing_before_unload_fired_.empty()); + + while (!tabs_needing_before_unload_fired_.empty()) { + TabContents* tab = tabs_needing_before_unload_fired_.back(); + NotificationService::current()-> + RemoveObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED, + Source<TabContents>(tab)); + tabs_needing_before_unload_fired_.pop_back(); } - NotificationService::current()-> - RemoveObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED, - Source<TabContents>(tab)); + while (!tabs_needing_unload_fired_.empty()) { + TabContents* tab = tabs_needing_unload_fired_.back(); + NotificationService::current()-> + RemoveObserver(this, NOTIFY_WEB_CONTENTS_DISCONNECTED, + Source<TabContents>(tab)); + tabs_needing_unload_fired_.pop_back(); + } - if (tabs_needing_unload_fired_.empty()) { - // We've finished all the unload events and can proceed to close the - // browser. - OnWindowClosing(); - } else { - ProcessPendingUnloadTabs(); + is_attempting_to_close_browser_ = false; +} + +void Browser::BeforeUnloadFired(TabContents* tab, + bool proceed, + bool* proceed_to_fire_unload) { + if (!is_attempting_to_close_browser_) { + *proceed_to_fire_unload = proceed; + return; } + + if (!proceed) { + CancelWindowClose(); + *proceed_to_fire_unload = false; + return; + } + + if (RemoveFromVector(&tabs_needing_before_unload_fired_, tab)) { + // Now that beforeunload has fired, put the tab on the queue to fire unload. + tabs_needing_unload_fired_.push_back(tab); + ProcessPendingTabs(); + // We want to handle firing the unload event ourselves since we want to + // fire all the beforeunload events before attempting to fire the unload + // events should the user cancel closing the browser. + *proceed_to_fire_unload = false; + return; + } + + *proceed_to_fire_unload = true; +} + +void Browser::UnloadFired(TabContents* tab) { + DCHECK(is_attempting_to_close_browser_); + RemoveFromVector(&tabs_needing_unload_fired_, tab); + ProcessPendingTabs(); } void Browser::ClearUnloadStateOnCrash(TabContents* tab) { - bool is_waiting_on_before_unload = false; + DCHECK(is_attempting_to_close_browser_); + RemoveFromVector(&tabs_needing_before_unload_fired_, tab); + RemoveFromVector(&tabs_needing_unload_fired_, tab); + ProcessPendingTabs(); +} - for (UnloadListenerVector::iterator it = - tabs_needing_before_unload_fired_.begin(); - it != tabs_needing_before_unload_fired_.end(); +bool Browser::RemoveFromVector(UnloadListenerVector* vector, TabContents* tab) { + DCHECK(is_attempting_to_close_browser_); + + for (UnloadListenerVector::iterator it = vector->begin(); + it != vector->end(); ++it) { if (*it == tab) { - is_waiting_on_before_unload = true; - break; + vector->erase(it); + return true; } } - - if (is_waiting_on_before_unload) { - // Even though beforeunload didn't really fire, we call the same function - // so that all the appropriate cleanup happens. - BeforeUnloadFired(tab, true); - } else { - // Even though unload didn't really fire, we call the same function - // so that all the appropriate cleanup happens. - UnloadFired(tab); - } + return false; } - void Browser::OnWindowClosing() { if (!ShouldCloseWindow()) return; |