Chromium Code Reviews| Index: chrome/browser/ui/unload_controller.cc |
| diff --git a/chrome/browser/ui/unload_controller.cc b/chrome/browser/ui/unload_controller.cc |
| index bc8c1805360beb6682c6f656a5baf12f41a4aa42..4c4a37aad4278f9e750ffb188b92b44edc9bc587 100644 |
| --- a/chrome/browser/ui/unload_controller.cc |
| +++ b/chrome/browser/ui/unload_controller.cc |
| @@ -4,6 +4,10 @@ |
| #include "chrome/browser/ui/unload_controller.h" |
| +#include <map> |
| + |
| +#include "base/callback.h" |
| +#include "base/memory/scoped_vector.h" |
| #include "base/message_loop.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_tabstrip.h" |
| @@ -15,15 +19,84 @@ |
| #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; |
| +typedef std::map<content::WebContents*, TabContents*> DetachedTabsMap; |
|
Ben Goodger (Google)
2012/10/31 18:28:32
this you can put at the start of the private area
|
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// 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. |
| +// |
| +// TODO(slamm): Support closes that destroy the browser: last tab close, window. |
| +// http://crbug.com/156896 |
| +// TODO(slamm): Hide unload time from the user for cross-process navigations. |
| +// http://crbug.com/156958 |
| +class UnloadDetachedHandler : public content::WebContentsDelegate { |
| + public: |
| + explicit UnloadDetachedHandler(const DetachedTabsClosedCallback& callback) |
| + : tabs_closed_callback_(callback) { } |
| + ~UnloadDetachedHandler() { } |
|
Ben Goodger (Google)
2012/10/31 18:28:32
virtual dtor
|
| + |
| + // Returns true if it succeeds. |
| + bool DetachWebContents(TabStripModel* tab_strip_model, |
| + content::WebContents* web_contents) { |
| + if (tab_strip_model->count() > 1) { |
| + // Only allow tab to be detached if it is not the last one. |
| + // Otherwise, the tab_strip_model would notify the browser that |
| + // the strip is empty, and cause it to close without waiting for |
| + // the unload handlers. |
| + int index = tab_strip_model->GetIndexOfWebContents(web_contents); |
| + if (index != TabStripModel::kNoTab && |
| + web_contents->NeedToFireBeforeUnload()) { |
| + TabContents* tab_contents = tab_strip_model->DetachTabContentsAt(index); |
| + detached_tabs_[web_contents] = tab_contents; |
| + web_contents->SetDelegate(this); |
| + web_contents->OnUnloadDetachedStarted(); |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + bool HasTabs() const { |
| + return !detached_tabs_.empty(); |
| + } |
| + |
| + private: |
| + // WebContentsDelegate implementation. |
| + virtual bool ShouldSuppressDialogs() OVERRIDE { |
| + return true; // Return true so dialogs are suppressed. |
| + } |
| + virtual void CloseContents(content::WebContents* source) OVERRIDE { |
| + DetachedTabsMap::iterator it = detached_tabs_.find(source); |
| + TabContents* tab_contents = (*it).second; |
|
Ben Goodger (Google)
2012/10/31 18:28:32
So, Avi is in the process of removing TC from Chro
|
| + delete tab_contents; |
| + detached_tabs_.erase(it); |
| + tabs_closed_callback_.Run(); |
| + } |
| + |
| + const DetachedTabsClosedCallback tabs_closed_callback_; |
| + DetachedTabsMap detached_tabs_; |
| + |
| + 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); |
| } |
| @@ -43,8 +116,14 @@ bool UnloadController::CanCloseContents(content::WebContents* contents) { |
| bool UnloadController::BeforeUnloadFired(content::WebContents* contents, |
| bool proceed) { |
| if (!is_attempting_to_close_browser_) { |
| - if (!proceed) |
| + if (proceed) { |
| + // No more dialogs are possible, so remove the tab and finish |
| + // running unload listeners asynchrounously. |
| + unload_detached_handler_->DetachWebContents(browser_->tab_strip_model(), |
| + contents); |
| + } else { |
| contents->SetClosedByUserGesture(false); |
| + } |
| return proceed; |
| } |
| @@ -75,7 +154,7 @@ bool UnloadController::ShouldCloseWindow() { |
| is_attempting_to_close_browser_ = true; |
| if (!TabsNeedBeforeUnloadFired()) |
| - return true; |
| + return !unload_detached_handler_->HasTabs(); |
| ProcessPendingTabs(); |
| return false; |
| @@ -182,6 +261,7 @@ void UnloadController::ProcessPendingTabs() { |
| // 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); |
| @@ -199,7 +279,9 @@ void UnloadController::ProcessPendingTabs() { |
| // 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(); |
| + if (!unload_detached_handler_->HasTabs()) { |
|
Ben Goodger (Google)
2012/10/31 18:28:32
nit, no braces.
|
| + web_contents->GetRenderViewHost()->ClosePage(); |
| + } |
| } else { |
| ClearUnloadState(web_contents, true); |
| } |