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) | |
tapted
2014/12/19 05:34:19
nit: explicit
mitchellj
2014/12/21 22:39:44
Done.
| |
162 : app_id_(app_id), observed_add_(false), observed_removed_(false) { | |
163 BrowserList::AddObserver(this); | |
164 } | |
165 | |
166 ~HostedAppBrowserListObserver() { BrowserList::RemoveObserver(this); } | |
167 | |
168 void WaitUntilAdded() { | |
169 if (observed_add_) | |
170 return; | |
171 | |
172 run_loop_.reset(new base::RunLoop); | |
173 run_loop_->Run(); | |
174 } | |
175 | |
176 void WaitUntilRemoved() { | |
177 if (observed_removed_) | |
178 return; | |
179 | |
180 run_loop_.reset(new base::RunLoop); | |
181 run_loop_->Run(); | |
182 } | |
183 | |
184 // BrowserListObserver overrides: | |
185 void OnBrowserAdded(Browser* browser) override { | |
186 const extensions::Extension* app = | |
187 apps::ExtensionAppShimHandler::GetAppForBrowser(browser); | |
188 if (app && app->id() == app_id_) { | |
189 observed_add_ = true; | |
190 if (run_loop_.get()) | |
191 run_loop_->Quit(); | |
192 } | |
193 } | |
194 | |
195 void OnBrowserRemoved(Browser* browser) override { | |
196 const extensions::Extension* app = | |
197 apps::ExtensionAppShimHandler::GetAppForBrowser(browser); | |
198 if (app && app->id() == app_id_) { | |
199 observed_removed_ = true; | |
200 if (run_loop_.get()) | |
201 run_loop_->Quit(); | |
202 } | |
203 } | |
204 | |
205 private: | |
206 std::string app_id_; | |
207 bool observed_add_; | |
208 bool observed_removed_; | |
209 scoped_ptr<base::RunLoop> run_loop_; | |
210 }; | |
tapted
2014/12/19 05:34:19
nit: DISALLOW_COPY_AND_ASSIGN(HostedAppBrowserList
mitchellj
2014/12/21 22:39:44
Done.
| |
211 | |
154 class AppLifetimeMonitorObserver : public apps::AppLifetimeMonitor::Observer { | 212 class AppLifetimeMonitorObserver : public apps::AppLifetimeMonitor::Observer { |
155 public: | 213 public: |
156 AppLifetimeMonitorObserver(Profile* profile) | 214 AppLifetimeMonitorObserver(Profile* profile) |
157 : profile_(profile), activated_count_(0), deactivated_count_(0) { | 215 : profile_(profile), activated_count_(0), deactivated_count_(0) { |
158 apps::AppLifetimeMonitorFactory::GetForProfile(profile_)->AddObserver(this); | 216 apps::AppLifetimeMonitorFactory::GetForProfile(profile_)->AddObserver(this); |
159 } | 217 } |
160 virtual ~AppLifetimeMonitorObserver() { | 218 virtual ~AppLifetimeMonitorObserver() { |
161 apps::AppLifetimeMonitorFactory::GetForProfile(profile_) | 219 apps::AppLifetimeMonitorFactory::GetForProfile(profile_) |
162 ->RemoveObserver(this); | 220 ->RemoveObserver(this); |
163 } | 221 } |
(...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 | 271 // 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 | 272 // 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 | 273 // same event on the FILE thread, everything will be done by the time the |
216 // watcher's callback is executed. | 274 // watcher's callback is executed. |
217 scoped_refptr<WindowedFilePathWatcher> file_watcher = | 275 scoped_refptr<WindowedFilePathWatcher> file_watcher = |
218 new WindowedFilePathWatcher(shim_path); | 276 new WindowedFilePathWatcher(shim_path); |
219 web_app::UpdateAllShortcuts(base::string16(), profile, app); | 277 web_app::UpdateAllShortcuts(base::string16(), profile, app); |
220 file_watcher->Wait(); | 278 file_watcher->Wait(); |
221 } | 279 } |
222 | 280 |
281 Browser* GetFirstHostedAppWindow() { | |
282 BrowserList* browsers = | |
283 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE); | |
284 for (Browser* browser : *browsers) { | |
285 const extensions::Extension* extension = | |
286 apps::ExtensionAppShimHandler::GetAppForBrowser(browser); | |
287 if (extension && extension->is_hosted_app()) | |
288 return browser; | |
289 } | |
290 return NULL; | |
tapted
2014/12/19 05:34:19
nit: nullptr
mitchellj
2014/12/21 22:39:44
Done.
| |
291 } | |
292 | |
223 } // namespace | 293 } // namespace |
224 | 294 |
225 // Watches for NSNotifications from the shared workspace. | 295 // Watches for NSNotifications from the shared workspace. |
226 @interface WindowedNSNotificationObserver : NSObject { | 296 @interface WindowedNSNotificationObserver : NSObject { |
227 @private | 297 @private |
228 base::scoped_nsobject<NSString> bundleId_; | 298 base::scoped_nsobject<NSString> bundleId_; |
229 BOOL notificationReceived_; | 299 BOOL notificationReceived_; |
230 scoped_ptr<base::RunLoop> runLoop_; | 300 scoped_ptr<base::RunLoop> runLoop_; |
231 } | 301 } |
232 | 302 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
273 runLoop_->Run(); | 343 runLoop_->Run(); |
274 } | 344 } |
275 | 345 |
276 @end | 346 @end |
277 | 347 |
278 namespace apps { | 348 namespace apps { |
279 | 349 |
280 // Shims require static libraries http://crbug.com/386024. | 350 // Shims require static libraries http://crbug.com/386024. |
281 #if defined(COMPONENT_BUILD) | 351 #if defined(COMPONENT_BUILD) |
282 #define MAYBE_Launch DISABLED_Launch | 352 #define MAYBE_Launch DISABLED_Launch |
353 #define MAYBE_HostedAppLaunch DISABLED_HostedAppLaunch | |
283 #define MAYBE_ShowWindow DISABLED_ShowWindow | 354 #define MAYBE_ShowWindow DISABLED_ShowWindow |
284 #define MAYBE_RebuildShim DISABLED_RebuildShim | 355 #define MAYBE_RebuildShim DISABLED_RebuildShim |
285 #else | 356 #else |
286 #define MAYBE_Launch Launch | 357 #define MAYBE_Launch Launch |
358 #define MAYBE_HostedAppLaunch HostedAppLaunch | |
287 #define MAYBE_ShowWindow ShowWindow | 359 #define MAYBE_ShowWindow ShowWindow |
288 #define MAYBE_RebuildShim RebuildShim | 360 #define MAYBE_RebuildShim RebuildShim |
289 #endif | 361 #endif |
290 | 362 |
363 IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, MAYBE_HostedAppLaunch) { | |
364 const extensions::Extension* app = InstallHostedApp(); | |
365 | |
366 base::FilePath shim_path = GetAppShimPath(profile(), app); | |
367 EXPECT_FALSE(base::PathExists(shim_path)); | |
368 | |
369 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); | |
370 ASSERT_TRUE(base::PathExists(shim_path)); | |
371 NSString* bundle_id = GetBundleID(shim_path); | |
372 | |
373 // Explicitly set the launch type to open in a new window. | |
374 extensions::SetLaunchType( | |
375 extensions::ExtensionSystem::Get(profile())->extension_service(), | |
376 app->id(), extensions::LAUNCH_TYPE_WINDOW); | |
377 | |
378 // Case 1: Launch the hosted app, it should start the shim. | |
379 { | |
380 base::scoped_nsobject<WindowedNSNotificationObserver> ns_observer; | |
381 ns_observer.reset([[WindowedNSNotificationObserver alloc] | |
382 initForNotification:NSWorkspaceDidLaunchApplicationNotification | |
383 andBundleId:bundle_id]); | |
384 WindowedAppShimLaunchObserver observer(app->id()); | |
385 LaunchHostedApp(app); | |
386 [ns_observer wait]; | |
387 observer.Wait(); | |
388 | |
389 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | |
390 EXPECT_TRUE(GetFirstHostedAppWindow()); | |
391 | |
392 NSArray* running_shim = [NSRunningApplication | |
393 runningApplicationsWithBundleIdentifier:bundle_id]; | |
394 ASSERT_EQ(1u, [running_shim count]); | |
395 | |
396 ns_observer.reset([[WindowedNSNotificationObserver alloc] | |
397 initForNotification:NSWorkspaceDidTerminateApplicationNotification | |
398 andBundleId:bundle_id]); | |
399 [base::mac::ObjCCastStrict<NSRunningApplication>( | |
tapted
2014/12/19 05:34:19
nit: usually we don't bother casting things that r
mitchellj
2014/12/21 22:39:44
Using this instead seems to throw some compiler er
| |
400 [running_shim objectAtIndex:0]) terminate]; | |
401 [ns_observer wait]; | |
402 | |
403 EXPECT_FALSE(GetFirstHostedAppWindow()); | |
404 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | |
405 } | |
406 | |
407 // Case 2: Launch the shim, it should start the hosted app. | |
408 { | |
409 HostedAppBrowserListObserver listener(app->id()); | |
410 CommandLine shim_cmdline(CommandLine::NO_PROGRAM); | |
411 shim_cmdline.AppendSwitch(app_mode::kLaunchedForTest); | |
412 ProcessSerialNumber shim_psn; | |
413 ASSERT_TRUE(base::mac::OpenApplicationWithPath( | |
414 shim_path, shim_cmdline, kLSLaunchDefaults, &shim_psn)); | |
415 listener.WaitUntilAdded(); | |
416 | |
417 ASSERT_TRUE(GetFirstHostedAppWindow()); | |
418 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | |
419 | |
420 // If the window is closed, the shim should quit. | |
421 pid_t shim_pid; | |
422 EXPECT_EQ(noErr, GetProcessPID(&shim_psn, &shim_pid)); | |
423 GetFirstHostedAppWindow()->window()->Close(); | |
424 // Wait for the window to be closed. | |
425 listener.WaitUntilRemoved(); | |
426 ASSERT_TRUE( | |
427 base::WaitForSingleProcess(shim_pid, TestTimeouts::action_timeout())); | |
428 | |
429 EXPECT_FALSE(GetFirstHostedAppWindow()); | |
430 EXPECT_FALSE(HasAppShimHost(profile(), app->id())); | |
431 } | |
432 } | |
433 | |
291 // Test that launching the shim for an app starts the app, and vice versa. | 434 // 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 | 435 // These two cases are combined because the time to run the test is dominated |
293 // by loading the extension and creating the shim. | 436 // by loading the extension and creating the shim. |
294 IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, MAYBE_Launch) { | 437 IN_PROC_BROWSER_TEST_F(AppShimInteractiveTest, MAYBE_Launch) { |
295 const extensions::Extension* app = InstallPlatformApp("minimal"); | 438 const extensions::Extension* app = InstallPlatformApp("minimal"); |
296 | 439 |
297 base::FilePath shim_path = GetAppShimPath(profile(), app); | 440 base::FilePath shim_path = GetAppShimPath(profile(), app); |
298 EXPECT_FALSE(base::PathExists(shim_path)); | 441 EXPECT_FALSE(base::PathExists(shim_path)); |
299 | 442 |
300 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); | 443 UpdateAppAndAwaitShimCreation(profile(), app, shim_path); |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
561 // the shim is rebuilt. | 704 // the shim is rebuilt. |
562 WindowedAppShimLaunchObserver(app->id()).Wait(); | 705 WindowedAppShimLaunchObserver(app->id()).Wait(); |
563 | 706 |
564 EXPECT_TRUE(GetFirstAppWindow()); | 707 EXPECT_TRUE(GetFirstAppWindow()); |
565 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); | 708 EXPECT_TRUE(HasAppShimHost(profile(), app->id())); |
566 } | 709 } |
567 | 710 |
568 #endif // defined(ARCH_CPU_64_BITS) | 711 #endif // defined(ARCH_CPU_64_BITS) |
569 | 712 |
570 } // namespace apps | 713 } // namespace apps |
OLD | NEW |