diff options
21 files changed, 175 insertions, 692 deletions
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index d6d87e3..112525a 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -584,14 +584,6 @@ bool Browser::ShouldCloseWindow() { return unload_controller_->ShouldCloseWindow(); } -bool Browser::TabsNeedBeforeUnloadFired() { - return unload_controller_->TabsNeedBeforeUnloadFired(); -} - -bool Browser::HasCompletedUnloadProcessing() const { - return unload_controller_->HasCompletedUnloadProcessing(); -} - bool Browser::IsAttemptingToCloseBrowser() const { return unload_controller_->is_attempting_to_close_browser(); } @@ -635,6 +627,8 @@ void Browser::OnWindowClosing() { chrome::NOTIFICATION_BROWSER_CLOSING, content::Source<Browser>(this), content::NotificationService::NoDetails()); + + tab_strip_model_->CloseAllTabs(); } //////////////////////////////////////////////////////////////////////////////// @@ -1171,6 +1165,10 @@ void Browser::HandleKeyboardEvent(content::WebContents* source, window()->HandleKeyboardEvent(event); } +bool Browser::TabsNeedBeforeUnloadFired() { + return unload_controller_->TabsNeedBeforeUnloadFired(); +} + bool Browser::IsMouseLocked() const { return fullscreen_controller_->IsMouseLocked(); } diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 60ac1ea..2766b78 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h @@ -293,13 +293,6 @@ class Browser : public TabStripModelObserver, // Gives beforeunload handlers the chance to cancel the close. bool ShouldCloseWindow(); - // Figure out if there are tabs that have beforeunload handlers. - // It starts beforeunload/unload processing as a side-effect. - bool TabsNeedBeforeUnloadFired(); - - // Returns true if all tabs' beforeunload/unload events have fired. - bool HasCompletedUnloadProcessing() const; - bool IsAttemptingToCloseBrowser() const; // Invoked when the window containing us is closing. Performs the necessary @@ -437,6 +430,9 @@ class Browser : public TabStripModelObserver, content::WebContents* source, const content::NativeWebKeyboardEvent& event) OVERRIDE; + // Figure out if there are tabs that have beforeunload handlers. + bool TabsNeedBeforeUnloadFired(); + bool is_type_tabbed() const { return type_ == TYPE_TABBED; } bool is_type_popup() const { return type_ == TYPE_POPUP; } diff --git a/chrome/browser/ui/browser_tabstrip.cc b/chrome/browser/ui/browser_tabstrip.cc index 9ffa615..aa308e0 100644 --- a/chrome/browser/ui/browser_tabstrip.cc +++ b/chrome/browser/ui/browser_tabstrip.cc @@ -9,7 +9,6 @@ #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_navigator.h" -#include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" @@ -29,9 +28,7 @@ void AddBlankTabAt(Browser* browser, int index, bool foreground) { params.disposition = foreground ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; params.tabstrip_index = index; chrome::Navigate(¶ms); - CoreTabHelper* core_tab_helper = - CoreTabHelper::FromWebContents(params.target_contents); - core_tab_helper->set_new_tab_start_time(new_tab_start_time); + params.target_contents->SetNewTabStartTime(new_tab_start_time); } content::WebContents* AddSelectedTabWithURL( diff --git a/chrome/browser/ui/cocoa/applescript/window_applescript.mm b/chrome/browser/ui/cocoa/applescript/window_applescript.mm index fe6677f..fe9e055 100644 --- a/chrome/browser/ui/cocoa/applescript/window_applescript.mm +++ b/chrome/browser/ui/cocoa/applescript/window_applescript.mm @@ -21,7 +21,6 @@ #include "chrome/browser/ui/cocoa/applescript/error_applescript.h" #import "chrome/browser/ui/cocoa/applescript/tab_applescript.h" #include "chrome/browser/ui/host_desktop.h" -#include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/url_constants.h" #include "content/public/browser/web_contents.h" @@ -180,8 +179,7 @@ browser_, GURL(chrome::kChromeUINewTabURL), content::PAGE_TRANSITION_TYPED); - CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); - core_tab_helper->set_new_tab_start_time(newTabStartTime); + contents->SetNewTabStartTime(newTabStartTime); [aTab setWebContents:contents]; } @@ -198,9 +196,7 @@ params.disposition = NEW_FOREGROUND_TAB; params.tabstrip_index = index; chrome::Navigate(¶ms); - CoreTabHelper* core_tab_helper = - CoreTabHelper::FromWebContents(params.target_contents); - core_tab_helper->set_new_tab_start_time(newTabStartTime); + params.target_contents->SetNewTabStartTime(newTabStartTime); [aTab setWebContents:params.target_contents]; } diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm index d200899..d4cc3a3 100644 --- a/chrome/browser/ui/cocoa/browser_window_controller.mm +++ b/chrome/browser/ui/cocoa/browser_window_controller.mm @@ -599,18 +599,11 @@ enum { [self saveWindowPositionIfNeeded]; if (!browser_->tab_strip_model()->empty()) { - // Tab strip isn't empty. Hide the window (so it appears to have closed + // Tab strip isn't empty. Hide the frame (so it appears to have closed // immediately) and close all the tabs, allowing the renderers to shut // down. When the tab strip is empty we'll be called back again. [[self window] orderOut:self]; browser_->OnWindowClosing(); - browser_->tab_strip_model()->CloseAllTabs(); - return NO; - } else if (!browser_->HasCompletedUnloadProcessing()) { - // The browser needs to finish running unload handlers. - // Hide the window (so it appears to have closed immediately), and - // the browser will call us back again when it is ready to close. - [[self window] orderOut:self]; return NO; } diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc index f343c44..16dfc66 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.cc +++ b/chrome/browser/ui/gtk/browser_window_gtk.cc @@ -1449,13 +1449,6 @@ bool BrowserWindowGtk::CanClose() const { // down. When the tab strip is empty we'll be called back again. gtk_widget_hide(GTK_WIDGET(window_)); browser_->OnWindowClosing(); - browser_->tab_strip_model()->CloseAllTabs(); - return false; - } else if (!browser_->HasCompletedUnloadProcessing()) { - // The browser needs to finish running unload handlers. - // Hide the window (so it appears to have closed immediately), and - // the browser will call us back again when it is ready to close. - gtk_widget_hide(GTK_WIDGET(window_)); return false; } diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc index f8dc42e..502a3f8 100644 --- a/chrome/browser/ui/tab_contents/core_tab_helper.cc +++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc @@ -4,7 +4,6 @@ #include "chrome/browser/ui/tab_contents/core_tab_helper.h" -#include "base/metrics/histogram.h" #include "chrome/browser/renderer_host/web_cache_manager.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" @@ -85,26 +84,6 @@ string16 CoreTabHelper::GetStatusText() const { return string16(); } -void CoreTabHelper::OnCloseStarted() { - if (close_start_time_.is_null()) - close_start_time_ = base::TimeTicks::Now(); -} - -void CoreTabHelper::OnCloseCanceled() { - close_start_time_ = base::TimeTicks(); - before_unload_end_time_ = base::TimeTicks(); - unload_detached_start_time_ = base::TimeTicks(); -} - -void CoreTabHelper::OnUnloadStarted() { - before_unload_end_time_ = base::TimeTicks::Now(); -} - -void CoreTabHelper::OnUnloadDetachedStarted() { - if (unload_detached_start_time_.is_null()) - unload_detached_start_time_ = base::TimeTicks::Now(); -} - //////////////////////////////////////////////////////////////////////////////// // WebContentsObserver overrides @@ -112,30 +91,3 @@ void CoreTabHelper::WasShown() { WebCacheManager::GetInstance()->ObserveActivity( web_contents()->GetRenderProcessHost()->GetID()); } - -void CoreTabHelper::WebContentsDestroyed(WebContents* web_contents) { - // OnCloseStarted isn't called in unit tests. - if (!close_start_time_.is_null()) { - base::TimeTicks now = base::TimeTicks::Now(); - base::TimeDelta close_time = now - close_start_time_; - UMA_HISTOGRAM_TIMES("Tab.Close", close_time); - - base::TimeTicks unload_start_time = close_start_time_; - base::TimeTicks unload_end_time = now; - if (!before_unload_end_time_.is_null()) - unload_start_time = before_unload_end_time_; - if (!unload_detached_start_time_.is_null()) - unload_end_time = unload_detached_start_time_; - base::TimeDelta unload_time = unload_end_time - unload_start_time; - UMA_HISTOGRAM_TIMES("Tab.Close.UnloadTime", unload_time); - - } -} - -void CoreTabHelper::BeforeUnloadFired(const base::TimeTicks& proceed_time) { - before_unload_end_time_ = proceed_time; -} - -void CoreTabHelper::BeforeUnloadDialogCancelled() { - OnCloseCanceled(); -} diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.h b/chrome/browser/ui/tab_contents/core_tab_helper.h index e5a37dd..834613e 100644 --- a/chrome/browser/ui/tab_contents/core_tab_helper.h +++ b/chrome/browser/ui/tab_contents/core_tab_helper.h @@ -5,7 +5,6 @@ #ifndef CHROME_BROWSER_UI_TAB_CONTENTS_CORE_TAB_HELPER_H_ #define CHROME_BROWSER_UI_TAB_CONTENTS_CORE_TAB_HELPER_H_ -#include "base/time.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" @@ -17,64 +16,25 @@ class CoreTabHelper : public content::WebContentsObserver, public: virtual ~CoreTabHelper(); + CoreTabHelperDelegate* delegate() const { return delegate_; } + void set_delegate(CoreTabHelperDelegate* d) { delegate_ = d; } + // Initial title assigned to NavigationEntries from Navigate. static string16 GetDefaultTitle(); // Returns a human-readable description the tab's loading state. string16 GetStatusText() const; - // Notification that tab closing has started. This can be called multiple - // times, subsequent calls are ignored. - void OnCloseStarted(); - - // Notification that tab closing was cancelled. This can happen when a user - // cancels a window close via another tab's beforeunload dialog. - void OnCloseCanceled(); - - // Set the time during close when unload is started. Normally, this is set - // after the beforeunload dialog. However, for a window close, it is set - // after all the beforeunload dialogs have finished. - void OnUnloadStarted(); - - // Set the time during close when the tab is no longer visible. - void OnUnloadDetachedStarted(); - - CoreTabHelperDelegate* delegate() const { return delegate_; } - void set_delegate(CoreTabHelperDelegate* d) { delegate_ = d; } - - void set_new_tab_start_time(const base::TimeTicks& time) { - new_tab_start_time_ = time; - } - - base::TimeTicks new_tab_start_time() const { return new_tab_start_time_; } - private: explicit CoreTabHelper(content::WebContents* web_contents); friend class content::WebContentsUserData<CoreTabHelper>; // content::WebContentsObserver overrides: virtual void WasShown() OVERRIDE; - virtual void WebContentsDestroyed( - content::WebContents* web_contents) OVERRIDE; - virtual void BeforeUnloadFired(const base::TimeTicks& proceed_time) OVERRIDE; - virtual void BeforeUnloadDialogCancelled() OVERRIDE; // Delegate for notifying our owner about stuff. Not owned by us. CoreTabHelperDelegate* delegate_; - // The time when we started to create the new tab page. This time is from - // before we created this WebContents. - base::TimeTicks new_tab_start_time_; - - // The time that we started to close this WebContents. - base::TimeTicks close_start_time_; - - // The time when onbeforeunload ended. - base::TimeTicks before_unload_end_time_; - - // The time when the tab was removed from view during close. - base::TimeTicks unload_detached_start_time_; - DISALLOW_COPY_AND_ASSIGN(CoreTabHelper); }; diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc index a550ca0..78c8f0e 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chrome/browser/ui/tabs/tab_strip_model.cc @@ -1115,9 +1115,7 @@ bool TabStripModel::InternalCloseTabs(const std::vector<int>& indices, if (index == kNoTab) continue; - CoreTabHelper* core_tab_helper = - CoreTabHelper::FromWebContents(closing_contents); - core_tab_helper->OnCloseStarted(); + closing_contents->OnCloseStarted(); // Update the explicitly closed state. If the unload handlers cancel the // close the state is reset in Browser. We don't update the explicitly diff --git a/chrome/browser/ui/unload_controller.cc b/chrome/browser/ui/unload_controller.cc index 19d6154..da61646 100644 --- a/chrome/browser/ui/unload_controller.cc +++ b/chrome/browser/ui/unload_controller.cc @@ -4,55 +4,25 @@ #include "chrome/browser/ui/unload_controller.h" -#include "base/logging.h" #include "base/message_loop.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tabstrip.h" -#include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" #include "chrome/common/chrome_notification_types.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_delegate.h" namespace chrome { - -//////////////////////////////////////////////////////////////////////////////// -// DetachedWebContentsDelegate will delete web contents when they close. -class UnloadController::DetachedWebContentsDelegate - : public content::WebContentsDelegate { - public: - DetachedWebContentsDelegate() { } - virtual ~DetachedWebContentsDelegate() { } - - private: - // WebContentsDelegate implementation. - virtual bool ShouldSuppressDialogs() OVERRIDE { - return true; // Return true so dialogs are suppressed. - } - - virtual void CloseContents(content::WebContents* source) OVERRIDE { - // Finished detached close. - // UnloadController will observe |NOTIFICATION_WEB_CONTENTS_DISCONNECTED|. - delete source; - } - - DISALLOW_COPY_AND_ASSIGN(DetachedWebContentsDelegate); -}; - //////////////////////////////////////////////////////////////////////////////// // UnloadController, public: UnloadController::UnloadController(Browser* browser) : browser_(browser), - tab_needing_before_unload_ack_(NULL), is_attempting_to_close_browser_(false), - detached_delegate_(new DetachedWebContentsDelegate()), weak_factory_(this) { browser_->tab_strip_model()->AddObserver(this); } @@ -64,20 +34,16 @@ UnloadController::~UnloadController() { bool UnloadController::CanCloseContents(content::WebContents* contents) { // Don't try to close the tab when the whole browser is being closed, since // that avoids the fast shutdown path where we just kill all the renderers. + if (is_attempting_to_close_browser_) + ClearUnloadState(contents, true); return !is_attempting_to_close_browser_; } bool UnloadController::BeforeUnloadFired(content::WebContents* contents, bool proceed) { if (!is_attempting_to_close_browser_) { - if (!proceed) { + if (!proceed) contents->SetClosedByUserGesture(false); - } else { - // No more dialogs are possible, so remove the tab and finish - // running unload listeners asynchrounously. - browser_->tab_strip_model()->delegate()->CreateHistoricalTab(contents); - DetachWebContents(contents); - } return proceed; } @@ -87,10 +53,10 @@ bool UnloadController::BeforeUnloadFired(content::WebContents* contents, return false; } - if (tab_needing_before_unload_ack_ == contents) { - // Now that beforeunload has fired, queue the tab to fire unload. - tab_needing_before_unload_ack_ = NULL; - tabs_needing_unload_.insert(contents); + if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { + // Now that beforeunload has fired, put the tab on the queue to fire + // unload. + tabs_needing_unload_fired_.insert(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 @@ -115,20 +81,15 @@ bool UnloadController::ShouldCloseWindow() { } bool UnloadController::TabsNeedBeforeUnloadFired() { - if (!tabs_needing_before_unload_.empty() || - tab_needing_before_unload_ack_ != NULL) - return true; - - if (!tabs_needing_unload_.empty()) - return false; - - for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { - content::WebContents* contents = - browser_->tab_strip_model()->GetWebContentsAt(i); - if (contents->NeedToFireBeforeUnload()) - tabs_needing_before_unload_.insert(contents); + if (tabs_needing_before_unload_fired_.empty()) { + for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { + content::WebContents* contents = + browser_->tab_strip_model()->GetWebContentsAt(i); + if (contents->NeedToFireBeforeUnload()) + tabs_needing_before_unload_fired_.insert(contents); + } } - return !tabs_needing_before_unload_.empty(); + return !tabs_needing_before_unload_fired_.empty(); } //////////////////////////////////////////////////////////////////////////////// @@ -138,15 +99,12 @@ void UnloadController::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { - case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: { - registrar_.Remove(this, - content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, - source); - content::WebContents* contents = - content::Source<content::WebContents>(source).ptr(); - ClearUnloadState(contents); + case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: + if (is_attempting_to_close_browser_) { + ClearUnloadState(content::Source<content::WebContents>(source).ptr(), + false); // See comment for ClearUnloadState(). + } break; - } default: NOTREACHED() << "Got a notification we didn't register for."; } @@ -193,41 +151,11 @@ void UnloadController::TabAttachedImpl(content::WebContents* contents) { } void UnloadController::TabDetachedImpl(content::WebContents* contents) { - if (tabs_needing_unload_ack_.find(contents) != - tabs_needing_unload_ack_.end()) { - // Tab needs unload to complete. - // It will send |NOTIFICATION_WEB_CONTENTS_DISCONNECTED| when done. - return; - } - - // If WEB_CONTENTS_DISCONNECTED was received then the notification may have - // already been unregistered. - const content::NotificationSource& source = - content::Source<content::WebContents>(contents); - if (registrar_.IsRegistered(this, - content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, - source)) { - registrar_.Remove(this, - content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, - source); - } - if (is_attempting_to_close_browser_) - ClearUnloadState(contents); -} - -bool UnloadController::DetachWebContents(content::WebContents* contents) { - int index = browser_->tab_strip_model()->GetIndexOfWebContents(contents); - if (index != TabStripModel::kNoTab && - contents->NeedToFireBeforeUnload()) { - tabs_needing_unload_ack_.insert(contents); - browser_->tab_strip_model()->DetachWebContentsAt(index); - contents->SetDelegate(detached_delegate_.get()); - CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); - core_tab_helper->OnUnloadDetachedStarted(); - return true; - } - return false; + ClearUnloadState(contents, false); + registrar_.Remove(this, + content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, + content::Source<content::WebContents>(contents)); } void UnloadController::ProcessPendingTabs() { @@ -238,105 +166,58 @@ void UnloadController::ProcessPendingTabs() { return; } - if (tab_needing_before_unload_ack_ != NULL) { - // Wait for |BeforeUnloadFired| before proceeding. + if (HasCompletedUnloadProcessing()) { + // We've finished all the unload events and can proceed to close the + // browser. + browser_->OnWindowClosing(); return; } - // Process a beforeunload handler. - if (!tabs_needing_before_unload_.empty()) { - WebContentsSet::iterator it = tabs_needing_before_unload_.begin(); - content::WebContents* contents = *it; - tabs_needing_before_unload_.erase(it); + // Process beforeunload tabs first. When that queue is empty, process + // unload tabs. + if (!tabs_needing_before_unload_fired_.empty()) { + content::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 (contents->GetRenderViewHost()) { - tab_needing_before_unload_ack_ = contents; - - CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); - core_tab_helper->OnCloseStarted(); - - contents->GetRenderViewHost()->FirePageBeforeUnload(false); + if (web_contents->GetRenderViewHost()) { + web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); } else { - ProcessPendingTabs(); + ClearUnloadState(web_contents, true); } - return; - } - - // Process all the unload handlers. (The beforeunload handlers have finished.) - if (!tabs_needing_unload_.empty()) { - browser_->OnWindowClosing(); - - // Run unload handlers detached since no more interaction is possible. - WebContentsSet::iterator it = tabs_needing_unload_.begin(); - while (it != tabs_needing_unload_.end()) { - WebContentsSet::iterator current = it++; - content::WebContents* contents = *current; - tabs_needing_unload_.erase(current); - // 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 (contents->GetRenderViewHost()) { - CoreTabHelper* core_tab_helper = - CoreTabHelper::FromWebContents(contents); - core_tab_helper->OnUnloadStarted(); - DetachWebContents(contents); - contents->GetRenderViewHost()->ClosePage(); - } - } - - // Get the browser hidden. - if (browser_->tab_strip_model()->empty()) { - browser_->TabStripEmpty(); - } else { - browser_->tab_strip_model()->CloseAllTabs(); // tabs not needing unload - } - return; - } - - if (HasCompletedUnloadProcessing()) { - browser_->OnWindowClosing(); - - // Get the browser closed. - if (browser_->tab_strip_model()->empty()) { - browser_->TabStripEmpty(); + } 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. + content::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 { - // There may be tabs if the last tab needing beforeunload crashed. - browser_->tab_strip_model()->CloseAllTabs(); + ClearUnloadState(web_contents, true); } - return; + } else { + NOTREACHED(); } } bool UnloadController::HasCompletedUnloadProcessing() const { return is_attempting_to_close_browser_ && - tabs_needing_before_unload_.empty() && - tab_needing_before_unload_ack_ == NULL && - tabs_needing_unload_.empty() && - tabs_needing_unload_ack_.empty(); + tabs_needing_before_unload_fired_.empty() && + tabs_needing_unload_fired_.empty(); } void UnloadController::CancelWindowClose() { // Closing of window can be canceled from a beforeunload handler. DCHECK(is_attempting_to_close_browser_); - tabs_needing_before_unload_.clear(); - if (tab_needing_before_unload_ack_ != NULL) { - - CoreTabHelper* core_tab_helper = - CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_); - core_tab_helper->OnCloseCanceled(); - tab_needing_before_unload_ack_ = NULL; - } - for (WebContentsSet::iterator it = tabs_needing_unload_.begin(); - it != tabs_needing_unload_.end(); it++) { - content::WebContents* contents = *it; - - CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); - core_tab_helper->OnCloseCanceled(); - } - tabs_needing_unload_.clear(); - - // No need to clear tabs_needing_unload_ack_. Those tabs are already detached. - + tabs_needing_before_unload_fired_.clear(); + tabs_needing_unload_fired_.clear(); is_attempting_to_close_browser_ = false; content::NotificationService::current()->Notify( @@ -345,34 +226,33 @@ void UnloadController::CancelWindowClose() { content::NotificationService::NoDetails()); } -void UnloadController::ClearUnloadState(content::WebContents* contents) { - if (tabs_needing_unload_ack_.erase(contents) > 0) { - if (HasCompletedUnloadProcessing()) - PostTaskForProcessPendingTabs(); - return; - } - - if (!is_attempting_to_close_browser_) - return; - - if (tab_needing_before_unload_ack_ == contents) { - tab_needing_before_unload_ack_ = NULL; - PostTaskForProcessPendingTabs(); - return; - } +bool UnloadController::RemoveFromSet(UnloadListenerSet* set, + content::WebContents* web_contents) { + DCHECK(is_attempting_to_close_browser_); - if (tabs_needing_before_unload_.erase(contents) > 0 || - tabs_needing_unload_.erase(contents) > 0) { - if (tab_needing_before_unload_ack_ == NULL) - PostTaskForProcessPendingTabs(); + UnloadListenerSet::iterator iter = + std::find(set->begin(), set->end(), web_contents); + if (iter != set->end()) { + set->erase(iter); + return true; } + return false; } -void UnloadController::PostTaskForProcessPendingTabs() { - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&UnloadController::ProcessPendingTabs, - weak_factory_.GetWeakPtr())); +void UnloadController::ClearUnloadState(content::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 { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&UnloadController::ProcessPendingTabs, + weak_factory_.GetWeakPtr())); + } + } } } // namespace chrome diff --git a/chrome/browser/ui/unload_controller.h b/chrome/browser/ui/unload_controller.h index 47e50b0..4c13c67 100644 --- a/chrome/browser/ui/unload_controller.h +++ b/chrome/browser/ui/unload_controller.h @@ -7,9 +7,7 @@ #include <set> -#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/strings/string_piece.h" #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -24,33 +22,7 @@ class WebContents; } namespace chrome { -// UnloadController manages closing tabs and windows -- especially in -// regards to beforeunload handlers (have proceed/cancel dialogs) and -// unload handlers (have no user interaction). -// -// Typical flow of closing a tab: -// 1. Browser calls CanCloseContents(). -// If true, browser calls contents::CloseWebContents(). -// 2. WebContents notifies us via its delegate and BeforeUnloadFired() -// that the beforeunload handler was run. If the user allowed the -// close to continue, we detached the tab and hold onto it while the -// close finishes. -// -// Typical flow of closing a window: -// 1. BrowserView::CanClose() calls TabsNeedBeforeUnloadFired(). -// If beforeunload/unload handlers need to run, UnloadController returns -// true and calls ProcessPendingTabs() (private method). -// 2. For each tab with a beforeunload/unload handler, ProcessPendingTabs() -// calls |CoreTabHelper::OnCloseStarted()| -// and |web_contents->GetRenderViewHost()->FirePageBeforeUnload()|. -// 3. If the user allowed the close to continue, we detach all the tabs with -// unload handlers, remove them from the tab strip, and finish closing -// the tabs in the background. -// 4. The browser gets notified that the tab strip is empty and calls -// CloseFrame where the empty tab strip causes the window to hide. -// Once the detached tabs finish, the browser calls CloseFrame again and -// the window is finally closed. -// + class UnloadController : public content::NotificationObserver, public TabStripModelObserver { public: @@ -77,7 +49,7 @@ class UnloadController : public content::NotificationObserver, } // Called in response to a request to close |browser_|'s window. Returns true - // when there are no remaining beforeunload handlers to be run. + // when there are no remaining unload handlers to be run. bool ShouldCloseWindow(); // Returns true if |browser_| has any tabs that have BeforeUnload handlers @@ -89,10 +61,9 @@ class UnloadController : public content::NotificationObserver, // could be pursued. bool TabsNeedBeforeUnloadFired(); - // Returns true if all tabs' beforeunload/unload events have fired. - bool HasCompletedUnloadProcessing() const; - private: + typedef std::set<content::WebContents*> UnloadListenerSet; + // Overridden from content::NotificationObserver: virtual void Observe(int type, const content::NotificationSource& source, @@ -113,52 +84,43 @@ class UnloadController : public content::NotificationObserver, void TabAttachedImpl(content::WebContents* contents); void TabDetachedImpl(content::WebContents* contents); - // Detach |contents| and wait for it to finish closing. - // The close must be inititiated outside of this method. - // Returns true if it succeeds. - bool DetachWebContents(content::WebContents* contents); - // Processes the next tab that needs it's beforeunload/unload event fired. void ProcessPendingTabs(); + // Whether we've completed firing all the tabs' beforeunload/unload events. + bool HasCompletedUnloadProcessing() const; + // Clears all the state associated with processing tabs' beforeunload/unload // events since the user cancelled closing the window. void CancelWindowClose(); - // Cleans up state appropriately when we are trying to close the - // browser or close a tab in the background. We also use this in the - // cases where a tab crashes or hangs even if the - // beforeunload/unload haven't successfully fired. - void ClearUnloadState(content::WebContents* contents); - - // Helper for |ClearUnloadState| to unwind stack before proceeding. - void PostTaskForProcessPendingTabs(); - - // Log a step of the unload processing. - void LogUnloadStep(const base::StringPiece& step_name, - content::WebContents* contents) const; + // Removes |web_contents| from the passed |set|. + // Returns whether the tab was in the set in the first place. + bool RemoveFromSet(UnloadListenerSet* set, + content::WebContents* web_contents); + + // Cleans up state appropriately when we are trying to close the browser and + // the tab has finished firing its unload handler. We also use this in the + // cases where a tab crashes or hangs even if the beforeunload/unload haven't + // successfully fired. If |process_now| is true |ProcessPendingTabs| is + // invoked immediately, otherwise it is invoked after a delay (PostTask). + // + // Typically you'll want to pass in true for |process_now|. Passing in true + // may result in deleting |tab|. If you know that shouldn't happen (because of + // the state of the stack), pass in false. + void ClearUnloadState(content::WebContents* web_contents, bool process_now); Browser* browser_; content::NotificationRegistrar registrar_; - typedef std::set<content::WebContents*> WebContentsSet; - - // Tracks tabs that need their beforeunload event started. - // Only gets populated when we try to close the browser. - WebContentsSet tabs_needing_before_unload_; + // Tracks tabs that need there beforeunload event fired before we can + // close the browser. Only gets populated when we try to close the browser. + UnloadListenerSet tabs_needing_before_unload_fired_; - // Tracks the tab that needs its beforeunload event result. - // Only gets populated when we try to close the browser. - content::WebContents* tab_needing_before_unload_ack_; - - // Tracks tabs that need their unload event started. - // Only gets populated when we try to close the browser. - WebContentsSet tabs_needing_unload_; - - // Tracks tabs that need to finish running their unload event. - // Populated both when closing individual tabs and when closing the browser. - WebContentsSet tabs_needing_unload_ack_; + // Tracks tabs that need there unload event fired before we can + // close the browser. Only gets populated when we try to close the browser. + UnloadListenerSet tabs_needing_unload_fired_; // Whether we are processing the beforeunload and unload events of each tab // in preparation for closing the browser. UnloadController owns this state @@ -166,10 +128,6 @@ class UnloadController : public content::NotificationObserver, // Browser window isn't just immediately closed. bool is_attempting_to_close_browser_; - // Manage tabs with beforeunload/unload handlers that close detached. - class DetachedWebContentsDelegate; - scoped_ptr<DetachedWebContentsDelegate> detached_delegate_; - base::WeakPtrFactory<UnloadController> weak_factory_; DISALLOW_COPY_AND_ASSIGN(UnloadController); diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index df661dc..e867372 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -1830,13 +1830,6 @@ bool BrowserView::CanClose() { // down. When the tab strip is empty we'll be called back again. frame_->Hide(); browser_->OnWindowClosing(); - browser_->tab_strip_model()->CloseAllTabs(); - return false; - } else if (!browser_->HasCompletedUnloadProcessing()) { - // The browser needs to finish running unload handlers. - // Hide the frame (so it appears to have closed immediately), and - // the browser will call us back again when it is ready to close. - frame_->Hide(); return false; } diff --git a/chrome/browser/ui/webui/metrics_handler.cc b/chrome/browser/ui/webui/metrics_handler.cc index 9394e07..b57ce0f 100644 --- a/chrome/browser/ui/webui/metrics_handler.cc +++ b/chrome/browser/ui/webui/metrics_handler.cc @@ -11,7 +11,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/metrics/metric_event_duration_details.h" -#include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/common/chrome_notification_types.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/user_metrics.h" @@ -83,12 +82,10 @@ void MetricsHandler::HandleLogEventTime(const ListValue* args) { // Not all new tab pages get timed. In those cases, we don't have a // new_tab_start_time_. - CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(tab); - if (core_tab_helper->new_tab_start_time().is_null()) + if (tab->GetNewTabStartTime().is_null()) return; - base::TimeDelta duration = - base::TimeTicks::Now() - core_tab_helper->new_tab_start_time(); + base::TimeDelta duration = base::TimeTicks::Now() - tab->GetNewTabStartTime(); MetricEventDurationDetails details(event_name, static_cast<int>(duration.InMilliseconds())); @@ -99,8 +96,7 @@ void MetricsHandler::HandleLogEventTime(const ListValue* args) { } else if (event_name == "Tab.NewTabOnload") { UMA_HISTOGRAM_TIMES("Tab.NewTabOnload", duration); // The new tab page has finished loading; reset it. - CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(tab); - core_tab_helper->set_new_tab_start_time(base::TimeTicks()); + tab->SetNewTabStartTime(base::TimeTicks()); } else { NOTREACHED(); } diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc index 331b23c..9552d0a 100644 --- a/chrome/browser/unload_browsertest.cc +++ b/chrome/browser/unload_browsertest.cc @@ -411,210 +411,5 @@ IN_PROC_BROWSER_TEST_F(UnloadTest, BrowserCloseTabWhenOtherTabHasListener) { CheckTitle("only_one_unload"); } - -class FastUnloadTest : public UnloadTest { - public: - virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { - ASSERT_TRUE(test_server()->Start()); - } - - virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { - test_server()->Stop(); - } - - GURL GetUrl(const std::string& name) { - return GURL(test_server()->GetURL( - "files/fast_tab_close/" + name + ".html")); - } - - void NavigateToPage(const char* name) { - ui_test_utils::NavigateToURL(browser(), GetUrl(name)); - CheckTitle(name); - } - - void NavigateToPageInNewTab(const char* name) { - ui_test_utils::NavigateToURLWithDisposition( - browser(), GetUrl(name), NEW_FOREGROUND_TAB, - ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - CheckTitle(name); - } - - std::string GetCookies(const char* name) { - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - return content::GetCookies(contents->GetBrowserContext(), GetUrl(name)); - } -}; - -class FastTabCloseTabStripModelObserver : public TabStripModelObserver { - public: - FastTabCloseTabStripModelObserver(TabStripModel* model, - base::RunLoop* run_loop) - : model_(model), - run_loop_(run_loop) { - model_->AddObserver(this); - } - - virtual ~FastTabCloseTabStripModelObserver() { - model_->RemoveObserver(this); - } - - // TabStripModelObserver: - virtual void TabDetachedAt(content::WebContents* contents, - int index) OVERRIDE { - run_loop_->Quit(); - } - - private: - TabStripModel* const model_; - base::RunLoop* const run_loop_; -}; - - -// Test that fast-tab-close works when closing a tab with an unload handler -// (http://crbug.com/142458). -IN_PROC_BROWSER_TEST_F(FastUnloadTest, UnloadHidden) { - NavigateToPage("no_listeners"); - NavigateToPageInNewTab("unload_sets_cookie"); - EXPECT_EQ("", GetCookies("no_listeners")); - - { - base::RunLoop run_loop; - FastTabCloseTabStripModelObserver observer( - browser()->tab_strip_model(), &run_loop); - chrome::CloseTab(browser()); - run_loop.Run(); - } - - // Check that the browser only has the original tab. - CheckTitle("no_listeners"); - EXPECT_EQ(1, browser()->tab_strip_model()->count()); - - // Show that the web contents to go away after the was removed. - // Without unload-detached, this times-out because it happens earlier. - content::WindowedNotificationObserver contents_destroyed_observer( - content::NOTIFICATION_WEB_CONTENTS_DESTROYED, - content::NotificationService::AllSources()); - contents_destroyed_observer.Wait(); - - // Browser still has the same tab. - CheckTitle("no_listeners"); - EXPECT_EQ(1, browser()->tab_strip_model()->count()); - EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners")); -} - -// Test that fast-tab-close does not break a solo tab. -IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_ClosingLastTabFinishesUnload) { - // The unload handler sleeps before setting the cookie to catch cases when - // unload handlers are not allowed to run to completion. (For example, - // using the detached handler for the tab and then closing the browser.) - NavigateToPage("unload_sleep_before_cookie"); - EXPECT_EQ(1, browser()->tab_strip_model()->count()); - EXPECT_EQ("", GetCookies("unload_sleep_before_cookie")); - - content::WindowedNotificationObserver window_observer( - chrome::NOTIFICATION_BROWSER_CLOSED, - content::NotificationService::AllSources()); - chrome::CloseTab(browser()); - window_observer.Wait(); -} -IN_PROC_BROWSER_TEST_F(FastUnloadTest, ClosingLastTabFinishesUnload) { - // Check for cookie set in unload handler of PRE_ test. - NavigateToPage("no_listeners"); - EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners")); -} - -// Test that fast-tab-close does not break window close. -IN_PROC_BROWSER_TEST_F(FastUnloadTest, PRE_WindowCloseFinishesUnload) { - NavigateToPage("no_listeners"); - - // The unload handler sleeps before setting the cookie to catch cases when - // unload handlers are not allowed to run to completion. Without the sleep, - // the cookie can get set even if the browser does not wait for - // the unload handler to finish. - NavigateToPageInNewTab("unload_sleep_before_cookie"); - EXPECT_EQ(2, browser()->tab_strip_model()->count()); - EXPECT_EQ("", GetCookies("no_listeners")); - - content::WindowedNotificationObserver window_observer( - chrome::NOTIFICATION_BROWSER_CLOSED, - content::NotificationService::AllSources()); - chrome::CloseWindow(browser()); - window_observer.Wait(); -} -IN_PROC_BROWSER_TEST_F(FastUnloadTest, WindowCloseFinishesUnload) { - // Check for cookie set in unload during PRE_ test. - NavigateToPage("no_listeners"); - EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners")); -} - -// Test that a tab crash during unload does not break window close. -// -// Hits assertion on Linux and Mac: -// [FATAL:profile_destroyer.cc(46)] Check failed: -// hosts.empty() || -// profile->IsOffTheRecord() || -// content::RenderProcessHost::run_renderer_in_process(). -// More details: The renderer process host matches the closed, crashed tab. -// The |UnloadController| receives |NOTIFICATION_WEB_CONTENTS_DISCONNECTED| -// and proceeds with the close. -IN_PROC_BROWSER_TEST_F(FastUnloadTest, DISABLED_WindowCloseAfterUnloadCrash) { - NavigateToPage("no_listeners"); - NavigateToPageInNewTab("unload_sets_cookie"); - content::WebContents* unload_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ("", GetCookies("no_listeners")); - - { - base::RunLoop run_loop; - FastTabCloseTabStripModelObserver observer( - browser()->tab_strip_model(), &run_loop); - chrome::CloseTab(browser()); - run_loop.Run(); - } - - // Check that the browser only has the original tab. - CheckTitle("no_listeners"); - EXPECT_EQ(1, browser()->tab_strip_model()->count()); - - CrashTab(unload_contents); - - // Check that the browser only has the original tab. - CheckTitle("no_listeners"); - EXPECT_EQ(1, browser()->tab_strip_model()->count()); - - content::WindowedNotificationObserver window_observer( - chrome::NOTIFICATION_BROWSER_CLOSED, - content::NotificationService::AllSources()); - chrome::CloseWindow(browser()); - window_observer.Wait(); -} - -// Times out on Windows and Linux. -#if defined(OS_WIN) || defined(OS_LINUX) -#define MAYBE_WindowCloseAfterBeforeUnloadCrash \ - DISABLED_WindowCloseAfterBeforeUnloadCrash -#else -#define MAYBE_WindowCloseAfterBeforeUnloadCrash \ - WindowCloseAfterBeforeUnloadCrash -#endif -IN_PROC_BROWSER_TEST_F(FastUnloadTest, - MAYBE_WindowCloseAfterBeforeUnloadCrash) { - // Tests makes no sense in single-process mode since the renderer is hung. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) - return; - - NavigateToDataURL(BEFORE_UNLOAD_HTML, "beforeunload"); - content::WebContents* beforeunload_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - - content::WindowedNotificationObserver window_observer( - chrome::NOTIFICATION_BROWSER_CLOSED, - content::NotificationService::AllSources()); - chrome::CloseWindow(browser()); - CrashTab(beforeunload_contents); - window_observer.Wait(); -} - // TODO(ojan): Add tests for unload/beforeunload that have multiple tabs // and multiple windows. diff --git a/chrome/test/data/fast_tab_close/no_listeners.html b/chrome/test/data/fast_tab_close/no_listeners.html deleted file mode 100644 index 8a61cb4..0000000 --- a/chrome/test/data/fast_tab_close/no_listeners.html +++ /dev/null @@ -1,11 +0,0 @@ -<!-- -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. ---> -<html> -<head> - <title>no_listeners</title> -</head> -<body></body> -</html> diff --git a/chrome/test/data/fast_tab_close/unload_sets_cookie.html b/chrome/test/data/fast_tab_close/unload_sets_cookie.html deleted file mode 100644 index 45b8a50..0000000 --- a/chrome/test/data/fast_tab_close/unload_sets_cookie.html +++ /dev/null @@ -1,18 +0,0 @@ -<!-- -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. ---> -<html> -<head> - <title>unload_sets_cookie</title> -<script> -addEventListener("unload", function() { - var expires = new Date(); - expires.setMinutes(expires.getMinutes() + 1); - document.cookie = "unloaded=ohyeah;path=/;expires=" + expires.toUTCString(); -}, false); -</script> -</head> -<body></body> -</html> diff --git a/chrome/test/data/fast_tab_close/unload_sleep_before_cookie.html b/chrome/test/data/fast_tab_close/unload_sleep_before_cookie.html deleted file mode 100644 index 93b700b..0000000 --- a/chrome/test/data/fast_tab_close/unload_sleep_before_cookie.html +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. ---> -<html> -<head> - <title>unload_sleep_before_cookie</title> -<script> -function sleep_ms(ms) { - var start_ms = Number(new Date()); - var remaining_ms = ms; - while (remaining_ms > 0) { - remaining_ms = ms - (Number(new Date()) - start_ms); - } -} -addEventListener("unload", function() { - sleep_ms(200); - - var expires = new Date(); - expires.setMinutes(expires.getMinutes() + 1); - document.cookie = "unloaded=ohyeah;path=/;expires=" + expires.toUTCString(); - console.log("set the cookie"); -}, false); -</script> -</head> -<body></body> -</html> diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 7a4e607..ee3eaa4 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -9,7 +9,6 @@ #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/lazy_instance.h" -#include "base/logging.h" #include "base/metrics/histogram.h" #include "base/metrics/stats_counters.h" #include "base/strings/string16.h" @@ -399,6 +398,16 @@ WebContentsImpl::~WebContentsImpl() { } #endif + // OnCloseStarted isn't called in unit tests. + if (!close_start_time_.is_null()) { + base::TimeTicks now = base::TimeTicks::Now(); + base::TimeTicks unload_start_time = close_start_time_; + if (!before_unload_end_time_.is_null()) + unload_start_time = before_unload_end_time_; + UMA_HISTOGRAM_TIMES("Tab.Close", now - close_start_time_); + UMA_HISTOGRAM_TIMES("Tab.Close.UnloadTime", now - unload_start_time); + } + FOR_EACH_OBSERVER(WebContentsObserver, observers_, WebContentsImplDestroyed()); @@ -1939,10 +1948,23 @@ RendererPreferences* WebContentsImpl::GetMutableRendererPrefs() { return &renderer_preferences_; } +void WebContentsImpl::SetNewTabStartTime(const base::TimeTicks& time) { + new_tab_start_time_ = time; +} + +base::TimeTicks WebContentsImpl::GetNewTabStartTime() const { + return new_tab_start_time_; +} + void WebContentsImpl::Close() { Close(GetRenderViewHost()); } +void WebContentsImpl::OnCloseStarted() { + if (close_start_time_.is_null()) + close_start_time_ = base::TimeTicks::Now(); +} + void WebContentsImpl::DragSourceEndedAt(int client_x, int client_y, int screen_x, int screen_y, WebKit::WebDragOperation operation) { if (browser_plugin_embedder_.get()) @@ -3476,11 +3498,9 @@ void WebContentsImpl::WorkerCrashed() { void WebContentsImpl::BeforeUnloadFiredFromRenderManager( bool proceed, const base::TimeTicks& proceed_time, bool* proceed_to_fire_unload) { - FOR_EACH_OBSERVER(WebContentsObserver, observers_, - BeforeUnloadFired(proceed_time)); + before_unload_end_time_ = proceed_time; if (delegate_) delegate_->BeforeUnloadFired(this, proceed, proceed_to_fire_unload); - // Note: |this| can be deleted at this point. } void WebContentsImpl::RenderViewGoneFromRenderManager( @@ -3621,8 +3641,8 @@ void WebContentsImpl::OnDialogClosed(RenderViewHost* rvh, DidStopLoading(rvh); controller_.DiscardNonCommittedEntries(); - FOR_EACH_OBSERVER(WebContentsObserver, observers_, - BeforeUnloadDialogCancelled()); + close_start_time_ = base::TimeTicks(); + before_unload_end_time_ = base::TimeTicks(); } is_showing_before_unload_dialog_ = false; static_cast<RenderViewHostImpl*>( diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index df7d535..a91a4c6 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -264,7 +264,10 @@ class CONTENT_EXPORT WebContentsImpl virtual void SetOverrideEncoding(const std::string& encoding) OVERRIDE; virtual void ResetOverrideEncoding() OVERRIDE; virtual RendererPreferences* GetMutableRendererPrefs() OVERRIDE; + virtual void SetNewTabStartTime(const base::TimeTicks& time) OVERRIDE; + virtual base::TimeTicks GetNewTabStartTime() const OVERRIDE; virtual void Close() OVERRIDE; + virtual void OnCloseStarted() OVERRIDE; virtual void SystemDragEnded() OVERRIDE; virtual void UserGestureDone() OVERRIDE; virtual void SetClosedByUserGesture(bool value) OVERRIDE; @@ -867,6 +870,15 @@ class CONTENT_EXPORT WebContentsImpl // Settings that get passed to the renderer process. RendererPreferences renderer_preferences_; + // The time that we started to create the new tab page. + base::TimeTicks new_tab_start_time_; + + // The time that we started to close this WebContents. + base::TimeTicks close_start_time_; + + // The time when onbeforeunload ended. + base::TimeTicks before_unload_end_time_; + // The time that this tab was last selected. base::TimeTicks last_selected_time_; diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 037fef84..309e264 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h @@ -361,10 +361,19 @@ class WebContents : public PageNavigator, // Returns the settings which get passed to the renderer. virtual content::RendererPreferences* GetMutableRendererPrefs() = 0; + // Set the time when we started to create the new tab page. This time is + // from before we created this WebContents. + virtual void SetNewTabStartTime(const base::TimeTicks& time) = 0; + virtual base::TimeTicks GetNewTabStartTime() const = 0; + // Tells the tab to close now. The tab will take care not to close until it's // out of nested message loops. virtual void Close() = 0; + // Notification that tab closing has started. This can be called multiple + // times, subsequent calls are ignored. + virtual void OnCloseStarted() = 0; + // A render view-originated drag has ended. Informs the render view host and // WebContentsDelegate. virtual void SystemDragEnded() = 0; diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h index b9ce1e6..0a01171 100644 --- a/content/public/browser/web_contents_observer.h +++ b/content/public/browser/web_contents_observer.h @@ -259,12 +259,6 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener, // Invoked before a form repost warning is shown. virtual void BeforeFormRepostWarningShow() {} - // Invoked when the before unload fires. The time is from the renderer. - virtual void BeforeUnloadFired(const base::TimeTicks& proceed_time) {} - - // Invoked when a user cancels a before unload dialog. - virtual void BeforeUnloadDialogCancelled() {} - // IPC::Listener implementation. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; |