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

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: Add tests. Drop test's use of non-public API. 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
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 "base/callback.h"
7 #include "base/message_loop.h" 8 #include "base/message_loop.h"
8 #include "chrome/browser/ui/browser.h" 9 #include "chrome/browser/ui/browser.h"
9 #include "chrome/browser/ui/browser_tabstrip.h" 10 #include "chrome/browser/ui/browser_tabstrip.h"
10 #include "chrome/browser/ui/tab_contents/tab_contents.h" 11 #include "chrome/browser/ui/tab_contents/tab_contents.h"
11 #include "chrome/browser/ui/tabs/tab_strip_model.h" 12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "chrome/browser/ui/unload_detached_handler.h"
12 #include "chrome/common/chrome_notification_types.h" 14 #include "chrome/common/chrome_notification_types.h"
13 #include "content/public/browser/notification_service.h" 15 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_source.h" 16 #include "content/public/browser/notification_source.h"
15 #include "content/public/browser/notification_types.h" 17 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_view_host.h" 18 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h" 19 #include "content/public/browser/web_contents.h"
18 20
19 namespace chrome { 21 namespace chrome {
20 22
21 //////////////////////////////////////////////////////////////////////////////// 23 ////////////////////////////////////////////////////////////////////////////////
22 // UnloadController, public: 24 // UnloadController, public:
23 25
24 UnloadController::UnloadController(Browser* browser) 26 UnloadController::UnloadController(Browser* browser)
25 : browser_(browser), 27 : browser_(browser),
26 is_attempting_to_close_browser_(false), 28 is_attempting_to_close_browser_(false),
29 unload_detached_handler_(new UnloadDetachedHandler(
30 base::Bind(&UnloadController::DetachedTabsClosedCallback,
31 base::Unretained(this)))),
27 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { 32 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
28 browser_->tab_strip_model()->AddObserver(this); 33 browser_->tab_strip_model()->AddObserver(this);
29 } 34 }
30 35
31 UnloadController::~UnloadController() { 36 UnloadController::~UnloadController() {
32 browser_->tab_strip_model()->RemoveObserver(this); 37 browser_->tab_strip_model()->RemoveObserver(this);
38 delete unload_detached_handler_;
33 } 39 }
34 40
35 bool UnloadController::CanCloseContents(content::WebContents* contents) { 41 bool UnloadController::CanCloseContents(content::WebContents* contents) {
36 // Don't try to close the tab when the whole browser is being closed, since 42 // 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. 43 // that avoids the fast shutdown path where we just kill all the renderers.
38 if (is_attempting_to_close_browser_) 44 if (is_attempting_to_close_browser_)
39 ClearUnloadState(contents, true); 45 ClearUnloadState(contents, true);
40 return !is_attempting_to_close_browser_; 46 return !is_attempting_to_close_browser_;
41 } 47 }
42 48
43 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, 49 bool UnloadController::BeforeUnloadFired(content::WebContents* contents,
44 bool proceed) { 50 bool proceed) {
45 if (!is_attempting_to_close_browser_) { 51 if (!is_attempting_to_close_browser_) {
46 if (!proceed) 52 if (proceed) {
53 // No more dialogs are possible, so remove the tab and finish
54 // running unload listeners asynchrounously.
55 unload_detached_handler_->DetachWebContents(browser_->tab_strip_model(),
56 contents);
57 } else {
47 contents->SetClosedByUserGesture(false); 58 contents->SetClosedByUserGesture(false);
59 }
48 return proceed; 60 return proceed;
49 } 61 }
50 62
51 if (!proceed) { 63 if (!proceed) {
52 CancelWindowClose(); 64 CancelWindowClose();
53 contents->SetClosedByUserGesture(false); 65 contents->SetClosedByUserGesture(false);
54 return false; 66 return false;
55 } 67 }
56 68
57 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { 69 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) {
(...skipping 10 matching lines...) Expand all
68 return true; 80 return true;
69 } 81 }
70 82
71 bool UnloadController::ShouldCloseWindow() { 83 bool UnloadController::ShouldCloseWindow() {
72 if (HasCompletedUnloadProcessing()) 84 if (HasCompletedUnloadProcessing())
73 return true; 85 return true;
74 86
75 is_attempting_to_close_browser_ = true; 87 is_attempting_to_close_browser_ = true;
76 88
77 if (!TabsNeedBeforeUnloadFired()) 89 if (!TabsNeedBeforeUnloadFired())
78 return true; 90 return !unload_detached_handler_->HasTabs();
79 91
80 ProcessPendingTabs(); 92 ProcessPendingTabs();
81 return false; 93 return false;
82 } 94 }
83 95
84 bool UnloadController::TabsNeedBeforeUnloadFired() { 96 bool UnloadController::TabsNeedBeforeUnloadFired() {
85 if (tabs_needing_before_unload_fired_.empty()) { 97 if (tabs_needing_before_unload_fired_.empty()) {
86 for (int i = 0; i < browser_->tab_count(); ++i) { 98 for (int i = 0; i < browser_->tab_count(); ++i) {
87 content::WebContents* contents = 99 content::WebContents* contents =
88 chrome::GetTabContentsAt(browser_, i)->web_contents(); 100 chrome::GetTabContentsAt(browser_, i)->web_contents();
89 if (contents->NeedToFireBeforeUnload()) 101 if (contents->NeedToFireBeforeUnload())
90 tabs_needing_before_unload_fired_.insert(contents); 102 tabs_needing_before_unload_fired_.insert(contents);
91 } 103 }
92 } 104 }
93 return !tabs_needing_before_unload_fired_.empty(); 105 return !tabs_needing_before_unload_fired_.empty();
94 } 106 }
95 107
108 void UnloadController::DetachedTabsClosedCallback() {
109 ProcessPendingTabs();
110 }
111
96 //////////////////////////////////////////////////////////////////////////////// 112 ////////////////////////////////////////////////////////////////////////////////
97 // UnloadController, content::NotificationObserver implementation: 113 // UnloadController, content::NotificationObserver implementation:
98 114
99 void UnloadController::Observe(int type, 115 void UnloadController::Observe(int type,
100 const content::NotificationSource& source, 116 const content::NotificationSource& source,
101 const content::NotificationDetails& details) { 117 const content::NotificationDetails& details) {
102 switch (type) { 118 switch (type) {
103 case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: 119 case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED:
104 if (is_attempting_to_close_browser_) { 120 if (is_attempting_to_close_browser_) {
105 ClearUnloadState(content::Source<content::WebContents>(source).ptr(), 121 ClearUnloadState(content::Source<content::WebContents>(source).ptr(),
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 } 191 }
176 192
177 // Process beforeunload tabs first. When that queue is empty, process 193 // Process beforeunload tabs first. When that queue is empty, process
178 // unload tabs. 194 // unload tabs.
179 if (!tabs_needing_before_unload_fired_.empty()) { 195 if (!tabs_needing_before_unload_fired_.empty()) {
180 content::WebContents* web_contents = 196 content::WebContents* web_contents =
181 *(tabs_needing_before_unload_fired_.begin()); 197 *(tabs_needing_before_unload_fired_.begin());
182 // Null check render_view_host here as this gets called on a PostTask and 198 // 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. 199 // the tab's render_view_host may have been nulled out.
184 if (web_contents->GetRenderViewHost()) { 200 if (web_contents->GetRenderViewHost()) {
201 web_contents->OnCloseStarted();
185 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); 202 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
186 } else { 203 } else {
187 ClearUnloadState(web_contents, true); 204 ClearUnloadState(web_contents, true);
188 } 205 }
189 } else if (!tabs_needing_unload_fired_.empty()) { 206 } else if (!tabs_needing_unload_fired_.empty()) {
190 // We've finished firing all beforeunload events and can proceed with unload 207 // We've finished firing all beforeunload events and can proceed with unload
191 // events. 208 // events.
192 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting 209 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting
193 // somewhere around here so that we have accurate measurements of shutdown 210 // somewhere around here so that we have accurate measurements of shutdown
194 // time. 211 // time.
195 // TODO(ojan): We can probably fire all the unload events in parallel and 212 // 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 213 // 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. 214 // unload handler or takes a long time to page in.
198 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); 215 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin());
199 // Null check render_view_host here as this gets called on a PostTask and 216 // 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. 217 // the tab's render_view_host may have been nulled out.
201 if (web_contents->GetRenderViewHost()) { 218 if (web_contents->GetRenderViewHost()) {
202 web_contents->GetRenderViewHost()->ClosePage(); 219 if (!unload_detached_handler_->HasTabs()) {
220 web_contents->GetRenderViewHost()->ClosePage();
221 }
203 } else { 222 } else {
204 ClearUnloadState(web_contents, true); 223 ClearUnloadState(web_contents, true);
205 } 224 }
206 } else { 225 } else {
207 NOTREACHED(); 226 NOTREACHED();
208 } 227 }
209 } 228 }
210 229
211 bool UnloadController::HasCompletedUnloadProcessing() const { 230 bool UnloadController::HasCompletedUnloadProcessing() const {
212 return is_attempting_to_close_browser_ && 231 return is_attempting_to_close_browser_ &&
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
250 } else { 269 } else {
251 MessageLoop::current()->PostTask( 270 MessageLoop::current()->PostTask(
252 FROM_HERE, 271 FROM_HERE,
253 base::Bind(&UnloadController::ProcessPendingTabs, 272 base::Bind(&UnloadController::ProcessPendingTabs,
254 weak_factory_.GetWeakPtr())); 273 weak_factory_.GetWeakPtr()));
255 } 274 }
256 } 275 }
257 } 276 }
258 277
259 } // namespace chrome 278 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698