Index: chrome/browser/ui/browser.cc |
=================================================================== |
--- chrome/browser/ui/browser.cc (revision 145675) |
+++ chrome/browser/ui/browser.cc (working copy) |
@@ -135,6 +135,7 @@ |
#include "chrome/browser/ui/tabs/dock_info.h" |
#include "chrome/browser/ui/tabs/tab_menu_model.h" |
#include "chrome/browser/ui/tabs/tab_strip_model.h" |
+#include "chrome/browser/ui/unload_controller.h" |
#include "chrome/browser/ui/web_applications/web_app_ui.h" |
#include "chrome/browser/ui/webui/feedback_ui.h" |
#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" |
@@ -302,10 +303,11 @@ |
tab_strip_model_(new TabStripModel(this, profile))), |
app_type_(APP_TYPE_HOST), |
chrome_updater_factory_(this), |
- is_attempting_to_close_browser_(false), |
cancel_download_confirmation_state_(NOT_PROMPTED), |
initial_show_state_(ui::SHOW_STATE_DEFAULT), |
is_session_restore_(false), |
+ ALLOW_THIS_IN_INITIALIZER_LIST( |
+ unload_controller_(new chrome::UnloadController(this))), |
weak_factory_(this), |
ALLOW_THIS_IN_INITIALIZER_LIST( |
content_setting_bubble_model_delegate_( |
@@ -575,16 +577,11 @@ |
if (!CanCloseWithInProgressDownloads()) |
return false; |
- if (HasCompletedUnloadProcessing()) |
- return true; |
+ return unload_controller_->ShouldCloseWindow(); |
+} |
- is_attempting_to_close_browser_ = true; |
- |
- if (!TabsNeedBeforeUnloadFired()) |
- return true; |
- |
- ProcessPendingTabs(); |
- return false; |
+bool Browser::IsAttemptingToCloseBrowser() const { |
+ return unload_controller_->is_attempting_to_close_browser(); |
} |
void Browser::OnWindowClosing() { |
@@ -669,7 +666,7 @@ |
DCHECK(num_downloads_blocking); |
*num_downloads_blocking = 0; |
- if (is_attempting_to_close_browser_) |
+ if (IsAttemptingToCloseBrowser()) |
return DOWNLOAD_CLOSE_OK; |
// If we're not running a full browser process with a profile manager |
@@ -689,10 +686,9 @@ |
iter != BrowserList::end(); ++iter) { |
// Don't count this browser window or any other in the process of closing. |
Browser* const browser = *iter; |
- // Check is_attempting_to_close_browser_ as window closing may be |
- // delayed, and windows that are in the process of closing don't |
- // count against our totals. |
- if (browser == this || browser->is_attempting_to_close_browser_) |
+ // Window closing may be delayed, and windows that are in the process of |
+ // closing don't count against our totals. |
+ if (browser == this || browser->IsAttemptingToCloseBrowser()) |
continue; |
if ((*iter)->profile() == profile()) |
@@ -1177,11 +1173,6 @@ |
// won't start if the page is loading. |
LoadingStateChanged(contents->web_contents()); |
- // If the tab crashes in the beforeunload or unload handler, it won't be |
- // able to ack. But we know we can close it. |
- registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, |
- content::Source<WebContents>(contents->web_contents())); |
- |
registrar_.Add(this, content::NOTIFICATION_INTERSTITIAL_ATTACHED, |
content::Source<WebContents>(contents->web_contents())); |
@@ -1350,9 +1341,6 @@ |
// still present. |
MessageLoop::current()->PostTask( |
FROM_HERE, base::Bind(&Browser::CloseFrame, weak_factory_.GetWeakPtr())); |
- // Set is_attempting_to_close_browser_ here, so that extensions, etc, do not |
- // attempt to add tabs to the browser before it closes. |
- is_attempting_to_close_browser_ = true; |
} |
bool Browser::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, |
@@ -1381,14 +1369,7 @@ |
} |
bool Browser::TabsNeedBeforeUnloadFired() { |
- if (tabs_needing_before_unload_fired_.empty()) { |
- for (int i = 0; i < tab_count(); ++i) { |
- WebContents* contents = chrome::GetTabContentsAt(this, i)->web_contents(); |
- if (contents->NeedToFireBeforeUnload()) |
- tabs_needing_before_unload_fired_.insert(contents); |
- } |
- } |
- return !tabs_needing_before_unload_fired_.empty(); |
+ return unload_controller_->TabsNeedBeforeUnloadFired(); |
} |
bool Browser::IsFullscreenForTabOrPending() const { |
@@ -1512,16 +1493,8 @@ |
} |
void Browser::CloseContents(WebContents* 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. |
- ClearUnloadState(source, true); |
- return; |
- } |
- |
- chrome::CloseWebContents(this, source); |
+ if (unload_controller_->CanCloseContents(source)) |
+ chrome::CloseWebContents(this, source); |
} |
void Browser::MoveContents(WebContents* source, const gfx::Rect& pos) { |
@@ -1587,33 +1560,8 @@ |
void Browser::BeforeUnloadFired(WebContents* web_contents, |
bool proceed, |
bool* proceed_to_fire_unload) { |
- if (!is_attempting_to_close_browser_) { |
- *proceed_to_fire_unload = proceed; |
- if (!proceed) |
- web_contents->SetClosedByUserGesture(false); |
- return; |
- } |
- |
- if (!proceed) { |
- CancelWindowClose(); |
- *proceed_to_fire_unload = false; |
- web_contents->SetClosedByUserGesture(false); |
- return; |
- } |
- |
- if (RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents)) { |
- // Now that beforeunload has fired, put the tab on the queue to fire |
- // unload. |
- tabs_needing_unload_fired_.insert(web_contents); |
- 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; |
+ *proceed_to_fire_unload = |
+ unload_controller_->BeforeUnloadFired(web_contents, proceed); |
} |
void Browser::SetFocusToLocationBar(bool select_all) { |
@@ -2041,15 +1989,6 @@ |
const content::NotificationSource& source, |
const content::NotificationDetails& details) { |
switch (type) { |
- case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: |
- if (is_attempting_to_close_browser_) { |
- // Pass in false so that we delay processing. We need to delay the |
- // processing as it may close the tab, which is currently on the call |
- // stack above us. |
- ClearUnloadState(content::Source<WebContents>(source).ptr(), false); |
- } |
- break; |
- |
case content::NOTIFICATION_SSL_VISIBLE_STATE_CHANGED: |
// When the current tab's SSL state changes, we need to update the URL |
// bar to reflect the new state. Note that it's possible for the selected |
@@ -2396,102 +2335,6 @@ |
} |
/////////////////////////////////////////////////////////////////////////////// |
-// Browser, OnBeforeUnload handling (private): |
- |
-void Browser::ProcessPendingTabs() { |
- if (!is_attempting_to_close_browser_) { |
- // Because we might invoke this after a delay it's possible for the value of |
- // is_attempting_to_close_browser_ to have changed since we scheduled the |
- // task. |
- return; |
- } |
- |
- if (HasCompletedUnloadProcessing()) { |
- // We've finished all the unload events and can proceed to close the |
- // browser. |
- OnWindowClosing(); |
- return; |
- } |
- |
- // Process beforeunload tabs first. When that queue is empty, process |
- // unload tabs. |
- if (!tabs_needing_before_unload_fired_.empty()) { |
- WebContents* web_contents = *(tabs_needing_before_unload_fired_.begin()); |
- // Null check render_view_host here as this gets called on a PostTask and |
- // the tab's render_view_host may have been nulled out. |
- if (web_contents->GetRenderViewHost()) { |
- web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); |
- } else { |
- ClearUnloadState(web_contents, true); |
- } |
- } 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. |
- // 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. |
- WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); |
- // Null check render_view_host here as this gets called on a PostTask and |
- // the tab's render_view_host may have been nulled out. |
- if (web_contents->GetRenderViewHost()) { |
- web_contents->GetRenderViewHost()->ClosePage(); |
- } else { |
- ClearUnloadState(web_contents, true); |
- } |
- } else { |
- NOTREACHED(); |
- } |
-} |
- |
-bool Browser::HasCompletedUnloadProcessing() const { |
- return is_attempting_to_close_browser_ && |
- tabs_needing_before_unload_fired_.empty() && |
- tabs_needing_unload_fired_.empty(); |
-} |
- |
-void Browser::CancelWindowClose() { |
- // Closing of window can be canceled from a beforeunload handler. |
- DCHECK(is_attempting_to_close_browser_); |
- tabs_needing_before_unload_fired_.clear(); |
- tabs_needing_unload_fired_.clear(); |
- is_attempting_to_close_browser_ = false; |
- |
- content::NotificationService::current()->Notify( |
- chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, |
- content::Source<Browser>(this), |
- content::NotificationService::NoDetails()); |
-} |
- |
-bool Browser::RemoveFromSet(UnloadListenerSet* set, WebContents* web_contents) { |
- DCHECK(is_attempting_to_close_browser_); |
- |
- UnloadListenerSet::iterator iter = |
- std::find(set->begin(), set->end(), web_contents); |
- if (iter != set->end()) { |
- set->erase(iter); |
- return true; |
- } |
- return false; |
-} |
- |
-void Browser::ClearUnloadState(WebContents* web_contents, bool process_now) { |
- if (is_attempting_to_close_browser_) { |
- RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents); |
- RemoveFromSet(&tabs_needing_unload_fired_, web_contents); |
- if (process_now) { |
- ProcessPendingTabs(); |
- } else { |
- MessageLoop::current()->PostTask( |
- FROM_HERE, |
- base::Bind(&Browser::ProcessPendingTabs, weak_factory_.GetWeakPtr())); |
- } |
- } |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
// Browser, In-progress download termination handling (private): |
bool Browser::CanCloseWithInProgressDownloads() { |
@@ -2559,14 +2402,6 @@ |
find_bar_controller_->ChangeTabContents(NULL); |
} |
- if (is_attempting_to_close_browser_) { |
- // If this is the last tab with unload handlers, then ProcessPendingTabs |
- // would call back into the TabStripModel (which is invoking this method on |
- // us). Avoid that by passing in false so that the call to |
- // ProcessPendingTabs is delayed. |
- ClearUnloadState(contents->web_contents(), false); |
- } |
- |
// Stop observing search model changes for this tab. |
search_delegate_->OnTabDetached(contents); |
@@ -2574,8 +2409,6 @@ |
content::Source<WebContents>(contents->web_contents())); |
registrar_.Remove(this, content::NOTIFICATION_INTERSTITIAL_DETACHED, |
content::Source<WebContents>(contents->web_contents())); |
- registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, |
- content::Source<WebContents>(contents->web_contents())); |
} |
bool Browser::SupportsWindowFeatureImpl(WindowFeature feature, |