| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/lifetime/application_lifetime.h" | 5 #include "chrome/browser/lifetime/application_lifetime.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/memory/scoped_ptr.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 "base/process/process.h" | 11 #include "base/process/process.h" |
| 14 #include "base/process/process_handle.h" | 12 #include "base/process/process_handle.h" |
| 15 #include "base/trace_event/trace_event.h" | 13 #include "base/trace_event/trace_event.h" |
| 16 #include "build/build_config.h" | |
| 17 #include "chrome/browser/browser_process.h" | 14 #include "chrome/browser/browser_process.h" |
| 18 #include "chrome/browser/browser_process_platform_part.h" | 15 #include "chrome/browser/browser_process_platform_part.h" |
| 19 #include "chrome/browser/browser_shutdown.h" | 16 #include "chrome/browser/browser_shutdown.h" |
| 20 #include "chrome/browser/chrome_notification_types.h" | 17 #include "chrome/browser/chrome_notification_types.h" |
| 21 #include "chrome/browser/download/download_service.h" | 18 #include "chrome/browser/lifetime/application_lifetime_internal.h" |
| 22 #include "chrome/browser/lifetime/browser_close_manager.h" | |
| 23 #include "chrome/browser/metrics/thread_watcher.h" | 19 #include "chrome/browser/metrics/thread_watcher.h" |
| 24 #include "chrome/browser/profiles/profile.h" | |
| 25 #include "chrome/browser/profiles/profile_manager.h" | |
| 26 #include "chrome/browser/ui/browser.h" | |
| 27 #include "chrome/browser/ui/browser_finder.h" | |
| 28 #include "chrome/browser/ui/browser_iterator.h" | |
| 29 #include "chrome/browser/ui/browser_tabstrip.h" | |
| 30 #include "chrome/browser/ui/browser_window.h" | |
| 31 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 32 #include "chrome/browser/ui/user_manager.h" | |
| 33 #include "chrome/common/chrome_constants.h" | 20 #include "chrome/common/chrome_constants.h" |
| 34 #include "chrome/common/chrome_switches.h" | |
| 35 #include "chrome/common/pref_names.h" | |
| 36 #include "components/tracing/tracing_switches.h" | 21 #include "components/tracing/tracing_switches.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" | 22 #include "content/public/browser/notification_service.h" |
| 40 | 23 |
| 41 #if defined(OS_CHROMEOS) | |
| 42 #include "base/sys_info.h" | |
| 43 #include "chrome/browser/chromeos/boot_times_recorder.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) | 24 #if defined(OS_WIN) |
| 50 #include "base/win/win_util.h" | 25 #include "base/win/win_util.h" |
| 51 #include "components/browser_watcher/exit_funnel_win.h" | 26 #include "components/browser_watcher/exit_funnel_win.h" |
| 52 #endif | 27 #endif |
| 53 | 28 |
| 54 #if defined(USE_ASH) | 29 #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| 55 #include "ash/shell.h" | 30 #include "chrome/browser/lifetime/application_lifetime_desktop.h" |
| 56 #endif | 31 #endif |
| 57 | 32 |
| 58 namespace chrome { | 33 namespace chrome { |
| 59 namespace { | |
| 60 | 34 |
| 61 #if !defined(OS_ANDROID) | 35 namespace internal { |
| 62 // Returns true if all browsers can be closed without user interaction. | |
| 63 // This currently checks if there is pending download, or if it needs to | |
| 64 // handle unload handler. | |
| 65 bool AreAllBrowsersCloseable() { | |
| 66 chrome::BrowserIterator browser_it; | |
| 67 if (browser_it.done()) | |
| 68 return true; | |
| 69 | |
| 70 // If there are any downloads active, all browsers are not closeable. | |
| 71 // However, this does not block for malicious downloads. | |
| 72 if (DownloadService::NonMaliciousDownloadCountAllProfiles() > 0) | |
| 73 return false; | |
| 74 | |
| 75 // Check TabsNeedBeforeUnloadFired(). | |
| 76 for (; !browser_it.done(); browser_it.Next()) { | |
| 77 if (browser_it->TabsNeedBeforeUnloadFired()) | |
| 78 return false; | |
| 79 } | |
| 80 return true; | |
| 81 } | |
| 82 #endif // !defined(OS_ANDROID) | |
| 83 | |
| 84 int g_keep_alive_count = 0; | |
| 85 bool g_disable_shutdown_for_testing = false; | |
| 86 | |
| 87 #if defined(OS_CHROMEOS) | |
| 88 // Whether chrome should send stop request to a session manager. | |
| 89 bool g_send_stop_request_to_session_manager = false; | |
| 90 #endif | |
| 91 | |
| 92 } // namespace | |
| 93 | |
| 94 void MarkAsCleanShutdown() { | |
| 95 // TODO(beng): Can this use ProfileManager::GetLoadedProfiles() instead? | |
| 96 for (chrome::BrowserIterator it; !it.done(); it.Next()) | |
| 97 it->profile()->SetExitType(Profile::EXIT_NORMAL); | |
| 98 } | |
| 99 | 36 |
| 100 void AttemptExitInternal(bool try_to_quit_application) { | 37 void AttemptExitInternal(bool try_to_quit_application) { |
| 101 // On Mac, the platform-specific part handles setting this. | 38 // On Mac, the platform-specific part handles setting this. |
| 102 #if !defined(OS_MACOSX) | 39 #if !defined(OS_MACOSX) |
| 103 if (try_to_quit_application) | 40 if (try_to_quit_application) |
| 104 browser_shutdown::SetTryingToQuit(true); | 41 browser_shutdown::SetTryingToQuit(true); |
| 105 #endif | 42 #endif |
| 106 | 43 |
| 107 content::NotificationService::current()->Notify( | 44 content::NotificationService::current()->Notify( |
| 108 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, | 45 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, |
| 109 content::NotificationService::AllSources(), | 46 content::NotificationService::AllSources(), |
| 110 content::NotificationService::NoDetails()); | 47 content::NotificationService::NoDetails()); |
| 111 | 48 |
| 112 g_browser_process->platform_part()->AttemptExit(); | 49 g_browser_process->platform_part()->AttemptExit(); |
| 113 } | 50 } |
| 114 | 51 |
| 115 void CloseAllBrowsersAndQuit() { | 52 } // namespace internal |
| 116 browser_shutdown::SetTryingToQuit(true); | |
| 117 CloseAllBrowsers(); | |
| 118 } | |
| 119 | |
| 120 void CloseAllBrowsers() { | |
| 121 // If there are no browsers and closing the last browser would quit the | |
| 122 // application, send the APP_TERMINATING action here. Otherwise, it will be | |
| 123 // sent by RemoveBrowser() when the last browser has closed. | |
| 124 if (chrome::GetTotalBrowserCount() == 0 && | |
| 125 (browser_shutdown::IsTryingToQuit() || !chrome::WillKeepAlive())) { | |
| 126 // Tell everyone that we are shutting down. | |
| 127 browser_shutdown::SetTryingToQuit(true); | |
| 128 | |
| 129 #if defined(ENABLE_SESSION_SERVICE) | |
| 130 // If ShuttingDownWithoutClosingBrowsers() returns true, the session | |
| 131 // services may not get a chance to shut down normally, so explicitly shut | |
| 132 // them down here to ensure they have a chance to persist their data. | |
| 133 ProfileManager::ShutdownSessionServices(); | |
| 134 #endif | |
| 135 | |
| 136 chrome::NotifyAndTerminate(true); | |
| 137 chrome::OnAppExiting(); | |
| 138 return; | |
| 139 } | |
| 140 | |
| 141 #if defined(OS_CHROMEOS) | |
| 142 chromeos::BootTimesRecorder::Get()->AddLogoutTimeMarker( | |
| 143 "StartedClosingWindows", false); | |
| 144 #endif | |
| 145 scoped_refptr<BrowserCloseManager> browser_close_manager = | |
| 146 new BrowserCloseManager; | |
| 147 browser_close_manager->StartClosingBrowsers(); | |
| 148 } | |
| 149 | |
| 150 void AttemptUserExit() { | |
| 151 #if defined(OS_CHROMEOS) | |
| 152 StartShutdownTracing(); | |
| 153 chromeos::BootTimesRecorder::Get()->AddLogoutTimeMarker("LogoutStarted", | |
| 154 false); | |
| 155 | |
| 156 PrefService* state = g_browser_process->local_state(); | |
| 157 if (state) { | |
| 158 chromeos::BootTimesRecorder::Get()->OnLogoutStarted(state); | |
| 159 | |
| 160 // Login screen should show up in owner's locale. | |
| 161 std::string owner_locale = state->GetString(prefs::kOwnerLocale); | |
| 162 if (!owner_locale.empty() && | |
| 163 state->GetString(prefs::kApplicationLocale) != owner_locale && | |
| 164 !state->IsManagedPreference(prefs::kApplicationLocale)) { | |
| 165 state->SetString(prefs::kApplicationLocale, owner_locale); | |
| 166 TRACE_EVENT0("shutdown", "CommitPendingWrite"); | |
| 167 state->CommitPendingWrite(); | |
| 168 } | |
| 169 } | |
| 170 g_send_stop_request_to_session_manager = true; | |
| 171 // On ChromeOS, always terminate the browser, regardless of the result of | |
| 172 // AreAllBrowsersCloseable(). See crbug.com/123107. | |
| 173 chrome::NotifyAndTerminate(true); | |
| 174 #else | |
| 175 // Reset the restart bit that might have been set in cancelled restart | |
| 176 // request. | |
| 177 UserManager::Hide(); | |
| 178 PrefService* pref_service = g_browser_process->local_state(); | |
| 179 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, false); | |
| 180 AttemptExitInternal(false); | |
| 181 #endif | |
| 182 } | |
| 183 | 53 |
| 184 void StartShutdownTracing() { | 54 void StartShutdownTracing() { |
| 185 const base::CommandLine& command_line = | 55 const base::CommandLine& command_line = |
| 186 *base::CommandLine::ForCurrentProcess(); | 56 *base::CommandLine::ForCurrentProcess(); |
| 187 if (command_line.HasSwitch(switches::kTraceShutdown)) { | 57 if (command_line.HasSwitch(switches::kTraceShutdown)) { |
| 188 base::trace_event::TraceConfig trace_config( | 58 base::trace_event::TraceConfig trace_config( |
| 189 command_line.GetSwitchValueASCII(switches::kTraceShutdown), ""); | 59 command_line.GetSwitchValueASCII(switches::kTraceShutdown), ""); |
| 190 base::trace_event::TraceLog::GetInstance()->SetEnabled( | 60 base::trace_event::TraceLog::GetInstance()->SetEnabled( |
| 191 trace_config, | 61 trace_config, |
| 192 base::trace_event::TraceLog::RECORDING_MODE); | 62 base::trace_event::TraceLog::RECORDING_MODE); |
| 193 } | 63 } |
| 194 TRACE_EVENT0("shutdown", "StartShutdownTracing"); | 64 TRACE_EVENT0("shutdown", "StartShutdownTracing"); |
| 195 } | 65 } |
| 196 | 66 |
| 197 // The Android implementation is in application_lifetime_android.cc | |
| 198 #if !defined(OS_ANDROID) | |
| 199 void AttemptRestart() { | |
| 200 // TODO(beng): Can this use ProfileManager::GetLoadedProfiles instead? | |
| 201 for (chrome::BrowserIterator it; !it.done(); it.Next()) | |
| 202 content::BrowserContext::SaveSessionState(it->profile()); | |
| 203 | |
| 204 PrefService* pref_service = g_browser_process->local_state(); | |
| 205 pref_service->SetBoolean(prefs::kWasRestarted, true); | |
| 206 | |
| 207 #if defined(OS_CHROMEOS) | |
| 208 chromeos::BootTimesRecorder::Get()->set_restart_requested(); | |
| 209 | |
| 210 DCHECK(!g_send_stop_request_to_session_manager); | |
| 211 // Make sure we don't send stop request to the session manager. | |
| 212 g_send_stop_request_to_session_manager = false; | |
| 213 // Run exit process in clean stack. | |
| 214 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
| 215 base::Bind(&ExitCleanly)); | |
| 216 #else | |
| 217 // Set the flag to restore state after the restart. | |
| 218 pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); | |
| 219 AttemptExit(); | |
| 220 #endif | |
| 221 } | |
| 222 #endif | |
| 223 | |
| 224 void AttemptExit() { | 67 void AttemptExit() { |
| 225 #if defined(OS_CHROMEOS) | 68 #if defined(OS_CHROMEOS) |
| 226 // On ChromeOS, user exit and system exits are the same. | 69 // On ChromeOS, user exit and system exits are the same. |
| 227 AttemptUserExit(); | 70 AttemptUserExit(); |
| 228 #else | 71 #else |
| 229 // If we know that all browsers can be closed without blocking, | 72 // If we know that all browsers can be closed without blocking, |
| 230 // don't notify users of crashes beyond this point. | 73 // don't notify users of crashes beyond this point. |
| 231 // Note that MarkAsCleanShutdown() does not set UMA's exit cleanly bit | 74 // Note that MarkAsCleanShutdown() does not set UMA's exit cleanly bit |
| 232 // so crashes during shutdown are still reported in UMA. | 75 // so crashes during shutdown are still reported in UMA. |
| 233 #if !defined(OS_ANDROID) | 76 #if !defined(OS_ANDROID) |
| 234 // Android doesn't use Browser. | 77 // Android doesn't use Browser. |
| 235 if (AreAllBrowsersCloseable()) | 78 internal::AttemptExitInternalDesktop(); |
| 236 MarkAsCleanShutdown(); | |
| 237 #endif | 79 #endif |
| 238 AttemptExitInternal(true); | 80 internal::AttemptExitInternal(true); |
| 239 #endif | 81 #endif |
| 240 } | 82 } |
| 241 | 83 |
| 242 #if defined(OS_CHROMEOS) | |
| 243 // A function called when SIGTERM is received. | |
| 244 void ExitCleanly() { | |
| 245 // We always mark exit cleanly. | |
| 246 MarkAsCleanShutdown(); | |
| 247 | |
| 248 // Don't block when SIGTERM is received. AreaAllBrowsersCloseable() | |
| 249 // can be false in following cases. a) power-off b) signout from | |
| 250 // screen locker. | |
| 251 if (!AreAllBrowsersCloseable()) | |
| 252 browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); | |
| 253 else | |
| 254 browser_shutdown::OnShutdownStarting(browser_shutdown::BROWSER_EXIT); | |
| 255 AttemptExitInternal(true); | |
| 256 } | |
| 257 #endif | |
| 258 | |
| 259 void SessionEnding() { | 84 void SessionEnding() { |
| 260 #if defined(OS_WIN) | 85 #if defined(OS_WIN) |
| 261 browser_watcher::ExitFunnel funnel; | 86 browser_watcher::ExitFunnel funnel; |
| 262 | 87 |
| 263 funnel.Init(kBrowserExitCodesRegistryPath, base::GetCurrentProcessHandle()); | 88 funnel.Init(kBrowserExitCodesRegistryPath, base::GetCurrentProcessHandle()); |
| 264 funnel.RecordEvent(L"SessionEnding"); | 89 funnel.RecordEvent(L"SessionEnding"); |
| 265 #endif | 90 #endif |
| 266 // This is a time-limited shutdown where we need to write as much to | 91 // This is a time-limited shutdown where we need to write as much to |
| 267 // disk as we can as soon as we can, and where we must kill the | 92 // disk as we can as soon as we can, and where we must kill the |
| 268 // process within a hang timeout to avoid user prompts. | 93 // process within a hang timeout to avoid user prompts. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 289 content::NotificationService::NoDetails()); | 114 content::NotificationService::NoDetails()); |
| 290 | 115 |
| 291 #if defined(OS_WIN) | 116 #if defined(OS_WIN) |
| 292 funnel.RecordEvent(L"EndSession"); | 117 funnel.RecordEvent(L"EndSession"); |
| 293 #endif | 118 #endif |
| 294 // Write important data first. | 119 // Write important data first. |
| 295 g_browser_process->EndSession(); | 120 g_browser_process->EndSession(); |
| 296 | 121 |
| 297 #if defined(OS_WIN) | 122 #if defined(OS_WIN) |
| 298 base::win::SetShouldCrashOnProcessDetach(false); | 123 base::win::SetShouldCrashOnProcessDetach(false); |
| 299 #endif | |
| 300 | 124 |
| 301 #if defined(OS_WIN) | |
| 302 // KillProcess ought to terminate the process without further ado, so if | 125 // KillProcess ought to terminate the process without further ado, so if |
| 303 // execution gets to this point, presumably this is normal exit. | 126 // execution gets to this point, presumably this is normal exit. |
| 304 funnel.RecordEvent(L"KillProcess"); | 127 funnel.RecordEvent(L"KillProcess"); |
| 305 #endif | 128 #endif |
| 306 | 129 |
| 307 // On Windows 7 and later, the system will consider the process ripe for | 130 // On Windows 7 and later, the system will consider the process ripe for |
| 308 // termination as soon as it hides or destroys its windows. Since any | 131 // termination as soon as it hides or destroys its windows. Since any |
| 309 // execution past that point will be non-deterministically cut short, we | 132 // execution past that point will be non-deterministically cut short, we |
| 310 // might as well put ourselves out of that misery deterministically. | 133 // might as well put ourselves out of that misery deterministically. |
| 311 base::Process::Current().Terminate(0, false); | 134 base::Process::Current().Terminate(0, false); |
| 312 } | 135 } |
| 313 | 136 |
| 314 void IncrementKeepAliveCount() { | |
| 315 // Increment the browser process refcount as long as we're keeping the | |
| 316 // application alive. | |
| 317 if (!WillKeepAlive()) | |
| 318 g_browser_process->AddRefModule(); | |
| 319 ++g_keep_alive_count; | |
| 320 } | |
| 321 | |
| 322 void CloseAllBrowsersIfNeeded() { | |
| 323 // If there are no browsers open and we aren't already shutting down, | |
| 324 // initiate a shutdown. Also skips shutdown if this is a unit test. | |
| 325 // (MessageLoop::current() == null or explicitly disabled). | |
| 326 if (chrome::GetTotalBrowserCount() == 0 && | |
| 327 !browser_shutdown::IsTryingToQuit() && base::MessageLoop::current() && | |
| 328 !g_disable_shutdown_for_testing) { | |
| 329 CloseAllBrowsers(); | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 void DecrementKeepAliveCount() { | |
| 334 DCHECK_GT(g_keep_alive_count, 0); | |
| 335 --g_keep_alive_count; | |
| 336 // Although we should have a browser process, if there is none, | |
| 337 // there is nothing to do. | |
| 338 if (!g_browser_process) return; | |
| 339 | |
| 340 // Allow the app to shutdown again. | |
| 341 if (!WillKeepAlive()) { | |
| 342 g_browser_process->ReleaseModule(); | |
| 343 CloseAllBrowsersIfNeeded(); | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 bool WillKeepAlive() { | |
| 348 return g_keep_alive_count > 0; | |
| 349 } | |
| 350 | |
| 351 void NotifyAppTerminating() { | 137 void NotifyAppTerminating() { |
| 352 static bool notified = false; | 138 static bool notified = false; |
| 353 if (notified) | 139 if (notified) |
| 354 return; | 140 return; |
| 355 notified = true; | 141 notified = true; |
| 356 content::NotificationService::current()->Notify( | 142 content::NotificationService::current()->Notify( |
| 357 chrome::NOTIFICATION_APP_TERMINATING, | 143 chrome::NOTIFICATION_APP_TERMINATING, |
| 358 content::NotificationService::AllSources(), | 144 content::NotificationService::AllSources(), |
| 359 content::NotificationService::NoDetails()); | 145 content::NotificationService::NoDetails()); |
| 360 } | 146 } |
| 361 | 147 |
| 362 void NotifyAndTerminate(bool fast_path) { | 148 void NotifyAndTerminate(bool fast_path) { |
| 363 #if defined(OS_CHROMEOS) | 149 #if defined(OS_CHROMEOS) |
| 364 static bool notified = false; | 150 static bool notified = false; |
| 365 // Return if a shutdown request has already been sent. | 151 // Return if a shutdown request has already been sent. |
| 366 if (notified) | 152 if (notified) |
| 367 return; | 153 return; |
| 368 notified = true; | 154 notified = true; |
| 369 #endif | 155 #endif |
| 370 | 156 |
| 371 if (fast_path) | 157 if (fast_path) |
| 372 NotifyAppTerminating(); | 158 NotifyAppTerminating(); |
| 373 | 159 |
| 374 #if defined(OS_CHROMEOS) | 160 #if defined(OS_CHROMEOS) |
| 375 if (base::SysInfo::IsRunningOnChromeOS()) { | 161 internal::NotifyAndTerminateInternalCrOS(); |
| 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 | 162 #endif |
| 398 } | 163 } |
| 399 | 164 |
| 400 void OnAppExiting() { | |
| 401 static bool notified = false; | |
| 402 if (notified) | |
| 403 return; | |
| 404 notified = true; | |
| 405 HandleAppExitingForPlatform(); | |
| 406 } | |
| 407 | |
| 408 bool 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 void DisableShutdownForTesting(bool disable_shutdown_for_testing) { | |
| 428 g_disable_shutdown_for_testing = disable_shutdown_for_testing; | |
| 429 if (!g_disable_shutdown_for_testing && !WillKeepAlive()) | |
| 430 CloseAllBrowsersIfNeeded(); | |
| 431 } | |
| 432 | |
| 433 } // namespace chrome | 165 } // namespace chrome |
| OLD | NEW |