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

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: rebase from master 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_(new UnloadControllerWebContentsDelegate()),
sky 2017/03/14 00:04:05 MakeUnique
zmin 2017/03/14 17:46:47 Done.
66 weak_factory_(this) { 42 weak_factory_(this) {
67 browser_->tab_strip_model()->AddObserver(this); 43 browser_->tab_strip_model()->AddObserver(this);
68 } 44 }
69 45
70 FastUnloadController::~FastUnloadController() { 46 FastUnloadController::~FastUnloadController() {
71 browser_->tab_strip_model()->RemoveObserver(this); 47 browser_->tab_strip_model()->RemoveObserver(this);
72 } 48 }
73 49
74 bool FastUnloadController::CanCloseContents(content::WebContents* contents) { 50 bool FastUnloadController::CanCloseContents(content::WebContents* contents) {
75 // Don't try to close the tab when the whole browser is being closed, since 51 // 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) { 125 if (!proceed) {
150 CancelWindowClose(); 126 CancelWindowClose();
151 contents->SetClosedByUserGesture(false); 127 contents->SetClosedByUserGesture(false);
152 return false; 128 return false;
153 } 129 }
154 130
155 if (tab_needing_before_unload_ack_ == contents) { 131 if (tab_needing_before_unload_ack_ == contents) {
156 // Now that beforeunload has fired, queue the tab to fire unload. 132 // Now that beforeunload has fired, queue the tab to fire unload.
157 tab_needing_before_unload_ack_ = NULL; 133 tab_needing_before_unload_ack_ = NULL;
158 tabs_needing_unload_.insert(contents); 134 tabs_needing_unload_.insert(contents);
159 ProcessPendingTabs(); 135 ProcessPendingTabs(false);
160 // We want to handle firing the unload event ourselves since we want to 136 // 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 137 // fire all the beforeunload events before attempting to fire the unload
162 // events should the user cancel closing the browser. 138 // events should the user cancel closing the browser.
163 return false; 139 return false;
164 } 140 }
165 141
166 return true; 142 return true;
167 } 143 }
168 144
169 bool FastUnloadController::ShouldCloseWindow() { 145 bool FastUnloadController::ShouldCloseWindow() {
(...skipping 22 matching lines...) Expand all
192 // return false. 168 // return false.
193 // 4. Otherwise: return true. 169 // 4. Otherwise: return true.
194 is_attempting_to_close_browser_ = true; 170 is_attempting_to_close_browser_ = true;
195 // Cases 1 and 4. 171 // Cases 1 and 4.
196 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); 172 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired();
197 if (need_beforeunload_fired == is_calling_before_unload_handlers()) 173 if (need_beforeunload_fired == is_calling_before_unload_handlers())
198 return !need_beforeunload_fired; 174 return !need_beforeunload_fired;
199 175
200 // Cases 2 and 3. 176 // Cases 2 and 3.
201 on_close_confirmed_.Reset(); 177 on_close_confirmed_.Reset();
202 ProcessPendingTabs(); 178 ProcessPendingTabs(false);
203 return false; 179 return false;
204 } 180 }
205 181
206 bool FastUnloadController::CallBeforeUnloadHandlers( 182 bool FastUnloadController::TryToCloseWindow(
183 bool skip_beforeunload,
207 const base::Callback<void(bool)>& on_close_confirmed) { 184 const base::Callback<void(bool)>& on_close_confirmed) {
208 // The devtools browser gets its beforeunload events as the results of 185 // 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. 186 // intercepting events from the inspected tab, so don't send them here as
187 // well.
210 if (browser_->is_devtools() || !TabsNeedBeforeUnloadFired()) 188 if (browser_->is_devtools() || !TabsNeedBeforeUnloadFired())
211 return false; 189 return false;
212 190
213 on_close_confirmed_ = on_close_confirmed; 191 on_close_confirmed_ = on_close_confirmed;
214 is_attempting_to_close_browser_ = true; 192 is_attempting_to_close_browser_ = true;
215 ProcessPendingTabs(); 193 ProcessPendingTabs(skip_beforeunload);
216 return true; 194 return true && !skip_beforeunload;
sky 2017/03/14 00:04:05 return !skip_beforeunload?
zmin 2017/03/14 17:46:47 Done.
217 } 195 }
218 196
219 void FastUnloadController::ResetBeforeUnloadHandlers() { 197 void FastUnloadController::ResetTryToCloseWindow() {
220 if (!is_calling_before_unload_handlers()) 198 if (!is_calling_before_unload_handlers())
221 return; 199 return;
222 CancelWindowClose(); 200 CancelWindowClose();
223 } 201 }
224 202
225 bool FastUnloadController::TabsNeedBeforeUnloadFired() { 203 bool FastUnloadController::TabsNeedBeforeUnloadFired() {
226 if (!tabs_needing_before_unload_.empty() || 204 if (!tabs_needing_before_unload_.empty() ||
227 tab_needing_before_unload_ack_ != NULL) 205 tab_needing_before_unload_ack_ != NULL)
228 return true; 206 return true;
229 207
(...skipping 15 matching lines...) Expand all
245 } 223 }
246 224
247 bool FastUnloadController::HasCompletedUnloadProcessing() const { 225 bool FastUnloadController::HasCompletedUnloadProcessing() const {
248 return is_attempting_to_close_browser_ && 226 return is_attempting_to_close_browser_ &&
249 tabs_needing_before_unload_.empty() && 227 tabs_needing_before_unload_.empty() &&
250 tab_needing_before_unload_ack_ == NULL && 228 tab_needing_before_unload_ack_ == NULL &&
251 tabs_needing_unload_.empty() && 229 tabs_needing_unload_.empty() &&
252 tabs_needing_unload_ack_.empty(); 230 tabs_needing_unload_ack_.empty();
253 } 231 }
254 232
255 void FastUnloadController::CancelWindowClose() { 233 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) { 234 if (tab_needing_before_unload_ack_ != NULL) {
260 CoreTabHelper* core_tab_helper = 235 CoreTabHelper* core_tab_helper =
261 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_); 236 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_);
262 core_tab_helper->OnCloseCanceled(); 237 core_tab_helper->OnCloseCanceled();
263 DevToolsWindow::OnPageCloseCanceled(tab_needing_before_unload_ack_); 238 DevToolsWindow::OnPageCloseCanceled(tab_needing_before_unload_ack_);
264 tab_needing_before_unload_ack_ = NULL; 239 tab_needing_before_unload_ack_ = NULL;
265 } 240 }
241 }
242
243 void FastUnloadController::CancelWindowClose() {
244 // Closing of window can be canceled from a beforeunload handler.
245 DCHECK(is_attempting_to_close_browser_);
246 tabs_needing_before_unload_.clear();
247 CancelTabNeedingBeforeUnloadAck();
266 for (WebContentsSet::iterator it = tabs_needing_unload_.begin(); 248 for (WebContentsSet::iterator it = tabs_needing_unload_.begin();
267 it != tabs_needing_unload_.end(); it++) { 249 it != tabs_needing_unload_.end(); it++) {
268 content::WebContents* contents = *it; 250 content::WebContents* contents = *it;
269 251
270 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 252 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
271 core_tab_helper->OnCloseCanceled(); 253 core_tab_helper->OnCloseCanceled();
272 DevToolsWindow::OnPageCloseCanceled(contents); 254 DevToolsWindow::OnPageCloseCanceled(contents);
273 } 255 }
274 tabs_needing_unload_.clear(); 256 tabs_needing_unload_.clear();
275 257
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 tabs_needing_unload_ack_.insert(contents); 357 tabs_needing_unload_ack_.insert(contents);
376 browser_->tab_strip_model()->DetachWebContentsAt(index); 358 browser_->tab_strip_model()->DetachWebContentsAt(index);
377 contents->SetDelegate(detached_delegate_.get()); 359 contents->SetDelegate(detached_delegate_.get());
378 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 360 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
379 core_tab_helper->OnUnloadDetachedStarted(); 361 core_tab_helper->OnUnloadDetachedStarted();
380 return true; 362 return true;
381 } 363 }
382 return false; 364 return false;
383 } 365 }
384 366
385 void FastUnloadController::ProcessPendingTabs() { 367 void FastUnloadController::ProcessPendingTabs(bool skip_beforeunload) {
386 if (!is_attempting_to_close_browser_) { 368 if (!is_attempting_to_close_browser_) {
387 // Because we might invoke this after a delay it's possible for the value of 369 // 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 370 // is_attempting_to_close_browser_ to have changed since we scheduled the
389 // task. 371 // task.
390 return; 372 return;
391 } 373 }
392 374
393 if (tab_needing_before_unload_ack_ != NULL) { 375 if (tab_needing_before_unload_ack_ != NULL) {
394 // Wait for |BeforeUnloadFired| before proceeding. 376 if (skip_beforeunload) {
395 return; 377 // Cancel and skip the ongoing before unload event.
378 tabs_needing_before_unload_.insert(tab_needing_before_unload_ack_);
379 CancelTabNeedingBeforeUnloadAck();
380 } else {
381 // Wait for |BeforeUnloadFired| before proceeding.
382 return;
383 }
396 } 384 }
397 385
398 // Process a beforeunload handler. 386 // Process a beforeunload handler.
399 if (!tabs_needing_before_unload_.empty()) { 387 if (!tabs_needing_before_unload_.empty()) {
400 WebContentsSet::iterator it = tabs_needing_before_unload_.begin(); 388 if (skip_beforeunload) {
401 content::WebContents* contents = *it; 389 tabs_needing_unload_.insert(tabs_needing_before_unload_.begin(),
402 tabs_needing_before_unload_.erase(it); 390 tabs_needing_before_unload_.end());
403 // Null check render_view_host here as this gets called on a PostTask and 391 tabs_needing_before_unload_.clear();
404 // the tab's render_view_host may have been nulled out. 392 } else {
405 if (contents->GetRenderViewHost()) { 393 WebContentsSet::iterator it = tabs_needing_before_unload_.begin();
406 tab_needing_before_unload_ack_ = contents; 394 content::WebContents* contents = *it;
395 tabs_needing_before_unload_.erase(it);
396 // Null check render_view_host here as this gets called on a PostTask and
397 // the tab's render_view_host may have been nulled out.
398 if (contents->GetRenderViewHost()) {
399 tab_needing_before_unload_ack_ = contents;
407 400
408 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); 401 CoreTabHelper* core_tab_helper =
409 core_tab_helper->OnCloseStarted(); 402 CoreTabHelper::FromWebContents(contents);
403 core_tab_helper->OnCloseStarted();
410 404
411 // If there's a devtools window attached to |contents|, 405 // If there's a devtools window attached to |contents|,
412 // we would like devtools to call its own beforeunload handlers first, 406 // we would like devtools to call its own beforeunload handlers first,
413 // and then call beforeunload handlers for |contents|. 407 // and then call beforeunload handlers for |contents|.
414 // See DevToolsWindow::InterceptPageBeforeUnload for details. 408 // See DevToolsWindow::InterceptPageBeforeUnload for details.
415 if (!DevToolsWindow::InterceptPageBeforeUnload(contents)) 409 if (!DevToolsWindow::InterceptPageBeforeUnload(contents))
416 contents->DispatchBeforeUnload(); 410 contents->DispatchBeforeUnload();
417 } else { 411 } else {
418 ProcessPendingTabs(); 412 ProcessPendingTabs(skip_beforeunload);
413 }
414 return;
419 } 415 }
416 }
417
418 if (is_calling_before_unload_handlers()) {
419 if (!skip_beforeunload)
420 on_close_confirmed_.Run(true);
420 return; 421 return;
421 } 422 }
422 423
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.) 424 // Process all the unload handlers. (The beforeunload handlers have finished.)
428 if (!tabs_needing_unload_.empty()) { 425 if (!tabs_needing_unload_.empty()) {
429 browser_->OnWindowClosing(); 426 browser_->OnWindowClosing();
430 427
431 // Run unload handlers detached since no more interaction is possible. 428 // Run unload handlers detached since no more interaction is possible.
432 WebContentsSet::iterator it = tabs_needing_unload_.begin(); 429 WebContentsSet::iterator it = tabs_needing_unload_.begin();
433 while (it != tabs_needing_unload_.end()) { 430 while (it != tabs_needing_unload_.end()) {
434 WebContentsSet::iterator current = it++; 431 WebContentsSet::iterator current = it++;
435 content::WebContents* contents = *current; 432 content::WebContents* contents = *current;
436 tabs_needing_unload_.erase(current); 433 tabs_needing_unload_.erase(current);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
486 483
487 if (tabs_needing_before_unload_.erase(contents) > 0 || 484 if (tabs_needing_before_unload_.erase(contents) > 0 ||
488 tabs_needing_unload_.erase(contents) > 0) { 485 tabs_needing_unload_.erase(contents) > 0) {
489 if (tab_needing_before_unload_ack_ == NULL) 486 if (tab_needing_before_unload_ack_ == NULL)
490 PostTaskForProcessPendingTabs(); 487 PostTaskForProcessPendingTabs();
491 } 488 }
492 } 489 }
493 490
494 void FastUnloadController::PostTaskForProcessPendingTabs() { 491 void FastUnloadController::PostTaskForProcessPendingTabs() {
495 base::ThreadTaskRunnerHandle::Get()->PostTask( 492 base::ThreadTaskRunnerHandle::Get()->PostTask(
496 FROM_HERE, base::Bind(&FastUnloadController::ProcessPendingTabs, 493 FROM_HERE,
497 weak_factory_.GetWeakPtr())); 494 base::Bind(&FastUnloadController::ProcessPendingTabs,
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