• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
AreAllBrowsersCloseable()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 
MarkAsCleanShutdown()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 
AttemptExitInternal(bool try_to_quit_application)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 
CloseAllBrowsersAndQuit()107 void CloseAllBrowsersAndQuit() {
108   browser_shutdown::SetTryingToQuit(true);
109   CloseAllBrowsers();
110 }
111 
CloseAllBrowsers()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 
AttemptUserExit()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 
StartShutdownTracing()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)
AttemptRestart()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 
AttemptExit()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.
ExitCleanly()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 
SessionEnding()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 
IncrementKeepAliveCount()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 
DecrementKeepAliveCount()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 
WillKeepAlive()326 bool WillKeepAlive() {
327   return g_keep_alive_count > 0;
328 }
329 
NotifyAppTerminating()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 
NotifyAndTerminate(bool fast_path)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 
OnAppExiting()379 void OnAppExiting() {
380   static bool notified = false;
381   if (notified)
382     return;
383   notified = true;
384   HandleAppExitingForPlatform();
385 }
386 
ShouldStartShutdown(Browser * browser)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