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_(new UnloadControllerWebContentsDelegate()), |
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 Loading... |
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 Loading... |
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_before_unload_event, |
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_before_unload_event); |
216 return true; | 194 return true; |
217 } | 195 } |
218 | 196 |
219 void FastUnloadController::ResetBeforeUnloadHandlers() { | 197 void FastUnloadController::ResetCloseWindow() { |
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 Loading... |
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 Loading... |
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_before_unload_event) { |
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_before_unload_event) { |
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_before_unload_event) { |
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_before_unload_event); |
| 413 } |
| 414 return; |
419 } | 415 } |
420 return; | |
421 } | 416 } |
422 | 417 |
423 if (is_calling_before_unload_handlers()) { | 418 if (is_calling_before_unload_handlers()) { |
424 on_close_confirmed_.Run(true); | 419 on_close_confirmed_.Run(true); |
425 return; | 420 return; |
426 } | 421 } |
| 422 |
427 // Process all the unload handlers. (The beforeunload handlers have finished.) | 423 // Process all the unload handlers. (The beforeunload handlers have finished.) |
428 if (!tabs_needing_unload_.empty()) { | 424 if (!tabs_needing_unload_.empty()) { |
429 browser_->OnWindowClosing(); | 425 browser_->OnWindowClosing(); |
430 | 426 |
431 // Run unload handlers detached since no more interaction is possible. | 427 // Run unload handlers detached since no more interaction is possible. |
432 WebContentsSet::iterator it = tabs_needing_unload_.begin(); | 428 WebContentsSet::iterator it = tabs_needing_unload_.begin(); |
433 while (it != tabs_needing_unload_.end()) { | 429 while (it != tabs_needing_unload_.end()) { |
434 WebContentsSet::iterator current = it++; | 430 WebContentsSet::iterator current = it++; |
435 content::WebContents* contents = *current; | 431 content::WebContents* contents = *current; |
436 tabs_needing_unload_.erase(current); | 432 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 || | 483 if (tabs_needing_before_unload_.erase(contents) > 0 || |
488 tabs_needing_unload_.erase(contents) > 0) { | 484 tabs_needing_unload_.erase(contents) > 0) { |
489 if (tab_needing_before_unload_ack_ == NULL) | 485 if (tab_needing_before_unload_ack_ == NULL) |
490 PostTaskForProcessPendingTabs(); | 486 PostTaskForProcessPendingTabs(); |
491 } | 487 } |
492 } | 488 } |
493 | 489 |
494 void FastUnloadController::PostTaskForProcessPendingTabs() { | 490 void FastUnloadController::PostTaskForProcessPendingTabs() { |
495 base::ThreadTaskRunnerHandle::Get()->PostTask( | 491 base::ThreadTaskRunnerHandle::Get()->PostTask( |
496 FROM_HERE, base::Bind(&FastUnloadController::ProcessPendingTabs, | 492 FROM_HERE, base::Bind(&FastUnloadController::ProcessPendingTabs, |
497 weak_factory_.GetWeakPtr())); | 493 weak_factory_.GetWeakPtr(), false)); |
498 } | 494 } |
499 | 495 |
500 } // namespace chrome | 496 } // namespace chrome |
OLD | NEW |