Index: chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm |
diff --git a/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm b/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm |
index 8df88e1b3f7dac2c767a6ee2c86bb9304535cdbb..e2e4982ac69fcc0d2af3cbb97d8f610c61a6f003 100644 |
--- a/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm |
+++ b/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm |
@@ -23,13 +23,17 @@ |
#include "chrome/browser/apps/app_shim/app_shim_host_manager_mac.h" |
#include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h" |
#include "chrome/browser/browser_process.h" |
+#include "chrome/browser/extensions/launch_util.h" |
#include "chrome/browser/profiles/profile.h" |
+#include "chrome/browser/ui/browser_list.h" |
+#include "chrome/browser/ui/browser_window.h" |
#include "chrome/browser/web_applications/web_app_mac.h" |
#include "chrome/common/chrome_paths.h" |
#include "chrome/common/chrome_switches.h" |
#include "chrome/common/mac/app_mode_common.h" |
#include "content/public/test/test_utils.h" |
#include "extensions/browser/app_window/native_app_window.h" |
+#include "extensions/browser/extension_prefs.h" |
#include "extensions/browser/extension_registry.h" |
#include "extensions/test/extension_test_message_listener.h" |
#import "ui/events/test/cocoa_test_event_utils.h" |
@@ -151,6 +155,62 @@ class WindowedAppShimLaunchObserver : public apps::AppShimHandler { |
DISALLOW_COPY_AND_ASSIGN(WindowedAppShimLaunchObserver); |
}; |
+// Watches for a hosted app browser window to open. |
+class HostedAppBrowserListObserver : public chrome::BrowserListObserver { |
+ public: |
+ explicit HostedAppBrowserListObserver(const std::string& app_id) |
+ : app_id_(app_id), observed_add_(false), observed_removed_(false) { |
+ BrowserList::AddObserver(this); |
+ } |
+ |
+ ~HostedAppBrowserListObserver() { BrowserList::RemoveObserver(this); } |
+ |
+ void WaitUntilAdded() { |
+ if (observed_add_) |
+ return; |
+ |
+ run_loop_.reset(new base::RunLoop); |
+ run_loop_->Run(); |
+ } |
+ |
+ void WaitUntilRemoved() { |
+ if (observed_removed_) |
+ return; |
+ |
+ run_loop_.reset(new base::RunLoop); |
+ run_loop_->Run(); |
+ } |
+ |
+ // BrowserListObserver overrides: |
+ void OnBrowserAdded(Browser* browser) override { |
+ const extensions::Extension* app = |
+ apps::ExtensionAppShimHandler::GetAppForBrowser(browser); |
+ if (app && app->id() == app_id_) { |
+ observed_add_ = true; |
+ if (run_loop_.get()) |
+ run_loop_->Quit(); |
+ } |
+ } |
+ |
+ void OnBrowserRemoved(Browser* browser) override { |
+ const extensions::Extension* app = |
+ apps::ExtensionAppShimHandler::GetAppForBrowser(browser); |
+ if (app && app->id() == app_id_) { |
+ observed_removed_ = true; |
+ if (run_loop_.get()) |
+ run_loop_->Quit(); |
+ } |
+ } |
+ |
+ private: |
+ std::string app_id_; |
+ bool observed_add_; |
+ bool observed_removed_; |
+ scoped_ptr<base::RunLoop> run_loop_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(HostedAppBrowserListObserver); |
+}; |
+ |
class AppLifetimeMonitorObserver : public apps::AppLifetimeMonitor::Observer { |
public: |
AppLifetimeMonitorObserver(Profile* profile) |
@@ -220,6 +280,18 @@ void UpdateAppAndAwaitShimCreation(Profile* profile, |
file_watcher->Wait(); |
} |
+Browser* GetFirstHostedAppWindow() { |
+ BrowserList* browsers = |
+ BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE); |
+ for (Browser* browser : *browsers) { |
+ const extensions::Extension* extension = |
+ apps::ExtensionAppShimHandler::GetAppForBrowser(browser); |
+ if (extension && extension->is_hosted_app()) |
+ return browser; |
+ } |
+ return nullptr; |
+} |
+ |
} // namespace |
// Watches for NSNotifications from the shared workspace. |
@@ -280,14 +352,87 @@ namespace apps { |
// Shims require static libraries http://crbug.com/386024. |
#if defined(COMPONENT_BUILD) |
#define MAYBE_Launch DISABLED_Launch |
+#define MAYBE_HostedAppLaunch DISABLED_HostedAppLaunch |
#define MAYBE_ShowWindow DISABLED_ShowWindow |
#define MAYBE_RebuildShim DISABLED_RebuildShim |
#else |
#define MAYBE_Launch Launch |
+#define MAYBE_HostedAppLaunch HostedAppLaunch |
#define MAYBE_ShowWindow ShowWindow |
#define MAYBE_RebuildShim RebuildShim |
#endif |
+IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, MAYBE_HostedAppLaunch) { |
+ const extensions::Extension* app = InstallHostedApp(); |
+ |
+ base::FilePath shim_path = GetAppShimPath(profile(), app); |
+ EXPECT_FALSE(base::PathExists(shim_path)); |
+ |
+ UpdateAppAndAwaitShimCreation(profile(), app, shim_path); |
+ ASSERT_TRUE(base::PathExists(shim_path)); |
+ NSString* bundle_id = GetBundleID(shim_path); |
+ |
+ // Explicitly set the launch type to open in a new window. |
+ extensions::SetLaunchType( |
+ extensions::ExtensionSystem::Get(profile())->extension_service(), |
+ app->id(), extensions::LAUNCH_TYPE_WINDOW); |
+ |
+ // Case 1: Launch the hosted app, it should start the shim. |
+ { |
+ base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer; |
+ ns_observer.reset([[WindowedNSNotificationObserver alloc] |
+ initForNotification:NSWorkspaceDidLaunchApplicationNotification |
+ andBundleId:bundle_id]); |
+ WindowedAppShimLaunchObserver observer(app->id()); |
+ LaunchHostedApp(app); |
+ [ns_observer wait]; |
+ observer.Wait(); |
+ |
+ EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
+ EXPECT_TRUE(GetFirstHostedAppWindow()); |
+ |
+ NSArray* running_shim = [NSRunningApplication |
+ runningApplicationsWithBundleIdentifier:bundle_id]; |
+ ASSERT_EQ(1u, [running_shim count]); |
+ |
+ ns_observer.reset([[WindowedNSNotificationObserver alloc] |
+ initForNotification:NSWorkspaceDidTerminateApplicationNotification |
+ andBundleId:bundle_id]); |
+ [base::mac::ObjCCastStrict<NSRunningApplication>( |
+ [running_shim objectAtIndex:0]) terminate]; |
+ [ns_observer wait]; |
+ |
+ EXPECT_FALSE(GetFirstHostedAppWindow()); |
+ EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
+ } |
+ |
+ // Case 2: Launch the shim, it should start the hosted app. |
+ { |
+ HostedAppBrowserListObserver listener(app->id()); |
+ CommandLine shim_cmdline(CommandLine::NO_PROGRAM); |
+ shim_cmdline.AppendSwitch(app_mode::kLaunchedForTest); |
+ ProcessSerialNumber shim_psn; |
+ ASSERT_TRUE(base::mac::OpenApplicationWithPath( |
+ shim_path, shim_cmdline, kLSLaunchDefaults, &shim_psn)); |
+ listener.WaitUntilAdded(); |
+ |
+ ASSERT_TRUE(GetFirstHostedAppWindow()); |
+ EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
+ |
+ // If the window is closed, the shim should quit. |
+ pid_t shim_pid; |
+ EXPECT_EQ(noErr, GetProcessPID(&shim_psn, &shim_pid)); |
+ GetFirstHostedAppWindow()->window()->Close(); |
+ // Wait for the window to be closed. |
+ listener.WaitUntilRemoved(); |
+ ASSERT_TRUE( |
+ base::WaitForSingleProcess(shim_pid, TestTimeouts::action_timeout())); |
+ |
+ EXPECT_FALSE(GetFirstHostedAppWindow()); |
+ EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
+ } |
+} |
+ |
// Test that launching the shim for an app starts the app, and vice versa. |
// These two cases are combined because the time to run the test is dominated |
// by loading the extension and creating the shim. |