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/callback.h" |
7 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
8 #include "chrome/browser/ui/browser.h" | 9 #include "chrome/browser/ui/browser.h" |
9 #include "chrome/browser/ui/browser_tabstrip.h" | 10 #include "chrome/browser/ui/browser_tabstrip.h" |
10 #include "chrome/browser/ui/tab_contents/tab_contents.h" | 11 #include "chrome/browser/ui/tab_contents/tab_contents.h" |
11 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 12 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 13 #include "chrome/browser/ui/unload_detached_handler.h" |
12 #include "chrome/common/chrome_notification_types.h" | 14 #include "chrome/common/chrome_notification_types.h" |
13 #include "content/public/browser/notification_service.h" | 15 #include "content/public/browser/notification_service.h" |
14 #include "content/public/browser/notification_source.h" | 16 #include "content/public/browser/notification_source.h" |
15 #include "content/public/browser/notification_types.h" | 17 #include "content/public/browser/notification_types.h" |
16 #include "content/public/browser/render_view_host.h" | 18 #include "content/public/browser/render_view_host.h" |
17 #include "content/public/browser/web_contents.h" | 19 #include "content/public/browser/web_contents.h" |
18 | 20 |
19 namespace chrome { | 21 namespace chrome { |
20 | 22 |
21 //////////////////////////////////////////////////////////////////////////////// | 23 //////////////////////////////////////////////////////////////////////////////// |
22 // UnloadController, public: | 24 // UnloadController, public: |
23 | 25 |
24 UnloadController::UnloadController(Browser* browser) | 26 UnloadController::UnloadController(Browser* browser) |
25 : browser_(browser), | 27 : browser_(browser), |
26 is_attempting_to_close_browser_(false), | 28 is_attempting_to_close_browser_(false), |
| 29 unload_detached_handler_(new UnloadDetachedHandler( |
| 30 base::Bind(&UnloadController::DetachedTabsClosedCallback, |
| 31 base::Unretained(this)))), |
27 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | 32 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
28 browser_->tab_strip_model()->AddObserver(this); | 33 browser_->tab_strip_model()->AddObserver(this); |
29 } | 34 } |
30 | 35 |
31 UnloadController::~UnloadController() { | 36 UnloadController::~UnloadController() { |
32 browser_->tab_strip_model()->RemoveObserver(this); | 37 browser_->tab_strip_model()->RemoveObserver(this); |
| 38 delete unload_detached_handler_; |
33 } | 39 } |
34 | 40 |
35 bool UnloadController::CanCloseContents(content::WebContents* contents) { | 41 bool UnloadController::CanCloseContents(content::WebContents* contents) { |
36 // Don't try to close the tab when the whole browser is being closed, since | 42 // 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. | 43 // that avoids the fast shutdown path where we just kill all the renderers. |
38 if (is_attempting_to_close_browser_) | 44 if (is_attempting_to_close_browser_) |
39 ClearUnloadState(contents, true); | 45 ClearUnloadState(contents, true); |
40 return !is_attempting_to_close_browser_; | 46 return !is_attempting_to_close_browser_; |
41 } | 47 } |
42 | 48 |
43 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, | 49 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, |
44 bool proceed) { | 50 bool proceed) { |
45 if (!is_attempting_to_close_browser_) { | 51 if (!is_attempting_to_close_browser_) { |
46 if (!proceed) | 52 if (proceed) { |
| 53 // No more dialogs are possible, so remove the tab and finish |
| 54 // running unload listeners asynchrounously. |
| 55 unload_detached_handler_->DetachWebContents(browser_->tab_strip_model(), |
| 56 contents); |
| 57 } else { |
47 contents->SetClosedByUserGesture(false); | 58 contents->SetClosedByUserGesture(false); |
| 59 } |
48 return proceed; | 60 return proceed; |
49 } | 61 } |
50 | 62 |
51 if (!proceed) { | 63 if (!proceed) { |
52 CancelWindowClose(); | 64 CancelWindowClose(); |
53 contents->SetClosedByUserGesture(false); | 65 contents->SetClosedByUserGesture(false); |
54 return false; | 66 return false; |
55 } | 67 } |
56 | 68 |
57 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { | 69 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { |
(...skipping 10 matching lines...) Expand all Loading... |
68 return true; | 80 return true; |
69 } | 81 } |
70 | 82 |
71 bool UnloadController::ShouldCloseWindow() { | 83 bool UnloadController::ShouldCloseWindow() { |
72 if (HasCompletedUnloadProcessing()) | 84 if (HasCompletedUnloadProcessing()) |
73 return true; | 85 return true; |
74 | 86 |
75 is_attempting_to_close_browser_ = true; | 87 is_attempting_to_close_browser_ = true; |
76 | 88 |
77 if (!TabsNeedBeforeUnloadFired()) | 89 if (!TabsNeedBeforeUnloadFired()) |
78 return true; | 90 return !unload_detached_handler_->HasTabs(); |
79 | 91 |
80 ProcessPendingTabs(); | 92 ProcessPendingTabs(); |
81 return false; | 93 return false; |
82 } | 94 } |
83 | 95 |
84 bool UnloadController::TabsNeedBeforeUnloadFired() { | 96 bool UnloadController::TabsNeedBeforeUnloadFired() { |
85 if (tabs_needing_before_unload_fired_.empty()) { | 97 if (tabs_needing_before_unload_fired_.empty()) { |
86 for (int i = 0; i < browser_->tab_count(); ++i) { | 98 for (int i = 0; i < browser_->tab_count(); ++i) { |
87 content::WebContents* contents = | 99 content::WebContents* contents = |
88 chrome::GetTabContentsAt(browser_, i)->web_contents(); | 100 chrome::GetTabContentsAt(browser_, i)->web_contents(); |
89 if (contents->NeedToFireBeforeUnload()) | 101 if (contents->NeedToFireBeforeUnload()) |
90 tabs_needing_before_unload_fired_.insert(contents); | 102 tabs_needing_before_unload_fired_.insert(contents); |
91 } | 103 } |
92 } | 104 } |
93 return !tabs_needing_before_unload_fired_.empty(); | 105 return !tabs_needing_before_unload_fired_.empty(); |
94 } | 106 } |
95 | 107 |
| 108 void UnloadController::DetachedTabsClosedCallback() { |
| 109 ProcessPendingTabs(); |
| 110 } |
| 111 |
96 //////////////////////////////////////////////////////////////////////////////// | 112 //////////////////////////////////////////////////////////////////////////////// |
97 // UnloadController, content::NotificationObserver implementation: | 113 // UnloadController, content::NotificationObserver implementation: |
98 | 114 |
99 void UnloadController::Observe(int type, | 115 void UnloadController::Observe(int type, |
100 const content::NotificationSource& source, | 116 const content::NotificationSource& source, |
101 const content::NotificationDetails& details) { | 117 const content::NotificationDetails& details) { |
102 switch (type) { | 118 switch (type) { |
103 case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: | 119 case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED: |
104 if (is_attempting_to_close_browser_) { | 120 if (is_attempting_to_close_browser_) { |
105 ClearUnloadState(content::Source<content::WebContents>(source).ptr(), | 121 ClearUnloadState(content::Source<content::WebContents>(source).ptr(), |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 } | 191 } |
176 | 192 |
177 // Process beforeunload tabs first. When that queue is empty, process | 193 // Process beforeunload tabs first. When that queue is empty, process |
178 // unload tabs. | 194 // unload tabs. |
179 if (!tabs_needing_before_unload_fired_.empty()) { | 195 if (!tabs_needing_before_unload_fired_.empty()) { |
180 content::WebContents* web_contents = | 196 content::WebContents* web_contents = |
181 *(tabs_needing_before_unload_fired_.begin()); | 197 *(tabs_needing_before_unload_fired_.begin()); |
182 // Null check render_view_host here as this gets called on a PostTask and | 198 // Null check render_view_host here as this gets called on a PostTask and |
183 // the tab's render_view_host may have been nulled out. | 199 // the tab's render_view_host may have been nulled out. |
184 if (web_contents->GetRenderViewHost()) { | 200 if (web_contents->GetRenderViewHost()) { |
| 201 web_contents->OnCloseStarted(); |
185 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); | 202 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); |
186 } else { | 203 } else { |
187 ClearUnloadState(web_contents, true); | 204 ClearUnloadState(web_contents, true); |
188 } | 205 } |
189 } else if (!tabs_needing_unload_fired_.empty()) { | 206 } else if (!tabs_needing_unload_fired_.empty()) { |
190 // We've finished firing all beforeunload events and can proceed with unload | 207 // We've finished firing all beforeunload events and can proceed with unload |
191 // events. | 208 // events. |
192 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting | 209 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting |
193 // somewhere around here so that we have accurate measurements of shutdown | 210 // somewhere around here so that we have accurate measurements of shutdown |
194 // time. | 211 // time. |
195 // TODO(ojan): We can probably fire all the unload events in parallel and | 212 // TODO(ojan): We can probably fire all the unload events in parallel and |
196 // get a perf benefit from that in the cases where the tab hangs in it's | 213 // get a perf benefit from that in the cases where the tab hangs in it's |
197 // unload handler or takes a long time to page in. | 214 // unload handler or takes a long time to page in. |
198 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); | 215 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); |
199 // Null check render_view_host here as this gets called on a PostTask and | 216 // Null check render_view_host here as this gets called on a PostTask and |
200 // the tab's render_view_host may have been nulled out. | 217 // the tab's render_view_host may have been nulled out. |
201 if (web_contents->GetRenderViewHost()) { | 218 if (web_contents->GetRenderViewHost()) { |
202 web_contents->GetRenderViewHost()->ClosePage(); | 219 if (!unload_detached_handler_->HasTabs()) { |
| 220 web_contents->GetRenderViewHost()->ClosePage(); |
| 221 } |
203 } else { | 222 } else { |
204 ClearUnloadState(web_contents, true); | 223 ClearUnloadState(web_contents, true); |
205 } | 224 } |
206 } else { | 225 } else { |
207 NOTREACHED(); | 226 NOTREACHED(); |
208 } | 227 } |
209 } | 228 } |
210 | 229 |
211 bool UnloadController::HasCompletedUnloadProcessing() const { | 230 bool UnloadController::HasCompletedUnloadProcessing() const { |
212 return is_attempting_to_close_browser_ && | 231 return is_attempting_to_close_browser_ && |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
250 } else { | 269 } else { |
251 MessageLoop::current()->PostTask( | 270 MessageLoop::current()->PostTask( |
252 FROM_HERE, | 271 FROM_HERE, |
253 base::Bind(&UnloadController::ProcessPendingTabs, | 272 base::Bind(&UnloadController::ProcessPendingTabs, |
254 weak_factory_.GetWeakPtr())); | 273 weak_factory_.GetWeakPtr())); |
255 } | 274 } |
256 } | 275 } |
257 } | 276 } |
258 | 277 |
259 } // namespace chrome | 278 } // namespace chrome |
OLD | NEW |