Chromium Code Reviews| Index: chrome/browser/ui/browser_list.cc |
| diff --git a/chrome/browser/ui/browser_list.cc b/chrome/browser/ui/browser_list.cc |
| index be11579dd2359840435112537ac8179c225e8e87..f05ee705ac7f0425d7b34a07cf0bc51fecd144e2 100644 |
| --- a/chrome/browser/ui/browser_list.cc |
| +++ b/chrome/browser/ui/browser_list.cc |
| @@ -40,6 +40,95 @@ |
| namespace { |
| +// ********************* IMPORTANT **************** |
| +// Please read and update if you need to change the quit/shutdown |
| +// process. |
| +// |
| +// This is an attempt to descritbe different kinds of quit/shutdown |
| +// process that chrome may process. This overs only Windows and Linux |
| +// because I (oshima) don't know about mac. Mac contribution to this |
| +// comment would be greatly appreciated. |
| +// |
| +// Common behavior. |
| +// APP_EXITING notification is always sent when chrome starts shutdown |
| +// process (with one exception. see ChromeOS guest mode below). |
| +// Note that shutdown process can be canceled, so APP_EXITING may |
| +// be sent MORE THAN ONCE. |
| +// |
| +// Normal shutdown on Linux & Windows |
| +// A user requested to quit the browser (win: by wrench menu or |
| +// ctrl-alt-q, or linux:wrench menu, ctrl-alt-q or SIGTERM). |
| +// It notifies APP_EXITING, then tries to close all browsers. |
| +// When the last browser gets deleted, it sends out APP_TERMINATING |
| +// notification as a signal that browser is really quitting. |
| +// Shutdown may be blocked and canceled by beforeunload handler or |
| +// downloads in progress. (in which case, APP_TERMINATING won't be sent) |
| +// |
| +// Restart on Linux & Windows |
| +// A user requested to restart the browser. This can happen when |
| +// certain preference has changed (flags page, language option), or |
| +// update is availabe. Chrome sets kRestartLastSessionOnShutdown |
| +// preference to true, follows normal shutdown process and re-launches |
| +// itself at the end of the process (see browser_shutdown::Shutdown()). |
| +// |
| +// Logoff/power off on Windows |
| +// This happens when chrome recieves WM_ENDSESSION message. It |
| +// notifies APP_EXITING, then close all tabs forcibly. This |
| +// process does send APP_TERMINATING notification. This shutdown |
| +// process cannot be blocked nor canceled. |
| +// |
| +// X IO Error on Linux and ChromeOS: |
| +// When X server reports IO error, it's most likely X has gone away and |
| +// chrome needs has to shutdown without make another call to X. |
| +// This is similar to Window's logoff/power off process, except that |
| +// chrome doesn't try to close browsers nor tabs. APP_TERMINATING is sent |
| +// after APP_EXITING. |
| +// |
| +// ChromeOS specific: |
| +// Fast shutdown |
| +// When a user requested to quit browser either by wrench menu or |
| +// ctrl-alt-q, and we know that shutdown will not be canceled (that is, |
| +// all contents have no beforeunload handlers and there is no |
| +// downloads in progress), chrome quits using fast shutdown process. |
| +// It notifies APP_EXITING, followed by APP_TERMINATING, then |
| +// tells SessionManager to start shutdown process. SessionManager sends |
| +// SIGTERM back to chrome, which closes all browsers. |
| +// |
| +// Slow shutdown |
| +// If there is a tab with beforeunload handlers, or a download is |
| +// in progress, it will follow normal shutdown process. That is, |
| +// it notifies APP_EXITING, tries to closes all browsers. APP_TERMINATING |
| +// will be sent when the last browser is closd. Shutdown may be cancelled |
| +// either by beforeunload handler or download dialog. |
| +// |
| +// Restart |
| +// ChromeOS does not support restarting. It follows regular shutdown process, |
| +// |
| +// Power off |
| +// A user pressed power button, or closed a lid on login screen. This is |
| +// similar to Windows log-off/power-off scenario. Chrome receives SIGTERM |
| +// and start shutdown process. It will close all tabs forcibly and shutdown |
| +// cannot be canceled. |
| +// |
| +// Signout from Screen locker. |
| +// A user clicked signout button on screen locker. This is basically same |
| +// as Power off. Chrome asks session manager to stop session and session |
| +// manager sends SIGTERM to chrome. |
| +// |
| +// Switching to Guest mode |
| +// When a user selected guest mode, Chrome first writes exit_cleanly bit |
| +// off to the disk (see g_browser_process->EndSession() in |
| +// chromeos/login_utils.cc), then tells SessionManager to switch to guest |
| +// mode. Session manager kills and restarts Chrome with guest mode flag. |
| +// This process never use method in BrowserList, nor sends APP_EXITING |
| +// or APP_TERMINATING (to restart fast). |
| +// |
| +// Note that different shutdown path |
| +// |
| +// Mac: |
| +// T.B.D. |
| +// |
| + |
| // This object is instantiated when the first Browser object is added to the |
| // list and delete when the last one is removed. It watches for loads and |
| // creates histograms of some global object counts. |
| @@ -201,15 +290,29 @@ bool AreAllBrowsersCloseable() { |
| return true; |
| } |
| +// Sends out NOTIFICATION_APP_TERMINATING notification. |
| +// Returns true if the notification is set, or false if the notification |
| +// has already sent out before. |
| +bool NotifyAppTerminating() { |
| + static bool notified = false; |
| + if (notified) |
| + return false; |
| + notified = true; |
| + NotificationService::current()->Notify( |
| + content::NOTIFICATION_APP_TERMINATING, |
| + NotificationService::AllSources(), |
| + NotificationService::NoDetails()); |
| + return true; |
| +} |
| + |
| #if defined(OS_CHROMEOS) |
| -bool signout = false; |
| +bool explicit_signout = false; |
| // Fast shutdown for ChromeOS. It tells session manager to start |
| // shutdown process when closing browser windows won't be canceled. |
| // Returns true if fast shutdown is successfully started. |
| bool FastShutdown() { |
| - signout = true; |
| if (chromeos::CrosLibrary::Get()->EnsureLoaded() |
| && AreAllBrowsersCloseable()) { |
| BrowserList::NotifyAndTerminate(true); |
| @@ -287,15 +390,12 @@ void BrowserList::AttemptExitInternal() { |
| // static |
| void BrowserList::NotifyAndTerminate(bool fast_path) { |
| #if defined(OS_CHROMEOS) |
| - if (!signout) |
| + if (browser_shutdown::ShutdownType() != browser_shutdown::BROWSER_EXIT); |
| return; |
| #endif |
| if (fast_path) { |
| - NotificationService::current()->Notify( |
| - content::NOTIFICATION_APP_TERMINATING, |
| - NotificationService::AllSources(), |
| - NotificationService::NoDetails()); |
| + NotifyAppTerminating(); |
| } |
| #if defined(OS_CHROMEOS) |
| @@ -357,10 +457,9 @@ void BrowserList::RemoveBrowser(Browser* browser) { |
| // to call ProfileManager::ShutdownSessionServices() as part of the |
| // shutdown, because Browser::WindowClosing() already makes sure that the |
| // SessionService is created and notified. |
| - NotificationService::current()->Notify( |
| - content::NOTIFICATION_APP_TERMINATING, |
| - NotificationService::AllSources(), |
| - NotificationService::NoDetails()); |
| + // On ChromeOS, APP_TERMINATING might have already been sent when |
| + // shutdown is first requested. The call will be ignored in that case. |
| + NotifyAppTerminating(); |
| AllBrowsersClosedAndAppExiting(); |
| } |
| } |
| @@ -377,6 +476,9 @@ void BrowserList::RemoveObserver(BrowserList::Observer* observer) { |
| // static |
| void BrowserList::CloseAllBrowsers() { |
| + browser_shutdown::ShutdownType shutdown_type = |
| + browser_shutdown::GetShutdownType() |
| + /* |
| bool session_ending = |
| browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION; |
| bool force_exit = false; |
| @@ -384,6 +486,7 @@ void BrowserList::CloseAllBrowsers() { |
| if (session_ending) |
| force_exit = true; |
| #endif |
| + */ |
| // Tell everyone that we are shutting down. |
| browser_shutdown::SetTryingToQuit(true); |
| @@ -391,21 +494,29 @@ void BrowserList::CloseAllBrowsers() { |
| // exit can restore all browsers open before exiting. |
| ProfileManager::ShutdownSessionServices(); |
| + // If we're not closing browser, send APP_TERMINATING |
| + if (shutdown_type == browser_shutdown::SHUTDOWN_WITHOUT_CLOSE) { |
| + NotifyAppTerminating(); |
| + return; |
| + } |
| + |
| // If there are no browsers, send the APP_TERMINATING action here. Otherwise, |
| // it will be sent by RemoveBrowser() when the last browser has closed. |
| if (force_exit || browsers_.empty()) { |
| NotifyAndTerminate(true); |
| return; |
| } |
| + |
| #if defined(OS_CHROMEOS) |
| chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( |
| "StartedClosingWindows", false); |
| #endif |
| + |
| for (BrowserList::const_iterator i = BrowserList::begin(); |
| i != BrowserList::end();) { |
| Browser* browser = *i; |
|
achuithb
2011/10/28 22:22:26
Do you think you could also clean this loop up if
|
| browser->window()->Close(); |
| - if (!session_ending) { |
| + if (shutdown_type != browser_shutdown::POWER_OFF) { |
| ++i; |
| } else { |
| // This path is hit during logoff/power-down. In this case we won't get |
| @@ -447,6 +558,7 @@ void BrowserList::CloseAllBrowsersWithProfile(Profile* profile) { |
| // static |
| void BrowserList::AttemptUserExit() { |
| + browser_shutdown::OnShutdownStarting(browser_shutdown::BROWSER_EXIT); |
| #if defined(OS_CHROMEOS) |
| chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("LogoutStarted", false); |
| // Write /tmp/uptime-logout-started as well. |
| @@ -464,6 +576,7 @@ void BrowserList::AttemptUserExit() { |
| state->SavePersistentPrefs(); |
| } |
| } |
| + |
| if (FastShutdown()) |
| return; |
| #else |
| @@ -481,6 +594,7 @@ void BrowserList::AttemptRestart() { |
| // For CrOS instead of browser restart (which is not supported) perform a full |
| // sign out. Session will be only restored if user has that setting set. |
| // Same session restore behavior happens in case of full restart after update. |
| + // TODO(oshima): Should we use LoginLibrary::RestartJob()? |
| AttemptUserExit(); |
| #else |
| // Set the flag to restore state after the restart. |
| @@ -506,11 +620,13 @@ void BrowserList::AttemptExit() { |
| void BrowserList::ExitCleanly() { |
| // We always mark exit cleanly. |
| g_browser_process->EndSession(); |
| + if (AreAllBrowsersCloseable()) |
| + |
| AttemptExitInternal(); |
| } |
| #endif |
| -static void TimeLimitedSessionEnding() { |
| +static void TimeLimitedForceShutdown(browser_shutdown::ShutdownType type) { |
| // Start watching for hang during shutdown, and crash it if takes too long. |
| // We disarm when |shutdown_watcher| object is destroyed, which is when we |
| // exit this function. |
| @@ -524,8 +640,8 @@ static void TimeLimitedSessionEnding() { |
| if (already_ended || !NotificationService::current()) |
| return; |
| already_ended = true; |
| - |
| - browser_shutdown::OnShutdownStarting(browser_shutdown::END_SESSION); |
| + |
| + browser_shutdown::OnShutdownStarting(type); |
| NotificationService::current()->Notify( |
| content::NOTIFICATION_APP_EXITING, |
| @@ -549,8 +665,8 @@ static void TimeLimitedSessionEnding() { |
| } |
| // static |
| -void BrowserList::SessionEnding() { |
| - TimeLimitedSessionEnding(); |
| +void BrowserList::ForceShutdown(SessionEndingReason raeson) { |
| + TimeLimitedForceShutdown(reason); |
| #if defined(OS_WIN) |
| // At this point the message loop is still running yet we've shut everything |