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