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" |
(...skipping 17 matching lines...) Expand all Loading... |
28 #include "chrome/browser/ui/browser_list.h" | 28 #include "chrome/browser/ui/browser_list.h" |
29 #include "chrome/browser/ui/browser_window.h" | 29 #include "chrome/browser/ui/browser_window.h" |
30 #include "chrome/browser/web_applications/web_app_mac.h" | 30 #include "chrome/browser/web_applications/web_app_mac.h" |
31 #include "chrome/common/chrome_paths.h" | 31 #include "chrome/common/chrome_paths.h" |
32 #include "chrome/common/chrome_switches.h" | 32 #include "chrome/common/chrome_switches.h" |
33 #include "chrome/common/mac/app_mode_common.h" | 33 #include "chrome/common/mac/app_mode_common.h" |
34 #include "content/public/test/test_utils.h" | 34 #include "content/public/test/test_utils.h" |
35 #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" | 36 #include "extensions/browser/extension_prefs.h" |
37 #include "extensions/test/extension_test_message_listener.h" | 37 #include "extensions/test/extension_test_message_listener.h" |
| 38 #import "ui/base/test/windowed_nsnotification_observer.h" |
38 #import "ui/events/test/cocoa_test_event_utils.h" | 39 #import "ui/events/test/cocoa_test_event_utils.h" |
39 | 40 |
40 namespace { | 41 namespace { |
41 | 42 |
42 // General end-to-end test for app shims. | 43 // General end-to-end test for app shims. |
43 class AppShimInteractiveTest : public extensions::PlatformAppBrowserTest { | 44 class AppShimInteractiveTest : public extensions::PlatformAppBrowserTest { |
44 protected: | 45 protected: |
45 AppShimInteractiveTest() | 46 AppShimInteractiveTest() |
46 : auto_reset_(&g_app_shims_allow_update_and_launch_in_tests, true) {} | 47 : auto_reset_(&g_app_shims_allow_update_and_launch_in_tests, true) {} |
47 | 48 |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 const extensions::Extension* extension = | 295 const extensions::Extension* extension = |
295 apps::ExtensionAppShimHandler::MaybeGetAppForBrowser(browser); | 296 apps::ExtensionAppShimHandler::MaybeGetAppForBrowser(browser); |
296 if (extension && extension->is_hosted_app()) | 297 if (extension && extension->is_hosted_app()) |
297 return browser; | 298 return browser; |
298 } | 299 } |
299 return nullptr; | 300 return nullptr; |
300 } | 301 } |
301 | 302 |
302 } // namespace | 303 } // namespace |
303 | 304 |
304 // Watches for NSNotifications from the shared workspace. | |
305 @interface WindowedNSNotificationObserver : NSObject { | |
306 @private | |
307 base::scoped_nsobject<NSString> bundleId_; | |
308 BOOL notificationReceived_; | |
309 scoped_ptr<base::RunLoop> runLoop_; | |
310 } | |
311 | |
312 - (id)initForNotification:(NSString*)name | |
313 andBundleId:(NSString*)bundleId; | |
314 - (void)observe:(NSNotification*)notification; | |
315 - (void)wait; | |
316 @end | |
317 | |
318 @implementation WindowedNSNotificationObserver | |
319 | |
320 - (id)initForNotification:(NSString*)name | |
321 andBundleId:(NSString*)bundleId { | |
322 if (self = [super init]) { | |
323 bundleId_.reset([[bundleId copy] retain]); | |
324 [[[NSWorkspace sharedWorkspace] notificationCenter] | |
325 addObserver:self | |
326 selector:@selector(observe:) | |
327 name:name | |
328 object:nil]; | |
329 } | |
330 return self; | |
331 } | |
332 | |
333 - (void)observe:(NSNotification*)notification { | |
334 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
335 | |
336 NSRunningApplication* application = | |
337 [[notification userInfo] objectForKey:NSWorkspaceApplicationKey]; | |
338 if (![[application bundleIdentifier] isEqualToString:bundleId_]) | |
339 return; | |
340 | |
341 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; | |
342 notificationReceived_ = YES; | |
343 if (runLoop_.get()) | |
344 runLoop_->Quit(); | |
345 } | |
346 | |
347 - (void)wait { | |
348 if (notificationReceived_) | |
349 return; | |
350 | |
351 runLoop_.reset(new base::RunLoop); | |
352 runLoop_->Run(); | |
353 } | |
354 | |
355 @end | |
356 | |
357 namespace apps { | 305 namespace apps { |
358 | 306 |
359 // Shims require static libraries http://crbug.com/386024. | 307 // Shims require static libraries http://crbug.com/386024. |
360 #if defined(COMPONENT_BUILD) | 308 #if defined(COMPONENT_BUILD) |
361 #define MAYBE_Launch DISABLED_Launch | 309 #define MAYBE_Launch DISABLED_Launch |
362 #define MAYBE_HostedAppLaunch DISABLED_HostedAppLaunch | 310 #define MAYBE_HostedAppLaunch DISABLED_HostedAppLaunch |
363 #define MAYBE_ShowWindow DISABLED_ShowWindow | 311 #define MAYBE_ShowWindow DISABLED_ShowWindow |
364 #define MAYBE_RebuildShim DISABLED_RebuildShim | 312 #define MAYBE_RebuildShim DISABLED_RebuildShim |
365 #else | 313 #else |
366 #define MAYBE_Launch Launch | 314 #define MAYBE_Launch Launch |
(...skipping 13 matching lines...) Expand all Loading... |
380 NSString* bundle_id = GetBundleID(shim_path); | 328 NSString* bundle_id = GetBundleID(shim_path); |
381 | 329 |
382 // Explicitly set the launch type to open in a new window. | 330 // Explicitly set the launch type to open in a new window. |
383 extensions::SetLaunchType(profile(), app->id(), | 331 extensions::SetLaunchType(profile(), app->id(), |
384 extensions::LAUNCH_TYPE_WINDOW); | 332 extensions::LAUNCH_TYPE_WINDOW); |
385 | 333 |
386 // Case 1: Launch the hosted app, it should start the shim. | 334 // Case 1: Launch the hosted app, it should start the shim. |
387 { | 335 { |
388 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer; | 336 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer; |
389 ns_observer.reset([[WindowedNSNotificationObserver alloc] | 337 ns_observer.reset([[WindowedNSNotificationObserver alloc] |
390 initForNotification:NSWorkspaceDidLaunchApplicationNotification | 338 initForWorkspaceNotification:NSWorkspaceDidLaunchApplicationNotification |
391 andBundleId:bundle_id]); | 339 bundleId:bundle_id]); |
392 WindowedAppShimLaunchObserver observer(app->id()); | 340 WindowedAppShimLaunchObserver observer(app->id()); |
393 LaunchHostedApp(app); | 341 LaunchHostedApp(app); |
394 [ns_observer wait]; | 342 [ns_observer wait]; |
395 observer.Wait(); | 343 observer.Wait(); |
396 | 344 |
397 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 345 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
398 EXPECT_TRUE(GetFirstHostedAppWindow()); | 346 EXPECT_TRUE(GetFirstHostedAppWindow()); |
399 | 347 |
400 NSArray* running_shim = [NSRunningApplication | 348 NSArray* running_shim = [NSRunningApplication |
401 runningApplicationsWithBundleIdentifier:bundle_id]; | 349 runningApplicationsWithBundleIdentifier:bundle_id]; |
402 ASSERT_EQ(1u, [running_shim count]); | 350 ASSERT_EQ(1u, [running_shim count]); |
403 | 351 |
404 ns_observer.reset([[WindowedNSNotificationObserver alloc] | 352 ns_observer.reset([[WindowedNSNotificationObserver alloc] |
405 initForNotification:NSWorkspaceDidTerminateApplicationNotification | 353 initForWorkspaceNotification: |
406 andBundleId:bundle_id]); | 354 NSWorkspaceDidTerminateApplicationNotification |
| 355 bundleId:bundle_id]); |
407 [base::mac::ObjCCastStrict<NSRunningApplication>( | 356 [base::mac::ObjCCastStrict<NSRunningApplication>( |
408 [running_shim objectAtIndex:0]) terminate]; | 357 [running_shim objectAtIndex:0]) terminate]; |
409 [ns_observer wait]; | 358 [ns_observer wait]; |
410 | 359 |
411 EXPECT_FALSE(GetFirstHostedAppWindow()); | 360 EXPECT_FALSE(GetFirstHostedAppWindow()); |
412 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | 361 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
413 } | 362 } |
414 | 363 |
415 // Case 2: Launch the shim, it should start the hosted app. | 364 // Case 2: Launch the shim, it should start the hosted app. |
416 { | 365 { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
451 EXPECT_FALSE(base::PathExists(shim_path)); | 400 EXPECT_FALSE(base::PathExists(shim_path)); |
452 | 401 |
453 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); | 402 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); |
454 ASSERT_TRUE(base::PathExists(shim_path)); | 403 ASSERT_TRUE(base::PathExists(shim_path)); |
455 NSString* bundle_id = GetBundleID(shim_path); | 404 NSString* bundle_id = GetBundleID(shim_path); |
456 | 405 |
457 // Case 1: Launch the app, it should start the shim. | 406 // Case 1: Launch the app, it should start the shim. |
458 { | 407 { |
459 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer; | 408 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer; |
460 ns_observer.reset([[WindowedNSNotificationObserver alloc] | 409 ns_observer.reset([[WindowedNSNotificationObserver alloc] |
461 initForNotification:NSWorkspaceDidLaunchApplicationNotification | 410 initForWorkspaceNotification:NSWorkspaceDidLaunchApplicationNotification |
462 andBundleId:bundle_id]); | 411 bundleId:bundle_id]); |
463 WindowedAppShimLaunchObserver observer(app->id()); | 412 WindowedAppShimLaunchObserver observer(app->id()); |
464 LaunchPlatformApp(app); | 413 LaunchPlatformApp(app); |
465 [ns_observer wait]; | 414 [ns_observer wait]; |
466 observer.Wait(); | 415 observer.Wait(); |
467 | 416 |
468 EXPECT_TRUE(GetFirstAppWindow()); | 417 EXPECT_TRUE(GetFirstAppWindow()); |
469 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 418 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
470 | 419 |
471 // Quitting the shim will eventually cause it to quit. It actually | 420 // Quitting the shim will eventually cause it to quit. It actually |
472 // intercepts the -terminate, sends an AppShimHostMsg_QuitApp to Chrome, | 421 // intercepts the -terminate, sends an AppShimHostMsg_QuitApp to Chrome, |
473 // and returns NSTerminateLater. Chrome responds by closing all windows of | 422 // and returns NSTerminateLater. Chrome responds by closing all windows of |
474 // the app. Once all windows are closed, Chrome closes the IPC channel, | 423 // the app. Once all windows are closed, Chrome closes the IPC channel, |
475 // which causes the shim to actually terminate. | 424 // which causes the shim to actually terminate. |
476 NSArray* running_shim = [NSRunningApplication | 425 NSArray* running_shim = [NSRunningApplication |
477 runningApplicationsWithBundleIdentifier:bundle_id]; | 426 runningApplicationsWithBundleIdentifier:bundle_id]; |
478 ASSERT_EQ(1u, [running_shim count]); | 427 ASSERT_EQ(1u, [running_shim count]); |
479 | 428 |
480 ns_observer.reset([[WindowedNSNotificationObserver alloc] | 429 ns_observer.reset([[WindowedNSNotificationObserver alloc] |
481 initForNotification:NSWorkspaceDidTerminateApplicationNotification | 430 initForWorkspaceNotification: |
482 andBundleId:bundle_id]); | 431 NSWorkspaceDidTerminateApplicationNotification |
| 432 bundleId:bundle_id]); |
483 [base::mac::ObjCCastStrict<NSRunningApplication>( | 433 [base::mac::ObjCCastStrict<NSRunningApplication>( |
484 [running_shim objectAtIndex:0]) terminate]; | 434 [running_shim objectAtIndex:0]) terminate]; |
485 [ns_observer wait]; | 435 [ns_observer wait]; |
486 | 436 |
487 EXPECT_FALSE(GetFirstAppWindow()); | 437 EXPECT_FALSE(GetFirstAppWindow()); |
488 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | 438 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
489 } | 439 } |
490 | 440 |
491 // Case 2: Launch the shim, it should start the app. | 441 // Case 2: Launch the shim, it should start the app. |
492 { | 442 { |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
539 EXPECT_TRUE(launched_listener.WaitUntilSatisfied()); | 489 EXPECT_TRUE(launched_listener.WaitUntilSatisfied()); |
540 } | 490 } |
541 extensions::AppWindow* window_1 = GetFirstAppWindow(); | 491 extensions::AppWindow* window_1 = GetFirstAppWindow(); |
542 ASSERT_TRUE(window_1); | 492 ASSERT_TRUE(window_1); |
543 EXPECT_TRUE(window_1->is_hidden()); | 493 EXPECT_TRUE(window_1->is_hidden()); |
544 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | 494 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
545 EXPECT_EQ(0, lifetime_observer.activated_count()); | 495 EXPECT_EQ(0, lifetime_observer.activated_count()); |
546 | 496 |
547 // Showing the window causes the shim to launch. | 497 // Showing the window causes the shim to launch. |
548 { | 498 { |
549 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( | 499 base::scoped_nsobject<WindowedNSNotificationObserver> |
550 [[WindowedNSNotificationObserver alloc] | 500 ns_observer([[WindowedNSNotificationObserver alloc] |
551 initForNotification:NSWorkspaceDidLaunchApplicationNotification | 501 initForWorkspaceNotification:NSWorkspaceDidLaunchApplicationNotification |
552 andBundleId:bundle_id]); | 502 bundleId:bundle_id]); |
553 WindowedAppShimLaunchObserver observer(app->id()); | 503 WindowedAppShimLaunchObserver observer(app->id()); |
554 window_1->Show(extensions::AppWindow::SHOW_INACTIVE); | 504 window_1->Show(extensions::AppWindow::SHOW_INACTIVE); |
555 [ns_observer wait]; | 505 [ns_observer wait]; |
556 observer.Wait(); | 506 observer.Wait(); |
557 EXPECT_EQ(1, lifetime_observer.activated_count()); | 507 EXPECT_EQ(1, lifetime_observer.activated_count()); |
558 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 508 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
559 } | 509 } |
560 | 510 |
561 // Hiding the window causes the shim to quit. | 511 // Hiding the window causes the shim to quit. |
562 { | 512 { |
563 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( | 513 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( |
564 [[WindowedNSNotificationObserver alloc] | 514 [[WindowedNSNotificationObserver alloc] |
565 initForNotification:NSWorkspaceDidTerminateApplicationNotification | 515 initForWorkspaceNotification: |
566 andBundleId:bundle_id]); | 516 NSWorkspaceDidTerminateApplicationNotification |
| 517 bundleId:bundle_id]); |
567 window_1->Hide(); | 518 window_1->Hide(); |
568 [ns_observer wait]; | 519 [ns_observer wait]; |
569 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | 520 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
570 } | 521 } |
571 | 522 |
572 // Launch a second window. It should not launch the shim. | 523 // Launch a second window. It should not launch the shim. |
573 { | 524 { |
574 ExtensionTestMessageListener launched_listener("Launched", false); | 525 ExtensionTestMessageListener launched_listener("Launched", false); |
575 LaunchPlatformApp(app); | 526 LaunchPlatformApp(app); |
576 EXPECT_TRUE(launched_listener.WaitUntilSatisfied()); | 527 EXPECT_TRUE(launched_listener.WaitUntilSatisfied()); |
577 } | 528 } |
578 const extensions::AppWindowRegistry::AppWindowList& app_windows = | 529 const extensions::AppWindowRegistry::AppWindowList& app_windows = |
579 extensions::AppWindowRegistry::Get(profile())->app_windows(); | 530 extensions::AppWindowRegistry::Get(profile())->app_windows(); |
580 EXPECT_EQ(2u, app_windows.size()); | 531 EXPECT_EQ(2u, app_windows.size()); |
581 extensions::AppWindow* window_2 = app_windows.front(); | 532 extensions::AppWindow* window_2 = app_windows.front(); |
582 EXPECT_NE(window_1, window_2); | 533 EXPECT_NE(window_1, window_2); |
583 ASSERT_TRUE(window_2); | 534 ASSERT_TRUE(window_2); |
584 EXPECT_TRUE(window_2->is_hidden()); | 535 EXPECT_TRUE(window_2->is_hidden()); |
585 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | 536 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
586 EXPECT_EQ(1, lifetime_observer.activated_count()); | 537 EXPECT_EQ(1, lifetime_observer.activated_count()); |
587 | 538 |
588 // Showing one of the windows should launch the shim. | 539 // Showing one of the windows should launch the shim. |
589 { | 540 { |
590 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( | 541 base::scoped_nsobject<WindowedNSNotificationObserver> |
591 [[WindowedNSNotificationObserver alloc] | 542 ns_observer([[WindowedNSNotificationObserver alloc] |
592 initForNotification:NSWorkspaceDidLaunchApplicationNotification | 543 initForWorkspaceNotification:NSWorkspaceDidLaunchApplicationNotification |
593 andBundleId:bundle_id]); | 544 bundleId:bundle_id]); |
594 WindowedAppShimLaunchObserver observer(app->id()); | 545 WindowedAppShimLaunchObserver observer(app->id()); |
595 window_1->Show(extensions::AppWindow::SHOW_INACTIVE); | 546 window_1->Show(extensions::AppWindow::SHOW_INACTIVE); |
596 [ns_observer wait]; | 547 [ns_observer wait]; |
597 observer.Wait(); | 548 observer.Wait(); |
598 EXPECT_EQ(2, lifetime_observer.activated_count()); | 549 EXPECT_EQ(2, lifetime_observer.activated_count()); |
599 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 550 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
600 EXPECT_TRUE(window_2->is_hidden()); | 551 EXPECT_TRUE(window_2->is_hidden()); |
601 } | 552 } |
602 | 553 |
603 // Showing the other window does nothing. | 554 // Showing the other window does nothing. |
(...skipping 14 matching lines...) Expand all Loading... |
618 window_1->Hide(); | 569 window_1->Hide(); |
619 EXPECT_EQ(0, deactivate_observer.deactivated_count()); | 570 EXPECT_EQ(0, deactivate_observer.deactivated_count()); |
620 } | 571 } |
621 | 572 |
622 // Hiding other window causes the shim to quit. | 573 // Hiding other window causes the shim to quit. |
623 { | 574 { |
624 AppLifetimeMonitorObserver deactivate_observer(profile()); | 575 AppLifetimeMonitorObserver deactivate_observer(profile()); |
625 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 576 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
626 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( | 577 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer( |
627 [[WindowedNSNotificationObserver alloc] | 578 [[WindowedNSNotificationObserver alloc] |
628 initForNotification:NSWorkspaceDidTerminateApplicationNotification | 579 initForWorkspaceNotification: |
629 andBundleId:bundle_id]); | 580 NSWorkspaceDidTerminateApplicationNotification |
| 581 bundleId:bundle_id]); |
630 window_2->Hide(); | 582 window_2->Hide(); |
631 [ns_observer wait]; | 583 [ns_observer wait]; |
632 EXPECT_EQ(1, deactivate_observer.deactivated_count()); | 584 EXPECT_EQ(1, deactivate_observer.deactivated_count()); |
633 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | 585 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); |
634 } | 586 } |
635 } | 587 } |
636 | 588 |
637 #if defined(ARCH_CPU_64_BITS) | 589 #if defined(ARCH_CPU_64_BITS) |
638 | 590 |
639 // Tests that a 32 bit shim attempting to launch 64 bit Chrome will eventually | 591 // Tests that a 32 bit shim attempting to launch 64 bit Chrome will eventually |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 // the shim is rebuilt. | 669 // the shim is rebuilt. |
718 WindowedAppShimLaunchObserver(app->id()).Wait(); | 670 WindowedAppShimLaunchObserver(app->id()).Wait(); |
719 | 671 |
720 EXPECT_TRUE(GetFirstAppWindow()); | 672 EXPECT_TRUE(GetFirstAppWindow()); |
721 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 673 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
722 } | 674 } |
723 | 675 |
724 #endif // defined(ARCH_CPU_64_BITS) | 676 #endif // defined(ARCH_CPU_64_BITS) |
725 | 677 |
726 } // namespace apps | 678 } // namespace apps |
OLD | NEW |