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

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

Issue 17571018: Reland fast tab closure behind a flag (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Diff against original patch Created 7 years, 5 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/fast_unload_controller.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/message_loop.h" 8 #include "base/message_loop.h"
9 #include "chrome/browser/ui/browser.h" 9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/browser/ui/browser_tabstrip.h" 10 #include "chrome/browser/ui/browser_tabstrip.h"
11 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" 11 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h" 12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" 13 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
14 #include "chrome/common/chrome_notification_types.h" 14 #include "chrome/common/chrome_notification_types.h"
15 #include "content/public/browser/notification_service.h" 15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/notification_source.h" 16 #include "content/public/browser/notification_source.h"
17 #include "content/public/browser/notification_types.h" 17 #include "content/public/browser/notification_types.h"
18 #include "content/public/browser/render_view_host.h" 18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/web_contents.h" 19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_contents_delegate.h" 20 #include "content/public/browser/web_contents_delegate.h"
21 21
22 namespace chrome { 22 namespace chrome {
23 23
24 24
25 //////////////////////////////////////////////////////////////////////////////// 25 ////////////////////////////////////////////////////////////////////////////////
26 // DetachedWebContentsDelegate will delete web contents when they close. 26 // DetachedWebContentsDelegate will delete web contents when they close.
27 class UnloadController::DetachedWebContentsDelegate 27 class FastUnloadController::DetachedWebContentsDelegate
28 : public content::WebContentsDelegate { 28 : public content::WebContentsDelegate {
29 public: 29 public:
30 DetachedWebContentsDelegate() { } 30 DetachedWebContentsDelegate() { }
31 virtual ~DetachedWebContentsDelegate() { } 31 virtual ~DetachedWebContentsDelegate() { }
32 32
33 private: 33 private:
34 // WebContentsDelegate implementation. 34 // WebContentsDelegate implementation.
35 virtual bool ShouldSuppressDialogs() OVERRIDE { 35 virtual bool ShouldSuppressDialogs() OVERRIDE {
36 return true; // Return true so dialogs are suppressed. 36 return true; // Return true so dialogs are suppressed.
37 } 37 }
38 38
39 virtual void CloseContents(content::WebContents* source) OVERRIDE { 39 virtual void CloseContents(content::WebContents* source) OVERRIDE {
40 // Finished detached close. 40 // Finished detached close.
41 // UnloadController will observe 41 // FastUnloadController will observe
42 // |NOTIFICATION_WEB_CONTENTS_DISCONNECTED|. 42 // |NOTIFICATION_WEB_CONTENTS_DISCONNECTED|.
43 delete source; 43 delete source;
44 } 44 }
45 45
46 DISALLOW_COPY_AND_ASSIGN(DetachedWebContentsDelegate); 46 DISALLOW_COPY_AND_ASSIGN(DetachedWebContentsDelegate);
47 }; 47 };
48 48
49 //////////////////////////////////////////////////////////////////////////////// 49 ////////////////////////////////////////////////////////////////////////////////
50 // UnloadController, public: 50 // FastUnloadController, public:
51 51
52 UnloadController::UnloadController(Browser* browser) 52 FastUnloadController::FastUnloadController(Browser* browser)
53 : browser_(browser), 53 : browser_(browser),
54 tab_needing_before_unload_ack_(NULL), 54 tab_needing_before_unload_ack_(NULL),
55 is_attempting_to_close_browser_(false), 55 is_attempting_to_close_browser_(false),
56 detached_delegate_(new DetachedWebContentsDelegate()), 56 detached_delegate_(new DetachedWebContentsDelegate()),
57 weak_factory_(this) { 57 weak_factory_(this) {
58 browser_->tab_strip_model()->AddObserver(this); 58 browser_->tab_strip_model()->AddObserver(this);
59 } 59 }
60 60
61 UnloadController::~UnloadController() { 61 FastUnloadController::~FastUnloadController() {
62 browser_->tab_strip_model()->RemoveObserver(this); 62 browser_->tab_strip_model()->RemoveObserver(this);
63 } 63 }
64 64
65 bool UnloadController::CanCloseContents(content::WebContents* contents) { 65 bool FastUnloadController::CanCloseContents(content::WebContents* contents) {
66 // Don't try to close the tab when the whole browser is being closed, since 66 // Don't try to close the tab when the whole browser is being closed, since
67 // that avoids the fast shutdown path where we just kill all the renderers. 67 // that avoids the fast shutdown path where we just kill all the renderers.
68 return !is_attempting_to_close_browser_; 68 return !is_attempting_to_close_browser_;
69 } 69 }
70 70
71 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, 71 bool FastUnloadController::BeforeUnloadFired(content::WebContents* contents,
72 bool proceed) { 72 bool proceed) {
73 if (!is_attempting_to_close_browser_) { 73 if (!is_attempting_to_close_browser_) {
74 if (!proceed) { 74 if (!proceed) {
75 contents->SetClosedByUserGesture(false); 75 contents->SetClosedByUserGesture(false);
76 } else { 76 } else {
77 // No more dialogs are possible, so remove the tab and finish 77 // No more dialogs are possible, so remove the tab and finish
78 // running unload listeners asynchrounously. 78 // running unload listeners asynchrounously.
79 browser_->tab_strip_model()->delegate()->CreateHistoricalTab(contents); 79 browser_->tab_strip_model()->delegate()->CreateHistoricalTab(contents);
80 DetachWebContents(contents); 80 DetachWebContents(contents);
81 } 81 }
82 return proceed; 82 return proceed;
(...skipping 12 matching lines...) Expand all
95 ProcessPendingTabs(); 95 ProcessPendingTabs();
96 // We want to handle firing the unload event ourselves since we want to 96 // We want to handle firing the unload event ourselves since we want to
97 // fire all the beforeunload events before attempting to fire the unload 97 // fire all the beforeunload events before attempting to fire the unload
98 // events should the user cancel closing the browser. 98 // events should the user cancel closing the browser.
99 return false; 99 return false;
100 } 100 }
101 101
102 return true; 102 return true;
103 } 103 }
104 104
105 bool UnloadController::ShouldCloseWindow() { 105 bool FastUnloadController::ShouldCloseWindow() {
106 if (HasCompletedUnloadProcessing()) 106 if (HasCompletedUnloadProcessing())
107 return true; 107 return true;
108 108
109 is_attempting_to_close_browser_ = true; 109 is_attempting_to_close_browser_ = true;
110 110
111 if (!TabsNeedBeforeUnloadFired()) 111 if (!TabsNeedBeforeUnloadFired())
112 return true; 112 return true;
113 113
114 ProcessPendingTabs(); 114 ProcessPendingTabs();
115 return false; 115 return false;
116 } 116 }
117 117
118 bool UnloadController::TabsNeedBeforeUnloadFired() { 118 bool FastUnloadController::TabsNeedBeforeUnloadFired() {
119 if (!tabs_needing_before_unload_.empty() || 119 if (!tabs_needing_before_unload_.empty() ||
120 tab_needing_before_unload_ack_ != NULL) 120 tab_needing_before_unload_ack_ != NULL)
121 return true; 121 return true;
122 122
123 if (!tabs_needing_unload_.empty()) 123 if (!tabs_needing_unload_.empty())
124 return false; 124 return false;
125 125
126 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { 126 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) {
127 content::WebContents* contents = 127 content::WebContents* contents =
128 browser_->tab_strip_model()->GetWebContentsAt(i); 128 browser_->tab_strip_model()->GetWebContentsAt(i);
129 if (contents->NeedToFireBeforeUnload()) 129 if (contents->NeedToFireBeforeUnload())
130 tabs_needing_before_unload_.insert(contents); 130 tabs_needing_before_unload_.insert(contents);
131 } 131 }
132 return !tabs_needing_before_unload_.empty(); 132 return !tabs_needing_before_unload_.empty();
133 } 133 }
134 134
135 //////////////////////////////////////////////////////////////////////////////// 135 ////////////////////////////////////////////////////////////////////////////////
136 // UnloadController, content::NotificationObserver implementation: 136 // FastUnloadController, content::NotificationObserver implementation:
137 137
138 void UnloadController::Observe(int type, 138 void FastUnloadController::Observe(
139 const content::NotificationSource& source, 139 int type,
140 const content::NotificationDetails& details) { 140 const content::NotificationSource& source,
141 const content::NotificationDetails& details) {
141 switch (type) { 142 switch (type) {
142 case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: { 143 case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: {
143 registrar_.Remove(this, 144 registrar_.Remove(this,
144 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 145 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
145 source); 146 source);
146 content::WebContents* contents = 147 content::WebContents* contents =
147 content::Source<content::WebContents>(source).ptr(); 148 content::Source<content::WebContents>(source).ptr();
148 ClearUnloadState(contents); 149 ClearUnloadState(contents);
149 break; 150 break;
150 } 151 }
151 default: 152 default:
152 NOTREACHED() << "Got a notification we didn't register for."; 153 NOTREACHED() << "Got a notification we didn't register for.";
153 } 154 }
154 } 155 }
155 156
156 //////////////////////////////////////////////////////////////////////////////// 157 ////////////////////////////////////////////////////////////////////////////////
157 // UnloadController, TabStripModelObserver implementation: 158 // FastUnloadController, TabStripModelObserver implementation:
158 159
159 void UnloadController::TabInsertedAt(content::WebContents* contents, 160 void FastUnloadController::TabInsertedAt(content::WebContents* contents,
160 int index, 161 int index,
161 bool foreground) { 162 bool foreground) {
162 TabAttachedImpl(contents); 163 TabAttachedImpl(contents);
163 } 164 }
164 165
165 void UnloadController::TabDetachedAt(content::WebContents* contents, 166 void FastUnloadController::TabDetachedAt(content::WebContents* contents,
166 int index) { 167 int index) {
167 TabDetachedImpl(contents); 168 TabDetachedImpl(contents);
168 } 169 }
169 170
170 void UnloadController::TabReplacedAt(TabStripModel* tab_strip_model, 171 void FastUnloadController::TabReplacedAt(TabStripModel* tab_strip_model,
171 content::WebContents* old_contents, 172 content::WebContents* old_contents,
172 content::WebContents* new_contents, 173 content::WebContents* new_contents,
173 int index) { 174 int index) {
174 TabDetachedImpl(old_contents); 175 TabDetachedImpl(old_contents);
175 TabAttachedImpl(new_contents); 176 TabAttachedImpl(new_contents);
176 } 177 }
177 178
178 void UnloadController::TabStripEmpty() { 179 void FastUnloadController::TabStripEmpty() {
179 // Set is_attempting_to_close_browser_ here, so that extensions, etc, do not 180 // Set is_attempting_to_close_browser_ here, so that extensions, etc, do not
180 // attempt to add tabs to the browser before it closes. 181 // attempt to add tabs to the browser before it closes.
181 is_attempting_to_close_browser_ = true; 182 is_attempting_to_close_browser_ = true;
182 } 183 }
183 184
184 //////////////////////////////////////////////////////////////////////////////// 185 ////////////////////////////////////////////////////////////////////////////////
185 // UnloadController, private: 186 // FastUnloadController, private:
186 187
187 void UnloadController::TabAttachedImpl(content::WebContents* contents) { 188 void FastUnloadController::TabAttachedImpl(content::WebContents* contents) {
188 // If the tab crashes in the beforeunload or unload handler, it won't be 189 // If the tab crashes in the beforeunload or unload handler, it won't be
189 // able to ack. But we know we can close it. 190 // able to ack. But we know we can close it.
190 registrar_.Add( 191 registrar_.Add(
191 this, 192 this,
192 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 193 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
193 content::Source<content::WebContents>(contents)); 194 content::Source<content::WebContents>(contents));
194 } 195 }
195 196
196 void UnloadController::TabDetachedImpl(content::WebContents* contents) { 197 void FastUnloadController::TabDetachedImpl(content::WebContents* contents) {
197 if (tabs_needing_unload_ack_.find(contents) != 198 if (tabs_needing_unload_ack_.find(contents) !=
198 tabs_needing_unload_ack_.end()) { 199 tabs_needing_unload_ack_.end()) {
199 // Tab needs unload to complete. 200 // Tab needs unload to complete.
200 // It will send |NOTIFICATION_WEB_CONTENTS_DISCONNECTED| when done. 201 // It will send |NOTIFICATION_WEB_CONTENTS_DISCONNECTED| when done.
201 return; 202 return;
202 } 203 }
203 204
204 // If WEB_CONTENTS_DISCONNECTED was received then the notification may have 205 // If WEB_CONTENTS_DISCONNECTED was received then the notification may have
205 // already been unregistered. 206 // already been unregistered.
206 const content::NotificationSource& source = 207 const content::NotificationSource& source =
207 content::Source<content::WebContents>(contents); 208 content::Source<content::WebContents>(contents);
208 if (registrar_.IsRegistered(this, 209 if (registrar_.IsRegistered(this,
209 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 210 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
210 source)) { 211 source)) {
211 registrar_.Remove(this, 212 registrar_.Remove(this,
212 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, 213 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
213 source); 214 source);
214 } 215 }
215 216
216 if (is_attempting_to_close_browser_) 217 if (is_attempting_to_close_browser_)
217 ClearUnloadState(contents); 218 ClearUnloadState(contents);
218 } 219 }
219 220
220 bool UnloadController::DetachWebContents(content::WebContents* contents) { 221 bool FastUnloadController::DetachWebContents(content::WebContents* contents) {
221 int index = browser_->tab_strip_model()->GetIndexOfWebContents(contents); 222 int index = browser_->tab_strip_model()->GetIndexOfWebContents(contents);
222 if (index != TabStripModel::kNoTab && 223 if (index != TabStripModel::kNoTab &&
223 contents->NeedToFireBeforeUnload()) { 224 contents->NeedToFireBeforeUnload()) {
224 tabs_needing_unload_ack_.insert(contents); 225 tabs_needing_unload_ack_.insert(contents);
225 browser_->tab_strip_model()->DetachWebContentsAt(index); 226 browser_->tab_strip_model()->DetachWebContentsAt(index);
226 contents->SetDelegate(detached_delegate_.get()); 227 contents->SetDelegate(detached_delegate_.get());
227 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 228 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
228 core_tab_helper->OnUnloadDetachedStarted(); 229 core_tab_helper->OnUnloadDetachedStarted();
229 return true; 230 return true;
230 } 231 }
231 return false; 232 return false;
232 } 233 }
233 234
234 void UnloadController::ProcessPendingTabs() { 235 void FastUnloadController::ProcessPendingTabs() {
235 if (!is_attempting_to_close_browser_) { 236 if (!is_attempting_to_close_browser_) {
236 // Because we might invoke this after a delay it's possible for the value of 237 // Because we might invoke this after a delay it's possible for the value of
237 // is_attempting_to_close_browser_ to have changed since we scheduled the 238 // is_attempting_to_close_browser_ to have changed since we scheduled the
238 // task. 239 // task.
239 return; 240 return;
240 } 241 }
241 242
242 if (tab_needing_before_unload_ack_ != NULL) { 243 if (tab_needing_before_unload_ack_ != NULL) {
243 // Wait for |BeforeUnloadFired| before proceeding. 244 // Wait for |BeforeUnloadFired| before proceeding.
244 return; 245 return;
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 if (browser_->tab_strip_model()->empty()) { 302 if (browser_->tab_strip_model()->empty()) {
302 browser_->TabStripEmpty(); 303 browser_->TabStripEmpty();
303 } else { 304 } else {
304 // There may be tabs if the last tab needing beforeunload crashed. 305 // There may be tabs if the last tab needing beforeunload crashed.
305 browser_->tab_strip_model()->CloseAllTabs(); 306 browser_->tab_strip_model()->CloseAllTabs();
306 } 307 }
307 return; 308 return;
308 } 309 }
309 } 310 }
310 311
311 bool UnloadController::HasCompletedUnloadProcessing() const { 312 bool FastUnloadController::HasCompletedUnloadProcessing() const {
312 return is_attempting_to_close_browser_ && 313 return is_attempting_to_close_browser_ &&
313 tabs_needing_before_unload_.empty() && 314 tabs_needing_before_unload_.empty() &&
314 tab_needing_before_unload_ack_ == NULL && 315 tab_needing_before_unload_ack_ == NULL &&
315 tabs_needing_unload_.empty() && 316 tabs_needing_unload_.empty() &&
316 tabs_needing_unload_ack_.empty(); 317 tabs_needing_unload_ack_.empty();
317 } 318 }
318 319
319 void UnloadController::CancelWindowClose() { 320 void FastUnloadController::CancelWindowClose() {
320 // Closing of window can be canceled from a beforeunload handler. 321 // Closing of window can be canceled from a beforeunload handler.
321 DCHECK(is_attempting_to_close_browser_); 322 DCHECK(is_attempting_to_close_browser_);
322 tabs_needing_before_unload_.clear(); 323 tabs_needing_before_unload_.clear();
323 if (tab_needing_before_unload_ack_ != NULL) { 324 if (tab_needing_before_unload_ack_ != NULL) {
324 325
325 CoreTabHelper* core_tab_helper = 326 CoreTabHelper* core_tab_helper =
326 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_); 327 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_);
327 core_tab_helper->OnCloseCanceled(); 328 core_tab_helper->OnCloseCanceled();
328 tab_needing_before_unload_ack_ = NULL; 329 tab_needing_before_unload_ack_ = NULL;
329 } 330 }
330 for (WebContentsSet::iterator it = tabs_needing_unload_.begin(); 331 for (WebContentsSet::iterator it = tabs_needing_unload_.begin();
331 it != tabs_needing_unload_.end(); it++) { 332 it != tabs_needing_unload_.end(); it++) {
332 content::WebContents* contents = *it; 333 content::WebContents* contents = *it;
333 334
334 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 335 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
335 core_tab_helper->OnCloseCanceled(); 336 core_tab_helper->OnCloseCanceled();
336 } 337 }
337 tabs_needing_unload_.clear(); 338 tabs_needing_unload_.clear();
338 339
339 // No need to clear tabs_needing_unload_ack_. Those tabs are already detached. 340 // No need to clear tabs_needing_unload_ack_. Those tabs are already detached.
340 341
341 is_attempting_to_close_browser_ = false; 342 is_attempting_to_close_browser_ = false;
342 343
343 content::NotificationService::current()->Notify( 344 content::NotificationService::current()->Notify(
344 chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, 345 chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
345 content::Source<Browser>(browser_), 346 content::Source<Browser>(browser_),
346 content::NotificationService::NoDetails()); 347 content::NotificationService::NoDetails());
347 } 348 }
348 349
349 void UnloadController::ClearUnloadState(content::WebContents* contents) { 350 void FastUnloadController::ClearUnloadState(content::WebContents* contents) {
350 if (tabs_needing_unload_ack_.erase(contents) > 0) { 351 if (tabs_needing_unload_ack_.erase(contents) > 0) {
351 if (HasCompletedUnloadProcessing()) 352 if (HasCompletedUnloadProcessing())
352 PostTaskForProcessPendingTabs(); 353 PostTaskForProcessPendingTabs();
353 return; 354 return;
354 } 355 }
355 356
356 if (!is_attempting_to_close_browser_) 357 if (!is_attempting_to_close_browser_)
357 return; 358 return;
358 359
359 if (tab_needing_before_unload_ack_ == contents) { 360 if (tab_needing_before_unload_ack_ == contents) {
360 tab_needing_before_unload_ack_ = NULL; 361 tab_needing_before_unload_ack_ = NULL;
361 PostTaskForProcessPendingTabs(); 362 PostTaskForProcessPendingTabs();
362 return; 363 return;
363 } 364 }
364 365
365 if (tabs_needing_before_unload_.erase(contents) > 0 || 366 if (tabs_needing_before_unload_.erase(contents) > 0 ||
366 tabs_needing_unload_.erase(contents) > 0) { 367 tabs_needing_unload_.erase(contents) > 0) {
367 if (tab_needing_before_unload_ack_ == NULL) 368 if (tab_needing_before_unload_ack_ == NULL)
368 PostTaskForProcessPendingTabs(); 369 PostTaskForProcessPendingTabs();
369 } 370 }
370 } 371 }
371 372
372 void UnloadController::PostTaskForProcessPendingTabs() { 373 void FastUnloadController::PostTaskForProcessPendingTabs() {
373 base::MessageLoop::current()->PostTask( 374 base::MessageLoop::current()->PostTask(
374 FROM_HERE, 375 FROM_HERE,
375 base::Bind(&UnloadController::ProcessPendingTabs, 376 base::Bind(&FastUnloadController::ProcessPendingTabs,
376 weak_factory_.GetWeakPtr())); 377 weak_factory_.GetWeakPtr()));
377 } 378 }
378 379
379 } // namespace chrome 380 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698