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

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

Issue 14362028: Speculative Revert 195108 "Changes to closing contents with beforeunload/unl..." (Closed) Base URL: svn://svn.chromium.org/chrome/
Patch Set: Created 7 years, 8 months 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 <set>
8
9 #include "base/callback.h"
10 #include "base/message_loop.h" 7 #include "base/message_loop.h"
11 #include "chrome/browser/ui/browser.h" 8 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_tabstrip.h" 9 #include "chrome/browser/ui/browser_tabstrip.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h" 10 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
15 #include "chrome/common/chrome_notification_types.h" 11 #include "chrome/common/chrome_notification_types.h"
16 #include "content/public/browser/notification_service.h" 12 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h" 13 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h" 14 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_view_host.h" 15 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/web_contents.h" 16 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_delegate.h"
22 17
23 namespace chrome { 18 namespace chrome {
24 19
25 typedef base::Callback<void(void)> DetachedTabsClosedCallback;
26
27 ////////////////////////////////////////////////////////////////////////////////
28 // UnloadDetachedHandler is used to close tabs quickly, http://crbug.com/142458.
29 // - Allows unload handlers to run in the background.
30 // - Comes into play after the beforeunload handlers (if any) have run.
31 // - Does not close the tabs; it holds tabs while they are closed.
32 class UnloadDetachedHandler : public content::WebContentsDelegate {
33 public:
34 explicit UnloadDetachedHandler(const DetachedTabsClosedCallback& callback)
35 : tabs_closed_callback_(callback) { }
36 virtual ~UnloadDetachedHandler() { }
37
38 // Returns true if it succeeds.
39 bool DetachWebContents(TabStripModel* tab_strip_model,
40 content::WebContents* web_contents) {
41 int index = tab_strip_model->GetIndexOfWebContents(web_contents);
42 if (index != TabStripModel::kNoTab &&
43 web_contents->NeedToFireBeforeUnload()) {
44 tab_strip_model->DetachWebContentsAt(index);
45 detached_web_contents_.insert(web_contents);
46 web_contents->SetDelegate(this);
47 web_contents->OnUnloadDetachedStarted();
48 return true;
49 }
50 return false;
51 }
52
53 bool HasTabs() const {
54 return !detached_web_contents_.empty();
55 }
56
57 private:
58 // WebContentsDelegate implementation.
59 virtual bool ShouldSuppressDialogs() OVERRIDE {
60 return true; // Return true so dialogs are suppressed.
61 }
62 virtual void CloseContents(content::WebContents* source) OVERRIDE {
63 detached_web_contents_.erase(source);
64 delete source;
65 if (detached_web_contents_.empty())
66 tabs_closed_callback_.Run();
67 }
68
69 const DetachedTabsClosedCallback tabs_closed_callback_;
70 std::set<content::WebContents*> detached_web_contents_;
71
72 DISALLOW_IMPLICIT_CONSTRUCTORS(UnloadDetachedHandler);
73 };
74
75
76 //////////////////////////////////////////////////////////////////////////////// 20 ////////////////////////////////////////////////////////////////////////////////
77 // UnloadController, public: 21 // UnloadController, public:
78 22
79 UnloadController::UnloadController(Browser* browser) 23 UnloadController::UnloadController(Browser* browser)
80 : browser_(browser), 24 : browser_(browser),
81 is_attempting_to_close_browser_(false), 25 is_attempting_to_close_browser_(false),
82 ALLOW_THIS_IN_INITIALIZER_LIST(
83 unload_detached_handler_(new UnloadDetachedHandler(
84 base::Bind(&UnloadController::ProcessPendingTabs,
85 base::Unretained(this))))),
86 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { 26 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
87 browser_->tab_strip_model()->AddObserver(this); 27 browser_->tab_strip_model()->AddObserver(this);
88 } 28 }
89 29
90 UnloadController::~UnloadController() { 30 UnloadController::~UnloadController() {
91 browser_->tab_strip_model()->RemoveObserver(this); 31 browser_->tab_strip_model()->RemoveObserver(this);
92 } 32 }
93 33
94 bool UnloadController::CanCloseContents(content::WebContents* contents) { 34 bool UnloadController::CanCloseContents(content::WebContents* contents) {
95 // Don't try to close the tab when the whole browser is being closed, since 35 // Don't try to close the tab when the whole browser is being closed, since
96 // that avoids the fast shutdown path where we just kill all the renderers. 36 // that avoids the fast shutdown path where we just kill all the renderers.
97 if (is_attempting_to_close_browser_) 37 if (is_attempting_to_close_browser_)
98 ClearUnloadState(contents, true); 38 ClearUnloadState(contents, true);
99 return !is_attempting_to_close_browser_; 39 return !is_attempting_to_close_browser_;
100 } 40 }
101 41
102 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, 42 bool UnloadController::BeforeUnloadFired(content::WebContents* contents,
103 bool proceed) { 43 bool proceed) {
104 if (!is_attempting_to_close_browser_) { 44 if (!is_attempting_to_close_browser_) {
105 if (!proceed) { 45 if (!proceed)
106 contents->SetClosedByUserGesture(false); 46 contents->SetClosedByUserGesture(false);
107 } else {
108 // No more dialogs are possible, so remove the tab and finish
109 // running unload listeners asynchrounously.
110 TabStripModel* model = browser_->tab_strip_model();
111 model->delegate()->CreateHistoricalTab(contents);
112 unload_detached_handler_->DetachWebContents(model, contents);
113 }
114 return proceed; 47 return proceed;
115 } 48 }
116 49
117 if (!proceed) { 50 if (!proceed) {
118 CancelWindowClose(); 51 CancelWindowClose();
119 contents->SetClosedByUserGesture(false); 52 contents->SetClosedByUserGesture(false);
120 return false; 53 return false;
121 } 54 }
122 55
123 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { 56 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) {
124 // Now that beforeunload has fired, queue the tab to fire unload. 57 // Now that beforeunload has fired, put the tab on the queue to fire
58 // unload.
125 tabs_needing_unload_fired_.insert(contents); 59 tabs_needing_unload_fired_.insert(contents);
126 ProcessPendingTabs(); 60 ProcessPendingTabs();
127 // We want to handle firing the unload event ourselves since we want to 61 // We want to handle firing the unload event ourselves since we want to
128 // fire all the beforeunload events before attempting to fire the unload 62 // fire all the beforeunload events before attempting to fire the unload
129 // events should the user cancel closing the browser. 63 // events should the user cancel closing the browser.
130 return false; 64 return false;
131 } 65 }
132 66
133 return true; 67 return true;
134 } 68 }
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
210 void UnloadController::TabAttachedImpl(content::WebContents* contents) { 144 void UnloadController::TabAttachedImpl(content::WebContents* contents) {
211 // If the tab crashes in the beforeunload or unload handler, it won't be 145 // If the tab crashes in the beforeunload or unload handler, it won't be
212 // able to ack. But we know we can close it. 146 // able to ack. But we know we can close it.
213 registrar_.Add( 147 registrar_.Add(
214 this, 148 this,
215 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 149 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
216 content::Source<content::WebContents>(contents)); 150 content::Source<content::WebContents>(contents));
217 } 151 }
218 152
219 void UnloadController::TabDetachedImpl(content::WebContents* contents) { 153 void UnloadController::TabDetachedImpl(content::WebContents* contents) {
220 if (is_attempting_to_close_browser_) { 154 if (is_attempting_to_close_browser_)
221 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents) &&
222 tabs_needing_before_unload_fired_.empty() &&
223 tabs_needing_unload_fired_.empty()) {
224 // The last tab needing beforeunload crashed.
225 // Continue with the close (ProcessPendingTabs would miss this).
226 browser_->OnWindowClosing();
227 if (browser_->tab_strip_model()->empty()) {
228 browser_->TabStripEmpty();
229 } else {
230 browser_->tab_strip_model()->CloseAllTabs();
231 }
232 }
233
234 ClearUnloadState(contents, false); 155 ClearUnloadState(contents, false);
235 }
236 registrar_.Remove(this, 156 registrar_.Remove(this,
237 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 157 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
238 content::Source<content::WebContents>(contents)); 158 content::Source<content::WebContents>(contents));
239 } 159 }
240 160
241 void UnloadController::ProcessPendingTabs() { 161 void UnloadController::ProcessPendingTabs() {
162 if (!is_attempting_to_close_browser_) {
163 // Because we might invoke this after a delay it's possible for the value of
164 // is_attempting_to_close_browser_ to have changed since we scheduled the
165 // task.
166 return;
167 }
168
242 if (HasCompletedUnloadProcessing()) { 169 if (HasCompletedUnloadProcessing()) {
170 // We've finished all the unload events and can proceed to close the
171 // browser.
243 browser_->OnWindowClosing(); 172 browser_->OnWindowClosing();
244 browser_->OnUnloadProcessingCompleted(); 173 return;
245 } else if (!tabs_needing_before_unload_fired_.empty()) { 174 }
246 // Process all the beforeunload handlers before the unload handlers. 175
176 // Process beforeunload tabs first. When that queue is empty, process
177 // unload tabs.
178 if (!tabs_needing_before_unload_fired_.empty()) {
247 content::WebContents* web_contents = 179 content::WebContents* web_contents =
248 *(tabs_needing_before_unload_fired_.begin()); 180 *(tabs_needing_before_unload_fired_.begin());
249 // Null check render_view_host here as this gets called on a PostTask and 181 // Null check render_view_host here as this gets called on a PostTask and
250 // the tab's render_view_host may have been nulled out. 182 // the tab's render_view_host may have been nulled out.
251 if (web_contents->GetRenderViewHost()) { 183 if (web_contents->GetRenderViewHost()) {
252 web_contents->OnCloseStarted();
253 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); 184 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
254 } else { 185 } else {
255 ClearUnloadState(web_contents, true); 186 ClearUnloadState(web_contents, true);
256 } 187 }
257 } else if (!tabs_needing_unload_fired_.empty()) { 188 } else if (!tabs_needing_unload_fired_.empty()) {
258 // All beforeunload handlers have fired. Proceed with unload handlers. 189 // We've finished firing all beforeunload events and can proceed with unload
259 190 // events.
260 browser_->OnWindowClosing(); 191 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting
261 192 // somewhere around here so that we have accurate measurements of shutdown
262 // Copy unload tabs to avoid iterator issues when detaching tabs. 193 // time.
263 UnloadListenerSet unload_tabs = tabs_needing_unload_fired_; 194 // TODO(ojan): We can probably fire all the unload events in parallel and
264 195 // get a perf benefit from that in the cases where the tab hangs in it's
265 // Run unload handlers detached since no more interaction is possible. 196 // unload handler or takes a long time to page in.
266 for (UnloadListenerSet::iterator it = unload_tabs.begin(); 197 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin());
267 it != unload_tabs.end(); it++) { 198 // Null check render_view_host here as this gets called on a PostTask and
268 content::WebContents* web_contents = *it; 199 // the tab's render_view_host may have been nulled out.
269 // Null check render_view_host here as this gets called on a PostTask 200 if (web_contents->GetRenderViewHost()) {
270 // and the tab's render_view_host may have been nulled out. 201 web_contents->GetRenderViewHost()->ClosePage();
271 if (web_contents->GetRenderViewHost()) { 202 } else {
272 web_contents->OnUnloadStarted(); 203 ClearUnloadState(web_contents, true);
273 unload_detached_handler_->DetachWebContents(
274 browser_->tab_strip_model(), web_contents);
275 web_contents->GetRenderViewHost()->ClosePage();
276 }
277 } 204 }
278 tabs_needing_unload_fired_.clear(); 205 } else {
279 if (browser_->tab_strip_model()->empty()) { 206 NOTREACHED();
280 browser_->TabStripEmpty();
281 } else {
282 browser_->tab_strip_model()->CloseAllTabs();
283 }
284 } 207 }
285 } 208 }
286 209
287 bool UnloadController::HasCompletedUnloadProcessing() const { 210 bool UnloadController::HasCompletedUnloadProcessing() const {
288 return is_attempting_to_close_browser_ && 211 return is_attempting_to_close_browser_ &&
289 tabs_needing_before_unload_fired_.empty() && 212 tabs_needing_before_unload_fired_.empty() &&
290 tabs_needing_unload_fired_.empty() && 213 tabs_needing_unload_fired_.empty();
291 !unload_detached_handler_->HasTabs();
292 } 214 }
293 215
294 void UnloadController::CancelWindowClose() { 216 void UnloadController::CancelWindowClose() {
295 // Closing of window can be canceled from a beforeunload handler. 217 // Closing of window can be canceled from a beforeunload handler.
296 DCHECK(is_attempting_to_close_browser_); 218 DCHECK(is_attempting_to_close_browser_);
297 for (UnloadListenerSet::iterator it = tabs_needing_unload_fired_.begin();
298 it != tabs_needing_unload_fired_.end(); it++) {
299 content::WebContents* web_contents = *it;
300 web_contents->OnCloseCanceled();
301 }
302 tabs_needing_before_unload_fired_.clear(); 219 tabs_needing_before_unload_fired_.clear();
303 tabs_needing_unload_fired_.clear(); 220 tabs_needing_unload_fired_.clear();
304 is_attempting_to_close_browser_ = false; 221 is_attempting_to_close_browser_ = false;
305 222
306 content::NotificationService::current()->Notify( 223 content::NotificationService::current()->Notify(
307 chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, 224 chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
308 content::Source<Browser>(browser_), 225 content::Source<Browser>(browser_),
309 content::NotificationService::NoDetails()); 226 content::NotificationService::NoDetails());
310 } 227 }
311 228
(...skipping 20 matching lines...) Expand all
332 } else { 249 } else {
333 MessageLoop::current()->PostTask( 250 MessageLoop::current()->PostTask(
334 FROM_HERE, 251 FROM_HERE,
335 base::Bind(&UnloadController::ProcessPendingTabs, 252 base::Bind(&UnloadController::ProcessPendingTabs,
336 weak_factory_.GetWeakPtr())); 253 weak_factory_.GetWeakPtr()));
337 } 254 }
338 } 255 }
339 } 256 }
340 257
341 } // namespace chrome 258 } // namespace chrome
OLDNEW
« no previous file with comments | « trunk/src/chrome/browser/ui/unload_controller.h ('k') | trunk/src/chrome/browser/ui/views/frame/browser_view.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698