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/browser_list.h" | 5 #include "chrome/browser/ui/browser_list.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/metrics/histogram.h" | |
11 #include "build/build_config.h" | 10 #include "build/build_config.h" |
12 #include "chrome/browser/browser_process.h" | 11 #include "chrome/browser/browser_process.h" |
13 #include "chrome/browser/browser_shutdown.h" | 12 #include "chrome/browser/browser_shutdown.h" |
14 #include "chrome/browser/download/download_service.h" | 13 #include "chrome/browser/download/download_service.h" |
15 #include "chrome/browser/metrics/thread_watcher.h" | 14 #include "chrome/browser/metrics/thread_watcher.h" |
16 #include "chrome/browser/prefs/pref_service.h" | 15 #include "chrome/browser/prefs/pref_service.h" |
17 #include "chrome/browser/printing/background_printing_manager.h" | |
18 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
19 #include "chrome/browser/profiles/profile_manager.h" | 17 #include "chrome/browser/profiles/profile_manager.h" |
20 #include "chrome/browser/ui/browser.h" | 18 #include "chrome/browser/ui/browser.h" |
21 #include "chrome/browser/ui/browser_window.h" | 19 #include "chrome/browser/ui/browser_window.h" |
22 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 20 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
23 #include "chrome/common/chrome_notification_types.h" | 21 #include "chrome/common/chrome_notification_types.h" |
24 #include "chrome/common/chrome_switches.h" | 22 #include "chrome/common/chrome_switches.h" |
25 #include "chrome/common/pref_names.h" | 23 #include "chrome/common/pref_names.h" |
26 #include "content/public/browser/browser_shutdown.h" | 24 #include "content/public/browser/browser_shutdown.h" |
27 #include "content/public/browser/browser_thread.h" | 25 #include "content/public/browser/browser_thread.h" |
28 #include "content/public/browser/navigation_details.h" | 26 #include "content/public/browser/navigation_details.h" |
29 #include "content/public/browser/notification_registrar.h" | |
30 #include "content/public/browser/notification_service.h" | 27 #include "content/public/browser/notification_service.h" |
31 #include "content/public/browser/render_process_host.h" | |
32 #include "content/public/common/result_codes.h" | |
33 | 28 |
34 #if defined(OS_MACOSX) | 29 #if defined(OS_MACOSX) |
35 #include "chrome/browser/chrome_browser_application_mac.h" | 30 #include "chrome/browser/chrome_browser_application_mac.h" |
36 #endif | 31 #endif |
37 | 32 |
38 #if defined(OS_CHROMEOS) | 33 #if defined(OS_CHROMEOS) |
39 #include "base/chromeos/chromeos_version.h" | 34 #include "base/chromeos/chromeos_version.h" |
40 #include "chrome/browser/chromeos/boot_times_loader.h" | 35 #include "chrome/browser/chromeos/boot_times_loader.h" |
41 #include "chrome/browser/chromeos/login/user_manager.h" | 36 #include "chrome/browser/chromeos/login/user_manager.h" |
42 #include "chromeos/dbus/dbus_thread_manager.h" | 37 #include "chromeos/dbus/dbus_thread_manager.h" |
43 #include "chromeos/dbus/session_manager_client.h" | 38 #include "chromeos/dbus/session_manager_client.h" |
44 #include "chromeos/dbus/update_engine_client.h" | 39 #include "chromeos/dbus/update_engine_client.h" |
45 #endif | 40 #endif |
46 | 41 |
47 using content::WebContents; | 42 namespace browser { |
48 | |
49 namespace { | 43 namespace { |
50 | 44 |
51 // This object is instantiated when the first Browser object is added to the | |
52 // list and delete when the last one is removed. It watches for loads and | |
53 // creates histograms of some global object counts. | |
54 class BrowserActivityObserver : public content::NotificationObserver { | |
55 public: | |
56 BrowserActivityObserver() { | |
57 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
58 content::NotificationService::AllSources()); | |
59 } | |
60 ~BrowserActivityObserver() {} | |
61 | |
62 private: | |
63 // content::NotificationObserver implementation. | |
64 virtual void Observe(int type, | |
65 const content::NotificationSource& source, | |
66 const content::NotificationDetails& details) { | |
67 DCHECK(type == content::NOTIFICATION_NAV_ENTRY_COMMITTED); | |
68 const content::LoadCommittedDetails& load = | |
69 *content::Details<content::LoadCommittedDetails>(details).ptr(); | |
70 if (!load.is_navigation_to_different_page()) | |
71 return; // Don't log for subframes or other trivial types. | |
72 | |
73 LogRenderProcessHostCount(); | |
74 LogBrowserTabCount(); | |
75 } | |
76 | |
77 // Counts the number of active RenderProcessHosts and logs them. | |
78 void LogRenderProcessHostCount() const { | |
79 int hosts_count = 0; | |
80 for (content::RenderProcessHost::iterator i( | |
81 content::RenderProcessHost::AllHostsIterator()); | |
82 !i.IsAtEnd(); i.Advance()) | |
83 ++hosts_count; | |
84 UMA_HISTOGRAM_CUSTOM_COUNTS("MPArch.RPHCountPerLoad", hosts_count, | |
85 1, 50, 50); | |
86 } | |
87 | |
88 // Counts the number of tabs in each browser window and logs them. This is | |
89 // different than the number of WebContents objects since WebContents objects | |
90 // can be used for popups and in dialog boxes. We're just counting toplevel | |
91 // tabs here. | |
92 void LogBrowserTabCount() const { | |
93 int tab_count = 0; | |
94 for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); | |
95 browser_iterator != BrowserList::end(); browser_iterator++) { | |
96 // Record how many tabs each window has open. | |
97 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerWindow", | |
98 (*browser_iterator)->tab_count(), 1, 200, 50); | |
99 tab_count += (*browser_iterator)->tab_count(); | |
100 } | |
101 // Record how many tabs total are open (across all windows). | |
102 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", tab_count, 1, 200, 50); | |
103 | |
104 Browser* browser = BrowserList::GetLastActive(); | |
105 if (browser) { | |
106 // Record how many tabs the active window has open. | |
107 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountActiveWindow", | |
108 browser->tab_count(), 1, 200, 50); | |
109 } | |
110 } | |
111 | |
112 content::NotificationRegistrar registrar_; | |
113 | |
114 DISALLOW_COPY_AND_ASSIGN(BrowserActivityObserver); | |
115 }; | |
116 | |
117 BrowserActivityObserver* activity_observer = NULL; | |
118 | |
119 static BrowserList::BrowserVector& browsers() { | |
120 CR_DEFINE_STATIC_LOCAL(BrowserList::BrowserVector, browser_vector, ()); | |
121 return browser_vector; | |
122 } | |
123 | |
124 static BrowserList::BrowserVector& last_active_browsers() { | |
125 CR_DEFINE_STATIC_LOCAL(BrowserList::BrowserVector, last_active_vector, ()); | |
126 return last_active_vector; | |
127 } | |
128 | |
129 static ObserverList<BrowserList::Observer>& observers() { | |
130 CR_DEFINE_STATIC_LOCAL( | |
131 ObserverList<BrowserList::Observer>, observer_vector, ()); | |
132 return observer_vector; | |
133 } | |
134 | |
135 printing::BackgroundPrintingManager* GetBackgroundPrintingManager() { | |
136 return g_browser_process->background_printing_manager(); | |
137 } | |
138 | |
139 // Returns true if all browsers can be closed without user interaction. | 45 // Returns true if all browsers can be closed without user interaction. |
140 // This currently checks if there is pending download, or if it needs to | 46 // This currently checks if there is pending download, or if it needs to |
141 // handle unload handler. | 47 // handle unload handler. |
142 bool AreAllBrowsersCloseable() { | 48 bool AreAllBrowsersCloseable() { |
143 BrowserList::const_iterator browser_it = BrowserList::begin(); | 49 BrowserList::const_iterator browser_it = BrowserList::begin(); |
144 if (browser_it == BrowserList::end()) | 50 if (browser_it == BrowserList::end()) |
145 return true; | 51 return true; |
146 | 52 |
147 // If there are any downloads active, all browsers are not closeable. | 53 // If there are any downloads active, all browsers are not closeable. |
148 if (DownloadService::DownloadCountAllProfiles() > 0) | 54 if (DownloadService::DownloadCountAllProfiles() > 0) |
149 return false; | 55 return false; |
150 | 56 |
151 // Check TabsNeedBeforeUnloadFired(). | 57 // Check TabsNeedBeforeUnloadFired(). |
152 for (; browser_it != BrowserList::end(); ++browser_it) { | 58 for (; browser_it != BrowserList::end(); ++browser_it) { |
153 if ((*browser_it)->TabsNeedBeforeUnloadFired()) | 59 if ((*browser_it)->TabsNeedBeforeUnloadFired()) |
154 return false; | 60 return false; |
155 } | 61 } |
156 return true; | 62 return true; |
157 } | 63 } |
158 | 64 |
159 // Emits APP_TERMINATING notification. It is guaranteed that the | 65 int g_keep_alive_count = 0; |
160 // notification is sent only once. | |
161 void NotifyAppTerminating() { | |
162 static bool notified = false; | |
163 if (notified) | |
164 return; | |
165 notified = true; | |
166 content::NotificationService::current()->Notify( | |
167 content::NOTIFICATION_APP_TERMINATING, | |
168 content::NotificationService::AllSources(), | |
169 content::NotificationService::NoDetails()); | |
170 } | |
171 | 66 |
172 #if defined(OS_CHROMEOS) | 67 #if defined(OS_CHROMEOS) |
173 // Whether a session manager requested to shutdown. | 68 // Whether a session manager requested to shutdown. |
174 bool g_session_manager_requested_shutdown = true; | 69 bool g_session_manager_requested_shutdown = true; |
175 #endif | 70 #endif |
176 | 71 |
177 } // namespace | 72 } // namespace |
178 | 73 |
179 // static | 74 void MarkAsCleanShutdown() { |
180 void BrowserList::AddBrowser(Browser* browser) { | 75 // TODO(beng): Can this use ProfileManager::GetLoadedProfiles() instead? |
181 DCHECK(browser); | 76 for (BrowserList::const_iterator i = BrowserList::begin(); |
182 browsers().push_back(browser); | 77 i != BrowserList::end(); ++i) { |
183 | |
184 g_browser_process->AddRefModule(); | |
185 | |
186 if (!activity_observer) | |
187 activity_observer = new BrowserActivityObserver; | |
188 | |
189 content::NotificationService::current()->Notify( | |
190 chrome::NOTIFICATION_BROWSER_OPENED, | |
191 content::Source<Browser>(browser), | |
192 content::NotificationService::NoDetails()); | |
193 | |
194 // Send out notifications after add has occurred. Do some basic checking to | |
195 // try to catch evil observers that change the list from under us. | |
196 size_t original_count = observers().size(); | |
197 FOR_EACH_OBSERVER(Observer, observers(), OnBrowserAdded(browser)); | |
198 DCHECK_EQ(original_count, observers().size()) | |
199 << "observer list modified during notification"; | |
200 } | |
201 | |
202 // static | |
203 void BrowserList::MarkAsCleanShutdown() { | |
204 for (const_iterator i = begin(); i != end(); ++i) { | |
205 (*i)->profile()->MarkAsCleanShutdown(); | 78 (*i)->profile()->MarkAsCleanShutdown(); |
206 } | 79 } |
207 } | 80 } |
208 | 81 |
209 void BrowserList::AttemptExitInternal() { | 82 void AttemptExitInternal() { |
210 content::NotificationService::current()->Notify( | 83 content::NotificationService::current()->Notify( |
211 content::NOTIFICATION_APP_EXITING, | 84 content::NOTIFICATION_APP_EXITING, |
212 content::NotificationService::AllSources(), | 85 content::NotificationService::AllSources(), |
213 content::NotificationService::NoDetails()); | 86 content::NotificationService::NoDetails()); |
214 | 87 |
215 #if !defined(OS_MACOSX) | 88 #if !defined(OS_MACOSX) |
216 // On most platforms, closing all windows causes the application to exit. | 89 // On most platforms, closing all windows causes the application to exit. |
217 CloseAllBrowsers(); | 90 CloseAllBrowsers(); |
218 #else | 91 #else |
219 // On the Mac, the application continues to run once all windows are closed. | 92 // On the Mac, the application continues to run once all windows are closed. |
220 // Terminate will result in a CloseAllBrowsers() call, and once (and if) | 93 // Terminate will result in a CloseAllBrowsers() call, and once (and if) |
221 // that is done, will cause the application to exit cleanly. | 94 // that is done, will cause the application to exit cleanly. |
222 chrome_browser_application_mac::Terminate(); | 95 chrome_browser_application_mac::Terminate(); |
223 #endif | 96 #endif |
224 } | 97 } |
225 | 98 |
226 // static | 99 void NotifyAppTerminating() { |
227 void BrowserList::NotifyAndTerminate(bool fast_path) { | 100 static bool notified = false; |
| 101 if (notified) |
| 102 return; |
| 103 notified = true; |
| 104 content::NotificationService::current()->Notify( |
| 105 content::NOTIFICATION_APP_TERMINATING, |
| 106 content::NotificationService::AllSources(), |
| 107 content::NotificationService::NoDetails()); |
| 108 } |
| 109 |
| 110 void NotifyAndTerminate(bool fast_path) { |
228 #if defined(OS_CHROMEOS) | 111 #if defined(OS_CHROMEOS) |
229 static bool notified = false; | 112 static bool notified = false; |
230 // Don't ask SessionManager to shutdown if | 113 // Don't ask SessionManager to shutdown if |
231 // a) a shutdown request has already been sent. | 114 // a) a shutdown request has already been sent. |
232 // b) shutdown request comes from session manager. | 115 // b) shutdown request comes from session manager. |
233 if (notified || g_session_manager_requested_shutdown) | 116 if (notified || g_session_manager_requested_shutdown) |
234 return; | 117 return; |
235 notified = true; | 118 notified = true; |
236 #endif | 119 #endif |
237 | 120 |
(...skipping 10 matching lines...) Expand all Loading... |
248 chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) { | 131 chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) { |
249 update_engine_client->RebootAfterUpdate(); | 132 update_engine_client->RebootAfterUpdate(); |
250 } else { | 133 } else { |
251 chromeos::DBusThreadManager::Get()->GetSessionManagerClient() | 134 chromeos::DBusThreadManager::Get()->GetSessionManagerClient() |
252 ->StopSession(); | 135 ->StopSession(); |
253 } | 136 } |
254 } else { | 137 } else { |
255 // If running the Chrome OS build, but we're not on the device, act | 138 // If running the Chrome OS build, but we're not on the device, act |
256 // as if we received signal from SessionManager. | 139 // as if we received signal from SessionManager. |
257 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 140 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
258 base::Bind(&BrowserList::ExitCleanly)); | 141 base::Bind(&browser::ExitCleanly)); |
259 } | 142 } |
260 #endif | 143 #endif |
261 } | 144 } |
262 | 145 |
263 void BrowserList::OnAppExiting() { | 146 void OnAppExiting() { |
264 static bool notified = false; | 147 static bool notified = false; |
265 if (notified) | 148 if (notified) |
266 return; | 149 return; |
267 notified = true; | 150 notified = true; |
268 | |
269 delete activity_observer; | |
270 activity_observer = NULL; | |
271 HandleAppExitingForPlatform(); | 151 HandleAppExitingForPlatform(); |
272 } | 152 } |
273 | 153 |
274 // static | 154 void CloseAllBrowsers() { |
275 void BrowserList::RemoveBrowser(Browser* browser) { | |
276 RemoveBrowserFrom(browser, &last_active_browsers()); | |
277 | |
278 // Many UI tests rely on closing the last browser window quitting the | |
279 // application. | |
280 // Mac: Closing all windows does not indicate quitting the application. Lie | |
281 // for now and ignore behavior outside of unit tests. | |
282 // ChromeOS: Force closing last window to close app with flag. | |
283 // TODO(andybons | pkotwicz): Fix the UI tests to Do The Right Thing. | |
284 #if defined(OS_CHROMEOS) | |
285 bool closing_app; | |
286 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
287 switches::kDisableZeroBrowsersOpenForTests)) | |
288 closing_app = (browsers().size() == 1); | |
289 else | |
290 closing_app = (browsers().size() == 1 && | |
291 browser_shutdown::IsTryingToQuit()); | |
292 #else | |
293 bool closing_app = (browsers().size() == 1); | |
294 #endif // OS_CHROMEOS | |
295 | |
296 content::NotificationService::current()->Notify( | |
297 chrome::NOTIFICATION_BROWSER_CLOSED, | |
298 content::Source<Browser>(browser), | |
299 content::Details<bool>(&closing_app)); | |
300 | |
301 RemoveBrowserFrom(browser, &browsers()); | |
302 | |
303 // Do some basic checking to try to catch evil observers | |
304 // that change the list from under us. | |
305 size_t original_count = observers().size(); | |
306 FOR_EACH_OBSERVER(Observer, observers(), OnBrowserRemoved(browser)); | |
307 DCHECK_EQ(original_count, observers().size()) | |
308 << "observer list modified during notification"; | |
309 | |
310 g_browser_process->ReleaseModule(); | |
311 | |
312 // If we're exiting, send out the APP_TERMINATING notification to allow other | |
313 // modules to shut themselves down. | |
314 if (browsers().empty() && | |
315 (browser_shutdown::IsTryingToQuit() || | |
316 g_browser_process->IsShuttingDown())) { | |
317 // Last browser has just closed, and this is a user-initiated quit or there | |
318 // is no module keeping the app alive, so send out our notification. No need | |
319 // to call ProfileManager::ShutdownSessionServices() as part of the | |
320 // shutdown, because Browser::WindowClosing() already makes sure that the | |
321 // SessionService is created and notified. | |
322 NotifyAppTerminating(); | |
323 OnAppExiting(); | |
324 } | |
325 } | |
326 | |
327 // static | |
328 void BrowserList::AddObserver(BrowserList::Observer* observer) { | |
329 observers().AddObserver(observer); | |
330 } | |
331 | |
332 // static | |
333 void BrowserList::RemoveObserver(BrowserList::Observer* observer) { | |
334 observers().RemoveObserver(observer); | |
335 } | |
336 | |
337 // static | |
338 void BrowserList::CloseAllBrowsers() { | |
339 bool session_ending = | 155 bool session_ending = |
340 browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION; | 156 browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION; |
341 // Tell everyone that we are shutting down. | 157 // Tell everyone that we are shutting down. |
342 browser_shutdown::SetTryingToQuit(true); | 158 browser_shutdown::SetTryingToQuit(true); |
343 | 159 |
344 // Before we close the browsers shutdown all session services. That way an | 160 // Before we close the browsers shutdown all session services. That way an |
345 // exit can restore all browsers open before exiting. | 161 // exit can restore all browsers open before exiting. |
346 ProfileManager::ShutdownSessionServices(); | 162 ProfileManager::ShutdownSessionServices(); |
347 | 163 |
348 // If there are no browsers, send the APP_TERMINATING action here. Otherwise, | 164 // If there are no browsers, send the APP_TERMINATING action here. Otherwise, |
349 // it will be sent by RemoveBrowser() when the last browser has closed. | 165 // it will be sent by RemoveBrowser() when the last browser has closed. |
350 if (browser_shutdown::ShuttingDownWithoutClosingBrowsers() || | 166 if (browser_shutdown::ShuttingDownWithoutClosingBrowsers() || |
351 browsers().empty()) { | 167 BrowserList::empty()) { |
352 NotifyAndTerminate(true); | 168 NotifyAndTerminate(true); |
353 OnAppExiting(); | 169 OnAppExiting(); |
354 return; | 170 return; |
355 } | 171 } |
356 | 172 |
357 #if defined(OS_CHROMEOS) | 173 #if defined(OS_CHROMEOS) |
358 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( | 174 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( |
359 "StartedClosingWindows", false); | 175 "StartedClosingWindows", false); |
360 #endif | 176 #endif |
361 for (BrowserList::const_iterator i = BrowserList::begin(); | 177 for (BrowserList::const_iterator i = BrowserList::begin(); |
(...skipping 17 matching lines...) Expand all Loading... |
379 if (i != BrowserList::end() && browser == *i) { | 195 if (i != BrowserList::end() && browser == *i) { |
380 // Destroying the browser should have removed it from the browser list. | 196 // Destroying the browser should have removed it from the browser list. |
381 // We should never get here. | 197 // We should never get here. |
382 NOTREACHED(); | 198 NOTREACHED(); |
383 return; | 199 return; |
384 } | 200 } |
385 } | 201 } |
386 } | 202 } |
387 } | 203 } |
388 | 204 |
389 void BrowserList::CloseAllBrowsersWithProfile(Profile* profile) { | 205 void AttemptUserExit() { |
390 BrowserVector browsers_to_close; | |
391 for (BrowserList::const_iterator i = BrowserList::begin(); | |
392 i != BrowserList::end(); ++i) { | |
393 if ((*i)->profile()->GetOriginalProfile() == profile->GetOriginalProfile()) | |
394 browsers_to_close.push_back(*i); | |
395 } | |
396 | |
397 for (BrowserVector::const_iterator i = browsers_to_close.begin(); | |
398 i != browsers_to_close.end(); ++i) { | |
399 (*i)->window()->Close(); | |
400 } | |
401 } | |
402 | |
403 // static | |
404 void BrowserList::AttemptUserExit() { | |
405 #if defined(OS_CHROMEOS) | 206 #if defined(OS_CHROMEOS) |
406 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("LogoutStarted", false); | 207 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("LogoutStarted", false); |
407 // Write /tmp/uptime-logout-started as well. | 208 // Write /tmp/uptime-logout-started as well. |
408 const char kLogoutStarted[] = "logout-started"; | 209 const char kLogoutStarted[] = "logout-started"; |
409 chromeos::BootTimesLoader::Get()->RecordCurrentStats(kLogoutStarted); | 210 chromeos::BootTimesLoader::Get()->RecordCurrentStats(kLogoutStarted); |
410 | 211 |
411 // Login screen should show up in owner's locale. | 212 // Login screen should show up in owner's locale. |
412 PrefService* state = g_browser_process->local_state(); | 213 PrefService* state = g_browser_process->local_state(); |
413 if (state) { | 214 if (state) { |
414 std::string owner_locale = state->GetString(prefs::kOwnerLocale); | 215 std::string owner_locale = state->GetString(prefs::kOwnerLocale); |
415 if (!owner_locale.empty() && | 216 if (!owner_locale.empty() && |
416 state->GetString(prefs::kApplicationLocale) != owner_locale && | 217 state->GetString(prefs::kApplicationLocale) != owner_locale && |
417 !state->IsManagedPreference(prefs::kApplicationLocale)) { | 218 !state->IsManagedPreference(prefs::kApplicationLocale)) { |
418 state->SetString(prefs::kApplicationLocale, owner_locale); | 219 state->SetString(prefs::kApplicationLocale, owner_locale); |
419 state->CommitPendingWrite(); | 220 state->CommitPendingWrite(); |
420 } | 221 } |
421 } | 222 } |
422 g_session_manager_requested_shutdown = false; | 223 g_session_manager_requested_shutdown = false; |
423 // On ChromeOS, always terminate the browser, regardless of the result of | 224 // On ChromeOS, always terminate the browser, regardless of the result of |
424 // AreAllBrowsersCloseable(). See crbug.com/123107. | 225 // AreAllBrowsersCloseable(). See crbug.com/123107. |
425 BrowserList::NotifyAndTerminate(true); | 226 NotifyAndTerminate(true); |
426 #else | 227 #else |
427 // Reset the restart bit that might have been set in cancelled restart | 228 // Reset the restart bit that might have been set in cancelled restart |
428 // request. | 229 // request. |
429 PrefService* pref_service = g_browser_process->local_state(); | 230 PrefService* pref_service = g_browser_process->local_state(); |
430 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, false); | 231 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, false); |
431 AttemptExitInternal(); | 232 AttemptExitInternal(); |
432 #endif | 233 #endif |
433 } | 234 } |
434 | 235 |
435 // static | 236 void AttemptRestart() { |
436 void BrowserList::AttemptRestart() { | |
437 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 237 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
438 switches::kDisableRestoreSessionState)) { | 238 switches::kDisableRestoreSessionState)) { |
439 BrowserVector::const_iterator it; | 239 // TODO(beng): Can this use ProfileManager::GetLoadedProfiles instead? |
440 for (it = begin(); it != end(); ++it) | 240 BrowserList::const_iterator it; |
| 241 for (it = BrowserList::begin(); it != BrowserList::end(); ++it) |
441 content::BrowserContext::SaveSessionState((*it)->profile()); | 242 content::BrowserContext::SaveSessionState((*it)->profile()); |
442 } | 243 } |
443 | 244 |
444 PrefService* pref_service = g_browser_process->local_state(); | 245 PrefService* pref_service = g_browser_process->local_state(); |
445 pref_service->SetBoolean(prefs::kWasRestarted, true); | 246 pref_service->SetBoolean(prefs::kWasRestarted, true); |
446 | 247 |
447 #if defined(OS_CHROMEOS) | 248 #if defined(OS_CHROMEOS) |
448 // For CrOS instead of browser restart (which is not supported) perform a full | 249 // For CrOS instead of browser restart (which is not supported) perform a full |
449 // sign out. Session will be only restored if user has that setting set. | 250 // sign out. Session will be only restored if user has that setting set. |
450 // Same session restore behavior happens in case of full restart after update. | 251 // Same session restore behavior happens in case of full restart after update. |
451 AttemptUserExit(); | 252 AttemptUserExit(); |
452 #else | 253 #else |
453 // Set the flag to restore state after the restart. | 254 // Set the flag to restore state after the restart. |
454 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); | 255 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); |
455 AttemptExit(); | 256 AttemptExit(); |
456 #endif | 257 #endif |
457 } | 258 } |
458 | 259 |
459 // static | 260 void AttemptExit() { |
460 void BrowserList::AttemptExit() { | |
461 // If we know that all browsers can be closed without blocking, | 261 // If we know that all browsers can be closed without blocking, |
462 // don't notify users of crashes beyond this point. | 262 // don't notify users of crashes beyond this point. |
463 // Note that MarkAsCleanShutdown does not set UMA's exit cleanly bit | 263 // Note that MarkAsCleanShutdown does not set UMA's exit cleanly bit |
464 // so crashes during shutdown are still reported in UMA. | 264 // so crashes during shutdown are still reported in UMA. |
465 if (AreAllBrowsersCloseable()) | 265 if (AreAllBrowsersCloseable()) |
466 MarkAsCleanShutdown(); | 266 MarkAsCleanShutdown(); |
467 AttemptExitInternal(); | 267 AttemptExitInternal(); |
468 } | 268 } |
469 | 269 |
470 #if defined(OS_CHROMEOS) | 270 #if defined(OS_CHROMEOS) |
471 // A function called when SIGTERM is received. | 271 // A function called when SIGTERM is received. |
472 // static | 272 void ExitCleanly() { |
473 void BrowserList::ExitCleanly() { | |
474 // We always mark exit cleanly because SessionManager may kill | 273 // We always mark exit cleanly because SessionManager may kill |
475 // chrome in 3 seconds after SIGTERM. | 274 // chrome in 3 seconds after SIGTERM. |
476 g_browser_process->EndSession(); | 275 g_browser_process->EndSession(); |
477 | 276 |
478 // Don't block when SIGTERM is received. AreaAllBrowsersCloseable() | 277 // Don't block when SIGTERM is received. AreaAllBrowsersCloseable() |
479 // can be false in following cases. a) power-off b) signout from | 278 // can be false in following cases. a) power-off b) signout from |
480 // screen locker. | 279 // screen locker. |
481 if (!AreAllBrowsersCloseable()) | 280 if (!AreAllBrowsersCloseable()) |
482 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); | 281 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); |
483 AttemptExitInternal(); | 282 AttemptExitInternal(); |
484 } | 283 } |
485 #endif | 284 #endif |
486 | 285 |
487 // static | 286 void SessionEnding() { |
488 void BrowserList::SessionEnding() { | |
489 // This is a time-limited shutdown where we need to write as much to | 287 // This is a time-limited shutdown where we need to write as much to |
490 // disk as we can as soon as we can, and where we must kill the | 288 // disk as we can as soon as we can, and where we must kill the |
491 // process within a hang timeout to avoid user prompts. | 289 // process within a hang timeout to avoid user prompts. |
492 | 290 |
493 // Start watching for hang during shutdown, and crash it if takes too long. | 291 // Start watching for hang during shutdown, and crash it if takes too long. |
494 // We disarm when |shutdown_watcher| object is destroyed, which is when we | 292 // We disarm when |shutdown_watcher| object is destroyed, which is when we |
495 // exit this function. | 293 // exit this function. |
496 ShutdownWatcherHelper shutdown_watcher; | 294 ShutdownWatcherHelper shutdown_watcher; |
497 shutdown_watcher.Arm(base::TimeDelta::FromSeconds(90)); | 295 shutdown_watcher.Arm(base::TimeDelta::FromSeconds(90)); |
498 | 296 |
499 // EndSession is invoked once per frame. Only do something the first time. | 297 // EndSession is invoked once per frame. Only do something the first time. |
500 static bool already_ended = false; | 298 static bool already_ended = false; |
501 // We may get called in the middle of shutdown, e.g. http://crbug.com/70852 | 299 // We may get called in the middle of shutdown, e.g. http://crbug.com/70852 |
502 // In this case, do nothing. | 300 // In this case, do nothing. |
503 if (already_ended || !content::NotificationService::current()) | 301 if (already_ended || !content::NotificationService::current()) |
504 return; | 302 return; |
505 already_ended = true; | 303 already_ended = true; |
506 | 304 |
507 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); | 305 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); |
508 | 306 |
509 content::NotificationService::current()->Notify( | 307 content::NotificationService::current()->Notify( |
510 content::NOTIFICATION_APP_EXITING, | 308 content::NOTIFICATION_APP_EXITING, |
511 content::NotificationService::AllSources(), | 309 content::NotificationService::AllSources(), |
512 content::NotificationService::NoDetails()); | 310 content::NotificationService::NoDetails()); |
513 | 311 |
514 // Write important data first. | 312 // Write important data first. |
515 g_browser_process->EndSession(); | 313 g_browser_process->EndSession(); |
516 | 314 |
517 BrowserList::CloseAllBrowsers(); | 315 CloseAllBrowsers(); |
518 | 316 |
519 // Send out notification. This is used during testing so that the test harness | 317 // Send out notification. This is used during testing so that the test harness |
520 // can properly shutdown before we exit. | 318 // can properly shutdown before we exit. |
521 content::NotificationService::current()->Notify( | 319 content::NotificationService::current()->Notify( |
522 chrome::NOTIFICATION_SESSION_END, | 320 chrome::NOTIFICATION_SESSION_END, |
523 content::NotificationService::AllSources(), | 321 content::NotificationService::AllSources(), |
524 content::NotificationService::NoDetails()); | 322 content::NotificationService::NoDetails()); |
525 | 323 |
526 // This will end by terminating the process. | 324 // This will end by terminating the process. |
527 content::ImmediateShutdownAndExitProcess(); | 325 content::ImmediateShutdownAndExitProcess(); |
528 } | 326 } |
529 | 327 |
530 // static | 328 void StartKeepAlive() { |
531 int BrowserList::keep_alive_count_ = 0; | |
532 | |
533 // static | |
534 void BrowserList::StartKeepAlive() { | |
535 // Increment the browser process refcount as long as we're keeping the | 329 // Increment the browser process refcount as long as we're keeping the |
536 // application alive. | 330 // application alive. |
537 if (!WillKeepAlive()) | 331 if (!WillKeepAlive()) |
538 g_browser_process->AddRefModule(); | 332 g_browser_process->AddRefModule(); |
539 keep_alive_count_++; | 333 ++g_keep_alive_count; |
540 } | 334 } |
541 | 335 |
542 // static | 336 void EndKeepAlive() { |
543 void BrowserList::EndKeepAlive() { | 337 DCHECK_GT(g_keep_alive_count, 0); |
544 DCHECK_GT(keep_alive_count_, 0); | 338 --g_keep_alive_count; |
545 keep_alive_count_--; | |
546 | 339 |
547 DCHECK(g_browser_process); | 340 DCHECK(g_browser_process); |
548 // Although we should have a browser process, if there is none, | 341 // Although we should have a browser process, if there is none, |
549 // there is nothing to do. | 342 // there is nothing to do. |
550 if (!g_browser_process) return; | 343 if (!g_browser_process) return; |
551 | 344 |
552 // Allow the app to shutdown again. | 345 // Allow the app to shutdown again. |
553 if (!WillKeepAlive()) { | 346 if (!WillKeepAlive()) { |
554 g_browser_process->ReleaseModule(); | 347 g_browser_process->ReleaseModule(); |
555 // If there are no browsers open and we aren't already shutting down, | 348 // If there are no browsers open and we aren't already shutting down, |
556 // initiate a shutdown. Also skips shutdown if this is a unit test | 349 // initiate a shutdown. Also skips shutdown if this is a unit test |
557 // (MessageLoop::current() == null). | 350 // (MessageLoop::current() == null). |
558 if (browsers().empty() && !browser_shutdown::IsTryingToQuit() && | 351 if (BrowserList::empty() && !browser_shutdown::IsTryingToQuit() && |
559 MessageLoop::current()) | 352 MessageLoop::current()) |
560 CloseAllBrowsers(); | 353 CloseAllBrowsers(); |
561 } | 354 } |
562 } | 355 } |
563 | 356 |
564 // static | 357 bool WillKeepAlive() { |
565 bool BrowserList::WillKeepAlive() { | 358 return g_keep_alive_count > 0; |
566 return keep_alive_count_ > 0; | |
567 } | 359 } |
568 | 360 |
569 // static | 361 } // namespace browser |
570 BrowserList::const_iterator BrowserList::begin() { | |
571 return browsers().begin(); | |
572 } | |
573 | |
574 // static | |
575 BrowserList::const_iterator BrowserList::end() { | |
576 return browsers().end(); | |
577 } | |
578 | |
579 // static | |
580 bool BrowserList::empty() { | |
581 return browsers().empty(); | |
582 } | |
583 | |
584 // static | |
585 size_t BrowserList::size() { | |
586 return browsers().size(); | |
587 } | |
588 | |
589 // static | |
590 void BrowserList::SetLastActive(Browser* browser) { | |
591 // If the browser is currently trying to quit, we don't want to set the last | |
592 // active browser because that can alter the last active browser that the user | |
593 // intended depending on the order in which the windows close. | |
594 if (browser_shutdown::IsTryingToQuit()) | |
595 return; | |
596 RemoveBrowserFrom(browser, &last_active_browsers()); | |
597 last_active_browsers().push_back(browser); | |
598 | |
599 FOR_EACH_OBSERVER(Observer, observers(), OnBrowserSetLastActive(browser)); | |
600 } | |
601 | |
602 // static | |
603 Browser* BrowserList::GetLastActive() { | |
604 if (!last_active_browsers().empty()) | |
605 return *(last_active_browsers().rbegin()); | |
606 | |
607 return NULL; | |
608 } | |
609 | |
610 // static | |
611 BrowserList::const_reverse_iterator BrowserList::begin_last_active() { | |
612 return last_active_browsers().rbegin(); | |
613 } | |
614 | |
615 // static | |
616 BrowserList::const_reverse_iterator BrowserList::end_last_active() { | |
617 return last_active_browsers().rend(); | |
618 } | |
619 | |
620 // static | |
621 bool BrowserList::IsOffTheRecordSessionActive() { | |
622 for (BrowserList::const_iterator i = BrowserList::begin(); | |
623 i != BrowserList::end(); ++i) { | |
624 if ((*i)->profile()->IsOffTheRecord()) | |
625 return true; | |
626 } | |
627 return false; | |
628 } | |
629 | |
630 // static | |
631 bool BrowserList::IsOffTheRecordSessionActiveForProfile(Profile* profile) { | |
632 #if defined(OS_CHROMEOS) | |
633 // In ChromeOS, we assume that the default profile is always valid, so if | |
634 // we are in guest mode, keep the OTR profile active so it won't be deleted. | |
635 if (chromeos::UserManager::Get()->IsLoggedInAsGuest()) | |
636 return true; | |
637 #endif | |
638 for (BrowserList::const_iterator i = BrowserList::begin(); | |
639 i != BrowserList::end(); ++i) { | |
640 if ((*i)->profile()->IsSameProfile(profile) && | |
641 (*i)->profile()->IsOffTheRecord()) { | |
642 return true; | |
643 } | |
644 } | |
645 return false; | |
646 } | |
647 | |
648 // static | |
649 void BrowserList::RemoveBrowserFrom(Browser* browser, | |
650 BrowserVector* browser_list) { | |
651 const iterator remove_browser = | |
652 std::find(browser_list->begin(), browser_list->end(), browser); | |
653 if (remove_browser != browser_list->end()) | |
654 browser_list->erase(remove_browser); | |
655 } | |
656 | |
657 TabContentsIterator::TabContentsIterator() | |
658 : browser_iterator_(BrowserList::begin()), | |
659 web_view_index_(-1), | |
660 bg_printing_iterator_(GetBackgroundPrintingManager()->begin()), | |
661 cur_(NULL) { | |
662 Advance(); | |
663 } | |
664 | |
665 void TabContentsIterator::Advance() { | |
666 // The current WebContents should be valid unless we are at the beginning. | |
667 DCHECK(cur_ || (web_view_index_ == -1 && | |
668 browser_iterator_ == BrowserList::begin())) | |
669 << "Trying to advance past the end"; | |
670 | |
671 // Update cur_ to the next WebContents in the list. | |
672 while (browser_iterator_ != BrowserList::end()) { | |
673 if (++web_view_index_ >= (*browser_iterator_)->tab_count()) { | |
674 // Advance to the next Browser in the list. | |
675 ++browser_iterator_; | |
676 web_view_index_ = -1; | |
677 continue; | |
678 } | |
679 | |
680 TabContentsWrapper* next_tab = | |
681 (*browser_iterator_)->GetTabContentsWrapperAt(web_view_index_); | |
682 if (next_tab) { | |
683 cur_ = next_tab; | |
684 return; | |
685 } | |
686 } | |
687 // If no more WebContents from Browsers, check the BackgroundPrintingManager. | |
688 while (bg_printing_iterator_ != GetBackgroundPrintingManager()->end()) { | |
689 cur_ = *bg_printing_iterator_; | |
690 CHECK(cur_); | |
691 ++bg_printing_iterator_; | |
692 return; | |
693 } | |
694 // Reached the end - no more WebContents. | |
695 cur_ = NULL; | |
696 } | |
OLD | NEW |