Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(507)

Side by Side Diff: chrome/browser/ui/unload_controller.cc

Issue 11016023: Quickly close tabs/window with long-running unload handlers. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Make UnloadDetachedHandler a delegate for all detached tabs. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/unload_controller.h ('k') | chrome/browser/unload_browsertest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/unload_controller.h" 5 #include "chrome/browser/ui/unload_controller.h"
6 6
7 #include <map>
8
9 #include "base/callback.h"
10 #include "base/memory/scoped_vector.h"
7 #include "base/message_loop.h" 11 #include "base/message_loop.h"
8 #include "chrome/browser/ui/browser.h" 12 #include "chrome/browser/ui/browser.h"
9 #include "chrome/browser/ui/browser_tabstrip.h" 13 #include "chrome/browser/ui/browser_tabstrip.h"
10 #include "chrome/browser/ui/tab_contents/tab_contents.h" 14 #include "chrome/browser/ui/tab_contents/tab_contents.h"
11 #include "chrome/browser/ui/tabs/tab_strip_model.h" 15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
12 #include "chrome/common/chrome_notification_types.h" 16 #include "chrome/common/chrome_notification_types.h"
13 #include "content/public/browser/notification_service.h" 17 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_source.h" 18 #include "content/public/browser/notification_source.h"
15 #include "content/public/browser/notification_types.h" 19 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_view_host.h" 20 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h" 21 #include "content/public/browser/web_contents.h"
22 #include "content/public/browser/web_contents_delegate.h"
18 23
19 namespace chrome { 24 namespace chrome {
20 25
26 typedef base::Callback<void(void)> DetachedTabsClosedCallback;
27 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
28
29 ////////////////////////////////////////////////////////////////////////////////
30 // UnloadDetachedHandler is used to close tabs quickly, http://crbug.com/142458.
31 // - Allows unload handlers to run in the background.
32 // - Comes into play after the beforeunload handlers (if any) have run.
33 // - Does not close the tabs; it holds tabs while they are closed.
34 //
35 // TODO(slamm): Support closes that destroy the browser: last tab close, window.
36 // http://crbug.com/156896
37 // TODO(slamm): Hide unload time from the user for cross-process navigations.
38 // http://crbug.com/156958
39 class UnloadDetachedHandler : public content::WebContentsDelegate {
40 public:
41 explicit UnloadDetachedHandler(const DetachedTabsClosedCallback& callback)
42 : tabs_closed_callback_(callback) { }
43 ~UnloadDetachedHandler() { }
Ben Goodger (Google) 2012/10/31 18:28:32 virtual dtor
44
45 // Returns true if it succeeds.
46 bool DetachWebContents(TabStripModel* tab_strip_model,
47 content::WebContents* web_contents) {
48 if (tab_strip_model->count() > 1) {
49 // Only allow tab to be detached if it is not the last one.
50 // Otherwise, the tab_strip_model would notify the browser that
51 // the strip is empty, and cause it to close without waiting for
52 // the unload handlers.
53 int index = tab_strip_model->GetIndexOfWebContents(web_contents);
54 if (index != TabStripModel::kNoTab &&
55 web_contents->NeedToFireBeforeUnload()) {
56 TabContents* tab_contents = tab_strip_model->DetachTabContentsAt(index);
57 detached_tabs_[web_contents] = tab_contents;
58 web_contents->SetDelegate(this);
59 web_contents->OnUnloadDetachedStarted();
60 return true;
61 }
62 }
63 return false;
64 }
65
66 bool HasTabs() const {
67 return !detached_tabs_.empty();
68 }
69
70 private:
71 // WebContentsDelegate implementation.
72 virtual bool ShouldSuppressDialogs() OVERRIDE {
73 return true; // Return true so dialogs are suppressed.
74 }
75 virtual void CloseContents(content::WebContents* source) OVERRIDE {
76 DetachedTabsMap::iterator it = detached_tabs_.find(source);
77 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
78 delete tab_contents;
79 detached_tabs_.erase(it);
80 tabs_closed_callback_.Run();
81 }
82
83 const DetachedTabsClosedCallback tabs_closed_callback_;
84 DetachedTabsMap detached_tabs_;
85
86 DISALLOW_IMPLICIT_CONSTRUCTORS(UnloadDetachedHandler);
87 };
88
89
21 //////////////////////////////////////////////////////////////////////////////// 90 ////////////////////////////////////////////////////////////////////////////////
22 // UnloadController, public: 91 // UnloadController, public:
23 92
24 UnloadController::UnloadController(Browser* browser) 93 UnloadController::UnloadController(Browser* browser)
25 : browser_(browser), 94 : browser_(browser),
26 is_attempting_to_close_browser_(false), 95 is_attempting_to_close_browser_(false),
96 ALLOW_THIS_IN_INITIALIZER_LIST(
97 unload_detached_handler_(new UnloadDetachedHandler(
98 base::Bind(&UnloadController::ProcessPendingTabs,
99 base::Unretained(this))))),
27 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { 100 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
28 browser_->tab_strip_model()->AddObserver(this); 101 browser_->tab_strip_model()->AddObserver(this);
29 } 102 }
30 103
31 UnloadController::~UnloadController() { 104 UnloadController::~UnloadController() {
32 browser_->tab_strip_model()->RemoveObserver(this); 105 browser_->tab_strip_model()->RemoveObserver(this);
33 } 106 }
34 107
35 bool UnloadController::CanCloseContents(content::WebContents* contents) { 108 bool UnloadController::CanCloseContents(content::WebContents* contents) {
36 // Don't try to close the tab when the whole browser is being closed, since 109 // Don't try to close the tab when the whole browser is being closed, since
37 // that avoids the fast shutdown path where we just kill all the renderers. 110 // that avoids the fast shutdown path where we just kill all the renderers.
38 if (is_attempting_to_close_browser_) 111 if (is_attempting_to_close_browser_)
39 ClearUnloadState(contents, true); 112 ClearUnloadState(contents, true);
40 return !is_attempting_to_close_browser_; 113 return !is_attempting_to_close_browser_;
41 } 114 }
42 115
43 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, 116 bool UnloadController::BeforeUnloadFired(content::WebContents* contents,
44 bool proceed) { 117 bool proceed) {
45 if (!is_attempting_to_close_browser_) { 118 if (!is_attempting_to_close_browser_) {
46 if (!proceed) 119 if (proceed) {
120 // No more dialogs are possible, so remove the tab and finish
121 // running unload listeners asynchrounously.
122 unload_detached_handler_->DetachWebContents(browser_->tab_strip_model(),
123 contents);
124 } else {
47 contents->SetClosedByUserGesture(false); 125 contents->SetClosedByUserGesture(false);
126 }
48 return proceed; 127 return proceed;
49 } 128 }
50 129
51 if (!proceed) { 130 if (!proceed) {
52 CancelWindowClose(); 131 CancelWindowClose();
53 contents->SetClosedByUserGesture(false); 132 contents->SetClosedByUserGesture(false);
54 return false; 133 return false;
55 } 134 }
56 135
57 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { 136 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) {
(...skipping 10 matching lines...) Expand all
68 return true; 147 return true;
69 } 148 }
70 149
71 bool UnloadController::ShouldCloseWindow() { 150 bool UnloadController::ShouldCloseWindow() {
72 if (HasCompletedUnloadProcessing()) 151 if (HasCompletedUnloadProcessing())
73 return true; 152 return true;
74 153
75 is_attempting_to_close_browser_ = true; 154 is_attempting_to_close_browser_ = true;
76 155
77 if (!TabsNeedBeforeUnloadFired()) 156 if (!TabsNeedBeforeUnloadFired())
78 return true; 157 return !unload_detached_handler_->HasTabs();
79 158
80 ProcessPendingTabs(); 159 ProcessPendingTabs();
81 return false; 160 return false;
82 } 161 }
83 162
84 bool UnloadController::TabsNeedBeforeUnloadFired() { 163 bool UnloadController::TabsNeedBeforeUnloadFired() {
85 if (tabs_needing_before_unload_fired_.empty()) { 164 if (tabs_needing_before_unload_fired_.empty()) {
86 for (int i = 0; i < browser_->tab_count(); ++i) { 165 for (int i = 0; i < browser_->tab_count(); ++i) {
87 content::WebContents* contents = 166 content::WebContents* contents =
88 chrome::GetTabContentsAt(browser_, i)->web_contents(); 167 chrome::GetTabContentsAt(browser_, i)->web_contents();
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 } 254 }
176 255
177 // Process beforeunload tabs first. When that queue is empty, process 256 // Process beforeunload tabs first. When that queue is empty, process
178 // unload tabs. 257 // unload tabs.
179 if (!tabs_needing_before_unload_fired_.empty()) { 258 if (!tabs_needing_before_unload_fired_.empty()) {
180 content::WebContents* web_contents = 259 content::WebContents* web_contents =
181 *(tabs_needing_before_unload_fired_.begin()); 260 *(tabs_needing_before_unload_fired_.begin());
182 // Null check render_view_host here as this gets called on a PostTask and 261 // Null check render_view_host here as this gets called on a PostTask and
183 // the tab's render_view_host may have been nulled out. 262 // the tab's render_view_host may have been nulled out.
184 if (web_contents->GetRenderViewHost()) { 263 if (web_contents->GetRenderViewHost()) {
264 web_contents->OnCloseStarted();
185 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); 265 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
186 } else { 266 } else {
187 ClearUnloadState(web_contents, true); 267 ClearUnloadState(web_contents, true);
188 } 268 }
189 } else if (!tabs_needing_unload_fired_.empty()) { 269 } else if (!tabs_needing_unload_fired_.empty()) {
190 // We've finished firing all beforeunload events and can proceed with unload 270 // We've finished firing all beforeunload events and can proceed with unload
191 // events. 271 // events.
192 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting 272 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting
193 // somewhere around here so that we have accurate measurements of shutdown 273 // somewhere around here so that we have accurate measurements of shutdown
194 // time. 274 // time.
195 // TODO(ojan): We can probably fire all the unload events in parallel and 275 // TODO(ojan): We can probably fire all the unload events in parallel and
196 // get a perf benefit from that in the cases where the tab hangs in it's 276 // get a perf benefit from that in the cases where the tab hangs in it's
197 // unload handler or takes a long time to page in. 277 // unload handler or takes a long time to page in.
198 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); 278 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin());
199 // Null check render_view_host here as this gets called on a PostTask and 279 // Null check render_view_host here as this gets called on a PostTask and
200 // the tab's render_view_host may have been nulled out. 280 // the tab's render_view_host may have been nulled out.
201 if (web_contents->GetRenderViewHost()) { 281 if (web_contents->GetRenderViewHost()) {
202 web_contents->GetRenderViewHost()->ClosePage(); 282 if (!unload_detached_handler_->HasTabs()) {
Ben Goodger (Google) 2012/10/31 18:28:32 nit, no braces.
283 web_contents->GetRenderViewHost()->ClosePage();
284 }
203 } else { 285 } else {
204 ClearUnloadState(web_contents, true); 286 ClearUnloadState(web_contents, true);
205 } 287 }
206 } else { 288 } else {
207 NOTREACHED(); 289 NOTREACHED();
208 } 290 }
209 } 291 }
210 292
211 bool UnloadController::HasCompletedUnloadProcessing() const { 293 bool UnloadController::HasCompletedUnloadProcessing() const {
212 return is_attempting_to_close_browser_ && 294 return is_attempting_to_close_browser_ &&
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
250 } else { 332 } else {
251 MessageLoop::current()->PostTask( 333 MessageLoop::current()->PostTask(
252 FROM_HERE, 334 FROM_HERE,
253 base::Bind(&UnloadController::ProcessPendingTabs, 335 base::Bind(&UnloadController::ProcessPendingTabs,
254 weak_factory_.GetWeakPtr())); 336 weak_factory_.GetWeakPtr()));
255 } 337 }
256 } 338 }
257 } 339 }
258 340
259 } // namespace chrome 341 } // namespace chrome
OLDNEW
« no previous file with comments | « chrome/browser/ui/unload_controller.h ('k') | chrome/browser/unload_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698