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 <map> | |
8 | |
9 #include "base/callback.h" | |
10 #include "base/memory/scoped_vector.h" | |
7 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
8 #include "chrome/browser/ui/browser.h" | 12 #include "chrome/browser/ui/browser.h" |
9 #include "chrome/browser/ui/browser_tabstrip.h" | 13 #include "chrome/browser/ui/browser_tabstrip.h" |
10 #include "chrome/browser/ui/tab_contents/tab_contents.h" | 14 #include "chrome/browser/ui/tab_contents/tab_contents.h" |
11 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 15 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
12 #include "chrome/common/chrome_notification_types.h" | 16 #include "chrome/common/chrome_notification_types.h" |
13 #include "content/public/browser/notification_service.h" | 17 #include "content/public/browser/notification_service.h" |
14 #include "content/public/browser/notification_source.h" | 18 #include "content/public/browser/notification_source.h" |
15 #include "content/public/browser/notification_types.h" | 19 #include "content/public/browser/notification_types.h" |
16 #include "content/public/browser/render_view_host.h" | 20 #include "content/public/browser/render_view_host.h" |
17 #include "content/public/browser/web_contents.h" | 21 #include "content/public/browser/web_contents.h" |
22 #include "content/public/browser/web_contents_delegate.h" | |
18 | 23 |
19 namespace chrome { | 24 namespace chrome { |
20 | 25 |
26 typedef base::Callback<void(void)> DetachedTabsClosedCallback; | |
27 typedef std::map<content::WebContents*, TabContents*> DetachedTabsMap; | |
Ben Goodger (Google)
2012/10/31 18:28:32
this you can put at the start of the private area
| |
28 | |
29 //////////////////////////////////////////////////////////////////////////////// | |
30 // UnloadDetachedHandler is used to close tabs quickly, http://crbug.com/142458. | |
31 // - Allows unload handlers to run in the background. | |
32 // - Comes into play after the beforeunload handlers (if any) have run. | |
33 // - Does not close the tabs; it holds tabs while they are closed. | |
34 // | |
35 // TODO(slamm): Support closes that destroy the browser: last tab close, window. | |
36 // http://crbug.com/156896 | |
37 // TODO(slamm): Hide unload time from the user for cross-process navigations. | |
38 // http://crbug.com/156958 | |
39 class UnloadDetachedHandler : public content::WebContentsDelegate { | |
40 public: | |
41 explicit UnloadDetachedHandler(const DetachedTabsClosedCallback& callback) | |
42 : tabs_closed_callback_(callback) { } | |
43 ~UnloadDetachedHandler() { } | |
Ben Goodger (Google)
2012/10/31 18:28:32
virtual dtor
| |
44 | |
45 // Returns true if it succeeds. | |
46 bool DetachWebContents(TabStripModel* tab_strip_model, | |
47 content::WebContents* web_contents) { | |
48 if (tab_strip_model->count() > 1) { | |
49 // Only allow tab to be detached if it is not the last one. | |
50 // Otherwise, the tab_strip_model would notify the browser that | |
51 // the strip is empty, and cause it to close without waiting for | |
52 // the unload handlers. | |
53 int index = tab_strip_model->GetIndexOfWebContents(web_contents); | |
54 if (index != TabStripModel::kNoTab && | |
55 web_contents->NeedToFireBeforeUnload()) { | |
56 TabContents* tab_contents = tab_strip_model->DetachTabContentsAt(index); | |
57 detached_tabs_[web_contents] = tab_contents; | |
58 web_contents->SetDelegate(this); | |
59 web_contents->OnUnloadDetachedStarted(); | |
60 return true; | |
61 } | |
62 } | |
63 return false; | |
64 } | |
65 | |
66 bool HasTabs() const { | |
67 return !detached_tabs_.empty(); | |
68 } | |
69 | |
70 private: | |
71 // WebContentsDelegate implementation. | |
72 virtual bool ShouldSuppressDialogs() OVERRIDE { | |
73 return true; // Return true so dialogs are suppressed. | |
74 } | |
75 virtual void CloseContents(content::WebContents* source) OVERRIDE { | |
76 DetachedTabsMap::iterator it = detached_tabs_.find(source); | |
77 TabContents* tab_contents = (*it).second; | |
Ben Goodger (Google)
2012/10/31 18:28:32
So, Avi is in the process of removing TC from Chro
| |
78 delete tab_contents; | |
79 detached_tabs_.erase(it); | |
80 tabs_closed_callback_.Run(); | |
81 } | |
82 | |
83 const DetachedTabsClosedCallback tabs_closed_callback_; | |
84 DetachedTabsMap detached_tabs_; | |
85 | |
86 DISALLOW_IMPLICIT_CONSTRUCTORS(UnloadDetachedHandler); | |
87 }; | |
88 | |
89 | |
21 //////////////////////////////////////////////////////////////////////////////// | 90 //////////////////////////////////////////////////////////////////////////////// |
22 // UnloadController, public: | 91 // UnloadController, public: |
23 | 92 |
24 UnloadController::UnloadController(Browser* browser) | 93 UnloadController::UnloadController(Browser* browser) |
25 : browser_(browser), | 94 : browser_(browser), |
26 is_attempting_to_close_browser_(false), | 95 is_attempting_to_close_browser_(false), |
96 ALLOW_THIS_IN_INITIALIZER_LIST( | |
97 unload_detached_handler_(new UnloadDetachedHandler( | |
98 base::Bind(&UnloadController::ProcessPendingTabs, | |
99 base::Unretained(this))))), | |
27 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | 100 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
28 browser_->tab_strip_model()->AddObserver(this); | 101 browser_->tab_strip_model()->AddObserver(this); |
29 } | 102 } |
30 | 103 |
31 UnloadController::~UnloadController() { | 104 UnloadController::~UnloadController() { |
32 browser_->tab_strip_model()->RemoveObserver(this); | 105 browser_->tab_strip_model()->RemoveObserver(this); |
33 } | 106 } |
34 | 107 |
35 bool UnloadController::CanCloseContents(content::WebContents* contents) { | 108 bool UnloadController::CanCloseContents(content::WebContents* contents) { |
36 // Don't try to close the tab when the whole browser is being closed, since | 109 // 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. | 110 // that avoids the fast shutdown path where we just kill all the renderers. |
38 if (is_attempting_to_close_browser_) | 111 if (is_attempting_to_close_browser_) |
39 ClearUnloadState(contents, true); | 112 ClearUnloadState(contents, true); |
40 return !is_attempting_to_close_browser_; | 113 return !is_attempting_to_close_browser_; |
41 } | 114 } |
42 | 115 |
43 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, | 116 bool UnloadController::BeforeUnloadFired(content::WebContents* contents, |
44 bool proceed) { | 117 bool proceed) { |
45 if (!is_attempting_to_close_browser_) { | 118 if (!is_attempting_to_close_browser_) { |
46 if (!proceed) | 119 if (proceed) { |
120 // No more dialogs are possible, so remove the tab and finish | |
121 // running unload listeners asynchrounously. | |
122 unload_detached_handler_->DetachWebContents(browser_->tab_strip_model(), | |
123 contents); | |
124 } else { | |
47 contents->SetClosedByUserGesture(false); | 125 contents->SetClosedByUserGesture(false); |
126 } | |
48 return proceed; | 127 return proceed; |
49 } | 128 } |
50 | 129 |
51 if (!proceed) { | 130 if (!proceed) { |
52 CancelWindowClose(); | 131 CancelWindowClose(); |
53 contents->SetClosedByUserGesture(false); | 132 contents->SetClosedByUserGesture(false); |
54 return false; | 133 return false; |
55 } | 134 } |
56 | 135 |
57 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { | 136 if (RemoveFromSet(&tabs_needing_before_unload_fired_, contents)) { |
(...skipping 10 matching lines...) Expand all Loading... | |
68 return true; | 147 return true; |
69 } | 148 } |
70 | 149 |
71 bool UnloadController::ShouldCloseWindow() { | 150 bool UnloadController::ShouldCloseWindow() { |
72 if (HasCompletedUnloadProcessing()) | 151 if (HasCompletedUnloadProcessing()) |
73 return true; | 152 return true; |
74 | 153 |
75 is_attempting_to_close_browser_ = true; | 154 is_attempting_to_close_browser_ = true; |
76 | 155 |
77 if (!TabsNeedBeforeUnloadFired()) | 156 if (!TabsNeedBeforeUnloadFired()) |
78 return true; | 157 return !unload_detached_handler_->HasTabs(); |
79 | 158 |
80 ProcessPendingTabs(); | 159 ProcessPendingTabs(); |
81 return false; | 160 return false; |
82 } | 161 } |
83 | 162 |
84 bool UnloadController::TabsNeedBeforeUnloadFired() { | 163 bool UnloadController::TabsNeedBeforeUnloadFired() { |
85 if (tabs_needing_before_unload_fired_.empty()) { | 164 if (tabs_needing_before_unload_fired_.empty()) { |
86 for (int i = 0; i < browser_->tab_count(); ++i) { | 165 for (int i = 0; i < browser_->tab_count(); ++i) { |
87 content::WebContents* contents = | 166 content::WebContents* contents = |
88 chrome::GetTabContentsAt(browser_, i)->web_contents(); | 167 chrome::GetTabContentsAt(browser_, i)->web_contents(); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
175 } | 254 } |
176 | 255 |
177 // Process beforeunload tabs first. When that queue is empty, process | 256 // Process beforeunload tabs first. When that queue is empty, process |
178 // unload tabs. | 257 // unload tabs. |
179 if (!tabs_needing_before_unload_fired_.empty()) { | 258 if (!tabs_needing_before_unload_fired_.empty()) { |
180 content::WebContents* web_contents = | 259 content::WebContents* web_contents = |
181 *(tabs_needing_before_unload_fired_.begin()); | 260 *(tabs_needing_before_unload_fired_.begin()); |
182 // Null check render_view_host here as this gets called on a PostTask and | 261 // 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. | 262 // the tab's render_view_host may have been nulled out. |
184 if (web_contents->GetRenderViewHost()) { | 263 if (web_contents->GetRenderViewHost()) { |
264 web_contents->OnCloseStarted(); | |
185 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); | 265 web_contents->GetRenderViewHost()->FirePageBeforeUnload(false); |
186 } else { | 266 } else { |
187 ClearUnloadState(web_contents, true); | 267 ClearUnloadState(web_contents, true); |
188 } | 268 } |
189 } else if (!tabs_needing_unload_fired_.empty()) { | 269 } else if (!tabs_needing_unload_fired_.empty()) { |
190 // We've finished firing all beforeunload events and can proceed with unload | 270 // We've finished firing all beforeunload events and can proceed with unload |
191 // events. | 271 // events. |
192 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting | 272 // TODO(ojan): We should add a call to browser_shutdown::OnShutdownStarting |
193 // somewhere around here so that we have accurate measurements of shutdown | 273 // somewhere around here so that we have accurate measurements of shutdown |
194 // time. | 274 // time. |
195 // TODO(ojan): We can probably fire all the unload events in parallel and | 275 // 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 | 276 // 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. | 277 // unload handler or takes a long time to page in. |
198 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); | 278 content::WebContents* web_contents = *(tabs_needing_unload_fired_.begin()); |
199 // Null check render_view_host here as this gets called on a PostTask and | 279 // 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. | 280 // the tab's render_view_host may have been nulled out. |
201 if (web_contents->GetRenderViewHost()) { | 281 if (web_contents->GetRenderViewHost()) { |
202 web_contents->GetRenderViewHost()->ClosePage(); | 282 if (!unload_detached_handler_->HasTabs()) { |
Ben Goodger (Google)
2012/10/31 18:28:32
nit, no braces.
| |
283 web_contents->GetRenderViewHost()->ClosePage(); | |
284 } | |
203 } else { | 285 } else { |
204 ClearUnloadState(web_contents, true); | 286 ClearUnloadState(web_contents, true); |
205 } | 287 } |
206 } else { | 288 } else { |
207 NOTREACHED(); | 289 NOTREACHED(); |
208 } | 290 } |
209 } | 291 } |
210 | 292 |
211 bool UnloadController::HasCompletedUnloadProcessing() const { | 293 bool UnloadController::HasCompletedUnloadProcessing() const { |
212 return is_attempting_to_close_browser_ && | 294 return is_attempting_to_close_browser_ && |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
250 } else { | 332 } else { |
251 MessageLoop::current()->PostTask( | 333 MessageLoop::current()->PostTask( |
252 FROM_HERE, | 334 FROM_HERE, |
253 base::Bind(&UnloadController::ProcessPendingTabs, | 335 base::Bind(&UnloadController::ProcessPendingTabs, |
254 weak_factory_.GetWeakPtr())); | 336 weak_factory_.GetWeakPtr())); |
255 } | 337 } |
256 } | 338 } |
257 } | 339 } |
258 | 340 |
259 } // namespace chrome | 341 } // namespace chrome |
OLD | NEW |