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