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/metrics/field_trial.h" 15 #include "base/prefs/pref_service.h" 16 #include "base/process/kill.h" 17 #include "base/process/process.h" 18 #include "build/build_config.h" 19 #include "chrome/browser/browser_process.h" 20 #include "chrome/browser/browser_process_platform_part.h" 21 #include "chrome/browser/browser_shutdown.h" 22 #include "chrome/browser/chrome_notification_types.h" 23 #include "chrome/browser/download/download_service.h" 24 #include "chrome/browser/lifetime/browser_close_manager.h" 25 #include "chrome/browser/metrics/thread_watcher.h" 26 #include "chrome/browser/profiles/profile.h" 27 #include "chrome/browser/profiles/profile_manager.h" 28 #include "chrome/browser/ui/browser.h" 29 #include "chrome/browser/ui/browser_finder.h" 30 #include "chrome/browser/ui/browser_iterator.h" 31 #include "chrome/browser/ui/browser_tabstrip.h" 32 #include "chrome/browser/ui/browser_window.h" 33 #include "chrome/browser/ui/tabs/tab_strip_model.h" 34 #include "chrome/common/chrome_switches.h" 35 #include "chrome/common/pref_names.h" 36 #include "content/public/browser/browser_shutdown.h" 37 #include "content/public/browser/browser_thread.h" 38 #include "content/public/browser/navigation_details.h" 39 #include "content/public/browser/notification_service.h" 40 41 #if defined(OS_CHROMEOS) 42 #include "base/sys_info.h" 43 #include "chrome/browser/chromeos/boot_times_loader.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 chrome should send stop request to a session manager. 83 bool g_send_stop_request_to_session_manager = false; 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 (chrome::GetTotalBrowserCount() == 0 && 119 (browser_shutdown::IsTryingToQuit() || !chrome::WillKeepAlive())) { 120 // Tell everyone that we are shutting down. 121 browser_shutdown::SetTryingToQuit(true); 122 123 #if defined(ENABLE_SESSION_SERVICE) 124 // If ShuttingDownWithoutClosingBrowsers() returns true, the session 125 // services may not get a chance to shut down normally, so explicitly shut 126 // them down here to ensure they have a chance to persist their data. 127 ProfileManager::ShutdownSessionServices(); 128 #endif 129 130 chrome::NotifyAndTerminate(true); 131 chrome::OnAppExiting(); 132 return; 133 } 134 135 #if defined(OS_CHROMEOS) 136 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( 137 "StartedClosingWindows", false); 138 #endif 139 scoped_refptr<BrowserCloseManager> browser_close_manager = 140 new BrowserCloseManager; 141 browser_close_manager->StartClosingBrowsers(); 142 } 143 144 void AttemptUserExit() { 145 #if defined(OS_CHROMEOS) 146 StartShutdownTracing(); 147 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("LogoutStarted", false); 148 149 PrefService* state = g_browser_process->local_state(); 150 if (state) { 151 chromeos::BootTimesLoader::Get()->OnLogoutStarted(state); 152 153 // Login screen should show up in owner's locale. 154 std::string owner_locale = state->GetString(prefs::kOwnerLocale); 155 if (!owner_locale.empty() && 156 state->GetString(prefs::kApplicationLocale) != owner_locale && 157 !state->IsManagedPreference(prefs::kApplicationLocale)) { 158 state->SetString(prefs::kApplicationLocale, owner_locale); 159 TRACE_EVENT0("shutdown", "CommitPendingWrite"); 160 state->CommitPendingWrite(); 161 } 162 } 163 g_send_stop_request_to_session_manager = true; 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(false); 173 #endif 174 } 175 176 void StartShutdownTracing() { 177 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 178 if (command_line.HasSwitch(switches::kTraceShutdown)) { 179 base::debug::CategoryFilter category_filter( 180 command_line.GetSwitchValueASCII(switches::kTraceShutdown)); 181 base::debug::TraceLog::GetInstance()->SetEnabled( 182 category_filter, 183 base::debug::TraceLog::RECORDING_MODE, 184 base::debug::TraceOptions()); 185 } 186 TRACE_EVENT0("shutdown", "StartShutdownTracing"); 187 } 188 189 // The Android implementation is in application_lifetime_android.cc 190 #if !defined(OS_ANDROID) 191 void AttemptRestart() { 192 // TODO(beng): Can this use ProfileManager::GetLoadedProfiles instead? 193 for (chrome::BrowserIterator it; !it.done(); it.Next()) 194 content::BrowserContext::SaveSessionState(it->profile()); 195 196 PrefService* pref_service = g_browser_process->local_state(); 197 pref_service->SetBoolean(prefs::kWasRestarted, true); 198 199 #if defined(OS_CHROMEOS) 200 chromeos::BootTimesLoader::Get()->set_restart_requested(); 201 202 DCHECK(!g_send_stop_request_to_session_manager); 203 // Make sure we don't send stop request to the session manager. 204 g_send_stop_request_to_session_manager = false; 205 // Run exit process in clean stack. 206 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 207 base::Bind(&ExitCleanly)); 208 #else 209 // Set the flag to restore state after the restart. 210 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); 211 AttemptExit(); 212 #endif 213 } 214 #endif 215 216 void AttemptExit() { 217 #if defined(OS_CHROMEOS) 218 // On ChromeOS, user exit and system exits are the same. 219 AttemptUserExit(); 220 #else 221 // If we know that all browsers can be closed without blocking, 222 // don't notify users of crashes beyond this point. 223 // Note that MarkAsCleanShutdown() does not set UMA's exit cleanly bit 224 // so crashes during shutdown are still reported in UMA. 225 #if !defined(OS_ANDROID) 226 // Android doesn't use Browser. 227 if (AreAllBrowsersCloseable()) 228 MarkAsCleanShutdown(); 229 #endif 230 AttemptExitInternal(true); 231 #endif 232 } 233 234 #if defined(OS_CHROMEOS) 235 // A function called when SIGTERM is received. 236 void ExitCleanly() { 237 // We always mark exit cleanly because SessionManager may kill 238 // chrome in 3 seconds after SIGTERM. 239 g_browser_process->EndSession(); 240 241 // Don't block when SIGTERM is received. AreaAllBrowsersCloseable() 242 // can be false in following cases. a) power-off b) signout from 243 // screen locker. 244 if (!AreAllBrowsersCloseable()) 245 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); 246 else 247 browser_shutdown::OnShutdownStarting(browser_shutdown::BROWSER_EXIT); 248 AttemptExitInternal(true); 249 } 250 #endif 251 252 namespace { 253 254 bool ExperimentUseBrokenSynchronization() { 255 const std::string group_name = 256 base::FieldTrialList::FindFullName("WindowsLogoffRace"); 257 return group_name == "BrokenSynchronization"; 258 } 259 260 } // namespace 261 262 void SessionEnding() { 263 // This is a time-limited shutdown where we need to write as much to 264 // disk as we can as soon as we can, and where we must kill the 265 // process within a hang timeout to avoid user prompts. 266 267 // Start watching for hang during shutdown, and crash it if takes too long. 268 // We disarm when |shutdown_watcher| object is destroyed, which is when we 269 // exit this function. 270 ShutdownWatcherHelper shutdown_watcher; 271 shutdown_watcher.Arm(base::TimeDelta::FromSeconds(90)); 272 273 // EndSession is invoked once per frame. Only do something the first time. 274 static bool already_ended = false; 275 // We may get called in the middle of shutdown, e.g. http://crbug.com/70852 276 // In this case, do nothing. 277 if (already_ended || !content::NotificationService::current()) 278 return; 279 already_ended = true; 280 281 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); 282 283 content::NotificationService::current()->Notify( 284 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, 285 content::NotificationService::AllSources(), 286 content::NotificationService::NoDetails()); 287 288 // Write important data first. 289 g_browser_process->EndSession(); 290 291 #if defined(OS_WIN) 292 base::win::SetShouldCrashOnProcessDetach(false); 293 #endif 294 295 if (ExperimentUseBrokenSynchronization()) { 296 CloseAllBrowsers(); 297 298 // Send out notification. This is used during testing so that the test 299 // harness can properly shutdown before we exit. 300 content::NotificationService::current()->Notify( 301 chrome::NOTIFICATION_SESSION_END, 302 content::NotificationService::AllSources(), 303 content::NotificationService::NoDetails()); 304 305 // This will end by terminating the process. 306 content::ImmediateShutdownAndExitProcess(); 307 } else { 308 // On Windows 7 and later, the system will consider the process ripe for 309 // termination as soon as it hides or destroys its windows. Since any 310 // execution past that point will be non-deterministically cut short, we 311 // might as well put ourselves out of that misery deterministically. 312 base::KillProcess(base::Process::Current().handle(), 0, false); 313 } 314 } 315 316 void IncrementKeepAliveCount() { 317 // Increment the browser process refcount as long as we're keeping the 318 // application alive. 319 if (!WillKeepAlive()) 320 g_browser_process->AddRefModule(); 321 ++g_keep_alive_count; 322 } 323 324 void DecrementKeepAliveCount() { 325 DCHECK_GT(g_keep_alive_count, 0); 326 --g_keep_alive_count; 327 328 DCHECK(g_browser_process); 329 // Although we should have a browser process, if there is none, 330 // there is nothing to do. 331 if (!g_browser_process) return; 332 333 // Allow the app to shutdown again. 334 if (!WillKeepAlive()) { 335 g_browser_process->ReleaseModule(); 336 // If there are no browsers open and we aren't already shutting down, 337 // initiate a shutdown. Also skips shutdown if this is a unit test 338 // (MessageLoop::current() == null). 339 if (chrome::GetTotalBrowserCount() == 0 && 340 !browser_shutdown::IsTryingToQuit() && 341 base::MessageLoop::current()) { 342 CloseAllBrowsers(); 343 } 344 } 345 } 346 347 bool WillKeepAlive() { 348 return g_keep_alive_count > 0; 349 } 350 351 void NotifyAppTerminating() { 352 static bool notified = false; 353 if (notified) 354 return; 355 notified = true; 356 content::NotificationService::current()->Notify( 357 chrome::NOTIFICATION_APP_TERMINATING, 358 content::NotificationService::AllSources(), 359 content::NotificationService::NoDetails()); 360 } 361 362 void NotifyAndTerminate(bool fast_path) { 363 #if defined(OS_CHROMEOS) 364 static bool notified = false; 365 // Return if a shutdown request has already been sent. 366 if (notified) 367 return; 368 notified = true; 369 #endif 370 371 if (fast_path) 372 NotifyAppTerminating(); 373 374 #if defined(OS_CHROMEOS) 375 if (base::SysInfo::IsRunningOnChromeOS()) { 376 // If we're on a ChromeOS device, reboot if an update has been applied, 377 // or else signal the session manager to log out. 378 chromeos::UpdateEngineClient* update_engine_client 379 = chromeos::DBusThreadManager::Get()->GetUpdateEngineClient(); 380 if (update_engine_client->GetLastStatus().status == 381 chromeos::UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) { 382 update_engine_client->RebootAfterUpdate(); 383 } else if (g_send_stop_request_to_session_manager) { 384 // Don't ask SessionManager to stop session if the shutdown request comes 385 // from session manager. 386 chromeos::DBusThreadManager::Get()->GetSessionManagerClient() 387 ->StopSession(); 388 } 389 } else { 390 if (g_send_stop_request_to_session_manager) { 391 // If running the Chrome OS build, but we're not on the device, act 392 // as if we received signal from SessionManager. 393 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 394 base::Bind(&ExitCleanly)); 395 } 396 } 397 #endif 398 } 399 400 void OnAppExiting() { 401 static bool notified = false; 402 if (notified) 403 return; 404 notified = true; 405 HandleAppExitingForPlatform(); 406 } 407 408 bool ShouldStartShutdown(Browser* browser) { 409 if (BrowserList::GetInstance(browser->host_desktop_type())->size() > 1) 410 return false; 411 #if defined(OS_WIN) 412 // On Windows 8 the desktop and ASH environments could be active 413 // at the same time. 414 // We should not start the shutdown process in the following cases:- 415 // 1. If the desktop type of the browser going away is ASH and there 416 // are browser windows open in the desktop. 417 // 2. If the desktop type of the browser going away is desktop and the ASH 418 // environment is still active. 419 if (browser->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_NATIVE) 420 return !ash::Shell::HasInstance(); 421 else if (browser->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) 422 return BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty(); 423 #endif 424 return true; 425 } 426 427 } // namespace chrome 428