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 53namespace chrome { 54namespace { 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. 60bool 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 79int g_keep_alive_count = 0; 80 81#if defined(OS_CHROMEOS) 82// Whether chrome should send stop request to a session manager. 83bool g_send_stop_request_to_session_manager = false; 84#endif 85 86} // namespace 87 88void 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 94void 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 109void CloseAllBrowsersAndQuit() { 110 browser_shutdown::SetTryingToQuit(true); 111 CloseAllBrowsers(); 112} 113 114void 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 144void 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 176void 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) 191void 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 216void 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. 236void 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 252namespace { 253 254bool ExperimentUseBrokenSynchronization() { 255 const std::string group_name = 256 base::FieldTrialList::FindFullName("WindowsLogoffRace"); 257 return group_name == "BrokenSynchronization"; 258} 259 260} // namespace 261 262void 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 316void 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 324void 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 347bool WillKeepAlive() { 348 return g_keep_alive_count > 0; 349} 350 351void 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 362void 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 400void OnAppExiting() { 401 static bool notified = false; 402 if (notified) 403 return; 404 notified = true; 405 HandleAppExitingForPlatform(); 406} 407 408bool 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