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