| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/unload_controller.h" | 5 #include "chrome/browser/ui/unload_controller.h" |
| 6 | 6 |
| 7 #include "base/location.h" | 7 #include "base/location.h" |
| 8 #include "base/single_thread_task_runner.h" | 8 #include "base/single_thread_task_runner.h" |
| 9 #include "base/threading/thread_task_runner_handle.h" | 9 #include "base/threading/thread_task_runner_handle.h" |
| 10 #include "chrome/browser/chrome_notification_types.h" | 10 #include "chrome/browser/chrome_notification_types.h" |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 if (!proceed) { | 113 if (!proceed) { |
| 114 CancelWindowClose(); | 114 CancelWindowClose(); |
| 115 contents->SetClosedByUserGesture(false); | 115 contents->SetClosedByUserGesture(false); |
| 116 return false; | 116 return false; |
| 117 } | 117 } |
| 118 | 118 |
| 119 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { | 119 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { |
| 120 // Now that beforeunload has fired, put the tab on the queue to fire | 120 // Now that beforeunload has fired, put the tab on the queue to fire |
| 121 // unload. | 121 // unload. |
| 122 tabs_needing_unload_fired_.insert(contents); | 122 tabs_needing_unload_fired_.insert(contents); |
| 123 ProcessPendingTabs(); | 123 ProcessPendingTabs(false); |
| 124 // We want to handle firing the unload event ourselves since we want to | 124 // We want to handle firing the unload event ourselves since we want to |
| 125 // fire all the beforeunload events before attempting to fire the unload | 125 // fire all the beforeunload events before attempting to fire the unload |
| 126 // events should the user cancel closing the browser. | 126 // events should the user cancel closing the browser. |
| 127 return false; | 127 return false; |
| 128 } | 128 } |
| 129 | 129 |
| 130 return true; | 130 return true; |
| 131 } | 131 } |
| 132 | 132 |
| 133 bool UnloadController::ShouldCloseWindow() { | 133 bool UnloadController::ShouldCloseWindow() { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 156 // return false. | 156 // return false. |
| 157 // 4. Otherwise: return true. | 157 // 4. Otherwise: return true. |
| 158 is_attempting_to_close_browser_ = true; | 158 is_attempting_to_close_browser_ = true; |
| 159 // Cases 1 and 4. | 159 // Cases 1 and 4. |
| 160 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); | 160 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); |
| 161 if (need_beforeunload_fired == is_calling_before_unload_handlers()) | 161 if (need_beforeunload_fired == is_calling_before_unload_handlers()) |
| 162 return !need_beforeunload_fired; | 162 return !need_beforeunload_fired; |
| 163 | 163 |
| 164 // Cases 2 and 3. | 164 // Cases 2 and 3. |
| 165 on_close_confirmed_.Reset(); | 165 on_close_confirmed_.Reset(); |
| 166 ProcessPendingTabs(); | 166 ProcessPendingTabs(false); |
| 167 return false; | 167 return false; |
| 168 } | 168 } |
| 169 | 169 |
| 170 bool UnloadController::CallBeforeUnloadHandlers( | 170 bool UnloadController::TryToCloseWindow( |
| 171 bool skip_beforeunload, |
| 171 const base::Callback<void(bool)>& on_close_confirmed) { | 172 const base::Callback<void(bool)>& on_close_confirmed) { |
| 172 // The devtools browser gets its beforeunload events as the results of | 173 // The devtools browser gets its beforeunload events as the results of |
| 173 // intercepting events from the inspected tab, so don't send them here as | 174 // intercepting events from the inspected tab, so don't send them here as |
| 174 // well. | 175 // well. |
| 175 if (browser_->is_devtools() || HasCompletedUnloadProcessing() || | 176 if (browser_->is_devtools() || HasCompletedUnloadProcessing() || |
| 176 !TabsNeedBeforeUnloadFired()) | 177 !TabsNeedBeforeUnloadFired()) |
| 177 return false; | 178 return false; |
| 178 | 179 |
| 179 is_attempting_to_close_browser_ = true; | 180 is_attempting_to_close_browser_ = true; |
| 180 on_close_confirmed_ = on_close_confirmed; | 181 on_close_confirmed_ = on_close_confirmed; |
| 181 | 182 |
| 182 ProcessPendingTabs(); | 183 ProcessPendingTabs(skip_beforeunload); |
| 183 return true; | 184 return !skip_beforeunload; |
| 184 } | 185 } |
| 185 | 186 |
| 186 void UnloadController::ResetBeforeUnloadHandlers() { | 187 void UnloadController::ResetTryToCloseWindow() { |
| 187 if (!is_calling_before_unload_handlers()) | 188 if (!is_calling_before_unload_handlers()) |
| 188 return; | 189 return; |
| 189 CancelWindowClose(); | 190 CancelWindowClose(); |
| 190 } | 191 } |
| 191 | 192 |
| 192 bool UnloadController::TabsNeedBeforeUnloadFired() { | 193 bool UnloadController::TabsNeedBeforeUnloadFired() { |
| 193 if (tabs_needing_before_unload_fired_.empty()) { | 194 if (tabs_needing_before_unload_fired_.empty()) { |
| 194 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { | 195 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { |
| 195 content::WebContents* contents = | 196 content::WebContents* contents = |
| 196 browser_->tab_strip_model()->GetWebContentsAt(i); | 197 browser_->tab_strip_model()->GetWebContentsAt(i); |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 } | 284 } |
| 284 | 285 |
| 285 void UnloadController::TabDetachedImpl(content::WebContents* contents) { | 286 void UnloadController::TabDetachedImpl(content::WebContents* contents) { |
| 286 if (is_attempting_to_close_browser_) | 287 if (is_attempting_to_close_browser_) |
| 287 ClearUnloadState(contents, false); | 288 ClearUnloadState(contents, false); |
| 288 registrar_.Remove(this, | 289 registrar_.Remove(this, |
| 289 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, | 290 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, |
| 290 content::Source<content::WebContents>(contents)); | 291 content::Source<content::WebContents>(contents)); |
| 291 } | 292 } |
| 292 | 293 |
| 293 void UnloadController::ProcessPendingTabs() { | 294 void UnloadController::ProcessPendingTabs(bool skip_beforeunload) { |
| 294 // Cancel posted/queued ProcessPendingTabs task if there is any. | 295 // Cancel posted/queued ProcessPendingTabs task if there is any. |
| 295 weak_factory_.InvalidateWeakPtrs(); | 296 weak_factory_.InvalidateWeakPtrs(); |
| 296 | 297 |
| 297 if (!is_attempting_to_close_browser_) { | 298 if (!is_attempting_to_close_browser_) { |
| 298 // Because we might invoke this after a delay it's possible for the value of | 299 // Because we might invoke this after a delay it's possible for the value of |
| 299 // is_attempting_to_close_browser_ to have changed since we scheduled the | 300 // is_attempting_to_close_browser_ to have changed since we scheduled the |
| 300 // task. | 301 // task. |
| 301 return; | 302 return; |
| 302 } | 303 } |
| 303 | 304 |
| 304 if (HasCompletedUnloadProcessing() && !TabsNeedBeforeUnloadFired()) { | 305 if (HasCompletedUnloadProcessing() && !TabsNeedBeforeUnloadFired()) { |
| 305 // We've finished all the unload events and can proceed to close the | 306 // We've finished all the unload events and can proceed to close the |
| 306 // browser. | 307 // browser. |
| 307 browser_->OnWindowClosing(); | 308 browser_->OnWindowClosing(); |
| 308 return; | 309 return; |
| 309 } | 310 } |
| 310 | 311 |
| 312 if (skip_beforeunload) { |
| 313 tabs_needing_unload_fired_.insert(tabs_needing_before_unload_fired_.begin(), |
| 314 tabs_needing_before_unload_fired_.end()); |
| 315 tabs_needing_before_unload_fired_.clear(); |
| 316 } |
| 317 |
| 311 // Process beforeunload tabs first. When that queue is empty, process | 318 // Process beforeunload tabs first. When that queue is empty, process |
| 312 // unload tabs. | 319 // unload tabs. |
| 313 if (!tabs_needing_before_unload_fired_.empty()) { | 320 if (!tabs_needing_before_unload_fired_.empty()) { |
| 314 content::WebContents* web_contents = | 321 content::WebContents* web_contents = |
| 315 *(tabs_needing_before_unload_fired_.begin()); | 322 *(tabs_needing_before_unload_fired_.begin()); |
| 316 // Null check render_view_host here as this gets called on a PostTask and | 323 // Null check render_view_host here as this gets called on a PostTask and |
| 317 // the tab's render_view_host may have been nulled out. | 324 // the tab's render_view_host may have been nulled out. |
| 318 if (web_contents->GetRenderViewHost()) { | 325 if (web_contents->GetRenderViewHost()) { |
| 319 // If there's a devtools window attached to |web_contents|, | 326 // If there's a devtools window attached to |web_contents|, |
| 320 // we would like devtools to call its own beforeunload handlers first, | 327 // we would like devtools to call its own beforeunload handlers first, |
| 321 // and then call beforeunload handlers for |web_contents|. | 328 // and then call beforeunload handlers for |web_contents|. |
| 322 // See DevToolsWindow::InterceptPageBeforeUnload for details. | 329 // See DevToolsWindow::InterceptPageBeforeUnload for details. |
| 323 if (!DevToolsWindow::InterceptPageBeforeUnload(web_contents)) | 330 if (!DevToolsWindow::InterceptPageBeforeUnload(web_contents)) |
| 324 web_contents->DispatchBeforeUnload(); | 331 web_contents->DispatchBeforeUnload(); |
| 325 } else { | 332 } else { |
| 326 ClearUnloadState(web_contents, true); | 333 ClearUnloadState(web_contents, true); |
| 327 } | 334 } |
| 328 } else if (is_calling_before_unload_handlers()) { | 335 } else if (is_calling_before_unload_handlers()) { |
| 329 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; | 336 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; |
| 330 // Reset |on_close_confirmed_| in case the callback tests | 337 // Reset |on_close_confirmed_| in case the callback tests |
| 331 // |is_calling_before_unload_handlers()|, we want to return that calling | 338 // |is_calling_before_unload_handlers()|, we want to return that calling |
| 332 // is complete. | 339 // is complete. |
| 333 if (tabs_needing_unload_fired_.empty()) | 340 if (tabs_needing_unload_fired_.empty()) |
| 334 on_close_confirmed_.Reset(); | 341 on_close_confirmed_.Reset(); |
| 335 on_close_confirmed.Run(true); | 342 if (!skip_beforeunload) |
| 343 on_close_confirmed.Run(true); |
| 336 } else if (!tabs_needing_unload_fired_.empty()) { | 344 } else if (!tabs_needing_unload_fired_.empty()) { |
| 337 // We've finished firing all beforeunload events and can proceed with unload | 345 // We've finished firing all beforeunload events and can proceed with unload |
| 338 // events. | 346 // events. |
| 339 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting | 347 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting |
| 340 // somewhere around here so that we have accurate measurements of shutdown | 348 // somewhere around here so that we have accurate measurements of shutdown |
| 341 // time. | 349 // time. |
| 342 // TODO(ojan): We can probably fire all the unload events in parallel and | 350 // TODO(ojan): We can probably fire all the unload events in parallel and |
| 343 // get a perf benefit from that in the cases where the tab hangs in it's | 351 // get a perf benefit from that in the cases where the tab hangs in it's |
| 344 // unload handler or takes a long time to page in. | 352 // unload handler or takes a long time to page in. |
| 345 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); | 353 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 373 } | 381 } |
| 374 return false; | 382 return false; |
| 375 } | 383 } |
| 376 | 384 |
| 377 void UnloadController::ClearUnloadState(content::WebContents* web_contents, | 385 void UnloadController::ClearUnloadState(content::WebContents* web_contents, |
| 378 bool process_now) { | 386 bool process_now) { |
| 379 if (is_attempting_to_close_browser_) { | 387 if (is_attempting_to_close_browser_) { |
| 380 RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents); | 388 RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents); |
| 381 RemoveFromSet(&tabs_needing_unload_fired_, web_contents); | 389 RemoveFromSet(&tabs_needing_unload_fired_, web_contents); |
| 382 if (process_now) { | 390 if (process_now) { |
| 383 ProcessPendingTabs(); | 391 ProcessPendingTabs(false); |
| 384 } else { | 392 } else { |
| 385 // Do not post a new task if there is already any. | 393 // Do not post a new task if there is already any. |
| 386 if (weak_factory_.HasWeakPtrs()) | 394 if (weak_factory_.HasWeakPtrs()) |
| 387 return; | 395 return; |
| 388 base::ThreadTaskRunnerHandle::Get()->PostTask( | 396 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 389 FROM_HERE, base::Bind(&UnloadController::ProcessPendingTabs, | 397 FROM_HERE, base::Bind(&UnloadController::ProcessPendingTabs, |
| 390 weak_factory_.GetWeakPtr())); | 398 weak_factory_.GetWeakPtr(), false)); |
| 391 } | 399 } |
| 392 } | 400 } |
| 393 } | 401 } |
| 394 | 402 |
| 395 } // namespace chrome | 403 } // namespace chrome |
| OLD | NEW |