| 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" |
| 11 #include "chrome/browser/devtools/devtools_window.h" | 11 #include "chrome/browser/devtools/devtools_window.h" |
| 12 #include "chrome/browser/ui/browser.h" | 12 #include "chrome/browser/ui/browser.h" |
| 13 #include "chrome/browser/ui/browser_tabstrip.h" | 13 #include "chrome/browser/ui/browser_tabstrip.h" |
| 14 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 14 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 15 #include "chrome/browser/ui/unload_controller_web_contents_delegate.h" |
| 15 #include "content/public/browser/notification_service.h" | 16 #include "content/public/browser/notification_service.h" |
| 16 #include "content/public/browser/notification_source.h" | 17 #include "content/public/browser/notification_source.h" |
| 17 #include "content/public/browser/notification_types.h" | 18 #include "content/public/browser/notification_types.h" |
| 18 #include "content/public/browser/render_view_host.h" | 19 #include "content/public/browser/render_view_host.h" |
| 19 #include "content/public/browser/web_contents.h" | 20 #include "content/public/browser/web_contents.h" |
| 20 #include "extensions/features/features.h" | 21 #include "extensions/features/features.h" |
| 21 | 22 |
| 22 #if BUILDFLAG(ENABLE_EXTENSIONS) | 23 #if BUILDFLAG(ENABLE_EXTENSIONS) |
| 23 #include "extensions/browser/extension_registry.h" | 24 #include "extensions/browser/extension_registry.h" |
| 24 #include "extensions/common/constants.h" | 25 #include "extensions/common/constants.h" |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 if (!proceed) { | 114 if (!proceed) { |
| 114 CancelWindowClose(); | 115 CancelWindowClose(); |
| 115 contents->SetClosedByUserGesture(false); | 116 contents->SetClosedByUserGesture(false); |
| 116 return false; | 117 return false; |
| 117 } | 118 } |
| 118 | 119 |
| 119 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { | 120 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { |
| 120 // Now that beforeunload has fired, put the tab on the queue to fire | 121 // Now that beforeunload has fired, put the tab on the queue to fire |
| 121 // unload. | 122 // unload. |
| 122 tabs_needing_unload_fired_.insert(contents); | 123 tabs_needing_unload_fired_.insert(contents); |
| 123 ProcessPendingTabs(); | 124 ProcessPendingTabs(false); |
| 124 // We want to handle firing the unload event ourselves since we want to | 125 // 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 | 126 // fire all the beforeunload events before attempting to fire the unload |
| 126 // events should the user cancel closing the browser. | 127 // events should the user cancel closing the browser. |
| 127 return false; | 128 return false; |
| 128 } | 129 } |
| 129 | 130 |
| 130 return true; | 131 return true; |
| 131 } | 132 } |
| 132 | 133 |
| 133 bool UnloadController::ShouldCloseWindow() { | 134 bool UnloadController::ShouldCloseWindow() { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 156 // return false. | 157 // return false. |
| 157 // 4. Otherwise: return true. | 158 // 4. Otherwise: return true. |
| 158 is_attempting_to_close_browser_ = true; | 159 is_attempting_to_close_browser_ = true; |
| 159 // Cases 1 and 4. | 160 // Cases 1 and 4. |
| 160 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); | 161 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); |
| 161 if (need_beforeunload_fired == is_calling_before_unload_handlers()) | 162 if (need_beforeunload_fired == is_calling_before_unload_handlers()) |
| 162 return !need_beforeunload_fired; | 163 return !need_beforeunload_fired; |
| 163 | 164 |
| 164 // Cases 2 and 3. | 165 // Cases 2 and 3. |
| 165 on_close_confirmed_.Reset(); | 166 on_close_confirmed_.Reset(); |
| 166 ProcessPendingTabs(); | 167 ProcessPendingTabs(false); |
| 167 return false; | 168 return false; |
| 168 } | 169 } |
| 169 | 170 |
| 170 bool UnloadController::CallBeforeUnloadHandlers( | 171 bool UnloadController::TryToCloseWindow( |
| 172 bool skip_before_unload_event, |
| 171 const base::Callback<void(bool)>& on_close_confirmed) { | 173 const base::Callback<void(bool)>& on_close_confirmed) { |
| 172 // The devtools browser gets its beforeunload events as the results of | 174 // 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 | 175 // intercepting events from the inspected tab, so don't send them here as |
| 174 // well. | 176 // well. |
| 175 if (browser_->is_devtools() || HasCompletedUnloadProcessing() || | 177 if (browser_->is_devtools() || HasCompletedUnloadProcessing() || |
| 176 !TabsNeedBeforeUnloadFired()) | 178 !TabsNeedBeforeUnloadFired()) |
| 177 return false; | 179 return false; |
| 178 | 180 |
| 179 is_attempting_to_close_browser_ = true; | 181 is_attempting_to_close_browser_ = true; |
| 180 on_close_confirmed_ = on_close_confirmed; | 182 on_close_confirmed_ = on_close_confirmed; |
| 181 | 183 |
| 182 ProcessPendingTabs(); | 184 ProcessPendingTabs(skip_before_unload_event); |
| 183 return true; | 185 return true; |
| 184 } | 186 } |
| 185 | 187 |
| 186 void UnloadController::ResetBeforeUnloadHandlers() { | 188 void UnloadController::ResetCloseWindow() { |
| 187 if (!is_calling_before_unload_handlers()) | 189 if (!is_calling_before_unload_handlers()) |
| 188 return; | 190 return; |
| 189 CancelWindowClose(); | 191 CancelWindowClose(); |
| 190 } | 192 } |
| 191 | 193 |
| 192 bool UnloadController::TabsNeedBeforeUnloadFired() { | 194 bool UnloadController::TabsNeedBeforeUnloadFired() { |
| 193 if (tabs_needing_before_unload_fired_.empty()) { | 195 if (tabs_needing_before_unload_fired_.empty()) { |
| 194 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { | 196 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { |
| 195 content::WebContents* contents = | 197 content::WebContents* contents = |
| 196 browser_->tab_strip_model()->GetWebContentsAt(i); | 198 browser_->tab_strip_model()->GetWebContentsAt(i); |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 } | 285 } |
| 284 | 286 |
| 285 void UnloadController::TabDetachedImpl(content::WebContents* contents) { | 287 void UnloadController::TabDetachedImpl(content::WebContents* contents) { |
| 286 if (is_attempting_to_close_browser_) | 288 if (is_attempting_to_close_browser_) |
| 287 ClearUnloadState(contents, false); | 289 ClearUnloadState(contents, false); |
| 288 registrar_.Remove(this, | 290 registrar_.Remove(this, |
| 289 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, | 291 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, |
| 290 content::Source<content::WebContents>(contents)); | 292 content::Source<content::WebContents>(contents)); |
| 291 } | 293 } |
| 292 | 294 |
| 293 void UnloadController::ProcessPendingTabs() { | 295 void UnloadController::ProcessPendingTabs(bool skip_before_unload_event) { |
| 294 if (!is_attempting_to_close_browser_) { | 296 if (!is_attempting_to_close_browser_) { |
| 295 // Because we might invoke this after a delay it's possible for the value of | 297 // Because we might invoke this after a delay it's possible for the value of |
| 296 // is_attempting_to_close_browser_ to have changed since we scheduled the | 298 // is_attempting_to_close_browser_ to have changed since we scheduled the |
| 297 // task. | 299 // task. |
| 298 return; | 300 return; |
| 299 } | 301 } |
| 300 | 302 |
| 301 if (HasCompletedUnloadProcessing()) { | 303 if (HasCompletedUnloadProcessing()) { |
| 302 // We've finished all the unload events and can proceed to close the | 304 // We've finished all the unload events and can proceed to close the |
| 303 // browser. | 305 // browser. |
| 304 browser_->OnWindowClosing(); | 306 browser_->OnWindowClosing(); |
| 305 return; | 307 return; |
| 306 } | 308 } |
| 307 | 309 |
| 310 if (skip_before_unload_event) { |
| 311 tabs_needing_unload_fired_.insert(tabs_needing_before_unload_fired_.begin(), |
| 312 tabs_needing_before_unload_fired_.end()); |
| 313 if (!web_contents_delegate_) |
| 314 web_contents_delegate_.reset(new UnloadControllerWebContentsDelegate()); |
| 315 for (UnloadListenerSet::iterator it = tabs_needing_unload_fired_.begin(); |
| 316 it != tabs_needing_unload_fired_.end(); ++it) { |
| 317 content::WebContents* contents = *it; |
| 318 if (contents->GetRenderViewHost()) |
| 319 contents->SetDelegate(web_contents_delegate_.get()); |
| 320 } |
| 321 tabs_needing_before_unload_fired_.clear(); |
| 322 } |
| 323 |
| 308 // Process beforeunload tabs first. When that queue is empty, process | 324 // Process beforeunload tabs first. When that queue is empty, process |
| 309 // unload tabs. | 325 // unload tabs. |
| 310 if (!tabs_needing_before_unload_fired_.empty()) { | 326 if (!tabs_needing_before_unload_fired_.empty()) { |
| 311 content::WebContents* web_contents = | 327 content::WebContents* web_contents = |
| 312 *(tabs_needing_before_unload_fired_.begin()); | 328 *(tabs_needing_before_unload_fired_.begin()); |
| 313 // Null check render_view_host here as this gets called on a PostTask and | 329 // Null check render_view_host here as this gets called on a PostTask and |
| 314 // the tab's render_view_host may have been nulled out. | 330 // the tab's render_view_host may have been nulled out. |
| 315 if (web_contents->GetRenderViewHost()) { | 331 if (web_contents->GetRenderViewHost()) { |
| 316 // If there's a devtools window attached to |web_contents|, | 332 // If there's a devtools window attached to |web_contents|, |
| 317 // we would like devtools to call its own beforeunload handlers first, | 333 // we would like devtools to call its own beforeunload handlers first, |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 370 } | 386 } |
| 371 return false; | 387 return false; |
| 372 } | 388 } |
| 373 | 389 |
| 374 void UnloadController::ClearUnloadState(content::WebContents* web_contents, | 390 void UnloadController::ClearUnloadState(content::WebContents* web_contents, |
| 375 bool process_now) { | 391 bool process_now) { |
| 376 if (is_attempting_to_close_browser_) { | 392 if (is_attempting_to_close_browser_) { |
| 377 RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents); | 393 RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents); |
| 378 RemoveFromSet(&tabs_needing_unload_fired_, web_contents); | 394 RemoveFromSet(&tabs_needing_unload_fired_, web_contents); |
| 379 if (process_now) { | 395 if (process_now) { |
| 380 ProcessPendingTabs(); | 396 ProcessPendingTabs(false); |
| 381 } else { | 397 } else { |
| 382 base::ThreadTaskRunnerHandle::Get()->PostTask( | 398 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 383 FROM_HERE, base::Bind(&UnloadController::ProcessPendingTabs, | 399 FROM_HERE, base::Bind(&UnloadController::ProcessPendingTabs, |
| 384 weak_factory_.GetWeakPtr())); | 400 weak_factory_.GetWeakPtr(), false)); |
| 385 } | 401 } |
| 386 } | 402 } |
| 387 } | 403 } |
| 388 | 404 |
| 389 } // namespace chrome | 405 } // namespace chrome |
| OLD | NEW |