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/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