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