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_beforeunload, | |
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_beforeunload); |
183 return true; | 185 return true && !skip_beforeunload; |
sky
2017/03/14 00:04:05
return !skip_beforeunload
zmin
2017/03/14 17:46:47
Done.
| |
184 } | 186 } |
185 | 187 |
186 void UnloadController::ResetBeforeUnloadHandlers() { | 188 void UnloadController::ResetTryToCloseWindow() { |
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_beforeunload) { |
294 // Cancel posted/queued ProcessPendingTabs task if there is any. | 296 // Cancel posted/queued ProcessPendingTabs task if there is any. |
295 weak_factory_.InvalidateWeakPtrs(); | 297 weak_factory_.InvalidateWeakPtrs(); |
296 | 298 |
297 if (!is_attempting_to_close_browser_) { | 299 if (!is_attempting_to_close_browser_) { |
298 // Because we might invoke this after a delay it's possible for the value of | 300 // 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 | 301 // is_attempting_to_close_browser_ to have changed since we scheduled the |
300 // task. | 302 // task. |
301 return; | 303 return; |
302 } | 304 } |
303 | 305 |
304 if (HasCompletedUnloadProcessing() && !TabsNeedBeforeUnloadFired()) { | 306 if (HasCompletedUnloadProcessing() && !TabsNeedBeforeUnloadFired()) { |
305 // We've finished all the unload events and can proceed to close the | 307 // We've finished all the unload events and can proceed to close the |
306 // browser. | 308 // browser. |
307 browser_->OnWindowClosing(); | 309 browser_->OnWindowClosing(); |
308 return; | 310 return; |
309 } | 311 } |
310 | 312 |
313 if (skip_beforeunload) { | |
314 tabs_needing_unload_fired_.insert(tabs_needing_before_unload_fired_.begin(), | |
315 tabs_needing_before_unload_fired_.end()); | |
316 if (!web_contents_delegate_) | |
317 web_contents_delegate_.reset(new UnloadControllerWebContentsDelegate()); | |
sky
2017/03/14 00:04:05
MakeUnique
zmin
2017/03/14 17:46:47
Done.
| |
318 for (UnloadListenerSet::iterator it = tabs_needing_unload_fired_.begin(); | |
319 it != tabs_needing_unload_fired_.end(); ++it) { | |
320 (*it)->SetDelegate(web_contents_delegate_.get()); | |
sky
2017/03/14 00:04:05
How do you know it's safe to reset the delegate? M
zmin
2017/03/14 17:46:47
This delegate makes sure that after windows are cl
sky
2017/03/14 20:26:34
AFAIK FastUnloadController isn't shipping yet, so
Charlie Reis
2017/03/14 23:21:44
That's a great question. It looks like it depends
| |
321 } | |
322 tabs_needing_before_unload_fired_.clear(); | |
323 } | |
324 | |
311 // Process beforeunload tabs first. When that queue is empty, process | 325 // Process beforeunload tabs first. When that queue is empty, process |
312 // unload tabs. | 326 // unload tabs. |
313 if (!tabs_needing_before_unload_fired_.empty()) { | 327 if (!tabs_needing_before_unload_fired_.empty()) { |
314 content::WebContents* web_contents = | 328 content::WebContents* web_contents = |
315 *(tabs_needing_before_unload_fired_.begin()); | 329 *(tabs_needing_before_unload_fired_.begin()); |
316 // Null check render_view_host here as this gets called on a PostTask and | 330 // 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. | 331 // the tab's render_view_host may have been nulled out. |
318 if (web_contents->GetRenderViewHost()) { | 332 if (web_contents->GetRenderViewHost()) { |
319 // If there's a devtools window attached to |web_contents|, | 333 // If there's a devtools window attached to |web_contents|, |
320 // we would like devtools to call its own beforeunload handlers first, | 334 // we would like devtools to call its own beforeunload handlers first, |
321 // and then call beforeunload handlers for |web_contents|. | 335 // and then call beforeunload handlers for |web_contents|. |
322 // See DevToolsWindow::InterceptPageBeforeUnload for details. | 336 // See DevToolsWindow::InterceptPageBeforeUnload for details. |
323 if (!DevToolsWindow::InterceptPageBeforeUnload(web_contents)) | 337 if (!DevToolsWindow::InterceptPageBeforeUnload(web_contents)) |
324 web_contents->DispatchBeforeUnload(); | 338 web_contents->DispatchBeforeUnload(); |
325 } else { | 339 } else { |
326 ClearUnloadState(web_contents, true); | 340 ClearUnloadState(web_contents, true); |
327 } | 341 } |
328 } else if (is_calling_before_unload_handlers()) { | 342 } else if (is_calling_before_unload_handlers()) { |
329 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; | 343 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; |
330 // Reset |on_close_confirmed_| in case the callback tests | 344 // Reset |on_close_confirmed_| in case the callback tests |
331 // |is_calling_before_unload_handlers()|, we want to return that calling | 345 // |is_calling_before_unload_handlers()|, we want to return that calling |
332 // is complete. | 346 // is complete. |
333 if (tabs_needing_unload_fired_.empty()) | 347 if (tabs_needing_unload_fired_.empty()) |
334 on_close_confirmed_.Reset(); | 348 on_close_confirmed_.Reset(); |
335 on_close_confirmed.Run(true); | 349 if (!skip_beforeunload) |
350 on_close_confirmed.Run(true); | |
336 } else if (!tabs_needing_unload_fired_.empty()) { | 351 } else if (!tabs_needing_unload_fired_.empty()) { |
337 // We've finished firing all beforeunload events and can proceed with unload | 352 // We've finished firing all beforeunload events and can proceed with unload |
338 // events. | 353 // events. |
339 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting | 354 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting |
340 // somewhere around here so that we have accurate measurements of shutdown | 355 // somewhere around here so that we have accurate measurements of shutdown |
341 // time. | 356 // time. |
342 // TODO(ojan): We can probably fire all the unload events in parallel and | 357 // 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 | 358 // 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. | 359 // unload handler or takes a long time to page in. |
345 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); | 360 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); |
(...skipping 27 matching lines...) Expand all Loading... | |
373 } | 388 } |
374 return false; | 389 return false; |
375 } | 390 } |
376 | 391 |
377 void UnloadController::ClearUnloadState(content::WebContents* web_contents, | 392 void UnloadController::ClearUnloadState(content::WebContents* web_contents, |
378 bool process_now) { | 393 bool process_now) { |
379 if (is_attempting_to_close_browser_) { | 394 if (is_attempting_to_close_browser_) { |
380 RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents); | 395 RemoveFromSet(&tabs_needing_before_unload_fired_, web_contents); |
381 RemoveFromSet(&tabs_needing_unload_fired_, web_contents); | 396 RemoveFromSet(&tabs_needing_unload_fired_, web_contents); |
382 if (process_now) { | 397 if (process_now) { |
383 ProcessPendingTabs(); | 398 ProcessPendingTabs(false); |
384 } else { | 399 } else { |
385 // Do not post a new task if there is already any. | 400 // Do not post a new task if there is already any. |
386 if (weak_factory_.HasWeakPtrs()) | 401 if (weak_factory_.HasWeakPtrs()) |
387 return; | 402 return; |
388 base::ThreadTaskRunnerHandle::Get()->PostTask( | 403 base::ThreadTaskRunnerHandle::Get()->PostTask( |
389 FROM_HERE, base::Bind(&UnloadController::ProcessPendingTabs, | 404 FROM_HERE, |
390 weak_factory_.GetWeakPtr())); | 405 base::Bind(&UnloadController::ProcessPendingTabs, |
406 weak_factory_.GetWeakPtr(), false)); | |
391 } | 407 } |
392 } | 408 } |
393 } | 409 } |
394 | 410 |
395 } // namespace chrome | 411 } // namespace chrome |
OLD | NEW |