Chromium Code Reviews| 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/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/message_loop/message_loop.h" | 8 #include "base/message_loop/message_loop.h" |
| 9 #include "chrome/browser/chrome_notification_types.h" | 9 #include "chrome/browser/chrome_notification_types.h" |
| 10 #include "chrome/browser/devtools/devtools_window.h" | 10 #include "chrome/browser/devtools/devtools_window.h" |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 64 } | 64 } |
| 65 | 65 |
| 66 bool FastUnloadController::CanCloseContents(content::WebContents* contents) { | 66 bool FastUnloadController::CanCloseContents(content::WebContents* contents) { |
| 67 // Don't try to close the tab when the whole browser is being closed, since | 67 // Don't try to close the tab when the whole browser is being closed, since |
| 68 // that avoids the fast shutdown path where we just kill all the renderers. | 68 // that avoids the fast shutdown path where we just kill all the renderers. |
| 69 return !is_attempting_to_close_browser_ || | 69 return !is_attempting_to_close_browser_ || |
| 70 is_calling_before_unload_handlers(); | 70 is_calling_before_unload_handlers(); |
| 71 } | 71 } |
| 72 | 72 |
| 73 // static | 73 // static |
| 74 bool FastUnloadController::CanFastShutdownWebContents( | |
| 75 content::WebContents* contents) { | |
|
jeremy
2013/11/07 14:18:34
Can you please provide a comment on what fastshutd
lushnikov
2013/11/07 17:18:10
Do you think this one is needed? Looks like it cou
jeremy
2013/11/10 12:50:29
There are way too many moving parts here and for s
| |
| 76 return DevToolsWindow::GetInstanceForInspectedRenderViewHost( | |
| 77 contents->GetRenderViewHost()) == NULL; | |
| 78 } | |
| 79 | |
| 80 // static | |
| 74 bool FastUnloadController::RunUnloadEventsHelper( | 81 bool FastUnloadController::RunUnloadEventsHelper( |
| 75 content::WebContents* contents) { | 82 content::WebContents* contents) { |
| 83 // If there's a devtools window attached to the web contents, | |
| 84 // then we would like to run its beforeunload handlers first. | |
|
jeremy
2013/11/07 14:18:34
Please point to the comment that explains why.
lushnikov
2013/11/07 17:18:10
Done.
| |
| 85 if (DevToolsWindow::InterceptPageBeforeUnload(contents)) { | |
|
jeremy
2013/11/10 12:50:29
2 things are unclear from this callsite:
* Does In
| |
| 86 return true; | |
| 87 } | |
| 76 // If the WebContents is not connected yet, then there's no unload | 88 // If the WebContents is not connected yet, then there's no unload |
| 77 // handler we can fire even if the WebContents has an unload listener. | 89 // handler we can fire even if the WebContents has an unload listener. |
| 78 // One case where we hit this is in a tab that has an infinite loop | 90 // One case where we hit this is in a tab that has an infinite loop |
| 79 // before load. | 91 // before load. |
| 80 if (contents->NeedToFireBeforeUnload()) { | 92 if (contents->NeedToFireBeforeUnload()) { |
| 81 // If the page has unload listeners, then we tell the renderer to fire | 93 // If the page has unload listeners, then we tell the renderer to fire |
| 82 // them. Once they have fired, we'll get a message back saying whether | 94 // them. Once they have fired, we'll get a message back saying whether |
| 83 // to proceed closing the page or not, which sends us back to this method | 95 // to proceed closing the page or not, which sends us back to this method |
| 84 // with the NeedToFireBeforeUnload bit cleared. | 96 // with the NeedToFireBeforeUnload bit cleared. |
| 85 contents->GetRenderViewHost()->FirePageBeforeUnload(false); | 97 contents->GetRenderViewHost()->FirePageBeforeUnload(false); |
| 86 return true; | 98 return true; |
| 87 } | 99 } |
| 88 return false; | 100 return false; |
| 89 } | 101 } |
| 90 | 102 |
| 91 bool FastUnloadController::BeforeUnloadFired(content::WebContents* contents, | 103 bool FastUnloadController::BeforeUnloadFired(content::WebContents* contents, |
| 92 bool proceed) { | 104 bool proceed) { |
| 105 if (!proceed) | |
| 106 DevToolsWindow::PageClosingCanceled(contents); | |
| 107 | |
| 93 if (!is_attempting_to_close_browser_) { | 108 if (!is_attempting_to_close_browser_) { |
| 94 if (!proceed) { | 109 if (!proceed) { |
| 95 contents->SetClosedByUserGesture(false); | 110 contents->SetClosedByUserGesture(false); |
| 96 } else { | 111 } else { |
| 97 // No more dialogs are possible, so remove the tab and finish | 112 // No more dialogs are possible, so remove the tab and finish |
| 98 // running unload listeners asynchrounously. | 113 // running unload listeners asynchrounously. |
| 99 browser_->tab_strip_model()->delegate()->CreateHistoricalTab(contents); | 114 browser_->tab_strip_model()->delegate()->CreateHistoricalTab(contents); |
| 100 DetachWebContents(contents); | 115 DetachWebContents(contents); |
| 101 } | 116 } |
| 102 return proceed; | 117 return proceed; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 119 return false; | 134 return false; |
| 120 } | 135 } |
| 121 | 136 |
| 122 return true; | 137 return true; |
| 123 } | 138 } |
| 124 | 139 |
| 125 bool FastUnloadController::ShouldCloseWindow() { | 140 bool FastUnloadController::ShouldCloseWindow() { |
| 126 if (HasCompletedUnloadProcessing()) | 141 if (HasCompletedUnloadProcessing()) |
| 127 return true; | 142 return true; |
| 128 | 143 |
| 144 if (browser_->is_devtools() && | |
| 145 DevToolsWindow::ShouldCloseDevToolsBrowser(browser_)) { | |
| 146 return true; | |
| 147 } | |
| 148 | |
| 129 // The behavior followed here varies based on the current phase of the | 149 // The behavior followed here varies based on the current phase of the |
| 130 // operation and whether a batched shutdown is in progress. | 150 // operation and whether a batched shutdown is in progress. |
| 131 // | 151 // |
| 132 // If there are tabs with outstanding beforeunload handlers: | 152 // If there are tabs with outstanding beforeunload handlers: |
| 133 // 1. If a batched shutdown is in progress: return false. | 153 // 1. If a batched shutdown is in progress: return false. |
| 134 // This is to prevent interference with batched shutdown already in | 154 // This is to prevent interference with batched shutdown already in |
| 135 // progress. | 155 // progress. |
| 136 // 2. Otherwise: start sending beforeunload events and return false. | 156 // 2. Otherwise: start sending beforeunload events and return false. |
| 137 // | 157 // |
| 138 // Otherwise, If there are no tabs with outstanding beforeunload handlers: | 158 // Otherwise, If there are no tabs with outstanding beforeunload handlers: |
| 139 // 3. If a batched shutdown is in progress: start sending unload events and | 159 // 3. If a batched shutdown is in progress: start sending unload events and |
| 140 // return false. | 160 // return false. |
| 141 // 4. Otherwise: return true. | 161 // 4. Otherwise: return true. |
| 142 is_attempting_to_close_browser_ = true; | 162 is_attempting_to_close_browser_ = true; |
| 143 // Cases 1 and 4. | 163 // Cases 1 and 4. |
| 144 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); | 164 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); |
| 145 if (need_beforeunload_fired == is_calling_before_unload_handlers()) | 165 if (need_beforeunload_fired == is_calling_before_unload_handlers()) |
| 146 return !need_beforeunload_fired; | 166 return !need_beforeunload_fired; |
| 147 | 167 |
| 148 // Cases 2 and 3. | 168 // Cases 2 and 3. |
| 149 on_close_confirmed_.Reset(); | 169 on_close_confirmed_.Reset(); |
| 150 ProcessPendingTabs(); | 170 ProcessPendingTabs(); |
| 151 return false; | 171 return false; |
| 152 } | 172 } |
| 153 | 173 |
| 154 bool FastUnloadController::CallBeforeUnloadHandlers( | 174 bool FastUnloadController::CallBeforeUnloadHandlers( |
| 155 const base::Callback<void(bool)>& on_close_confirmed) { | 175 const base::Callback<void(bool)>& on_close_confirmed) { |
| 156 if (!TabsNeedBeforeUnloadFired()) | 176 // DevTools browser will get its beforeunload events triggered on |
| 177 // inspected tab closing | |
|
jeremy
2013/11/07 14:18:34
will get -> gets
lushnikov
2013/11/07 17:18:10
Done.
| |
| 178 if (browser_->is_devtools() || !TabsNeedBeforeUnloadFired()) | |
| 157 return false; | 179 return false; |
| 158 | 180 |
| 159 on_close_confirmed_ = on_close_confirmed; | 181 on_close_confirmed_ = on_close_confirmed; |
| 160 is_attempting_to_close_browser_ = true; | 182 is_attempting_to_close_browser_ = true; |
| 161 ProcessPendingTabs(); | 183 ProcessPendingTabs(); |
| 162 return true; | 184 return true; |
| 163 } | 185 } |
| 164 | 186 |
| 165 void FastUnloadController::ResetBeforeUnloadHandlers() { | 187 void FastUnloadController::ResetBeforeUnloadHandlers() { |
| 166 if (!is_calling_before_unload_handlers()) | 188 if (!is_calling_before_unload_handlers()) |
| 167 return; | 189 return; |
| 168 CancelWindowClose(); | 190 CancelWindowClose(); |
| 169 } | 191 } |
| 170 | 192 |
| 171 bool FastUnloadController::TabsNeedBeforeUnloadFired() { | 193 bool FastUnloadController::TabsNeedBeforeUnloadFired() { |
| 172 if (!tabs_needing_before_unload_.empty() || | 194 if (!tabs_needing_before_unload_.empty() || |
| 173 tab_needing_before_unload_ack_ != NULL) | 195 tab_needing_before_unload_ack_ != NULL) |
| 174 return true; | 196 return true; |
| 175 | 197 |
| 176 if (!is_calling_before_unload_handlers() && !tabs_needing_unload_.empty()) | 198 if (!is_calling_before_unload_handlers() && !tabs_needing_unload_.empty()) |
| 177 return false; | 199 return false; |
| 178 | 200 |
| 179 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { | 201 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { |
| 180 content::WebContents* contents = | 202 content::WebContents* contents = |
| 181 browser_->tab_strip_model()->GetWebContentsAt(i); | 203 browser_->tab_strip_model()->GetWebContentsAt(i); |
| 182 if (!ContainsKey(tabs_needing_unload_, contents) && | 204 if (!ContainsKey(tabs_needing_unload_, contents) && |
| 183 !ContainsKey(tabs_needing_unload_ack_, contents) && | 205 !ContainsKey(tabs_needing_unload_ack_, contents) && |
| 184 tab_needing_before_unload_ack_ != contents && | 206 tab_needing_before_unload_ack_ != contents && |
| 185 contents->NeedToFireBeforeUnload()) | 207 (contents->NeedToFireBeforeUnload() || |
| 208 DevToolsWindow::NeedToFireBeforeUnload(contents))) | |
|
jeremy
2013/11/07 14:18:34
This expression is already kind of crazy - can we
lushnikov
2013/11/07 17:18:10
Done.
| |
| 186 tabs_needing_before_unload_.insert(contents); | 209 tabs_needing_before_unload_.insert(contents); |
| 187 } | 210 } |
| 188 return !tabs_needing_before_unload_.empty(); | 211 return !tabs_needing_before_unload_.empty(); |
| 189 } | 212 } |
| 190 | 213 |
| 191 //////////////////////////////////////////////////////////////////////////////// | 214 //////////////////////////////////////////////////////////////////////////////// |
| 192 // FastUnloadController, content::NotificationObserver implementation: | 215 // FastUnloadController, content::NotificationObserver implementation: |
| 193 | 216 |
| 194 void FastUnloadController::Observe( | 217 void FastUnloadController::Observe( |
| 195 int type, | 218 int type, |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 content::WebContents* contents = *it; | 330 content::WebContents* contents = *it; |
| 308 tabs_needing_before_unload_.erase(it); | 331 tabs_needing_before_unload_.erase(it); |
| 309 // Null check render_view_host here as this gets called on a PostTask and | 332 // Null check render_view_host here as this gets called on a PostTask and |
| 310 // the tab's render_view_host may have been nulled out. | 333 // the tab's render_view_host may have been nulled out. |
| 311 if (contents->GetRenderViewHost()) { | 334 if (contents->GetRenderViewHost()) { |
| 312 tab_needing_before_unload_ack_ = contents; | 335 tab_needing_before_unload_ack_ = contents; |
| 313 | 336 |
| 314 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); | 337 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); |
| 315 core_tab_helper->OnCloseStarted(); | 338 core_tab_helper->OnCloseStarted(); |
| 316 | 339 |
| 317 contents->GetRenderViewHost()->FirePageBeforeUnload(false); | 340 if (!DevToolsWindow::InterceptPageBeforeUnload(contents)) |
|
jeremy
2013/11/07 14:18:34
I don't really think this method name conveys what
lushnikov
2013/11/07 17:18:10
Done.
| |
| 341 contents->GetRenderViewHost()->FirePageBeforeUnload(false); | |
| 318 } else { | 342 } else { |
| 319 ProcessPendingTabs(); | 343 ProcessPendingTabs(); |
| 320 } | 344 } |
| 321 return; | 345 return; |
| 322 } | 346 } |
| 323 | 347 |
| 324 if (is_calling_before_unload_handlers()) { | 348 if (is_calling_before_unload_handlers()) { |
| 325 on_close_confirmed_.Run(true); | 349 on_close_confirmed_.Run(true); |
| 326 return; | 350 return; |
| 327 } | 351 } |
| 328 | |
| 329 // Process all the unload handlers. (The beforeunload handlers have finished.) | 352 // Process all the unload handlers. (The beforeunload handlers have finished.) |
| 330 if (!tabs_needing_unload_.empty()) { | 353 if (!tabs_needing_unload_.empty()) { |
| 331 browser_->OnWindowClosing(); | 354 browser_->OnWindowClosing(); |
| 332 | 355 |
| 333 // Run unload handlers detached since no more interaction is possible. | 356 // Run unload handlers detached since no more interaction is possible. |
| 334 WebContentsSet::iterator it = tabs_needing_unload_.begin(); | 357 WebContentsSet::iterator it = tabs_needing_unload_.begin(); |
| 335 while (it != tabs_needing_unload_.end()) { | 358 while (it != tabs_needing_unload_.end()) { |
| 336 WebContentsSet::iterator current = it++; | 359 WebContentsSet::iterator current = it++; |
| 337 content::WebContents* contents = *current; | 360 content::WebContents* contents = *current; |
| 338 tabs_needing_unload_.erase(current); | 361 tabs_needing_unload_.erase(current); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 376 tab_needing_before_unload_ack_ == NULL && | 399 tab_needing_before_unload_ack_ == NULL && |
| 377 tabs_needing_unload_.empty() && | 400 tabs_needing_unload_.empty() && |
| 378 tabs_needing_unload_ack_.empty(); | 401 tabs_needing_unload_ack_.empty(); |
| 379 } | 402 } |
| 380 | 403 |
| 381 void FastUnloadController::CancelWindowClose() { | 404 void FastUnloadController::CancelWindowClose() { |
| 382 // Closing of window can be canceled from a beforeunload handler. | 405 // Closing of window can be canceled from a beforeunload handler. |
| 383 DCHECK(is_attempting_to_close_browser_); | 406 DCHECK(is_attempting_to_close_browser_); |
| 384 tabs_needing_before_unload_.clear(); | 407 tabs_needing_before_unload_.clear(); |
| 385 if (tab_needing_before_unload_ack_ != NULL) { | 408 if (tab_needing_before_unload_ack_ != NULL) { |
| 386 | |
| 387 CoreTabHelper* core_tab_helper = | 409 CoreTabHelper* core_tab_helper = |
| 388 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_); | 410 CoreTabHelper::FromWebContents(tab_needing_before_unload_ack_); |
| 389 core_tab_helper->OnCloseCanceled(); | 411 core_tab_helper->OnCloseCanceled(); |
| 412 DevToolsWindow::PageClosingCanceled(tab_needing_before_unload_ack_); | |
|
jeremy
2013/11/07 14:18:34
*OnPageCloseCanceled() ?
lushnikov
2013/11/07 17:18:10
Done.
| |
| 390 tab_needing_before_unload_ack_ = NULL; | 413 tab_needing_before_unload_ack_ = NULL; |
| 391 } | 414 } |
| 392 for (WebContentsSet::iterator it = tabs_needing_unload_.begin(); | 415 for (WebContentsSet::iterator it = tabs_needing_unload_.begin(); |
| 393 it != tabs_needing_unload_.end(); it++) { | 416 it != tabs_needing_unload_.end(); it++) { |
| 394 content::WebContents* contents = *it; | 417 content::WebContents* contents = *it; |
| 395 | 418 |
| 396 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); | 419 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents); |
| 397 core_tab_helper->OnCloseCanceled(); | 420 core_tab_helper->OnCloseCanceled(); |
| 421 DevToolsWindow::PageClosingCanceled(contents); | |
| 398 } | 422 } |
| 399 tabs_needing_unload_.clear(); | 423 tabs_needing_unload_.clear(); |
| 400 | 424 |
| 401 // No need to clear tabs_needing_unload_ack_. Those tabs are already detached. | 425 // No need to clear tabs_needing_unload_ack_. Those tabs are already detached. |
| 402 | 426 |
| 403 if (is_calling_before_unload_handlers()) { | 427 if (is_calling_before_unload_handlers()) { |
| 404 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; | 428 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; |
| 405 on_close_confirmed_.Reset(); | 429 on_close_confirmed_.Reset(); |
| 406 on_close_confirmed.Run(false); | 430 on_close_confirmed.Run(false); |
| 407 } | 431 } |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 438 } | 462 } |
| 439 | 463 |
| 440 void FastUnloadController::PostTaskForProcessPendingTabs() { | 464 void FastUnloadController::PostTaskForProcessPendingTabs() { |
| 441 base::MessageLoop::current()->PostTask( | 465 base::MessageLoop::current()->PostTask( |
| 442 FROM_HERE, | 466 FROM_HERE, |
| 443 base::Bind(&FastUnloadController::ProcessPendingTabs, | 467 base::Bind(&FastUnloadController::ProcessPendingTabs, |
| 444 weak_factory_.GetWeakPtr())); | 468 weak_factory_.GetWeakPtr())); |
| 445 } | 469 } |
| 446 | 470 |
| 447 } // namespace chrome | 471 } // namespace chrome |
| OLD | NEW |