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
44namespace chrome {
45namespace {
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.
50bool 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
67int g_keep_alive_count = 0;
68
69#if defined(OS_CHROMEOS)
70// Whether a session manager requested to shutdown.
71bool g_session_manager_requested_shutdown = true;
72#endif
73
74}  // namespace
75
76void 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
82void 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
91void 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
145void 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)
178void 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)
200void 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
209void 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.
224void 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
238void 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
280void 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
288void 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
311bool WillKeepAlive() {
312  return g_keep_alive_count > 0;
313}
314
315void 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
326void 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
362void OnAppExiting() {
363  static bool notified = false;
364  if (notified)
365    return;
366  notified = true;
367  HandleAppExitingForPlatform();
368}
369
370bool 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