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