summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authoratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-20 20:52:20 +0000
committeratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-20 20:52:20 +0000
commitc984d9f8b393418f26c8a3a90ceaff17f2d421af (patch)
tree1e6d691629226bee23228b87b435957a01587c82 /chrome/browser
parenta82ac135a3453615f7289b5fbacc512576858639 (diff)
downloadchromium_src-c984d9f8b393418f26c8a3a90ceaff17f2d421af.zip
chromium_src-c984d9f8b393418f26c8a3a90ceaff17f2d421af.tar.gz
chromium_src-c984d9f8b393418f26c8a3a90ceaff17f2d421af.tar.bz2
Moved code that generates APP_TERMINATING notification into BrowserList so it can be used on all platforms. Updated observers (ExtensionProcessManager,
BackgroundContents) to listen for APP_TERMINATING instead of listening for BROWSER_CLOSED on some platforms. APP_TERMINATING is now sent just before the main message loop exits rather than just after, but no code depends on this timing. Updated Mac code to always call BrowserList::CloseAllBrowsers() even if there are no open browsers, to ensure that APP_TERMINATING is always fired. Changed BackgroundContentsService to keep the browser process alive when there are BackgroundContents running and updated the unit tests. Renamed BrowserList::IsInPersistentMode() => WillShutdownWhenLastBrowserCloses() and AllBrowsersClosed() => AllBrowsersClosedAndAppExiting() to more precisely indicate their true functions. Exposed BrowserProcess::ModuleRefCount() so BrowserList can determine when the application is going to exit so the right notifications/callbacks can be generated. Updated background-auto-update-restart code to use new BrowserList APIs to determine whether the application is running "in the background". Added code to cancel shutdown on all plaforms if the user selects cancel in an onbeforeunload dialog. BUG=45275 TEST=RunInBackgroundTest (ui_test) Review URL: http://codereview.chromium.org/2866034 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53092 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/app_controller_mac.mm29
-rw-r--r--chrome/browser/automation/automation_provider.cc4
-rw-r--r--chrome/browser/background_contents_service.cc38
-rw-r--r--chrome/browser/background_contents_service.h4
-rw-r--r--chrome/browser/background_contents_service_unittest.cc13
-rw-r--r--chrome/browser/browser.cc13
-rw-r--r--chrome/browser/browser_init.cc3
-rw-r--r--chrome/browser/browser_list.cc65
-rw-r--r--chrome/browser/browser_list.h23
-rw-r--r--chrome/browser/browser_list_gtk.cc2
-rw-r--r--chrome/browser/browser_list_mac.mm7
-rw-r--r--chrome/browser/browser_list_win.cc2
-rw-r--r--chrome/browser/browser_main_mac.mm3
-rw-r--r--chrome/browser/browser_process_impl.cc3
-rw-r--r--chrome/browser/browser_shutdown.cc4
-rw-r--r--chrome/browser/browser_shutdown.h25
-rw-r--r--chrome/browser/browser_uitest.cc26
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc17
-rw-r--r--chrome/browser/js_modal_dialog.cc6
-rw-r--r--chrome/browser/profile_manager.cc2
-rw-r--r--chrome/browser/tab_contents/background_contents.cc10
21 files changed, 199 insertions, 100 deletions
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 6214edd..1ebff27 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -232,21 +232,18 @@ void RecordLastRunAppBundlePath() {
![self shouldQuitWithInProgressDownloads])
return NO;
- // Set the state to "trying to quit", so that closing all browser windows will
- // lead to termination.
- browser_shutdown::SetTryingToQuit(true);
-
// TODO(viettrungluu): Remove Apple Event handlers here? (It's safe to leave
// them in, but I'm not sure about UX; we'd also want to disable other things
// though.) http://crbug.com/40861
- if (!BrowserList::size())
- return YES;
+ size_t num_browsers = BrowserList::size();
- // Try to close all the windows.
- BrowserList::CloseAllBrowsers(true);
+ // Initiate a shutdown (via BrowserList::CloseAllBrowsers()) if we aren't
+ // already shutting down.
+ if (!browser_shutdown::IsTryingToQuit())
+ BrowserList::CloseAllBrowsers(true);
- return NO;
+ return num_browsers == 0 ? YES : NO;
}
- (void)stopTryingToTerminateApplication:(NSApplication*)app {
@@ -271,9 +268,9 @@ void RecordLastRunAppBundlePath() {
// There better be no browser windows left at this point.
CHECK_EQ(BrowserList::size(), 0u);
- // Release the reference to the browser process. Once all the browsers get
- // dealloc'd, it will stop the RunLoop and fall back into main().
- g_browser_process->ReleaseModule();
+ // Tell BrowserList not to keep the browser process alive. Once all the
+ // browsers get dealloc'd, it will stop the RunLoop and fall back into main().
+ BrowserList::EndKeepAlive();
// Close these off if they have open windows.
[prefsController_ close];
@@ -455,11 +452,9 @@ void RecordLastRunAppBundlePath() {
// This is called after profiles have been loaded and preferences registered.
// It is safe to access the default profile here.
- (void)applicationDidFinishLaunching:(NSNotification*)notify {
- // Hold an extra ref to the BrowserProcess singleton so it doesn't go away
- // when all the browser windows get closed. We'll release it on quit which
- // will be the signal to exit.
- DCHECK(g_browser_process);
- g_browser_process->AddRefModule();
+ // Notify BrowserList to keep the application running so it doesn't go away
+ // when all the browser windows get closed.
+ BrowserList::StartKeepAlive();
bookmarkMenuBridge_.reset(new BookmarkMenuBridge([self defaultProfile]));
historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
index bd3fac1..1460041 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -83,6 +83,7 @@
#include "chrome/common/automation_constants.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/net/url_request_context_getter.h"
@@ -3341,7 +3342,8 @@ void TestingAutomationProvider::OnBrowserRemoving(const Browser* browser) {
// For backwards compatibility with the testing automation interface, we
// want the automation provider (and hence the process) to go away when the
// last browser goes away.
- if (BrowserList::size() == 1) {
+ if (BrowserList::size() == 1 && !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kKeepAliveForTest)) {
// If you change this, update Observer for NotificationType::SESSION_END
// below.
MessageLoop::current()->PostTask(FROM_HERE,
diff --git a/chrome/browser/background_contents_service.cc b/chrome/browser/background_contents_service.cc
index f35420b..c10d9d2 100644
--- a/chrome/browser/background_contents_service.cc
+++ b/chrome/browser/background_contents_service.cc
@@ -9,7 +9,7 @@
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
-#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_list.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
@@ -39,7 +39,8 @@ const wchar_t kFrameNameKey[] = L"name";
BackgroundContentsService::BackgroundContentsService(
Profile* profile, const CommandLine* command_line)
- : prefs_(NULL) {
+ : prefs_(NULL),
+ always_keep_alive_(command_line->HasSwitch(switches::kKeepAliveForTest)) {
// Don't load/store preferences if the proper switch is not enabled, or if
// the parent profile is off the record.
if (!profile->IsOffTheRecord() &&
@@ -83,6 +84,14 @@ void BackgroundContentsService::StartObserving(Profile* profile) {
// BackgroundContents.
registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
Source<Profile>(profile));
+
+ if (always_keep_alive_ && !profile->IsOffTheRecord()) {
+ // For testing, keep the browser process alive until there is an explicit
+ // shutdown.
+ registrar_.Add(this, NotificationType::APP_TERMINATING,
+ NotificationService::AllSources());
+ BrowserList::StartKeepAlive();
+ }
}
void BackgroundContentsService::Observe(NotificationType type,
@@ -111,6 +120,11 @@ void BackgroundContentsService::Observe(NotificationType type,
ShutdownAssociatedBackgroundContents(
ASCIIToUTF16(Details<Extension>(details)->id()));
break;
+ case NotificationType::APP_TERMINATING:
+ // Performing an explicit shutdown, so allow the browser process to exit.
+ DCHECK(always_keep_alive_);
+ BrowserList::EndKeepAlive();
+ break;
default:
NOTREACHED();
break;
@@ -172,8 +186,12 @@ void BackgroundContentsService::CreateBackgroundContents(
BackgroundContents* contents = new BackgroundContents(
SiteInstance::CreateSiteInstanceForURL(profile, url), MSG_ROUTING_NONE);
- contents_map_[application_id].contents = contents;
- contents_map_[application_id].frame_name = frame_name;
+ // TODO(atwilson): Change this to send a BACKGROUND_CONTENTS_CREATED
+ // notification when we have a listener outside of BackgroundContentsService.
+ BackgroundContentsOpenedDetails details = {contents,
+ frame_name,
+ application_id};
+ BackgroundContentsOpened(&details);
RenderViewHost* render_view_host = contents->render_view_host();
// TODO(atwilson): Create RenderViews asynchronously to avoid increasing
@@ -233,10 +251,8 @@ void BackgroundContentsService::BackgroundContentsOpened(
BackgroundContentsOpenedDetails* details) {
// If this is the first BackgroundContents loaded, kick ourselves into
// persistent mode.
- // TODO(atwilson): Enable this when we support running with no active windows
- // on all platforms (http://crbug.com/45275).
- // if (contents_map_.empty())
- // g_browser_process->AddRefModule();
+ if (contents_map_.empty())
+ BrowserList::StartKeepAlive();
// Add the passed object to our list. Should not already be tracked.
DCHECK(!IsTracked(details->contents));
@@ -260,10 +276,8 @@ void BackgroundContentsService::BackgroundContentsShutdown(
contents_map_.erase(appid);
// If we have no more BackgroundContents active, then stop keeping the browser
// process alive.
- // TODO(atwilson): Enable this when we support running with no active windows
- // on all platforms (http://crbug.com/45275).
- // if (contents_map_.empty())
- // g_browser_process->ReleaseModule();
+ if (contents_map_.empty())
+ BrowserList::EndKeepAlive();
}
BackgroundContents* BackgroundContentsService::GetAppBackgroundContents(
diff --git a/chrome/browser/background_contents_service.h b/chrome/browser/background_contents_service.h
index 48a9a3c..73d9944 100644
--- a/chrome/browser/background_contents_service.h
+++ b/chrome/browser/background_contents_service.h
@@ -108,6 +108,10 @@ class BackgroundContentsService : private NotificationObserver {
typedef std::map<string16, BackgroundContentsInfo> BackgroundContentsMap;
BackgroundContentsMap contents_map_;
+ // If true, should always keep the browser process alive regardless of whether
+ // there are any BackgroundContents (used for manual/automated testing).
+ bool always_keep_alive_;
+
DISALLOW_COPY_AND_ASSIGN(BackgroundContentsService);
};
diff --git a/chrome/browser/background_contents_service_unittest.cc b/chrome/browser/background_contents_service_unittest.cc
index 8e4394e..6383e2f 100644
--- a/chrome/browser/background_contents_service_unittest.cc
+++ b/chrome/browser/background_contents_service_unittest.cc
@@ -8,6 +8,7 @@
#include "base/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/background_contents_service.h"
+#include "chrome/browser/browser_list.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/tab_contents/background_contents.h"
#include "chrome/common/chrome_switches.h"
@@ -112,20 +113,14 @@ TEST_F(BackgroundContentsServiceTest, Create) {
TEST_F(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy) {
TestingProfile profile;
BackgroundContentsService service(&profile, command_line_.get());
- // TODO(atwilson): Enable the refcount part of the test once we fix the
- // APP_TERMINATING notification to be sent at the proper time.
- // (http://crbug.com/45275).
- //
- // TestingBrowserProcess* process =
- // static_cast<TestingBrowserProcess*>(g_browser_process);
- // unsigned int starting_ref_count = process->module_ref_count();
MockBackgroundContents* contents = new MockBackgroundContents(&profile);
EXPECT_FALSE(service.IsTracked(contents));
+ EXPECT_FALSE(BrowserList::WillKeepAlive());
contents->SendOpenedNotification();
- // EXPECT_EQ(starting_ref_count+1, process->module_ref_count());
+ EXPECT_TRUE(BrowserList::WillKeepAlive());
EXPECT_TRUE(service.IsTracked(contents));
delete contents;
- // EXPECT_EQ(starting_ref_count, process->module_ref_count());
+ EXPECT_FALSE(BrowserList::WillKeepAlive());
EXPECT_FALSE(service.IsTracked(contents));
}
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 51c7764..3ad0276 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -808,13 +808,12 @@ void Browser::OnWindowClosing() {
bool exiting = false;
-#if defined(OS_MACOSX)
- // On Mac, closing the last window isn't usually a sign that the app is
- // shutting down.
- bool should_quit_if_last_browser = browser_shutdown::IsTryingToQuit();
-#else
- bool should_quit_if_last_browser = true;
-#endif
+ // Application should shutdown on last window close if the user is explicitly
+ // trying to quit, or if there is nothing keeping the browser alive (such as
+ // AppController on the Mac, or BackgroundContentsService for background
+ // pages).
+ bool should_quit_if_last_browser =
+ browser_shutdown::IsTryingToQuit() || !BrowserList::WillKeepAlive();
if (should_quit_if_last_browser && BrowserList::size() == 1) {
browser_shutdown::OnShutdownStarting(browser_shutdown::WINDOW_CLOSE);
diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc
index 97a7743..e9c2087 100644
--- a/chrome/browser/browser_init.cc
+++ b/chrome/browser/browser_init.cc
@@ -460,7 +460,8 @@ bool BrowserInit::LaunchBrowser(
}
#endif
- if (command_line.HasSwitch(switches::kRestoreBackgroundContents)) {
+ if (command_line.HasSwitch(switches::kRestoreBackgroundContents) ||
+ command_line.HasSwitch(switches::kKeepAliveForTest)) {
// Create status icons
StatusTrayManager* tray = g_browser_process->status_tray_manager();
if (tray)
diff --git a/chrome/browser/browser_list.cc b/chrome/browser/browser_list.cc
index 1b41be7..ed0991a 100644
--- a/chrome/browser/browser_list.cc
+++ b/chrome/browser/browser_list.cc
@@ -172,10 +172,10 @@ void BrowserList::RemoveBrowser(Browser* browser) {
// however, many UI tests rely on this behavior so leave it be for now and
// simply ignore the behavior on the Mac outside of unit tests.
// TODO(andybons): Fix the UI tests to Do The Right Thing.
- bool close_app_non_mac = (browsers_.size() == 1);
+ bool closing_last_browser = (browsers_.size() == 1);
NotificationService::current()->Notify(
NotificationType::BROWSER_CLOSED,
- Source<Browser>(browser), Details<bool>(&close_app_non_mac));
+ Source<Browser>(browser), Details<bool>(&closing_last_browser));
// Send out notifications before anything changes. Do some basic checking to
// try to catch evil observers that change the list from under us.
@@ -189,13 +189,24 @@ void BrowserList::RemoveBrowser(Browser* browser) {
// If the last Browser object was destroyed, make sure we try to close any
// remaining dependent windows too.
if (browsers_.empty()) {
- AllBrowsersClosed();
-
delete activity_observer;
activity_observer = NULL;
}
g_browser_process->ReleaseModule();
+
+ // If we're exiting, send out the APP_TERMINATING notification to allow other
+ // modules to shut themselves down.
+ if (browsers_.empty() &&
+ (browser_shutdown::IsTryingToQuit() ||
+ g_browser_process->IsShuttingDown())) {
+ // Last browser has just closed, and this is a user-initiated quit or there
+ // is no module keeping the app alive, so send out our notification.
+ NotificationService::current()->Notify(NotificationType::APP_TERMINATING,
+ NotificationService::AllSources(),
+ NotificationService::NoDetails());
+ AllBrowsersClosedAndAppExiting();
+ }
}
// static
@@ -210,10 +221,22 @@ void BrowserList::RemoveObserver(BrowserList::Observer* observer) {
// static
void BrowserList::CloseAllBrowsers(bool use_post) {
+ // Tell everyone that we are shutting down.
+ browser_shutdown::SetTryingToQuit(true);
+
// Before we close the browsers shutdown all session services. That way an
// exit can restore all browsers open before exiting.
ProfileManager::ShutdownSessionServices();
+ // 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 (browsers_.empty()) {
+ NotificationService::current()->Notify(NotificationType::APP_TERMINATING,
+ NotificationService::AllSources(),
+ NotificationService::NoDetails());
+ AllBrowsersClosedAndAppExiting();
+ return;
+ }
for (BrowserList::const_iterator i = BrowserList::begin();
i != BrowserList::end();) {
if (use_post) {
@@ -309,10 +332,36 @@ bool BrowserList::HasBrowserWithProfile(Profile* profile) {
}
// static
-bool BrowserList::IsInPersistentMode() {
- // TODO(atwilson): check the boolean state variable that you will set for
- // persisent instances.
- return false;
+int BrowserList::keep_alive_count_ = 0;
+
+// static
+void BrowserList::StartKeepAlive() {
+ // Increment the browser process refcount as long as we're keeping the
+ // application alive.
+ if (!WillKeepAlive())
+ g_browser_process->AddRefModule();
+ keep_alive_count_++;
+}
+
+// static
+void BrowserList::EndKeepAlive() {
+ DCHECK(keep_alive_count_ > 0);
+ keep_alive_count_--;
+ // Allow the app to shutdown again.
+ if (!WillKeepAlive()) {
+ g_browser_process->ReleaseModule();
+ // If there are no browsers open and we aren't already shutting down,
+ // initiate a shutdown. Also skips shutdown if this is a unit test
+ // (MessageLoop::current() == null).
+ if (browsers_.empty() && !browser_shutdown::IsTryingToQuit() &&
+ MessageLoop::current())
+ CloseAllBrowsers(true);
+ }
+}
+
+// static
+bool BrowserList::WillKeepAlive() {
+ return keep_alive_count_ > 0;
}
// static
diff --git a/chrome/browser/browser_list.h b/chrome/browser/browser_list.h
index a6113a7..9c477c0 100644
--- a/chrome/browser/browser_list.h
+++ b/chrome/browser/browser_list.h
@@ -113,8 +113,19 @@ class BrowserList {
// Returns true if there is at least one Browser with the specified profile.
static bool HasBrowserWithProfile(Profile* profile);
- // Returns true if browser is in persistent mode and false otherwise.
- static bool IsInPersistentMode();
+ // Tells the BrowserList to keep the application alive after the last Browser
+ // closes. This is implemented as a count, so callers should pair their calls
+ // to StartKeepAlive() with matching calls to EndKeepAlive() when they no
+ // longer need to keep the application running.
+ static void StartKeepAlive();
+
+ // Stops keeping the application alive after the last Browser is closed.
+ // Should match a previous call to StartKeepAlive().
+ static void EndKeepAlive();
+
+ // Returns true if application will continue running after the last Browser
+ // closes.
+ static bool WillKeepAlive();
static const_iterator begin() { return browsers_.begin(); }
static const_iterator end() { return browsers_.end(); }
@@ -144,8 +155,8 @@ class BrowserList {
// Returns true if at least one off the record session is active.
static bool IsOffTheRecordSessionActive();
- // Called when the last browser is closed.
- static void AllBrowsersClosed();
+ // Called once there are no more browsers open and the application is exiting.
+ static void AllBrowsersClosedAndAppExiting();
private:
// Helper method to remove a browser instance from a list of browsers
@@ -154,6 +165,10 @@ class BrowserList {
static BrowserVector browsers_;
static BrowserVector last_active_browsers_;
static ObserverList<Observer> observers_;
+
+ // Counter of calls to StartKeepAlive(). If non-zero, the application will
+ // continue running after the last browser has exited.
+ static int keep_alive_count_;
};
class TabContents;
diff --git a/chrome/browser/browser_list_gtk.cc b/chrome/browser/browser_list_gtk.cc
index 8633121..b6e0f06 100644
--- a/chrome/browser/browser_list_gtk.cc
+++ b/chrome/browser/browser_list_gtk.cc
@@ -7,7 +7,7 @@
#include <gtk/gtk.h>
// static
-void BrowserList::AllBrowsersClosed() {
+void BrowserList::AllBrowsersClosedAndAppExiting() {
// Close non-browser windows.
GList* window_list = gtk_window_list_toplevels();
g_list_foreach(window_list, (GFunc)g_object_ref, NULL);
diff --git a/chrome/browser/browser_list_mac.mm b/chrome/browser/browser_list_mac.mm
index f65a3e5..5c2ed9a 100644
--- a/chrome/browser/browser_list_mac.mm
+++ b/chrome/browser/browser_list_mac.mm
@@ -8,8 +8,7 @@
#import "chrome/browser/chrome_browser_application_mac.h"
// static
-void BrowserList::AllBrowsersClosed() {
- // Only terminate after all browsers are closed if trying quit.
- if (browser_shutdown::IsTryingToQuit())
- chrome_browser_application_mac::Terminate();
+void BrowserList::AllBrowsersClosedAndAppExiting() {
+ // Last browser is closed, so call back to controller to shutdown the app.
+ chrome_browser_application_mac::Terminate();
}
diff --git a/chrome/browser/browser_list_win.cc b/chrome/browser/browser_list_win.cc
index a66ddec..37ee4fc 100644
--- a/chrome/browser/browser_list_win.cc
+++ b/chrome/browser/browser_list_win.cc
@@ -7,6 +7,6 @@
#include "views/window/window.h"
// static
-void BrowserList::AllBrowsersClosed() {
+void BrowserList::AllBrowsersClosedAndAppExiting() {
views::Window::CloseAllSecondaryWindows();
}
diff --git a/chrome/browser/browser_main_mac.mm b/chrome/browser/browser_main_mac.mm
index f0f0861..34cca50 100644
--- a/chrome/browser/browser_main_mac.mm
+++ b/chrome/browser/browser_main_mac.mm
@@ -77,9 +77,6 @@ void WillInitializeMainMessageLoop(const MainFunctionParams& parameters) {
void DidEndMainMessageLoop() {
AppController* appController = [NSApp delegate];
[appController didEndMainMessageLoop];
- NotificationService::current()->Notify(NotificationType::APP_TERMINATING,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
}
void RecordBreakpadStatusUMA(MetricsService* metrics) {
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index bee3eaf..2fac31a 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -547,7 +547,8 @@ void BrowserProcessImpl::DoInspectorFilesCheck() {
bool BrowserProcessImpl::CanAutorestartForUpdate() const {
// Check if browser is in the background and if it needs to be restarted to
// apply a pending update.
- return BrowserList::IsInPersistentMode() && Upgrade::IsUpdatePendingRestart();
+ return BrowserList::size() == 0 && !BrowserList::WillKeepAlive() &&
+ Upgrade::IsUpdatePendingRestart();
}
// Switches enumerated here will be removed when a background instance of
diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc
index dd541ef..7e5fded 100644
--- a/chrome/browser/browser_shutdown.cc
+++ b/chrome/browser/browser_shutdown.cc
@@ -43,10 +43,8 @@ using base::TimeDelta;
namespace browser_shutdown {
-#if defined(OS_MACOSX)
// Whether the browser is trying to quit (e.g., Quit chosen from menu).
bool g_trying_to_quit = false;
-#endif // OS_MACOSX
Time shutdown_started_;
ShutdownType shutdown_type_ = NOT_VALID;
@@ -262,7 +260,6 @@ void ReadLastShutdownInfo() {
&ReadLastShutdownFile, type, num_procs, num_procs_slow));
}
-#if defined(OS_MACOSX)
void SetTryingToQuit(bool quitting) {
g_trying_to_quit = quitting;
}
@@ -270,6 +267,5 @@ void SetTryingToQuit(bool quitting) {
bool IsTryingToQuit() {
return g_trying_to_quit;
}
-#endif
} // namespace browser_shutdown
diff --git a/chrome/browser/browser_shutdown.h b/chrome/browser/browser_shutdown.h
index 3ab2c2e..c782f36 100644
--- a/chrome/browser/browser_shutdown.h
+++ b/chrome/browser/browser_shutdown.h
@@ -44,21 +44,26 @@ void Shutdown();
// Called at startup to create a histogram from our previous shutdown time.
void ReadLastShutdownInfo();
-#if defined(OS_MACOSX)
-// On Mac, closing the last window does not automatically quit the application.
-// To actually quit, set a flag which makes final window closure trigger a quit.
-// If the quit is aborted, then the flag should be reset (but see notes below on
-// the proper way to do this, i.e., usually not using |SetTryingToQuit()|).
+// There are various situations where the browser process should continue to
+// run after the last browser window has closed - the Mac always continues
+// running until the user explicitly quits, and on Windows/Linux the application
+// should not shutdown when the last browser window closes if there are any
+// BackgroundContents running.
+// When the user explicitly chooses to shutdown the app (via the "Exit" or
+// "Quit" menu items) BrowserList will call SetTryingToQuit() to tell itself to
+// initiate a shutdown when the last window closes.
+// If the quit is aborted, then the flag should be reset.
-// This is a low-level mutator; in general, don't call it, except from
-// appropriate places in the app controller. To quit, use usual means, e.g.,
-// using |chrome_browser_application_mac::Terminate()|. To stop quitting, use
-// |chrome_browser_application_mac::CancelTerminate()|.
+// This is a low-level mutator; in general, don't call SetTryingToQuit(true),
+// except from appropriate places in BrowserList. To quit, use usual means,
+// e.g., using |chrome_browser_application_mac::Terminate()| on the Mac, or
+// |BrowserList::CloseAllWindowsAndExit()| on other platforms. To stop quitting,
+// use |chrome_browser_application_mac::CancelTerminate()| on the Mac; other
+// platforms can call SetTryingToQuit(false) directly.
void SetTryingToQuit(bool quitting);
// General accessor.
bool IsTryingToQuit();
-#endif // OS_MACOSX
} // namespace browser_shutdown
diff --git a/chrome/browser/browser_uitest.cc b/chrome/browser/browser_uitest.cc
index eb66e13..2058b18 100644
--- a/chrome/browser/browser_uitest.cc
+++ b/chrome/browser/browser_uitest.cc
@@ -329,4 +329,30 @@ TEST_F(AppModeTest, EnableAppModeTest) {
EXPECT_EQ(Browser::TYPE_APP, type);
}
+// Tests to ensure that the browser continues running in the background after
+// the last window closes.
+class RunInBackgroundTest : public UITest {
+ public:
+ RunInBackgroundTest() {
+ launch_arguments_.AppendSwitch(switches::kKeepAliveForTest);
+ }
+};
+
+TEST_F(RunInBackgroundTest, RunInBackgroundBasicTest) {
+ // Close the browser window, then open a new one - the browser should keep
+ // running.
+ scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser.get());
+ int window_count;
+ ASSERT_TRUE(automation()->GetBrowserWindowCount(&window_count));
+ EXPECT_EQ(1, window_count);
+ ASSERT_TRUE(browser->RunCommand(IDC_CLOSE_WINDOW));
+ ASSERT_TRUE(automation()->GetBrowserWindowCount(&window_count));
+ EXPECT_EQ(0, window_count);
+ ASSERT_TRUE(IsBrowserRunning());
+ ASSERT_TRUE(automation()->OpenNewBrowserWindow(Browser::TYPE_NORMAL, true));
+ ASSERT_TRUE(automation()->GetBrowserWindowCount(&window_count));
+ EXPECT_EQ(1, window_count);
+}
+
} // namespace
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index cd040f8..3cde54e 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -51,13 +51,8 @@ ExtensionProcessManager::ExtensionProcessManager(Profile* profile)
NotificationService::AllSources());
registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED,
NotificationService::AllSources());
-#if defined(OS_WIN) || defined(OS_LINUX)
- registrar_.Add(this, NotificationType::BROWSER_CLOSED,
- NotificationService::AllSources());
-#elif defined(OS_MACOSX)
registrar_.Add(this, NotificationType::APP_TERMINATING,
NotificationService::AllSources());
-#endif
}
ExtensionProcessManager::~ExtensionProcessManager() {
@@ -285,22 +280,14 @@ void ExtensionProcessManager::Observe(NotificationType type,
UnregisterExtensionProcess(host->id());
break;
}
-#if defined(OS_WIN) || defined(OS_LINUX)
- case NotificationType::BROWSER_CLOSED: {
+
+ case NotificationType::APP_TERMINATING: {
// Close background hosts when the last browser is closed so that they
// have time to shutdown various objects on different threads. Our
// destructor is called too late in the shutdown sequence.
- bool app_closing = *Details<bool>(details).ptr();
- if (app_closing)
- CloseBackgroundHosts();
- break;
- }
-#elif defined(OS_MACOSX)
- case NotificationType::APP_TERMINATING: {
CloseBackgroundHosts();
break;
}
-#endif
default:
NOTREACHED();
diff --git a/chrome/browser/js_modal_dialog.cc b/chrome/browser/js_modal_dialog.cc
index 111aa01..7c42c71 100644
--- a/chrome/browser/js_modal_dialog.cc
+++ b/chrome/browser/js_modal_dialog.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/js_modal_dialog.h"
#include "base/string_util.h"
+#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_service.h"
@@ -90,6 +91,11 @@ void JavaScriptAppModalDialog::InitNotifications() {
}
void JavaScriptAppModalDialog::OnCancel() {
+ // If we are shutting down and this is an onbeforeunload dialog, cancel the
+ // shutdown.
+ if (is_before_unload_dialog_)
+ browser_shutdown::SetTryingToQuit(false);
+
// We need to do this before WM_DESTROY (WindowClosing()) as any parent frame
// will receive its activation messages before this dialog receives
// WM_DESTROY. The parent frame would then try to activate any modal dialogs
diff --git a/chrome/browser/profile_manager.cc b/chrome/browser/profile_manager.cc
index 1e948a3..8770b1d 100644
--- a/chrome/browser/profile_manager.cc
+++ b/chrome/browser/profile_manager.cc
@@ -39,6 +39,8 @@
// static
void ProfileManager::ShutdownSessionServices() {
ProfileManager* pm = g_browser_process->profile_manager();
+ if (!pm) // Is NULL when running unit tests.
+ return;
for (ProfileManager::const_iterator i = pm->begin(); i != pm->end(); ++i)
(*i)->ShutdownSessionService();
}
diff --git a/chrome/browser/tab_contents/background_contents.cc b/chrome/browser/tab_contents/background_contents.cc
index 2e65d9d..f8c466d 100644
--- a/chrome/browser/tab_contents/background_contents.cc
+++ b/chrome/browser/tab_contents/background_contents.cc
@@ -34,8 +34,13 @@ BackgroundContents::BackgroundContents(SiteInstance* site_instance,
session_storage_namespace_id);
render_view_host_->AllowScriptToClose(true);
+ // Close ourselves when the application is shutting down.
+ registrar_.Add(this, NotificationType::APP_TERMINATING,
+ NotificationService::AllSources());
+
// Register for our parent profile to shutdown, so we can shut ourselves down
- // as well.
+ // as well (should only be called for OTR profiles, as we should receive
+ // APP_TERMINATING before non-OTR profiles are destroyed).
registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
Source<Profile>(profile));
}
@@ -51,7 +56,8 @@ void BackgroundContents::Observe(NotificationType type,
// TODO(rafaelw): Implement pagegroup ref-counting so that non-persistent
// background pages are closed when the last referencing frame is closed.
switch (type.value) {
- case NotificationType::PROFILE_DESTROYED: {
+ case NotificationType::PROFILE_DESTROYED:
+ case NotificationType::APP_TERMINATING: {
delete this;
break;
}