Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
| 6 #include <vector> | 6 #include <vector> |
| 7 | 7 |
| 8 #include "apps/app_lifetime_monitor_factory.h" | 8 #include "apps/app_lifetime_monitor_factory.h" |
| 9 #include "apps/switches.h" | 9 #include "apps/switches.h" |
| 10 #include "base/auto_reset.h" | 10 #include "base/auto_reset.h" |
| 11 #include "base/callback.h" | 11 #include "base/callback.h" |
| 12 #include "base/files/file_path_watcher.h" | 12 #include "base/files/file_path_watcher.h" |
| 13 #include "base/mac/foundation_util.h" | 13 #include "base/mac/foundation_util.h" |
| 14 #include "base/mac/launch_services_util.h" | 14 #include "base/mac/launch_services_util.h" |
| 15 #include "base/mac/mac_util.h" | 15 #include "base/mac/mac_util.h" |
| 16 #include "base/mac/scoped_nsobject.h" | 16 #include "base/mac/scoped_nsobject.h" |
| 17 #include "base/path_service.h" | 17 #include "base/path_service.h" |
| 18 #include "base/process/launch.h" | 18 #include "base/process/launch.h" |
| 19 #include "base/strings/sys_string_conversions.h" | 19 #include "base/strings/sys_string_conversions.h" |
| 20 #include "base/test/test_timeouts.h" | 20 #include "base/test/test_timeouts.h" |
| 21 #include "chrome/browser/apps/app_browsertest_util.h" | 21 #include "chrome/browser/apps/app_browsertest_util.h" |
| 22 #include "chrome/browser/apps/app_shim/app_shim_handler_mac.h" | 22 #include "chrome/browser/apps/app_shim/app_shim_handler_mac.h" |
| 23 #include "chrome/browser/apps/app_shim/app_shim_host_manager_mac.h" | 23 #include "chrome/browser/apps/app_shim/app_shim_host_manager_mac.h" |
| 24 #include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h" | 24 #include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h" |
| 25 #include "chrome/browser/browser_process.h" | 25 #include "chrome/browser/browser_process.h" |
| 26 #include "chrome/browser/extensions/launch_util.h" | |
| 26 #include "chrome/browser/profiles/profile.h" | 27 #include "chrome/browser/profiles/profile.h" |
| 28 #include "chrome/browser/ui/browser_list.h" | |
| 29 #include "chrome/browser/ui/browser_window.h" | |
| 27 #include "chrome/browser/web_applications/web_app_mac.h" | 30 #include "chrome/browser/web_applications/web_app_mac.h" |
| 28 #include "chrome/common/chrome_paths.h" | 31 #include "chrome/common/chrome_paths.h" |
| 29 #include "chrome/common/chrome_switches.h" | 32 #include "chrome/common/chrome_switches.h" |
| 30 #include "chrome/common/mac/app_mode_common.h" | 33 #include "chrome/common/mac/app_mode_common.h" |
| 31 #include "content/public/test/test_utils.h" | 34 #include "content/public/test/test_utils.h" |
| 32 #include "extensions/browser/app_window/native_app_window.h" | 35 #include "extensions/browser/app_window/native_app_window.h" |
| 36 #include "extensions/browser/extension_prefs.h" | |
| 33 #include "extensions/browser/extension_registry.h" | 37 #include "extensions/browser/extension_registry.h" |
| 34 #include "extensions/test/extension_test_message_listener.h" | 38 #include "extensions/test/extension_test_message_listener.h" |
| 35 #import "ui/events/test/cocoa_test_event_utils.h" | 39 #import "ui/events/test/cocoa_test_event_utils.h" |
| 36 | 40 |
| 37 namespace { | 41 namespace { |
| 38 | 42 |
| 39 // General end-to-end test for app shims. | 43 // General end-to-end test for app shims. |
| 40 class AppShimInteractiveTest : public extensions::PlatformAppBrowserTest { | 44 class AppShimInteractiveTest : public extensions::PlatformAppBrowserTest { |
| 41 protected: | 45 protected: |
| 42 AppShimInteractiveTest() | 46 AppShimInteractiveTest() |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 144 void OnShimQuit(Host* host) override {} | 148 void OnShimQuit(Host* host) override {} |
| 145 | 149 |
| 146 private: | 150 private: |
| 147 std::string app_mode_id_; | 151 std::string app_mode_id_; |
| 148 bool observed_; | 152 bool observed_; |
| 149 scoped_ptr<base::RunLoop> run_loop_; | 153 scoped_ptr<base::RunLoop> run_loop_; |
| 150 | 154 |
| 151 DISALLOW_COPY_AND_ASSIGN(WindowedAppShimLaunchObserver); | 155 DISALLOW_COPY_AND_ASSIGN(WindowedAppShimLaunchObserver); |
| 152 }; | 156 }; |
| 153 | 157 |
| 158 // Watches for a hosted app browser window to open. | |
| 159 class HostedAppBrowserListObserver : public chrome::BrowserListObserver { | |
| 160 public: | |
| 161 HostedAppBrowserListObserver(const std::string& app_id) | |
| 162 : app_id_(app_id), observed_(false) { | |
| 163 BrowserList::AddObserver(this); | |
| 164 } | |
| 165 | |
| 166 ~HostedAppBrowserListObserver() { BrowserList::RemoveObserver(this); } | |
| 167 | |
| 168 void Wait() { | |
| 169 if (observed_) | |
| 170 return; | |
| 171 | |
| 172 run_loop_.reset(new base::RunLoop); | |
| 173 run_loop_->Run(); | |
| 174 } | |
| 175 | |
| 176 void ResetObservation() { observed_ = false; } | |
|
jackhou1
2014/12/19 04:22:01
Instead of reseting it in the middle, maybe have a
mitchellj
2014/12/19 05:04:58
Done.
| |
| 177 | |
| 178 // BrowserListObserver overrides: | |
| 179 void OnBrowserAdded(Browser* browser) override { | |
| 180 const extensions::Extension* app = | |
| 181 apps::ExtensionAppShimHandler::GetAppForBrowser(browser); | |
| 182 if (app && app->id() == app_id_) { | |
| 183 observed_ = true; | |
| 184 if (run_loop_.get()) | |
| 185 run_loop_->Quit(); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 void OnBrowserRemoved(Browser* browser) override { | |
| 190 const extensions::Extension* app = | |
| 191 apps::ExtensionAppShimHandler::GetAppForBrowser(browser); | |
| 192 if (app && app->id() == app_id_) { | |
| 193 observed_ = true; | |
| 194 if (run_loop_.get()) | |
| 195 run_loop_->Quit(); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 private: | |
| 200 std::string app_id_; | |
| 201 bool observed_; | |
| 202 scoped_ptr<base::RunLoop> run_loop_; | |
| 203 }; | |
| 204 | |
| 154 class AppLifetimeMonitorObserver : public apps::AppLifetimeMonitor::Observer { | 205 class AppLifetimeMonitorObserver : public apps::AppLifetimeMonitor::Observer { |
| 155 public: | 206 public: |
| 156 AppLifetimeMonitorObserver(Profile* profile) | 207 AppLifetimeMonitorObserver(Profile* profile) |
| 157 : profile_(profile), activated_count_(0), deactivated_count_(0) { | 208 : profile_(profile), activated_count_(0), deactivated_count_(0) { |
| 158 apps::AppLifetimeMonitorFactory::GetForProfile(profile_)->AddObserver(this); | 209 apps::AppLifetimeMonitorFactory::GetForProfile(profile_)->AddObserver(this); |
| 159 } | 210 } |
| 160 virtual ~AppLifetimeMonitorObserver() { | 211 virtual ~AppLifetimeMonitorObserver() { |
| 161 apps::AppLifetimeMonitorFactory::GetForProfile(profile_) | 212 apps::AppLifetimeMonitorFactory::GetForProfile(profile_) |
| 162 ->RemoveObserver(this); | 213 ->RemoveObserver(this); |
| 163 } | 214 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 213 // is used to wait for file operations on the shim to be finished before | 264 // is used to wait for file operations on the shim to be finished before |
| 214 // attempting to launch it. Since all of the file operations are done in the | 265 // attempting to launch it. Since all of the file operations are done in the |
| 215 // same event on the FILE thread, everything will be done by the time the | 266 // same event on the FILE thread, everything will be done by the time the |
| 216 // watcher's callback is executed. | 267 // watcher's callback is executed. |
| 217 scoped_refptr<WindowedFilePathWatcher> file_watcher = | 268 scoped_refptr<WindowedFilePathWatcher> file_watcher = |
| 218 new WindowedFilePathWatcher(shim_path); | 269 new WindowedFilePathWatcher(shim_path); |
| 219 web_app::UpdateAllShortcuts(base::string16(), profile, app); | 270 web_app::UpdateAllShortcuts(base::string16(), profile, app); |
| 220 file_watcher->Wait(); | 271 file_watcher->Wait(); |
| 221 } | 272 } |
| 222 | 273 |
| 274 Browser* GetFirstHostedAppWindow() { | |
| 275 BrowserList* browsers = | |
| 276 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE); | |
| 277 for (Browser* browser : *browsers) { | |
| 278 const extensions::Extension* extension = | |
| 279 apps::ExtensionAppShimHandler::GetAppForBrowser(browser); | |
| 280 if (extension && extension->is_hosted_app()) | |
| 281 return browser; | |
| 282 } | |
| 283 return NULL; | |
| 284 } | |
| 285 | |
| 223 } // namespace | 286 } // namespace |
| 224 | 287 |
| 225 // Watches for NSNotifications from the shared workspace. | 288 // Watches for NSNotifications from the shared workspace. |
| 226 @interface WindowedNSNotificationObserver : NSObject { | 289 @interface WindowedNSNotificationObserver : NSObject { |
| 227 @private | 290 @private |
| 228 base::scoped_nsobject<NSString> bundleId_; | 291 base::scoped_nsobject<NSString> bundleId_; |
| 229 BOOL notificationReceived_; | 292 BOOL notificationReceived_; |
| 230 scoped_ptr<base::RunLoop> runLoop_; | 293 scoped_ptr<base::RunLoop> runLoop_; |
| 231 } | 294 } |
| 232 | 295 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 273 runLoop_->Run(); | 336 runLoop_->Run(); |
| 274 } | 337 } |
| 275 | 338 |
| 276 @end | 339 @end |
| 277 | 340 |
| 278 namespace apps { | 341 namespace apps { |
| 279 | 342 |
| 280 // Shims require static libraries http://crbug.com/386024. | 343 // Shims require static libraries http://crbug.com/386024. |
| 281 #if defined(COMPONENT_BUILD) | 344 #if defined(COMPONENT_BUILD) |
| 282 #define MAYBE_Launch DISABLED_Launch | 345 #define MAYBE_Launch DISABLED_Launch |
| 346 #define MAYBE_HostedAppLaunch DISABLED_HostedAppLaunch | |
| 283 #define MAYBE_ShowWindow DISABLED_ShowWindow | 347 #define MAYBE_ShowWindow DISABLED_ShowWindow |
| 284 #define MAYBE_RebuildShim DISABLED_RebuildShim | 348 #define MAYBE_RebuildShim DISABLED_RebuildShim |
| 285 #else | 349 #else |
| 286 #define MAYBE_Launch Launch | 350 #define MAYBE_Launch Launch |
| 351 #define MAYBE_HostedAppLaunch HostedAppLaunch | |
| 287 #define MAYBE_ShowWindow ShowWindow | 352 #define MAYBE_ShowWindow ShowWindow |
| 288 #define MAYBE_RebuildShim RebuildShim | 353 #define MAYBE_RebuildShim RebuildShim |
| 289 #endif | 354 #endif |
| 290 | 355 |
| 356 IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, MAYBE_HostedAppLaunch) { | |
| 357 const extensions::Extension* app = InstallHostedApp(); | |
| 358 | |
| 359 base::FilePath shim_path = GetAppShimPath(profile(), app); | |
| 360 EXPECT_FALSE(base::PathExists(shim_path)); | |
| 361 | |
| 362 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); | |
| 363 ASSERT_TRUE(base::PathExists(shim_path)); | |
| 364 NSString* bundle_id = GetBundleID(shim_path); | |
| 365 | |
| 366 // Explicitly set the launch type to open in a new window. | |
| 367 extensions::SetLaunchType( | |
| 368 extensions::ExtensionSystem::Get(profile())->extension_service(), | |
| 369 app->id(), extensions::LAUNCH_TYPE_WINDOW); | |
| 370 | |
| 371 // Case 1: Launch the hosted app, it should start the shim. | |
| 372 { | |
| 373 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer; | |
| 374 ns_observer.reset([[WindowedNSNotificationObserver alloc] | |
| 375 initForNotification:NSWorkspaceDidLaunchApplicationNotification | |
| 376 andBundleId:bundle_id]); | |
| 377 WindowedAppShimLaunchObserver observer(app->id()); | |
| 378 LaunchHostedApp(app); | |
| 379 [ns_observer wait]; | |
| 380 observer.Wait(); | |
| 381 | |
| 382 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | |
| 383 EXPECT_TRUE(GetFirstHostedAppWindow()); | |
| 384 | |
| 385 NSArray* running_shim = [NSRunningApplication | |
| 386 runningApplicationsWithBundleIdentifier:bundle_id]; | |
| 387 ASSERT_EQ(1u, [running_shim count]); | |
| 388 | |
| 389 ns_observer.reset([[WindowedNSNotificationObserver alloc] | |
| 390 initForNotification:NSWorkspaceDidTerminateApplicationNotification | |
| 391 andBundleId:bundle_id]); | |
| 392 [base::mac::ObjCCastStrict<NSRunningApplication>( | |
| 393 [running_shim objectAtIndex:0]) terminate]; | |
| 394 [ns_observer wait]; | |
| 395 | |
| 396 EXPECT_FALSE(GetFirstHostedAppWindow()); | |
| 397 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | |
| 398 } | |
| 399 | |
| 400 // Case 2: Launch the shim, it should start the hosted app. | |
| 401 { | |
| 402 HostedAppBrowserListObserver listener(app->id()); | |
| 403 CommandLine shim_cmdline(CommandLine::NO_PROGRAM); | |
| 404 shim_cmdline.AppendSwitch(app_mode::kLaunchedForTest); | |
| 405 ProcessSerialNumber shim_psn; | |
| 406 ASSERT_TRUE(base::mac::OpenApplicationWithPath( | |
| 407 shim_path, shim_cmdline, kLSLaunchDefaults, &shim_psn)); | |
| 408 listener.Wait(); | |
| 409 | |
| 410 ASSERT_TRUE(GetFirstHostedAppWindow()); | |
| 411 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | |
| 412 | |
| 413 // If the window is closed, the shim should quit. | |
| 414 pid_t shim_pid; | |
| 415 EXPECT_EQ(noErr, GetProcessPID(&shim_psn, &shim_pid)); | |
| 416 GetFirstHostedAppWindow()->window()->Close(); | |
| 417 // Wait for the window to be closed. | |
| 418 listener.ResetObservation(); | |
| 419 listener.Wait(); | |
| 420 ASSERT_TRUE( | |
| 421 base::WaitForSingleProcess(shim_pid, TestTimeouts::action_timeout())); | |
| 422 | |
| 423 EXPECT_FALSE(GetFirstHostedAppWindow()); | |
| 424 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | |
| 425 } | |
| 426 } | |
| 427 | |
| 291 // Test that launching the shim for an app starts the app, and vice versa. | 428 // Test that launching the shim for an app starts the app, and vice versa. |
| 292 // These two cases are combined because the time to run the test is dominated | 429 // These two cases are combined because the time to run the test is dominated |
| 293 // by loading the extension and creating the shim. | 430 // by loading the extension and creating the shim. |
| 294 IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, MAYBE_Launch) { | 431 IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, MAYBE_Launch) { |
| 295 const extensions::Extension* app = InstallPlatformApp("minimal"); | 432 const extensions::Extension* app = InstallPlatformApp("minimal"); |
| 296 | 433 |
| 297 base::FilePath shim_path = GetAppShimPath(profile(), app); | 434 base::FilePath shim_path = GetAppShimPath(profile(), app); |
| 298 EXPECT_FALSE(base::PathExists(shim_path)); | 435 EXPECT_FALSE(base::PathExists(shim_path)); |
| 299 | 436 |
| 300 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); | 437 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 561 // the shim is rebuilt. | 698 // the shim is rebuilt. |
| 562 WindowedAppShimLaunchObserver(app->id()).Wait(); | 699 WindowedAppShimLaunchObserver(app->id()).Wait(); |
| 563 | 700 |
| 564 EXPECT_TRUE(GetFirstAppWindow()); | 701 EXPECT_TRUE(GetFirstAppWindow()); |
| 565 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 702 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
| 566 } | 703 } |
| 567 | 704 |
| 568 #endif // defined(ARCH_CPU_64_BITS) | 705 #endif // defined(ARCH_CPU_64_BITS) |
| 569 | 706 |
| 570 } // namespace apps | 707 } // namespace apps |
| OLD | NEW |