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/message_loop/message_loop.h" | 7 #include "base/message_loop/message_loop.h" |
8 #include "chrome/browser/chrome_notification_types.h" | 8 #include "chrome/browser/chrome_notification_types.h" |
9 #include "chrome/browser/devtools/devtools_window.h" | 9 #include "chrome/browser/devtools/devtools_window.h" |
10 #include "chrome/browser/ui/browser.h" | 10 #include "chrome/browser/ui/browser.h" |
(...skipping 24 matching lines...) Expand all Loading... |
35 bool UnloadController::CanCloseContents(content::WebContents* contents) { | 35 bool UnloadController::CanCloseContents(content::WebContents* contents) { |
36 // Don't try to close the tab when the whole browser is being closed, since | 36 // Don't try to close the tab when the whole browser is being closed, since |
37 // that avoids the fast shutdown path where we just kill all the renderers. | 37 // that avoids the fast shutdown path where we just kill all the renderers. |
38 if (is_attempting_to_close_browser_) | 38 if (is_attempting_to_close_browser_) |
39 ClearUnloadState(contents, true); | 39 ClearUnloadState(contents, true); |
40 return !is_attempting_to_close_browser_ || | 40 return !is_attempting_to_close_browser_ || |
41 is_calling_before_unload_handlers(); | 41 is_calling_before_unload_handlers(); |
42 } | 42 } |
43 | 43 |
44 // static | 44 // static |
| 45 bool UnloadController::ShouldRunUnloadEventsHelper( |
| 46 content::WebContents* contents) { |
| 47 return DevToolsWindow::GetInstanceForInspectedRenderViewHost( |
| 48 contents->GetRenderViewHost()) != NULL; |
| 49 } |
| 50 |
| 51 // static |
45 bool UnloadController::RunUnloadEventsHelper(content::WebContents* contents) { | 52 bool UnloadController::RunUnloadEventsHelper(content::WebContents* contents) { |
| 53 // If there's a devtools window attached to |contents|, |
| 54 // we would like devtools to call its own beforeunload handlers first, |
| 55 // and then call beforeunload handlers for |contents|. |
| 56 // See DevToolsWindow::InterceptPageBeforeUnload for details. |
| 57 if (DevToolsWindow::InterceptPageBeforeUnload(contents)) { |
| 58 return true; |
| 59 } |
46 // If the WebContents is not connected yet, then there's no unload | 60 // If the WebContents is not connected yet, then there's no unload |
47 // handler we can fire even if the WebContents has an unload listener. | 61 // handler we can fire even if the WebContents has an unload listener. |
48 // One case where we hit this is in a tab that has an infinite loop | 62 // One case where we hit this is in a tab that has an infinite loop |
49 // before load. | 63 // before load. |
50 if (contents->NeedToFireBeforeUnload()) { | 64 if (contents->NeedToFireBeforeUnload()) { |
51 // If the page has unload listeners, then we tell the renderer to fire | 65 // If the page has unload listeners, then we tell the renderer to fire |
52 // them. Once they have fired, we'll get a message back saying whether | 66 // them. Once they have fired, we'll get a message back saying whether |
53 // to proceed closing the page or not, which sends us back to this method | 67 // to proceed closing the page or not, which sends us back to this method |
54 // with the NeedToFireBeforeUnload bit cleared. | 68 // with the NeedToFireBeforeUnload bit cleared. |
55 contents->GetRenderViewHost()->FirePageBeforeUnload(false); | 69 contents->GetRenderViewHost()->FirePageBeforeUnload(false); |
56 return true; | 70 return true; |
57 } | 71 } |
58 return false; | 72 return false; |
59 } | 73 } |
60 | 74 |
61 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, | 75 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, |
62 bool proceed) { | 76 bool proceed) { |
| 77 if (!proceed) |
| 78 DevToolsWindow::OnPageCloseCanceled(contents); |
| 79 |
63 if (!is_attempting_to_close_browser_) { | 80 if (!is_attempting_to_close_browser_) { |
64 if (!proceed) | 81 if (!proceed) |
65 contents->SetClosedByUserGesture(false); | 82 contents->SetClosedByUserGesture(false); |
66 return proceed; | 83 return proceed; |
67 } | 84 } |
68 | 85 |
69 if (!proceed) { | 86 if (!proceed) { |
70 CancelWindowClose(); | 87 CancelWindowClose(); |
71 contents->SetClosedByUserGesture(false); | 88 contents->SetClosedByUserGesture(false); |
72 return false; | 89 return false; |
(...skipping 10 matching lines...) Expand all Loading... |
83 return false; | 100 return false; |
84 } | 101 } |
85 | 102 |
86 return true; | 103 return true; |
87 } | 104 } |
88 | 105 |
89 bool UnloadController::ShouldCloseWindow() { | 106 bool UnloadController::ShouldCloseWindow() { |
90 if (HasCompletedUnloadProcessing()) | 107 if (HasCompletedUnloadProcessing()) |
91 return true; | 108 return true; |
92 | 109 |
| 110 if (browser_->is_devtools() && |
| 111 DevToolsWindow::ShouldCloseDevToolsBrowser(browser_)) { |
| 112 return true; |
| 113 } |
| 114 |
93 // The behavior followed here varies based on the current phase of the | 115 // The behavior followed here varies based on the current phase of the |
94 // operation and whether a batched shutdown is in progress. | 116 // operation and whether a batched shutdown is in progress. |
95 // | 117 // |
96 // If there are tabs with outstanding beforeunload handlers: | 118 // If there are tabs with outstanding beforeunload handlers: |
97 // 1. If a batched shutdown is in progress: return false. | 119 // 1. If a batched shutdown is in progress: return false. |
98 // This is to prevent interference with batched shutdown already in | 120 // This is to prevent interference with batched shutdown already in |
99 // progress. | 121 // progress. |
100 // 2. Otherwise: start sending beforeunload events and return false. | 122 // 2. Otherwise: start sending beforeunload events and return false. |
101 // | 123 // |
102 // Otherwise, If there are no tabs with outstanding beforeunload handlers: | 124 // Otherwise, If there are no tabs with outstanding beforeunload handlers: |
103 // 3. If a batched shutdown is in progress: start sending unload events and | 125 // 3. If a batched shutdown is in progress: start sending unload events and |
104 // return false. | 126 // return false. |
105 // 4. Otherwise: return true. | 127 // 4. Otherwise: return true. |
106 is_attempting_to_close_browser_ = true; | 128 is_attempting_to_close_browser_ = true; |
107 // Cases 1 and 4. | 129 // Cases 1 and 4. |
108 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); | 130 bool need_beforeunload_fired = TabsNeedBeforeUnloadFired(); |
109 if (need_beforeunload_fired == is_calling_before_unload_handlers()) | 131 if (need_beforeunload_fired == is_calling_before_unload_handlers()) |
110 return !need_beforeunload_fired; | 132 return !need_beforeunload_fired; |
111 | 133 |
112 // Cases 2 and 3. | 134 // Cases 2 and 3. |
113 on_close_confirmed_.Reset(); | 135 on_close_confirmed_.Reset(); |
114 ProcessPendingTabs(); | 136 ProcessPendingTabs(); |
115 return false; | 137 return false; |
116 } | 138 } |
117 | 139 |
118 bool UnloadController::CallBeforeUnloadHandlers( | 140 bool UnloadController::CallBeforeUnloadHandlers( |
119 const base::Callback<void(bool)>& on_close_confirmed) { | 141 const base::Callback<void(bool)>& on_close_confirmed) { |
120 if (HasCompletedUnloadProcessing() || !TabsNeedBeforeUnloadFired()) | 142 // DevTools browser will get its beforeunload events triggered on |
| 143 // inspected tab closing. |
| 144 if (browser_->is_devtools() || HasCompletedUnloadProcessing() || |
| 145 !TabsNeedBeforeUnloadFired()) |
121 return false; | 146 return false; |
122 | 147 |
123 is_attempting_to_close_browser_ = true; | 148 is_attempting_to_close_browser_ = true; |
124 on_close_confirmed_ = on_close_confirmed; | 149 on_close_confirmed_ = on_close_confirmed; |
125 | 150 |
126 ProcessPendingTabs(); | 151 ProcessPendingTabs(); |
127 return true; | 152 return true; |
128 } | 153 } |
129 | 154 |
130 void UnloadController::ResetBeforeUnloadHandlers() { | 155 void UnloadController::ResetBeforeUnloadHandlers() { |
131 if (!is_calling_before_unload_handlers()) | 156 if (!is_calling_before_unload_handlers()) |
132 return; | 157 return; |
133 CancelWindowClose(); | 158 CancelWindowClose(); |
134 } | 159 } |
135 | 160 |
136 bool UnloadController::TabsNeedBeforeUnloadFired() { | 161 bool UnloadController::TabsNeedBeforeUnloadFired() { |
137 if (tabs_needing_before_unload_fired_.empty()) { | 162 if (tabs_needing_before_unload_fired_.empty()) { |
138 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { | 163 for (int i = 0; i < browser_->tab_strip_model()->count(); ++i) { |
139 content::WebContents* contents = | 164 content::WebContents* contents = |
140 browser_->tab_strip_model()->GetWebContentsAt(i); | 165 browser_->tab_strip_model()->GetWebContentsAt(i); |
| 166 bool should_fire_beforeunload = contents->NeedToFireBeforeUnload() || |
| 167 DevToolsWindow::NeedsToInterceptBeforeUnload(contents); |
141 if (!ContainsKey(tabs_needing_unload_fired_, contents) && | 168 if (!ContainsKey(tabs_needing_unload_fired_, contents) && |
142 contents->NeedToFireBeforeUnload()) { | 169 should_fire_beforeunload) { |
143 tabs_needing_before_unload_fired_.insert(contents); | 170 tabs_needing_before_unload_fired_.insert(contents); |
144 } | 171 } |
145 } | 172 } |
146 } | 173 } |
147 return !tabs_needing_before_unload_fired_.empty(); | 174 return !tabs_needing_before_unload_fired_.empty(); |
148 } | 175 } |
149 | 176 |
150 //////////////////////////////////////////////////////////////////////////////// | 177 //////////////////////////////////////////////////////////////////////////////// |
151 // UnloadController, content::NotificationObserver implementation: | 178 // UnloadController, content::NotificationObserver implementation: |
152 | 179 |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 } | 256 } |
230 | 257 |
231 // Process beforeunload tabs first. When that queue is empty, process | 258 // Process beforeunload tabs first. When that queue is empty, process |
232 // unload tabs. | 259 // unload tabs. |
233 if (!tabs_needing_before_unload_fired_.empty()) { | 260 if (!tabs_needing_before_unload_fired_.empty()) { |
234 content::WebContents* web_contents = | 261 content::WebContents* web_contents = |
235 *(tabs_needing_before_unload_fired_.begin()); | 262 *(tabs_needing_before_unload_fired_.begin()); |
236 // Null check render_view_host here as this gets called on a PostTask and | 263 // Null check render_view_host here as this gets called on a PostTask and |
237 // the tab's render_view_host may have been nulled out. | 264 // the tab's render_view_host may have been nulled out. |
238 if (web_contents->GetRenderViewHost()) { | 265 if (web_contents->GetRenderViewHost()) { |
239 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); | 266 // If there's a devtools window attached to |web_contents|, |
| 267 // we would like devtools to call its own beforeunload handlers first, |
| 268 // and then call beforeunload handlers for |web_contents|. |
| 269 // See DevToolsWindow::InterceptPageBeforeUnload for details. |
| 270 if (!DevToolsWindow::InterceptPageBeforeUnload(web_contents)) |
| 271 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); |
240 } else { | 272 } else { |
241 ClearUnloadState(web_contents, true); | 273 ClearUnloadState(web_contents, true); |
242 } | 274 } |
243 } else if (is_calling_before_unload_handlers()) { | 275 } else if (is_calling_before_unload_handlers()) { |
244 on_close_confirmed_.Run(true); | 276 on_close_confirmed_.Run(true); |
245 } else if (!tabs_needing_unload_fired_.empty()) { | 277 } else if (!tabs_needing_unload_fired_.empty()) { |
246 // We've finished firing all beforeunload events and can proceed with unload | 278 // We've finished firing all beforeunload events and can proceed with unload |
247 // events. | 279 // events. |
248 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting | 280 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting |
249 // somewhere around here so that we have accurate measurements of shutdown | 281 // somewhere around here so that we have accurate measurements of shutdown |
(...skipping 17 matching lines...) Expand all Loading... |
267 bool UnloadController::HasCompletedUnloadProcessing() const { | 299 bool UnloadController::HasCompletedUnloadProcessing() const { |
268 return is_attempting_to_close_browser_ && | 300 return is_attempting_to_close_browser_ && |
269 tabs_needing_before_unload_fired_.empty() && | 301 tabs_needing_before_unload_fired_.empty() && |
270 tabs_needing_unload_fired_.empty(); | 302 tabs_needing_unload_fired_.empty(); |
271 } | 303 } |
272 | 304 |
273 void UnloadController::CancelWindowClose() { | 305 void UnloadController::CancelWindowClose() { |
274 // Closing of window can be canceled from a beforeunload handler. | 306 // Closing of window can be canceled from a beforeunload handler. |
275 DCHECK(is_attempting_to_close_browser_); | 307 DCHECK(is_attempting_to_close_browser_); |
276 tabs_needing_before_unload_fired_.clear(); | 308 tabs_needing_before_unload_fired_.clear(); |
| 309 for (UnloadListenerSet::iterator it = tabs_needing_unload_fired_.begin(); |
| 310 it != tabs_needing_unload_fired_.end(); ++it) { |
| 311 DevToolsWindow::OnPageCloseCanceled(*it); |
| 312 } |
277 tabs_needing_unload_fired_.clear(); | 313 tabs_needing_unload_fired_.clear(); |
278 if (is_calling_before_unload_handlers()) { | 314 if (is_calling_before_unload_handlers()) { |
279 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; | 315 base::Callback<void(bool)> on_close_confirmed = on_close_confirmed_; |
280 on_close_confirmed_.Reset(); | 316 on_close_confirmed_.Reset(); |
281 on_close_confirmed.Run(false); | 317 on_close_confirmed.Run(false); |
282 } | 318 } |
283 is_attempting_to_close_browser_ = false; | 319 is_attempting_to_close_browser_ = false; |
284 | 320 |
285 content::NotificationService::current()->Notify( | 321 content::NotificationService::current()->Notify( |
286 chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, | 322 chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, |
(...skipping 24 matching lines...) Expand all Loading... |
311 } else { | 347 } else { |
312 base::MessageLoop::current()->PostTask( | 348 base::MessageLoop::current()->PostTask( |
313 FROM_HERE, | 349 FROM_HERE, |
314 base::Bind(&UnloadController::ProcessPendingTabs, | 350 base::Bind(&UnloadController::ProcessPendingTabs, |
315 weak_factory_.GetWeakPtr())); | 351 weak_factory_.GetWeakPtr())); |
316 } | 352 } |
317 } | 353 } |
318 } | 354 } |
319 | 355 |
320 } // namespace chrome | 356 } // namespace chrome |
OLD | NEW |