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 3// found in the LICENSE file. 4 5#include "chrome/browser/lifetime/application_lifetime.h" 6 7#include "ash/shell.h" 8#include "base/command_line.h" 9#include "base/logging.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/message_loop/message_loop.h" 12#include "base/prefs/pref_service.h" 13#include "build/build_config.h" 14#include "chrome/browser/browser_process.h" 15#include "chrome/browser/browser_process_platform_part.h" 16#include "chrome/browser/browser_shutdown.h" 17#include "chrome/browser/chrome_notification_types.h" 18#include "chrome/browser/download/download_service.h" 19#include "chrome/browser/metrics/thread_watcher.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chrome/browser/profiles/profile_manager.h" 22#include "chrome/browser/ui/browser.h" 23#include "chrome/browser/ui/browser_finder.h" 24#include "chrome/browser/ui/browser_iterator.h" 25#include "chrome/browser/ui/browser_tabstrip.h" 26#include "chrome/browser/ui/browser_window.h" 27#include "chrome/browser/ui/tabs/tab_strip_model.h" 28#include "chrome/common/chrome_switches.h" 29#include "chrome/common/pref_names.h" 30#include "content/public/browser/browser_shutdown.h" 31#include "content/public/browser/browser_thread.h" 32#include "content/public/browser/navigation_details.h" 33#include "content/public/browser/notification_service.h" 34 35#if defined(OS_CHROMEOS) 36#include "base/chromeos/chromeos_version.h" 37#include "chrome/browser/chromeos/boot_times_loader.h" 38#include "chrome/browser/chromeos/login/user_manager.h" 39#include "chromeos/dbus/dbus_thread_manager.h" 40#include "chromeos/dbus/session_manager_client.h" 41#include "chromeos/dbus/update_engine_client.h" 42#endif 43 44namespace chrome { 45namespace { 46 47// Returns true if all browsers can be closed without user interaction. 48// This currently checks if there is pending download, or if it needs to 49// handle unload handler. 50bool AreAllBrowsersCloseable() { 51 chrome::BrowserIterator browser_it; 52 if (browser_it.done()) 53 return true; 54 55 // If there are any downloads active, all browsers are not closeable. 56 if (DownloadService::DownloadCountAllProfiles() > 0) 57 return false; 58 59 // Check TabsNeedBeforeUnloadFired(). 60 for (; !browser_it.done(); browser_it.Next()) { 61 if (browser_it->TabsNeedBeforeUnloadFired()) 62 return false; 63 } 64 return true; 65} 66 67int g_keep_alive_count = 0; 68 69#if defined(OS_CHROMEOS) 70// Whether a session manager requested to shutdown. 71bool g_session_manager_requested_shutdown = true; 72#endif 73 74} // namespace 75 76void MarkAsCleanShutdown() { 77 // TODO(beng): Can this use ProfileManager::GetLoadedProfiles() instead? 78 for (chrome::BrowserIterator it; !it.done(); it.Next()) 79 it->profile()->SetExitType(Profile::EXIT_NORMAL); 80} 81 82void AttemptExitInternal() { 83 content::NotificationService::current()->Notify( 84 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, 85 content::NotificationService::AllSources(), 86 content::NotificationService::NoDetails()); 87 88 g_browser_process->platform_part()->AttemptExit(); 89} 90 91void CloseAllBrowsers() { 92 bool session_ending = 93 browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION; 94 // Tell everyone that we are shutting down. 95 browser_shutdown::SetTryingToQuit(true); 96 97#if defined(ENABLE_SESSION_SERVICE) 98 // Before we close the browsers shutdown all session services. That way an 99 // exit can restore all browsers open before exiting. 100 ProfileManager::ShutdownSessionServices(); 101#endif 102 103 // If there are no browsers, send the APP_TERMINATING action here. Otherwise, 104 // it will be sent by RemoveBrowser() when the last browser has closed. 105 if (browser_shutdown::ShuttingDownWithoutClosingBrowsers() || 106 chrome::GetTotalBrowserCount() == 0) { 107 chrome::NotifyAndTerminate(true); 108 chrome::OnAppExiting(); 109 return; 110 } 111 112#if defined(OS_CHROMEOS) 113 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( 114 "StartedClosingWindows", false); 115#endif 116 for (scoped_ptr<chrome::BrowserIterator> it_ptr( 117 new chrome::BrowserIterator()); 118 !it_ptr->done();) { 119 Browser* browser = **it_ptr; 120 browser->window()->Close(); 121 if (!session_ending) { 122 it_ptr->Next(); 123 } else { 124 // This path is hit during logoff/power-down. In this case we won't get 125 // a final message and so we force the browser to be deleted. 126 // Close doesn't immediately destroy the browser 127 // (Browser::TabStripEmpty() uses invoke later) but when we're ending the 128 // session we need to make sure the browser is destroyed now. So, invoke 129 // DestroyBrowser to make sure the browser is deleted and cleanup can 130 // happen. 131 while (browser->tab_strip_model()->count()) 132 delete browser->tab_strip_model()->GetWebContentsAt(0); 133 browser->window()->DestroyBrowser(); 134 it_ptr.reset(new chrome::BrowserIterator()); 135 if (!it_ptr->done() && browser == **it_ptr) { 136 // Destroying the browser should have removed it from the browser list. 137 // We should never get here. 138 NOTREACHED(); 139 return; 140 } 141 } 142 } 143} 144 145void AttemptUserExit() { 146#if defined(OS_CHROMEOS) 147 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("LogoutStarted", false); 148 // Write /tmp/uptime-logout-started as well. 149 const char kLogoutStarted[] = "logout-started"; 150 chromeos::BootTimesLoader::Get()->RecordCurrentStats(kLogoutStarted); 151 152 // Login screen should show up in owner's locale. 153 PrefService* state = g_browser_process->local_state(); 154 if (state) { 155 std::string owner_locale = state->GetString(prefs::kOwnerLocale); 156 if (!owner_locale.empty() && 157 state->GetString(prefs::kApplicationLocale) != owner_locale && 158 !state->IsManagedPreference(prefs::kApplicationLocale)) { 159 state->SetString(prefs::kApplicationLocale, owner_locale); 160 state->CommitPendingWrite(); 161 } 162 } 163 g_session_manager_requested_shutdown = false; 164 // On ChromeOS, always terminate the browser, regardless of the result of 165 // AreAllBrowsersCloseable(). See crbug.com/123107. 166 chrome::NotifyAndTerminate(true); 167#else 168 // Reset the restart bit that might have been set in cancelled restart 169 // request. 170 PrefService* pref_service = g_browser_process->local_state(); 171 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, false); 172 AttemptExitInternal(); 173#endif 174} 175 176// The Android implementation is in application_lifetime_android.cc 177#if !defined(OS_ANDROID) 178void AttemptRestart() { 179 // TODO(beng): Can this use ProfileManager::GetLoadedProfiles instead? 180 for (chrome::BrowserIterator it; !it.done(); it.Next()) 181 content::BrowserContext::SaveSessionState(it->profile()); 182 183 PrefService* pref_service = g_browser_process->local_state(); 184 pref_service->SetBoolean(prefs::kWasRestarted, true); 185 186#if defined(OS_CHROMEOS) 187 // For CrOS instead of browser restart (which is not supported) perform a full 188 // sign out. Session will be only restored if user has that setting set. 189 // Same session restore behavior happens in case of full restart after update. 190 AttemptUserExit(); 191#else 192 // Set the flag to restore state after the restart. 193 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); 194 AttemptExit(); 195#endif 196} 197#endif 198 199#if defined(OS_WIN) 200void AttemptRestartWithModeSwitch() { 201 // The kRestartSwitchMode preference does not exists for Windows 7 and older 202 // operating systems so there is no need for OS version check. 203 PrefService* prefs = g_browser_process->local_state(); 204 prefs->SetBoolean(prefs::kRestartSwitchMode, true); 205 AttemptRestart(); 206} 207#endif 208 209void AttemptExit() { 210 // If we know that all browsers can be closed without blocking, 211 // don't notify users of crashes beyond this point. 212 // Note that MarkAsCleanShutdown() does not set UMA's exit cleanly bit 213 // so crashes during shutdown are still reported in UMA. 214#if !defined(OS_ANDROID) 215 // Android doesn't use Browser. 216 if (AreAllBrowsersCloseable()) 217 MarkAsCleanShutdown(); 218#endif 219 AttemptExitInternal(); 220} 221 222#if defined(OS_CHROMEOS) 223// A function called when SIGTERM is received. 224void ExitCleanly() { 225 // We always mark exit cleanly because SessionManager may kill 226 // chrome in 3 seconds after SIGTERM. 227 g_browser_process->EndSession(); 228 229 // Don't block when SIGTERM is received. AreaAllBrowsersCloseable() 230 // can be false in following cases. a) power-off b) signout from 231 // screen locker. 232 if (!AreAllBrowsersCloseable()) 233 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); 234 AttemptExitInternal(); 235} 236#endif 237 238void SessionEnding() { 239 // This is a time-limited shutdown where we need to write as much to 240 // disk as we can as soon as we can, and where we must kill the 241 // process within a hang timeout to avoid user prompts. 242 243 // Start watching for hang during shutdown, and crash it if takes too long. 244 // We disarm when |shutdown_watcher| object is destroyed, which is when we 245 // exit this function. 246 ShutdownWatcherHelper shutdown_watcher; 247 shutdown_watcher.Arm(base::TimeDelta::FromSeconds(90)); 248 249 // EndSession is invoked once per frame. Only do something the first time. 250 static bool already_ended = false; 251 // We may get called in the middle of shutdown, e.g. http://crbug.com/70852 252 // In this case, do nothing. 253 if (already_ended || !content::NotificationService::current()) 254 return; 255 already_ended = true; 256 257 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); 258 259 content::NotificationService::current()->Notify( 260 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, 261 content::NotificationService::AllSources(), 262 content::NotificationService::NoDetails()); 263 264 // Write important data first. 265 g_browser_process->EndSession(); 266 267 CloseAllBrowsers(); 268 269 // Send out notification. This is used during testing so that the test harness 270 // can properly shutdown before we exit. 271 content::NotificationService::current()->Notify( 272 chrome::NOTIFICATION_SESSION_END, 273 content::NotificationService::AllSources(), 274 content::NotificationService::NoDetails()); 275 276 // This will end by terminating the process. 277 content::ImmediateShutdownAndExitProcess(); 278} 279 280void StartKeepAlive() { 281 // Increment the browser process refcount as long as we're keeping the 282 // application alive. 283 if (!WillKeepAlive()) 284 g_browser_process->AddRefModule(); 285 ++g_keep_alive_count; 286} 287 288void EndKeepAlive() { 289 DCHECK_GT(g_keep_alive_count, 0); 290 --g_keep_alive_count; 291 292 DCHECK(g_browser_process); 293 // Although we should have a browser process, if there is none, 294 // there is nothing to do. 295 if (!g_browser_process) return; 296 297 // Allow the app to shutdown again. 298 if (!WillKeepAlive()) { 299 g_browser_process->ReleaseModule(); 300 // If there are no browsers open and we aren't already shutting down, 301 // initiate a shutdown. Also skips shutdown if this is a unit test 302 // (MessageLoop::current() == null). 303 if (chrome::GetTotalBrowserCount() == 0 && 304 !browser_shutdown::IsTryingToQuit() && 305 base::MessageLoop::current()) { 306 CloseAllBrowsers(); 307 } 308 } 309} 310 311bool WillKeepAlive() { 312 return g_keep_alive_count > 0; 313} 314 315void NotifyAppTerminating() { 316 static bool notified = false; 317 if (notified) 318 return; 319 notified = true; 320 content::NotificationService::current()->Notify( 321 chrome::NOTIFICATION_APP_TERMINATING, 322 content::NotificationService::AllSources(), 323 content::NotificationService::NoDetails()); 324} 325 326void NotifyAndTerminate(bool fast_path) { 327#if defined(OS_CHROMEOS) 328 static bool notified = false; 329 // Return if a shutdown request has already been sent. 330 if (notified) 331 return; 332 notified = true; 333#endif 334 335 if (fast_path) 336 NotifyAppTerminating(); 337 338#if defined(OS_CHROMEOS) 339 if (base::chromeos::IsRunningOnChromeOS()) { 340 // If we're on a ChromeOS device, reboot if an update has been applied, 341 // or else signal the session manager to log out. 342 chromeos::UpdateEngineClient* update_engine_client 343 = chromeos::DBusThreadManager::Get()->GetUpdateEngineClient(); 344 if (update_engine_client->GetLastStatus().status == 345 chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) { 346 update_engine_client->RebootAfterUpdate(); 347 } else if (!g_session_manager_requested_shutdown) { 348 // Don't ask SessionManager to stop session if the shutdown request comes 349 // from session manager. 350 chromeos::DBusThreadManager::Get()->GetSessionManagerClient() 351 ->StopSession(); 352 } 353 } else { 354 // If running the Chrome OS build, but we're not on the device, act 355 // as if we received signal from SessionManager. 356 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 357 base::Bind(&ExitCleanly)); 358 } 359#endif 360} 361 362void OnAppExiting() { 363 static bool notified = false; 364 if (notified) 365 return; 366 notified = true; 367 HandleAppExitingForPlatform(); 368} 369 370bool ShouldStartShutdown(Browser* browser) { 371 if (BrowserList::GetInstance(browser->host_desktop_type())->size() > 1) 372 return false; 373#if defined(OS_WIN) && defined(USE_AURA) 374 // On Windows 8 the desktop and ASH environments could be active 375 // at the same time. 376 // We should not start the shutdown process in the following cases:- 377 // 1. If the desktop type of the browser going away is ASH and there 378 // are browser windows open in the desktop. 379 // 2. If the desktop type of the browser going away is desktop and the ASH 380 // environment is still active. 381 if (browser->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_NATIVE) 382 return !ash::Shell::HasInstance(); 383 else if (browser->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) 384 return BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty(); 385#endif 386 return true; 387} 388 389} // namespace chrome 390