OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |