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

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

Issue 2681203002: Add Browser::SkipCallBeforeUnload so that the browser windows can be closed regardless of beforeunl… (Closed)
Patch Set: cr - remove delegate in UnloadController Created 3 years, 9 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
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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/fast_unload_controller.h" 5 #include "chrome/browser/ui/fast_unload_controller.h"
6 6
7 #include "base/location.h" 7 #include "base/location.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/macros.h" 9 #include "base/macros.h"
10 #include "base/single_thread_task_runner.h" 10 #include "base/single_thread_task_runner.h"
11 #include "base/threading/thread_task_runner_handle.h" 11 #include "base/threading/thread_task_runner_handle.h"
12 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/devtools/devtools_window.h" 13 #include "chrome/browser/devtools/devtools_window.h"
14 #include "chrome/browser/ui/browser.h" 14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_tabstrip.h" 15 #include "chrome/browser/ui/browser_tabstrip.h"
16 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" 16 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h" 17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" 18 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
19 #include "chrome/browser/ui/unload_controller_web_contents_delegate.h"
19 #include "content/public/browser/notification_service.h" 20 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/notification_source.h" 21 #include "content/public/browser/notification_source.h"
21 #include "content/public/browser/notification_types.h" 22 #include "content/public/browser/notification_types.h"
22 #include "content/public/browser/render_view_host.h" 23 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/web_contents.h" 24 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_contents_delegate.h"
25 #include "extensions/features/features.h" 25 #include "extensions/features/features.h"
26 26
27 #if BUILDFLAG(ENABLE_EXTENSIONS) 27 #if BUILDFLAG(ENABLE_EXTENSIONS)
28 #include "extensions/browser/extension_registry.h" 28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/common/constants.h" 29 #include "extensions/common/constants.h"
30 #endif // (ENABLE_EXTENSIONS) 30 #endif // (ENABLE_EXTENSIONS)
31 31
32 namespace chrome { 32 namespace chrome {
33 33
34 //////////////////////////////////////////////////////////////////////////////// 34 ////////////////////////////////////////////////////////////////////////////////
35 // DetachedWebContentsDelegate will delete web contents when they close.
36 class FastUnloadController::DetachedWebContentsDelegate
37 : public content::WebContentsDelegate {
38 public:
39 DetachedWebContentsDelegate() { }
40 ~DetachedWebContentsDelegate() override {}
41
42 private:
43 // WebContentsDelegate implementation.
44 bool ShouldSuppressDialogs(content::WebContents* source) override {
45 return true; // Return true so dialogs are suppressed.
46 }
47
48 void CloseContents(content::WebContents* source) override {
49 // Finished detached close.
50 // FastUnloadController will observe
51 // |NOTIFICATION_WEB_CONTENTS_DISCONNECTED|.
52 delete source;
53 }
54
55 DISALLOW_COPY_AND_ASSIGN(DetachedWebContentsDelegate);
56 };
57
58 ////////////////////////////////////////////////////////////////////////////////
59 // FastUnloadController, public: 35 // FastUnloadController, public:
60 36
61 FastUnloadController::FastUnloadController(Browser* browser) 37 FastUnloadController::FastUnloadController(Browser* browser)
62 : browser_(browser), 38 : browser_(browser),
63 tab_needing_before_unload_ack_(NULL), 39 tab_needing_before_unload_ack_(NULL),
64 is_attempting_to_close_browser_(false), 40 is_attempting_to_close_browser_(false),
65 detached_delegate_(new DetachedWebContentsDelegate()), 41 detached_delegate_(
42 base::MakeUnique<UnloadControllerWebContentsDelegate>()),
66 weak_factory_(this) { 43 weak_factory_(this) {
67 browser_->tab_strip_model()->AddObserver(this); 44 browser_->tab_strip_model()->AddObserver(this);
68 } 45 }
69 46
70 FastUnloadController::~FastUnloadController() { 47 FastUnloadController::~FastUnloadController() {
71 browser_->tab_strip_model()->RemoveObserver(this); 48 browser_->tab_strip_model()->RemoveObserver(this);
72 } 49 }
73 50
74 bool FastUnloadController::CanCloseContents(content::WebContents* contents) { 51 bool FastUnloadController::CanCloseContents(content::WebContents* contents) {
75 // Don't try to close the tab when the whole browser is being closed, since 52 // Don't try to close the tab when the whole browser is being closed, since
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 if (!proceed) { 126 if (!proceed) {
150 CancelWindowClose(); 127 CancelWindowClose();
151 contents->SetClosedByUserGesture(false); 128 contents->SetClosedByUserGesture(false);
152 return false; 129 return false;
153 } 130 }
154 131
155 if (tab_needing_before_unload_ack_ == contents) { 132 if (tab_needing_before_unload_ack_ == contents) {
156 // Now that beforeunload has fired, queue the tab to fire unload. 133 // Now that beforeunload has fired, queue the tab to fire unload.
157 tab_needing_before_unload_ack_ = NULL; 134 tab_needing_before_unload_ack_ = NULL;
158 tabs_needing_unload_.insert(contents); 135 tabs_needing_unload_.insert(contents);
159 ProcessPendingTabs(); 136 ProcessPendingTabs(false);
160 // We want to handle firing the unload event ourselves since we want to 137 // We want to handle firing the unload event ourselves since we want to
161 // fire all the beforeunload events before attempting to fire the unload 138 // fire all the beforeunload events before attempting to fire the unload
162 // events should the user cancel closing the browser. 139 // events should the user cancel closing the browser.
163 return false; 140 return false;
164 } 141 }
165 142
166 return true; 143 return true;
167 } 144 }
168 145
169 bool FastUnloadController::ShouldCloseWindow() { 146 bool FastUnloadController::ShouldCloseWindow() {
(...skipping 22 matching lines...) Expand all
192 // return false. 169 // return false.
193 // 4. Otherwise: return true. 170 // 4. Otherwise: return true.
194 is_attempting_to_close_browser_ = true; 171 is_attempting_to_close_browser_ = true;
195 // Cases 1 and 4. 172 // Cases 1 and 4.
196 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); 173 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired();
197 if (need_beforeunload_fired == is_calling_before_unload_handlers()) 174 if (need_beforeunload_fired == is_calling_before_unload_handlers())
198 return !need_beforeunload_fired; 175 return !need_beforeunload_fired;
199 176
200 // Cases 2 and 3. 177 // Cases 2 and 3.
201 on_close_confirmed_.Reset(); 178 on_close_confirmed_.Reset();
202 ProcessPendingTabs(); 179 ProcessPendingTabs(false);
203 return false; 180 return false;
204 } 181 }
205 182
206 bool FastUnloadController::CallBeforeUnloadHandlers( 183 bool FastUnloadController::TryToCloseWindow(
184 bool skip_beforeunload,
207 const base::Callback<void(bool)>& on_close_confirmed) { 185 const base::Callback<void(bool)>& on_close_confirmed) {
208 // The devtools browser gets its beforeunload events as the results of 186 // The devtools browser gets its beforeunload events as the results of
209 // intercepting events from the inspected tab, so don't send them here as well. 187 // intercepting events from the inspected tab, so don't send them here as
188 // well.
210 if (browser_->is_devtools() || !TabsNeedBeforeUnloadFired()) 189 if (browser_->is_devtools() || !TabsNeedBeforeUnloadFired())
211 return false; 190 return false;
212 191
213 on_close_confirmed_ = on_close_confirmed; 192 on_close_confirmed_ = on_close_confirmed;
214 is_attempting_to_close_browser_ = true; 193 is_attempting_to_close_browser_ = true;
215 ProcessPendingTabs(); 194 ProcessPendingTabs(skip_beforeunload);
216 return true; 195 return !skip_beforeunload;
217 } 196 }
218 197
219 void FastUnloadController::ResetBeforeUnloadHandlers() { 198 void FastUnloadController::ResetTryToCloseWindow() {
220 if (!is_calling_before_unload_handlers()) 199 if (!is_calling_before_unload_handlers())
221 return; 200 return;
222 CancelWindowClose(); 201 CancelWindowClose();
223 } 202 }
224 203
225 bool FastUnloadController::TabsNeedBeforeUnloadFired() { 204 bool FastUnloadController::TabsNeedBeforeUnloadFired() {
226 if (!tabs_needing_before_unload_.empty() || 205 if (!tabs_needing_before_unload_.empty() ||
227 tab_needing_before_unload_ack_ != NULL) 206 tab_needing_before_unload_ack_ != NULL)
228 return true; 207 return true;
229 208
(...skipping 15 matching lines...) Expand all
245 } 224 }
246 225
247 bool FastUnloadController::HasCompletedUnloadProcessing() const { 226 bool FastUnloadController::HasCompletedUnloadProcessing() const {
248 return is_attempting_to_close_browser_ && 227 return is_attempting_to_close_browser_ &&
249 tabs_needing_before_unload_.empty() && 228 tabs_needing_before_unload_.empty() &&
250 tab_needing_before_unload_ack_ == NULL && 229 tab_needing_before_unload_ack_ == NULL &&
251 tabs_needing_unload_.empty() && 230 tabs_needing_unload_.empty() &&
252 tabs_needing_unload_ack_.empty(); 231 tabs_needing_unload_ack_.empty();
253 } 232 }
254 233
255 void FastUnloadController::CancelWindowClose() { 234 void FastUnloadController::CancelTabNeedingBeforeUnloadAck() {
256 // Closing of window can be canceled from a beforeunload handler.
257 DCHECK(is_attempting_to_close_browser_);
258 tabs_needing_before_unload_.clear();
259 if (tab_needing_before_unload_ack_ != NULL) { 235 if (tab_needing_before_unload_ack_ != NULL) {
260 CoreTabHelper* core_tab_helper = 236 CoreTabHelper* core_tab_helper =
261 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_); 237 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_);
262 core_tab_helper->OnCloseCanceled(); 238 core_tab_helper->OnCloseCanceled();
263 DevToolsWindow::OnPageCloseCanceled(tab_needing_before_unload_ack_); 239 DevToolsWindow::OnPageCloseCanceled(tab_needing_before_unload_ack_);
264 tab_needing_before_unload_ack_ = NULL; 240 tab_needing_before_unload_ack_ = NULL;
265 } 241 }
242 }
243
244 void FastUnloadController::CancelWindowClose() {
245 // Closing of window can be canceled from a beforeunload handler.
246 DCHECK(is_attempting_to_close_browser_);
247 tabs_needing_before_unload_.clear();
248 CancelTabNeedingBeforeUnloadAck();
266 for (WebContentsSet::iterator it = tabs_needing_unload_.begin(); 249 for (WebContentsSet::iterator it = tabs_needing_unload_.begin();
267 it != tabs_needing_unload_.end(); it++) { 250 it != tabs_needing_unload_.end(); it++) {
268 content::WebContents* contents = *it; 251 content::WebContents* contents = *it;
269 252
270 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 253 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
271 core_tab_helper->OnCloseCanceled(); 254 core_tab_helper->OnCloseCanceled();
272 DevToolsWindow::OnPageCloseCanceled(contents); 255 DevToolsWindow::OnPageCloseCanceled(contents);
273 } 256 }
274 tabs_needing_unload_.clear(); 257 tabs_needing_unload_.clear();
275 258
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 tabs_needing_unload_ack_.insert(contents); 358 tabs_needing_unload_ack_.insert(contents);
376 browser_->tab_strip_model()->DetachWebContentsAt(index); 359 browser_->tab_strip_model()->DetachWebContentsAt(index);
377 contents->SetDelegate(detached_delegate_.get()); 360 contents->SetDelegate(detached_delegate_.get());
378 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 361 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
379 core_tab_helper->OnUnloadDetachedStarted(); 362 core_tab_helper->OnUnloadDetachedStarted();
380 return true; 363 return true;
381 } 364 }
382 return false; 365 return false;
383 } 366 }
384 367
385 void FastUnloadController::ProcessPendingTabs() { 368 void FastUnloadController::ProcessPendingTabs(bool skip_beforeunload) {
386 if (!is_attempting_to_close_browser_) { 369 if (!is_attempting_to_close_browser_) {
387 // Because we might invoke this after a delay it's possible for the value of 370 // Because we might invoke this after a delay it's possible for the value of
388 // is_attempting_to_close_browser_ to have changed since we scheduled the 371 // is_attempting_to_close_browser_ to have changed since we scheduled the
389 // task. 372 // task.
390 return; 373 return;
391 } 374 }
392 375
393 if (tab_needing_before_unload_ack_ != NULL) { 376 if (tab_needing_before_unload_ack_ != NULL) {
394 // Wait for |BeforeUnloadFired| before proceeding. 377 if (skip_beforeunload) {
395 return; 378 // Cancel and skip the ongoing before unload event.
379 tabs_needing_before_unload_.insert(tab_needing_before_unload_ack_);
380 CancelTabNeedingBeforeUnloadAck();
381 } else {
382 // Wait for |BeforeUnloadFired| before proceeding.
383 return;
384 }
396 } 385 }
397 386
398 // Process a beforeunload handler. 387 // Process a beforeunload handler.
399 if (!tabs_needing_before_unload_.empty()) { 388 if (!tabs_needing_before_unload_.empty()) {
400 WebContentsSet::iterator it = tabs_needing_before_unload_.begin(); 389 if (skip_beforeunload) {
401 content::WebContents* contents = *it; 390 tabs_needing_unload_.insert(tabs_needing_before_unload_.begin(),
402 tabs_needing_before_unload_.erase(it); 391 tabs_needing_before_unload_.end());
403 // Null check render_view_host here as this gets called on a PostTask and 392 tabs_needing_before_unload_.clear();
404 // the tab's render_view_host may have been nulled out. 393 } else {
405 if (contents->GetRenderViewHost()) { 394 WebContentsSet::iterator it = tabs_needing_before_unload_.begin();
406 tab_needing_before_unload_ack_ = contents; 395 content::WebContents* contents = *it;
396 tabs_needing_before_unload_.erase(it);
397 // Null check render_view_host here as this gets called on a PostTask and
398 // the tab's render_view_host may have been nulled out.
399 if (contents->GetRenderViewHost()) {
400 tab_needing_before_unload_ack_ = contents;
407 401
408 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 402 CoreTabHelper* core_tab_helper =
409 core_tab_helper->OnCloseStarted(); 403 CoreTabHelper::FromWebContents(contents);
404 core_tab_helper->OnCloseStarted();
410 405
411 // If there's a devtools window attached to |contents|, 406 // If there's a devtools window attached to |contents|,
412 // we would like devtools to call its own beforeunload handlers first, 407 // we would like devtools to call its own beforeunload handlers first,
413 // and then call beforeunload handlers for |contents|. 408 // and then call beforeunload handlers for |contents|.
414 // See DevToolsWindow::InterceptPageBeforeUnload for details. 409 // See DevToolsWindow::InterceptPageBeforeUnload for details.
415 if (!DevToolsWindow::InterceptPageBeforeUnload(contents)) 410 if (!DevToolsWindow::InterceptPageBeforeUnload(contents))
416 contents->DispatchBeforeUnload(); 411 contents->DispatchBeforeUnload();
417 } else { 412 } else {
418 ProcessPendingTabs(); 413 ProcessPendingTabs(skip_beforeunload);
414 }
415 return;
419 } 416 }
417 }
418
419 if (is_calling_before_unload_handlers()) {
420 if (!skip_beforeunload)
421 on_close_confirmed_.Run(true);
420 return; 422 return;
421 } 423 }
422 424
423 if (is_calling_before_unload_handlers()) {
424 on_close_confirmed_.Run(true);
425 return;
426 }
427 // Process all the unload handlers. (The beforeunload handlers have finished.) 425 // Process all the unload handlers. (The beforeunload handlers have finished.)
428 if (!tabs_needing_unload_.empty()) { 426 if (!tabs_needing_unload_.empty()) {
429 browser_->OnWindowClosing(); 427 browser_->OnWindowClosing();
430 428
431 // Run unload handlers detached since no more interaction is possible. 429 // Run unload handlers detached since no more interaction is possible.
432 WebContentsSet::iterator it = tabs_needing_unload_.begin(); 430 WebContentsSet::iterator it = tabs_needing_unload_.begin();
433 while (it != tabs_needing_unload_.end()) { 431 while (it != tabs_needing_unload_.end()) {
434 WebContentsSet::iterator current = it++; 432 WebContentsSet::iterator current = it++;
435 content::WebContents* contents = *current; 433 content::WebContents* contents = *current;
436 tabs_needing_unload_.erase(current); 434 tabs_needing_unload_.erase(current);
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 if (tabs_needing_before_unload_.erase(contents) > 0 || 485 if (tabs_needing_before_unload_.erase(contents) > 0 ||
488 tabs_needing_unload_.erase(contents) > 0) { 486 tabs_needing_unload_.erase(contents) > 0) {
489 if (tab_needing_before_unload_ack_ == NULL) 487 if (tab_needing_before_unload_ack_ == NULL)
490 PostTaskForProcessPendingTabs(); 488 PostTaskForProcessPendingTabs();
491 } 489 }
492 } 490 }
493 491
494 void FastUnloadController::PostTaskForProcessPendingTabs() { 492 void FastUnloadController::PostTaskForProcessPendingTabs() {
495 base::ThreadTaskRunnerHandle::Get()->PostTask( 493 base::ThreadTaskRunnerHandle::Get()->PostTask(
496 FROM_HERE, base::Bind(&FastUnloadController::ProcessPendingTabs, 494 FROM_HERE, base::Bind(&FastUnloadController::ProcessPendingTabs,
497 weak_factory_.GetWeakPtr())); 495 weak_factory_.GetWeakPtr(), false));
498 } 496 }
499 497
500 } // namespace chrome 498 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698