Home | History | Annotate | Download | only in lifetime
      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