| Index: trunk/src/chrome/browser/ui/unload_controller.cc
|
| ===================================================================
|
| --- trunk/src/chrome/browser/ui/unload_controller.cc (revision 195133)
|
| +++ trunk/src/chrome/browser/ui/unload_controller.cc (working copy)
|
| @@ -4,85 +4,25 @@
|
|
|
| #include "chrome/browser/ui/unload_controller.h"
|
|
|
| -#include <set>
|
| -
|
| -#include "base/callback.h"
|
| #include "base/message_loop.h"
|
| #include "chrome/browser/ui/browser.h"
|
| #include "chrome/browser/ui/browser_tabstrip.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 {
|
|
|
| -typedef base::Callback<void(void)> DetachedTabsClosedCallback;
|
| -
|
| ////////////////////////////////////////////////////////////////////////////////
|
| -// UnloadDetachedHandler is used to close tabs quickly, http://crbug.com/142458.
|
| -// - Allows unload handlers to run in the background.
|
| -// - Comes into play after the beforeunload handlers (if any) have run.
|
| -// - Does not close the tabs; it holds tabs while they are closed.
|
| -class UnloadDetachedHandler : public content::WebContentsDelegate {
|
| - public:
|
| - explicit UnloadDetachedHandler(const DetachedTabsClosedCallback& callback)
|
| - : tabs_closed_callback_(callback) { }
|
| - virtual ~UnloadDetachedHandler() { }
|
| -
|
| - // Returns true if it succeeds.
|
| - bool DetachWebContents(TabStripModel* tab_strip_model,
|
| - content::WebContents* web_contents) {
|
| - int index = tab_strip_model->GetIndexOfWebContents(web_contents);
|
| - if (index != TabStripModel::kNoTab &&
|
| - web_contents->NeedToFireBeforeUnload()) {
|
| - tab_strip_model->DetachWebContentsAt(index);
|
| - detached_web_contents_.insert(web_contents);
|
| - web_contents->SetDelegate(this);
|
| - web_contents->OnUnloadDetachedStarted();
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - bool HasTabs() const {
|
| - return !detached_web_contents_.empty();
|
| - }
|
| -
|
| - private:
|
| - // WebContentsDelegate implementation.
|
| - virtual bool ShouldSuppressDialogs() OVERRIDE {
|
| - return true; // Return true so dialogs are suppressed.
|
| - }
|
| - virtual void CloseContents(content::WebContents* source) OVERRIDE {
|
| - detached_web_contents_.erase(source);
|
| - delete source;
|
| - if (detached_web_contents_.empty())
|
| - tabs_closed_callback_.Run();
|
| - }
|
| -
|
| - const DetachedTabsClosedCallback tabs_closed_callback_;
|
| - std::set<content::WebContents*> detached_web_contents_;
|
| -
|
| - DISALLOW_IMPLICIT_CONSTRUCTORS(UnloadDetachedHandler);
|
| -};
|
| -
|
| -
|
| -////////////////////////////////////////////////////////////////////////////////
|
| // UnloadController, public:
|
|
|
| UnloadController::UnloadController(Browser* browser)
|
| : browser_(browser),
|
| is_attempting_to_close_browser_(false),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(
|
| - unload_detached_handler_(new UnloadDetachedHandler(
|
| - base::Bind(&UnloadController::ProcessPendingTabs,
|
| - base::Unretained(this))))),
|
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
|
| browser_->tab_strip_model()->AddObserver(this);
|
| }
|
| @@ -102,15 +42,8 @@
|
| 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.
|
| - TabStripModel* model = browser_->tab_strip_model();
|
| - model->delegate()->CreateHistoricalTab(contents);
|
| - unload_detached_handler_->DetachWebContents(model, contents);
|
| - }
|
| return proceed;
|
| }
|
|
|
| @@ -121,7 +54,8 @@
|
| }
|
|
|
| if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) {
|
| - // Now that beforeunload has fired, queue the tab to fire unload.
|
| + // 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
|
| @@ -217,88 +151,71 @@
|
| }
|
|
|
| void UnloadController::TabDetachedImpl(content::WebContents* contents) {
|
| - if (is_attempting_to_close_browser_) {
|
| - if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents) &&
|
| - tabs_needing_before_unload_fired_.empty() &&
|
| - tabs_needing_unload_fired_.empty()) {
|
| - // The last tab needing beforeunload crashed.
|
| - // Continue with the close (ProcessPendingTabs would miss this).
|
| - browser_->OnWindowClosing();
|
| - if (browser_->tab_strip_model()->empty()) {
|
| - browser_->TabStripEmpty();
|
| - } else {
|
| - browser_->tab_strip_model()->CloseAllTabs();
|
| - }
|
| - }
|
| -
|
| + if (is_attempting_to_close_browser_)
|
| ClearUnloadState(contents, false);
|
| - }
|
| registrar_.Remove(this,
|
| content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
|
| content::Source<content::WebContents>(contents));
|
| }
|
|
|
| void UnloadController::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.
|
| browser_->OnWindowClosing();
|
| - browser_->OnUnloadProcessingCompleted();
|
| - } else if (!tabs_needing_before_unload_fired_.empty()) {
|
| - // Process all the beforeunload handlers before the unload handlers.
|
| + return;
|
| + }
|
| +
|
| + // 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 (web_contents->GetRenderViewHost()) {
|
| - web_contents->OnCloseStarted();
|
| web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
|
| } else {
|
| ClearUnloadState(web_contents, true);
|
| }
|
| } else if (!tabs_needing_unload_fired_.empty()) {
|
| - // All beforeunload handlers have fired. Proceed with unload handlers.
|
| -
|
| - browser_->OnWindowClosing();
|
| -
|
| - // Copy unload tabs to avoid iterator issues when detaching tabs.
|
| - UnloadListenerSet unload_tabs = tabs_needing_unload_fired_;
|
| -
|
| - // Run unload handlers detached since no more interaction is possible.
|
| - for (UnloadListenerSet::iterator it = unload_tabs.begin();
|
| - it != unload_tabs.end(); it++) {
|
| - content::WebContents* web_contents = *it;
|
| - // 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->OnUnloadStarted();
|
| - unload_detached_handler_->DetachWebContents(
|
| - browser_->tab_strip_model(), web_contents);
|
| - web_contents->GetRenderViewHost()->ClosePage();
|
| - }
|
| - }
|
| - tabs_needing_unload_fired_.clear();
|
| - if (browser_->tab_strip_model()->empty()) {
|
| - browser_->TabStripEmpty();
|
| + // 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 {
|
| - browser_->tab_strip_model()->CloseAllTabs();
|
| + ClearUnloadState(web_contents, true);
|
| }
|
| + } else {
|
| + NOTREACHED();
|
| }
|
| }
|
|
|
| bool UnloadController::HasCompletedUnloadProcessing() const {
|
| return is_attempting_to_close_browser_ &&
|
| tabs_needing_before_unload_fired_.empty() &&
|
| - tabs_needing_unload_fired_.empty() &&
|
| - !unload_detached_handler_->HasTabs();
|
| + tabs_needing_unload_fired_.empty();
|
| }
|
|
|
| void UnloadController::CancelWindowClose() {
|
| // Closing of window can be canceled from a beforeunload handler.
|
| DCHECK(is_attempting_to_close_browser_);
|
| - for (UnloadListenerSet::iterator it = tabs_needing_unload_fired_.begin();
|
| - it != tabs_needing_unload_fired_.end(); it++) {
|
| - content::WebContents* web_contents = *it;
|
| - web_contents->OnCloseCanceled();
|
| - }
|
| tabs_needing_before_unload_fired_.clear();
|
| tabs_needing_unload_fired_.clear();
|
| is_attempting_to_close_browser_ = false;
|
|
|