summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/chromium_strings.grd7
-rw-r--r--chrome/app/generated_resources.grd22
-rw-r--r--chrome/app/google_chrome_strings.grd7
-rw-r--r--chrome/browser/about_flags.cc9
-rw-r--r--chrome/browser/app_controller_mac.h5
-rw-r--r--chrome/browser/app_controller_mac.mm11
-rw-r--r--chrome/browser/prefs/browser_prefs.cc2
-rw-r--r--chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc150
-rw-r--r--chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h49
-rw-r--r--chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.cc127
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/pref_names.cc5
-rw-r--r--chrome/common/pref_names.h1
16 files changed, 402 insertions, 0 deletions
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 2f40467..7ca657d 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -657,6 +657,13 @@ Chromium is unable to recover your settings.
Yes, exit Chromium
</message>
+ <!-- Quit all apps confirmation dialog -->
+ <if expr="is_macosx">
+ <message name="IDS_QUIT_WITH_APPS_TITLE" desc="Title for a notification explaining that Chrome is running in the background.">
+ Chromium is running in the background.
+ </message>
+ </if>
+
<!-- Autolaunch infobar -->
<message name="IDS_AUTO_LAUNCH_INFOBAR_TEXT" desc="The text to show in the infobar when Chromium was automatically launched on startup">
Chromium is configured to automatically launch when you start your computer.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 4969065..464091e 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2174,6 +2174,22 @@ Even if you have downloaded files from this website before, the website might ha
Open as tab
</message>
+ <!-- Quit all apps confirmation dialog -->
+ <if expr="is_macosx">
+ <message name="IDS_QUIT_WITH_APPS_NOTIFICATION_DISPLAY_SOURCE" desc="Display source for the notification when quitting with apps open.">
+ Quit
+ </message>
+ <message name="IDS_QUIT_WITH_APPS_EXPLANATION" desc="Text displayed in a notification explaining that Chrome will continue running in the background as long as there are Chrome Apps open.">
+ Chrome will keep running while Chrome Apps are open.
+ </message>
+ <message name="IDS_QUIT_WITH_APPS_QUIT_LABEL" desc="Button text to quit all running Chrome apps.">
+ Quit all apps
+ </message>
+ <message name="IDS_QUIT_WITH_APPS_SUPPRESSION_LABEL" desc="Button text to prevent a notification from showing in future.">
+ Don't show this again
+ </message>
+ </if>
+
<!-- "Create application shortcuts" menu item -->
<if expr="not use_titlecase">
<message name="IDS_CREATE_SHORTCUTS" desc="Default installation menu label">
@@ -6530,6 +6546,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
Enables directory support for sync filesystem.
</message>
<if expr="is_macosx">
+ <message name="IDS_FLAGS_APPS_KEEP_CHROME_ALIVE_NAME" desc="Title for the flag to prevent Chrome from quitting when Chrome Apps are open.">
+ Apps keep Chrome alive.
+ </message>
+ <message name="IDS_FLAGS_APPS_KEEP_CHROME_ALIVE_DESCRIPTION" desc="Description for the flag to prevent Chrome from quitting when Chrome Apps are open.">
+ Prevent Chrome from quitting when Chrome Apps are open.
+ </message>
<message name="IDS_FLAGS_DISABLE_APP_SHIMS_NAME" desc="Title for the flag to disable shortcuts for packaged apps from being added to Applications and from appearing in the dock.">
Disable packaged app shortcuts.
</message>
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 75740aed..778d96b 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -581,6 +581,13 @@ Google Chrome is unable to recover your settings.
Yes, exit Chrome
</message>
+ <!-- Quit all apps confirmation dialog -->
+ <if expr="is_macosx">
+ <message name="IDS_QUIT_WITH_APPS_TITLE" desc="Title for a notification explaining that Chrome is running in the background.">
+ Google Chrome is running in the background.
+ </message>
+ </if>
+
<!-- Autolaunch infobar -->
<message name="IDS_AUTO_LAUNCH_INFOBAR_TEXT" desc="The text to show in the infobar when Chrome was automatically launched on startup">
Google Chrome is configured to automatically launch when you start your computer.
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8559d1c..a963a01 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1778,6 +1778,15 @@ const Experiment kExperiments[] = {
SINGLE_VALUE_TYPE(chromeos::switches::kEnableFileManagerMTP)
},
#endif
+#if defined(OS_MACOSX)
+ {
+ "apps-keep-chrome-alive",
+ IDS_FLAGS_APPS_KEEP_CHROME_ALIVE_NAME,
+ IDS_FLAGS_APPS_KEEP_CHROME_ALIVE_DESCRIPTION,
+ kOsMac,
+ SINGLE_VALUE_TYPE(switches::kAppsKeepChromeAlive)
+ }
+#endif
};
const Experiment* experiments = kExperiments;
diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h
index efb0783..f148c13 100644
--- a/chrome/browser/app_controller_mac.h
+++ b/chrome/browser/app_controller_mac.h
@@ -25,6 +25,8 @@ class GURL;
class HistoryMenuBridge;
class Profile;
@class ProfileMenuController;
+class QuitWithAppsController;
+
namespace ui {
class WorkAreaWatcherObserver;
}
@@ -87,6 +89,9 @@ class WorkAreaWatcherObserver;
scoped_ptr<PrefChangeRegistrar> profilePrefRegistrar_;
PrefChangeRegistrar localPrefRegistrar_;
+
+ // Displays a notification when quitting while apps are running.
+ scoped_refptr<QuitWithAppsController> quitWithAppsController_;
}
@property(readonly, nonatomic) BOOL startupComplete;
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index c8a6a16..2464c0d 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -52,6 +52,7 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h"
+#include "chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
#import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
@@ -371,6 +372,12 @@ class AppControllerProfileObserver : public ProfileInfoCacheObserver {
![self shouldQuitWithInProgressDownloads])
return NO;
+ // Check for active apps, and prompt the user if they really want to quit
+ // (and also quit the apps).
+ if (!browser_shutdown::IsTryingToQuit() &&
+ quitWithAppsController_.get() && !quitWithAppsController_->ShouldQuit())
+ return NO;
+
// 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
@@ -695,6 +702,10 @@ class AppControllerProfileObserver : public ProfileInfoCacheObserver {
// main menu item titles are not yet initialized in awakeFromNib.
[self initAppShimMenuController];
+ // If enabled, keep Chrome alive when apps are open instead of quitting all
+ // apps.
+ quitWithAppsController_ = new QuitWithAppsController();
+
// Build up the encoding menu, the order of the items differs based on the
// current locale (see http://crbug.com/7647 for details).
// We need a valid g_browser_process to get the profile which is why we can't
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index fd2bd10..4053fca 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -172,6 +172,7 @@
#endif
#if defined(OS_MACOSX)
+#include "chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h"
#include "chrome/browser/ui/cocoa/confirm_quit.h"
#include "chrome/browser/ui/cocoa/extensions/browser_actions_controller_prefs.h"
#endif
@@ -315,6 +316,7 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
#if defined(OS_MACOSX)
confirm_quit::RegisterLocalState(registry);
+ QuitWithAppsController::RegisterPrefs(registry);
#endif
#if defined(OS_WIN)
diff --git a/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc
new file mode 100644
index 0000000..9bdb3cf
--- /dev/null
+++ b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc
@@ -0,0 +1,150 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h"
+
+#include "apps/app_window.h"
+#include "apps/app_window_registry.h"
+#include "apps/ui/native_app_window.h"
+#include "base/command_line.h"
+#include "base/i18n/number_formatting.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser_iterator.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "extensions/common/extension.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/google_chrome_strings.h"
+#include "grit/theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/base/resource/resource_bundle.h"
+
+const char kQuitWithAppsOriginUrl[] = "chrome://quit-with-apps";
+const int kQuitAllAppsButtonIndex = 0;
+const int kDontShowAgainButtonIndex = 1;
+
+const char QuitWithAppsController::kQuitWithAppsNotificationID[] =
+ "quit-with-apps";
+
+QuitWithAppsController::QuitWithAppsController()
+ : suppress_for_session_(false) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ // There is only ever one notification to replace, so use the same replace_id
+ // each time.
+ base::string16 replace_id = base::UTF8ToUTF16(id());
+
+ message_center::ButtonInfo quit_apps_button_info(
+ l10n_util::GetStringUTF16(IDS_QUIT_WITH_APPS_QUIT_LABEL));
+ message_center::ButtonInfo suppression_button_info(
+ l10n_util::GetStringUTF16(IDS_QUIT_WITH_APPS_SUPPRESSION_LABEL));
+ message_center::RichNotificationData rich_notification_data;
+ rich_notification_data.buttons.push_back(quit_apps_button_info);
+ rich_notification_data.buttons.push_back(suppression_button_info);
+
+ notification_.reset(new Notification(
+ message_center::NOTIFICATION_TYPE_SIMPLE,
+ GURL(kQuitWithAppsOriginUrl),
+ l10n_util::GetStringUTF16(IDS_QUIT_WITH_APPS_TITLE),
+ l10n_util::GetStringUTF16(IDS_QUIT_WITH_APPS_EXPLANATION),
+ ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_APP_DEFAULT_ICON),
+ blink::WebTextDirectionDefault,
+ message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
+ kQuitWithAppsNotificationID),
+ l10n_util::GetStringUTF16(IDS_QUIT_WITH_APPS_NOTIFICATION_DISPLAY_SOURCE),
+ replace_id,
+ rich_notification_data,
+ this));
+}
+
+QuitWithAppsController::~QuitWithAppsController() {}
+
+void QuitWithAppsController::Display() {}
+
+void QuitWithAppsController::Error() {}
+
+void QuitWithAppsController::Close(bool by_user) {
+ if (by_user)
+ suppress_for_session_ = true;
+}
+
+void QuitWithAppsController::Click() {
+ g_browser_process->notification_ui_manager()->CancelById(id());
+}
+
+void QuitWithAppsController::ButtonClick(int button_index) {
+ typedef apps::AppWindowRegistry::AppWindowList AppWindowList;
+
+ g_browser_process->notification_ui_manager()->CancelById(id());
+ if (button_index == kQuitAllAppsButtonIndex) {
+ apps::AppWindowRegistry::CloseAllAppWindows();
+ } else if (button_index == kDontShowAgainButtonIndex) {
+ g_browser_process->local_state()->SetBoolean(
+ prefs::kNotifyWhenAppsKeepChromeAlive, false);
+ }
+}
+
+content::WebContents* QuitWithAppsController::GetWebContents() const {
+ return NULL;
+}
+
+std::string QuitWithAppsController::id() const {
+ return kQuitWithAppsNotificationID;
+}
+
+bool QuitWithAppsController::ShouldQuit() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAppsKeepChromeAlive)) {
+ g_browser_process->local_state()->ClearPref(
+ prefs::kNotifyWhenAppsKeepChromeAlive);
+ return true;
+ }
+
+ // Quit immediately if there are no windows or the confirmation has been
+ // suppressed.
+ if (!apps::AppWindowRegistry::IsAppWindowRegisteredInAnyProfile(0))
+ return true;
+
+ // If there are browser windows, and this notification has been suppressed for
+ // this session or permanently, then just return false to prevent Chrome from
+ // quitting. If there are no browser windows, always show the notification.
+ bool suppress_always = !g_browser_process->local_state()->GetBoolean(
+ prefs::kNotifyWhenAppsKeepChromeAlive);
+ if (!chrome::BrowserIterator().done() &&
+ (suppress_for_session_ || suppress_always)) {
+ return false;
+ }
+
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ DCHECK(profile_manager);
+
+ std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
+ DCHECK(profiles.size());
+
+ // Delete any existing notification to ensure this one is shown.
+ g_browser_process->notification_ui_manager()->CancelById(id());
+ g_browser_process->notification_ui_manager()->Add(*notification_,
+ profiles[0]);
+
+ // Always return false, the notification UI can be used to quit all apps which
+ // will cause Chrome to quit.
+ return false;
+}
+
+// static
+void QuitWithAppsController::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(prefs::kNotifyWhenAppsKeepChromeAlive, true);
+}
diff --git a/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h
new file mode 100644
index 0000000..f8b32fa
--- /dev/null
+++ b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_APPS_QUIT_WITH_APPS_CONTROLLER_MAC_H_
+#define CHROME_BROWSER_UI_COCOA_APPS_QUIT_WITH_APPS_CONTROLLER_MAC_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/notifications/notification.h"
+
+class PrefRegistrySimple;
+
+// QuitWithAppsController checks whether any apps are running and shows a
+// notification to quit all of them.
+class QuitWithAppsController : public NotificationDelegate {
+ public:
+ static const char kQuitWithAppsNotificationID[];
+
+ QuitWithAppsController();
+
+ // NotificationDelegate interface.
+ virtual void Display() OVERRIDE;
+ virtual void Error() OVERRIDE;
+ virtual void Close(bool by_user) OVERRIDE;
+ virtual void Click() OVERRIDE;
+ virtual void ButtonClick(int button_index) OVERRIDE;
+ virtual content::WebContents* GetWebContents() const OVERRIDE;
+ virtual std::string id() const OVERRIDE;
+
+ // Attempt to quit Chrome. This will display a notification and return false
+ // if there are apps running.
+ bool ShouldQuit();
+
+ // Register prefs used by QuitWithAppsController.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ private:
+ virtual ~QuitWithAppsController();
+
+ scoped_ptr<Notification> notification_;
+
+ // Whether to suppress showing the notification for the rest of the session.
+ bool suppress_for_session_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuitWithAppsController);
+};
+
+#endif // CHROME_BROWSER_UI_COCOA_APPS_QUIT_WITH_APPS_CONTROLLER_MAC_H_
diff --git a/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.cc b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.cc
new file mode 100644
index 0000000..de3d15f
--- /dev/null
+++ b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.cc
@@ -0,0 +1,127 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h"
+
+#include "apps/app_window_registry.h"
+#include "apps/ui/native_app_window.h"
+#include "base/command_line.h"
+#include "chrome/browser/apps/app_browsertest_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/notifications/message_center_notification_manager.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_iterator.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/common/chrome_switches.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/common/extension.h"
+#include "ui/message_center/message_center.h"
+
+typedef apps::AppWindowRegistry::AppWindowList AppWindowList;
+
+namespace {
+
+class QuitWithAppsControllerInteractiveTest
+ : public extensions::PlatformAppBrowserTest {
+ protected:
+ QuitWithAppsControllerInteractiveTest() : app_(NULL) {}
+
+ virtual ~QuitWithAppsControllerInteractiveTest() {}
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ PlatformAppBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(switches::kAppsKeepChromeAlive);
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ ExtensionBrowserTest::SetUpOnMainThread();
+
+ ExtensionTestMessageListener listener("Launched", false);
+ app_ = InstallAndLaunchPlatformApp("minimal_id");
+ ASSERT_TRUE(listener.WaitUntilSatisfied());
+ }
+
+ const extensions::Extension* app_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuitWithAppsControllerInteractiveTest);
+};
+
+} // namespace
+
+// Test that quitting while apps are open shows a notification instead.
+IN_PROC_BROWSER_TEST_F(QuitWithAppsControllerInteractiveTest, QuitBehavior) {
+ scoped_refptr<QuitWithAppsController> controller =
+ new QuitWithAppsController();
+ const Notification* notification;
+ message_center::MessageCenter* message_center =
+ message_center::MessageCenter::Get();
+ // One browser and one app window at this point.
+ EXPECT_FALSE(chrome::BrowserIterator().done());
+ EXPECT_EQ(1u, GetAppWindowCount());
+
+ // On the first quit, show notification.
+ EXPECT_FALSE(controller->ShouldQuit());
+ EXPECT_EQ(1u, GetAppWindowCount());
+ notification = g_browser_process->notification_ui_manager()->FindById(
+ QuitWithAppsController::kQuitWithAppsNotificationID);
+ ASSERT_TRUE(notification);
+
+ // If notification was dismissed by click, show again on next quit.
+ notification->delegate()->Click();
+ message_center->RemoveAllNotifications(false);
+ EXPECT_FALSE(controller->ShouldQuit());
+ EXPECT_EQ(1u, GetAppWindowCount());
+ notification = g_browser_process->notification_ui_manager()->FindById(
+ QuitWithAppsController::kQuitWithAppsNotificationID);
+ ASSERT_TRUE(notification);
+
+ EXPECT_FALSE(chrome::BrowserIterator().done());
+ EXPECT_EQ(1u, GetAppWindowCount());
+
+ // If notification is closed by user, don't show it next time.
+ notification->delegate()->Close(true);
+ message_center->RemoveAllNotifications(false);
+ EXPECT_FALSE(controller->ShouldQuit());
+ EXPECT_EQ(1u, GetAppWindowCount());
+ notification = g_browser_process->notification_ui_manager()->FindById(
+ QuitWithAppsController::kQuitWithAppsNotificationID);
+ EXPECT_EQ(NULL, notification);
+
+ EXPECT_FALSE(chrome::BrowserIterator().done());
+ EXPECT_EQ(1u, GetAppWindowCount());
+
+ // Normally, quitting would close all browsers, but since we're just
+ // simulating a quit, close it here.
+ content::WindowedNotificationObserver observer(
+ chrome::NOTIFICATION_BROWSER_CLOSED,
+ content::NotificationService::AllSources());
+ chrome::BrowserIterator()->window()->Close();
+ observer.Wait();
+
+ EXPECT_TRUE(chrome::BrowserIterator().done());
+ EXPECT_EQ(1u, GetAppWindowCount());
+
+ // Trying to quit while there are no browsers always shows notification.
+ EXPECT_FALSE(controller->ShouldQuit());
+ notification = g_browser_process->notification_ui_manager()->FindById(
+ QuitWithAppsController::kQuitWithAppsNotificationID);
+ ASSERT_TRUE(notification);
+
+ // Clicking "Quit All Apps." button closes all app windows.
+ notification->delegate()->ButtonClick(0);
+ message_center->RemoveAllNotifications(false);
+ EXPECT_EQ(0u, GetAppWindowCount());
+
+ // With no app windows open, ShouldQuit returns true.
+ EXPECT_TRUE(controller->ShouldQuit());
+ notification = g_browser_process->notification_ui_manager()->FindById(
+ QuitWithAppsController::kQuitWithAppsNotificationID);
+ EXPECT_EQ(NULL, notification);
+}
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 485db76..bcadf96 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -534,6 +534,8 @@
'browser/ui/cocoa/apps/chrome_app_window_delegate_cocoa.mm',
'browser/ui/cocoa/apps/native_app_window_cocoa.h',
'browser/ui/cocoa/apps/native_app_window_cocoa.mm',
+ 'browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc',
+ 'browser/ui/cocoa/apps/quit_with_apps_controller_mac.h',
'browser/ui/cocoa/autofill/autofill_account_chooser.h',
'browser/ui/cocoa/autofill/autofill_account_chooser.mm',
'browser/ui/cocoa/autofill/autofill_details_container.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 14e3b1a..2f57d31 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -81,6 +81,7 @@
'browser/ui/app_list/app_list_service_mac_interactive_uitest.mm',
'browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc',
'browser/ui/browser_focus_uitest.cc',
+ 'browser/ui/cocoa/apps/quit_with_apps_controller_mac_interactive_uitest.cc',
'browser/ui/cocoa/panels/panel_cocoa_browsertest.mm',
'browser/ui/find_bar/find_bar_host_interactive_uitest.cc',
'browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 87168e7..7c93680 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -1359,6 +1359,9 @@ const char kEnableSpeechDispatcher[] = "enable-speech-dispatcher";
#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
#if defined(OS_MACOSX)
+// Prevents Chrome from quitting when Chrome Apps are open.
+const char kAppsKeepChromeAlive[] = "apps-keep-chrome-alive";
+
// Disables the creation and launch of app shims for platform apps.
const char kDisableAppShims[] = "disable-app-shims";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 5512234b..4217a55 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -382,6 +382,7 @@ extern const char kMigrateDataDirForSxS[];
#endif
#if defined(OS_MACOSX)
+extern const char kAppsKeepChromeAlive[];
extern const char kDisableAppShims[];
extern const char kDisableSystemFullscreenForTesting[];
extern const char kEnableSimplifiedFullscreen[];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 823d46b..308d4bb 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2240,6 +2240,11 @@ const char kChromeCreatedLoginItem[] =
// time.
const char kMigratedLoginItemPref[] =
"background_mode.migrated_login_item_pref";
+
+// A boolean that tracks whether to show a notification when trying to quit
+// while there are apps running.
+const char kNotifyWhenAppsKeepChromeAlive[] =
+ "apps.notify-when-apps-keep-chrome-alive";
#endif
// Set to true if background mode is enabled on this browser.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 561ac67..d5bed91 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -755,6 +755,7 @@ extern const char kCustomHandlersEnabled[];
extern const char kUserRemovedLoginItem[];
extern const char kChromeCreatedLoginItem[];
extern const char kMigratedLoginItemPref[];
+extern const char kNotifyWhenAppsKeepChromeAlive[];
#endif
extern const char kBackgroundModeEnabled[];