Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / apps / native_app_window_cocoa_browsertest.mm
blob22f98a34ac4f057725ad53cf71ebb92c80cc7cdb
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/app_window/native_app_window.h"
7 #import <Cocoa/Cocoa.h>
9 #import "base/mac/foundation_util.h"
10 #import "base/mac/mac_util.h"
11 #import "base/mac/scoped_cftyperef.h"
12 #import "base/mac/scoped_nsobject.h"
13 #import "base/mac/sdk_forward_declarations.h"
14 #include "chrome/browser/apps/app_browsertest_util.h"
15 #include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
16 #include "chrome/browser/apps/app_shim/test/app_shim_host_manager_test_api_mac.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/extensions/app_launch_params.h"
20 #include "chrome/browser/ui/extensions/application_launch.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/test/test_utils.h"
24 #include "extensions/browser/app_window/app_window_registry.h"
25 #include "extensions/common/constants.h"
26 #include "skia/ext/skia_utils_mac.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #import "testing/gtest_mac.h"
29 #import "ui/base/test/nswindow_fullscreen_notification_waiter.h"
30 #import "ui/base/test/scoped_fake_nswindow_focus.h"
31 #import "ui/base/test/scoped_fake_nswindow_fullscreen.h"
32 #import "ui/base/test/windowed_nsnotification_observer.h"
33 #import "ui/gfx/mac/nswindow_frame_controls.h"
35 using extensions::AppWindow;
36 using extensions::PlatformAppBrowserTest;
38 using ::testing::_;
39 using ::testing::Invoke;
40 using ::testing::Return;
42 namespace {
44 // The param selects whether to use ChromeNativeAppWindowViewsMac, otherwise it
45 // will use NativeAppWindowCocoa.
46 class NativeAppWindowCocoaBrowserTest
47     : public testing::WithParamInterface<bool>,
48       public PlatformAppBrowserTest {
49  protected:
50   NativeAppWindowCocoaBrowserTest() {}
52   void SetUpCommandLine(base::CommandLine* command_line) override {
53     PlatformAppBrowserTest::SetUpCommandLine(command_line);
54     command_line->AppendSwitch(
55         GetParam() ? switches::kEnableMacViewsNativeAppWindows
56                    : switches::kDisableMacViewsNativeAppWindows);
57   }
59   void SetUpAppWithWindows(int num_windows) {
60     app_ = InstallExtension(
61         test_data_dir_.AppendASCII("platform_apps").AppendASCII("minimal"), 1);
62     EXPECT_TRUE(app_);
64     for (int i = 0; i < num_windows; ++i) {
65       content::WindowedNotificationObserver app_loaded_observer(
66           content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
67           content::NotificationService::AllSources());
68       OpenApplication(
69           AppLaunchParams(profile(), app_, extensions::LAUNCH_CONTAINER_NONE,
70                           NEW_WINDOW, extensions::SOURCE_TEST));
71       app_loaded_observer.Wait();
72     }
73   }
75   const extensions::Extension* app_;
77  private:
78   DISALLOW_COPY_AND_ASSIGN(NativeAppWindowCocoaBrowserTest);
81 }  // namespace
83 // Test interaction of Hide/Show() with Hide/ShowWithApp().
84 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
85   SetUpAppWithWindows(2);
86   extensions::AppWindowRegistry::AppWindowList windows =
87       extensions::AppWindowRegistry::Get(profile())->app_windows();
89   AppWindow* app_window = windows.front();
90   extensions::NativeAppWindow* native_window = app_window->GetBaseWindow();
91   NSWindow* ns_window = native_window->GetNativeWindow();
93   AppWindow* other_app_window = windows.back();
94   extensions::NativeAppWindow* other_native_window =
95       other_app_window->GetBaseWindow();
96   NSWindow* other_ns_window = other_native_window->GetNativeWindow();
98   // Normal Hide/Show.
99   app_window->Hide();
100   EXPECT_FALSE([ns_window isVisible]);
101   app_window->Show(AppWindow::SHOW_ACTIVE);
102   EXPECT_TRUE([ns_window isVisible]);
104   // Normal Hide/ShowWithApp.
105   native_window->HideWithApp();
106   EXPECT_FALSE([ns_window isVisible]);
107   native_window->ShowWithApp();
108   EXPECT_TRUE([ns_window isVisible]);
110   // HideWithApp, Hide, ShowWithApp does not show.
111   native_window->HideWithApp();
112   app_window->Hide();
113   native_window->ShowWithApp();
114   EXPECT_FALSE([ns_window isVisible]);
116   // Hide, HideWithApp, ShowWithApp does not show.
117   native_window->HideWithApp();
118   native_window->ShowWithApp();
119   EXPECT_FALSE([ns_window isVisible]);
121   // Return to shown state.
122   app_window->Show(AppWindow::SHOW_ACTIVE);
123   EXPECT_TRUE([ns_window isVisible]);
125   // HideWithApp the other window.
126   EXPECT_TRUE([other_ns_window isVisible]);
127   other_native_window->HideWithApp();
128   EXPECT_FALSE([other_ns_window isVisible]);
130   // HideWithApp, Show shows just one window since there's no shim.
131   native_window->HideWithApp();
132   EXPECT_FALSE([ns_window isVisible]);
133   app_window->Show(AppWindow::SHOW_ACTIVE);
134   EXPECT_TRUE([ns_window isVisible]);
135   EXPECT_FALSE([other_ns_window isVisible]);
137   // Hide the other window.
138   other_app_window->Hide();
139   EXPECT_FALSE([other_ns_window isVisible]);
141   // HideWithApp, ShowWithApp does not show the other window.
142   native_window->HideWithApp();
143   EXPECT_FALSE([ns_window isVisible]);
144   native_window->ShowWithApp();
145   EXPECT_TRUE([ns_window isVisible]);
146   EXPECT_FALSE([other_ns_window isVisible]);
149 namespace {
151 class MockAppShimHost : public apps::AppShimHandler::Host {
152  public:
153   MockAppShimHost() {}
154   ~MockAppShimHost() override {}
156   MOCK_METHOD1(OnAppLaunchComplete, void(apps::AppShimLaunchResult));
157   MOCK_METHOD0(OnAppClosed, void());
158   MOCK_METHOD0(OnAppHide, void());
159   MOCK_METHOD0(OnAppUnhideWithoutActivation, void());
160   MOCK_METHOD1(OnAppRequestUserAttention, void(apps::AppShimAttentionType));
161   MOCK_CONST_METHOD0(GetProfilePath, base::FilePath());
162   MOCK_CONST_METHOD0(GetAppId, std::string());
165 class MockExtensionAppShimHandler : public apps::ExtensionAppShimHandler {
166  public:
167   MockExtensionAppShimHandler() {
168     ON_CALL(*this, FindHost(_, _))
169         .WillByDefault(Invoke(this, &apps::ExtensionAppShimHandler::FindHost));
170   }
171   ~MockExtensionAppShimHandler() override {}
173   MOCK_METHOD2(FindHost, AppShimHandler::Host*(Profile*, const std::string&));
176 }  // namespace
178 // Test Hide/Show and Hide/ShowWithApp() behavior when shims are enabled.
179 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest,
180                        HideShowWithAppWithShim) {
181   test::AppShimHostManagerTestApi test_api(
182       g_browser_process->platform_part()->app_shim_host_manager());
183   MockExtensionAppShimHandler* mock = new MockExtensionAppShimHandler();
184   test_api.SetExtensionAppShimHandler(
185       scoped_ptr<apps::ExtensionAppShimHandler>(mock));  // Takes ownership.
186   MockAppShimHost mock_host;
188   SetUpAppWithWindows(1);
189   extensions::AppWindowRegistry::AppWindowList windows =
190       extensions::AppWindowRegistry::Get(profile())->app_windows();
192   extensions::AppWindow* app_window = windows.front();
193   extensions::NativeAppWindow* native_window = app_window->GetBaseWindow();
194   NSWindow* ns_window = native_window->GetNativeWindow();
196   // HideWithApp.
197   native_window->HideWithApp();
198   EXPECT_FALSE([ns_window isVisible]);
200   // Show notifies the shim to unhide.
201   EXPECT_CALL(mock_host, OnAppUnhideWithoutActivation());
202   EXPECT_CALL(*mock, FindHost(_, _)).WillOnce(Return(&mock_host));
203   app_window->Show(extensions::AppWindow::SHOW_ACTIVE);
204   EXPECT_TRUE([ns_window isVisible]);
205   testing::Mock::VerifyAndClearExpectations(mock);
206   testing::Mock::VerifyAndClearExpectations(&mock_host);
208   // HideWithApp
209   native_window->HideWithApp();
210   EXPECT_FALSE([ns_window isVisible]);
212   // Activate does the same.
213   EXPECT_CALL(mock_host, OnAppUnhideWithoutActivation());
214   EXPECT_CALL(*mock, FindHost(_, _)).WillOnce(Return(&mock_host));
215   native_window->Activate();
216   EXPECT_TRUE([ns_window isVisible]);
217   testing::Mock::VerifyAndClearExpectations(mock);
218   testing::Mock::VerifyAndClearExpectations(&mock_host);
221 // Test that NativeAppWindow and AppWindow fullscreen state is updated when
222 // the window is fullscreened natively.
223 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, Fullscreen) {
224   if (!base::mac::IsOSLionOrLater())
225     return;
227   ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
229   extensions::AppWindow* app_window =
230       CreateTestAppWindow("{\"alwaysOnTop\": true }");
231   extensions::NativeAppWindow* window = app_window->GetBaseWindow();
232   NSWindow* ns_window = app_window->GetNativeWindow();
233   base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter(
234       [[NSWindowFullscreenNotificationWaiter alloc] initWithWindow:ns_window]);
236   EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
237             app_window->fullscreen_types_for_test());
238   EXPECT_FALSE(window->IsFullscreen());
239   EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
240   EXPECT_TRUE(gfx::IsNSWindowAlwaysOnTop(ns_window));
242   [ns_window toggleFullScreen:nil];
243   [waiter waitForEnterCount:1 exitCount:0];
244   EXPECT_TRUE(app_window->fullscreen_types_for_test() &
245               AppWindow::FULLSCREEN_TYPE_OS);
246   EXPECT_TRUE(window->IsFullscreen());
247   EXPECT_TRUE([ns_window styleMask] & NSFullScreenWindowMask);
248   EXPECT_FALSE(gfx::IsNSWindowAlwaysOnTop(ns_window));
250   app_window->Restore();
251   EXPECT_FALSE(window->IsFullscreenOrPending());
252   [waiter waitForEnterCount:1 exitCount:1];
253   EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
254             app_window->fullscreen_types_for_test());
255   EXPECT_FALSE(window->IsFullscreen());
256   EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
257   EXPECT_TRUE(gfx::IsNSWindowAlwaysOnTop(ns_window));
259   app_window->Fullscreen();
260   EXPECT_TRUE(window->IsFullscreenOrPending());
261   [waiter waitForEnterCount:2 exitCount:1];
262   EXPECT_TRUE(app_window->fullscreen_types_for_test() &
263               AppWindow::FULLSCREEN_TYPE_WINDOW_API);
264   EXPECT_TRUE(window->IsFullscreen());
265   EXPECT_TRUE([ns_window styleMask] & NSFullScreenWindowMask);
266   EXPECT_FALSE(gfx::IsNSWindowAlwaysOnTop(ns_window));
268   [ns_window toggleFullScreen:nil];
269   [waiter waitForEnterCount:2 exitCount:2];
270   EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
271             app_window->fullscreen_types_for_test());
272   EXPECT_FALSE(window->IsFullscreen());
273   EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
274   EXPECT_TRUE(gfx::IsNSWindowAlwaysOnTop(ns_window));
277 // Test Minimize, Restore combinations with their native equivalents.
278 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, Minimize) {
279   SetUpAppWithWindows(1);
280   AppWindow* app_window = GetFirstAppWindow();
281   extensions::NativeAppWindow* window = app_window->GetBaseWindow();
282   NSWindow* ns_window = app_window->GetNativeWindow();
284   NSRect initial_frame = [ns_window frame];
286   EXPECT_FALSE(window->IsMinimized());
287   EXPECT_FALSE([ns_window isMiniaturized]);
289   // Native minimize, Restore.
290   [ns_window miniaturize:nil];
291   EXPECT_NSEQ(initial_frame, [ns_window frame]);
292   EXPECT_TRUE(window->IsMinimized());
293   EXPECT_TRUE([ns_window isMiniaturized]);
295   app_window->Restore();
296   EXPECT_NSEQ(initial_frame, [ns_window frame]);
297   EXPECT_FALSE(window->IsMinimized());
298   EXPECT_FALSE([ns_window isMiniaturized]);
300   // Minimize, native restore.
301   app_window->Minimize();
302   EXPECT_NSEQ(initial_frame, [ns_window frame]);
303   EXPECT_TRUE(window->IsMinimized());
304   EXPECT_TRUE([ns_window isMiniaturized]);
306   [ns_window deminiaturize:nil];
307   EXPECT_NSEQ(initial_frame, [ns_window frame]);
308   EXPECT_FALSE(window->IsMinimized());
309   EXPECT_FALSE([ns_window isMiniaturized]);
312 // Test Maximize, Restore combinations with their native equivalents.
313 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, Maximize) {
314   // This test is flaky on 10.6. Disable it until we're sure we need MacViews on
315   // 10.6. See http://crbug.com/503208
316   if (GetParam() && base::mac::IsOSSnowLeopard())
317     return;
319   SetUpAppWithWindows(1);
320   AppWindow* app_window = GetFirstAppWindow();
321   extensions::NativeAppWindow* window = app_window->GetBaseWindow();
322   NSWindow* ns_window = app_window->GetNativeWindow();
323   base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
325   gfx::Rect initial_restored_bounds = window->GetRestoredBounds();
326   NSRect initial_frame = [ns_window frame];
327   NSRect maximized_frame = [[ns_window screen] visibleFrame];
329   EXPECT_FALSE(window->IsMaximized());
331   // Native maximize, Restore.
332   watcher.reset([[WindowedNSNotificationObserver alloc]
333       initForNotification:NSWindowDidResizeNotification
334                    object:ns_window]);
335   [ns_window zoom:nil];
336   [watcher wait];
337   EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
338   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
339   EXPECT_TRUE(window->IsMaximized());
341   watcher.reset([[WindowedNSNotificationObserver alloc]
342       initForNotification:NSWindowDidResizeNotification
343                    object:ns_window]);
344   app_window->Restore();
345   [watcher wait];
346   EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
347   EXPECT_NSEQ(initial_frame, [ns_window frame]);
348   EXPECT_FALSE(window->IsMaximized());
350   // Maximize, native restore.
351   watcher.reset([[WindowedNSNotificationObserver alloc]
352       initForNotification:NSWindowDidResizeNotification
353                    object:ns_window]);
354   app_window->Maximize();
355   [watcher wait];
356   EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
357   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
358   EXPECT_TRUE(window->IsMaximized());
360   watcher.reset([[WindowedNSNotificationObserver alloc]
361       initForNotification:NSWindowDidResizeNotification
362                    object:ns_window]);
363   [ns_window zoom:nil];
364   [watcher wait];
365   EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
366   EXPECT_NSEQ(initial_frame, [ns_window frame]);
367   EXPECT_FALSE(window->IsMaximized());
370 // Test Maximize when the window has a maximum size. The maximum size means that
371 // the window is not user-maximizable. However, calling Maximize() via the
372 // javascript API should still maximize and since the zoom button is removed,
373 // the codepath changes.
374 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, MaximizeConstrained) {
375   AppWindow* app_window = CreateTestAppWindow(
376       "{\"outerBounds\": {\"maxWidth\":200, \"maxHeight\":300}}");
377   extensions::NativeAppWindow* window = app_window->GetBaseWindow();
378   NSWindow* ns_window = app_window->GetNativeWindow();
379   base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
381   gfx::Rect initial_restored_bounds = window->GetRestoredBounds();
382   NSRect initial_frame = [ns_window frame];
383   NSRect maximized_frame = [[ns_window screen] visibleFrame];
385   EXPECT_FALSE(window->IsMaximized());
387   // Maximize, Restore.
388   watcher.reset([[WindowedNSNotificationObserver alloc]
389       initForNotification:NSWindowDidResizeNotification
390                    object:ns_window]);
391   app_window->Maximize();
392   [watcher wait];
393   EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
394   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
395   EXPECT_TRUE(window->IsMaximized());
397   watcher.reset([[WindowedNSNotificationObserver alloc]
398       initForNotification:NSWindowDidResizeNotification
399                    object:ns_window]);
400   app_window->Restore();
401   [watcher wait];
402   EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
403   EXPECT_NSEQ(initial_frame, [ns_window frame]);
404   EXPECT_FALSE(window->IsMaximized());
407 // Test Minimize, Maximize, Restore combinations with their native equivalents.
408 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, MinimizeMaximize) {
409   SetUpAppWithWindows(1);
410   AppWindow* app_window = GetFirstAppWindow();
411   extensions::NativeAppWindow* window = app_window->GetBaseWindow();
412   NSWindow* ns_window = app_window->GetNativeWindow();
413   base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
415   NSRect initial_frame = [ns_window frame];
416   NSRect maximized_frame = [[ns_window screen] visibleFrame];
418   EXPECT_FALSE(window->IsMaximized());
419   EXPECT_FALSE(window->IsMinimized());
420   EXPECT_FALSE([ns_window isMiniaturized]);
422   // Maximize, Minimize, Restore.
423   watcher.reset([[WindowedNSNotificationObserver alloc]
424       initForNotification:NSWindowDidResizeNotification
425                    object:ns_window]);
426   app_window->Maximize();
427   [watcher wait];
428   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
429   EXPECT_TRUE(window->IsMaximized());
431   app_window->Minimize();
432   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
433   EXPECT_FALSE(window->IsMaximized());
434   EXPECT_TRUE(window->IsMinimized());
435   EXPECT_TRUE([ns_window isMiniaturized]);
437   app_window->Restore();
438   EXPECT_NSEQ(initial_frame, [ns_window frame]);
439   EXPECT_FALSE(window->IsMaximized());
440   EXPECT_FALSE(window->IsMinimized());
441   EXPECT_FALSE([ns_window isMiniaturized]);
443   // Minimize, Maximize.
444   app_window->Minimize();
445   EXPECT_NSEQ(initial_frame, [ns_window frame]);
446   EXPECT_TRUE(window->IsMinimized());
447   EXPECT_TRUE([ns_window isMiniaturized]);
449   app_window->Maximize();
450   EXPECT_TRUE([ns_window isVisible]);
451   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
452   EXPECT_TRUE(window->IsMaximized());
453   EXPECT_FALSE(window->IsMinimized());
454   EXPECT_FALSE([ns_window isMiniaturized]);
457 // Test Maximize, Fullscreen, Restore combinations.
458 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, MaximizeFullscreen) {
459   if (base::mac::IsOSSnowLeopard())
460     return;
462   ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
464   SetUpAppWithWindows(1);
465   AppWindow* app_window = GetFirstAppWindow();
466   extensions::NativeAppWindow* window = app_window->GetBaseWindow();
467   NSWindow* ns_window = app_window->GetNativeWindow();
468   base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
469   base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter(
470       [[NSWindowFullscreenNotificationWaiter alloc] initWithWindow:ns_window]);
472   NSRect initial_frame = [ns_window frame];
473   NSRect maximized_frame = [[ns_window screen] visibleFrame];
475   EXPECT_FALSE(window->IsMaximized());
476   EXPECT_FALSE(window->IsFullscreen());
478   // Maximize, Fullscreen, Restore, Restore.
479   watcher.reset([[WindowedNSNotificationObserver alloc]
480       initForNotification:NSWindowDidResizeNotification
481                    object:ns_window]);
482   app_window->Maximize();
483   [watcher wait];
484   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
485   EXPECT_TRUE(window->IsMaximized());
487   EXPECT_EQ(0, [waiter enterCount]);
488   app_window->Fullscreen();
489   [waiter waitForEnterCount:1 exitCount:0];
490   EXPECT_FALSE(window->IsMaximized());
491   EXPECT_TRUE(window->IsFullscreen());
493   app_window->Restore();
494   [waiter waitForEnterCount:1 exitCount:1];
495   EXPECT_NSEQ(maximized_frame, [ns_window frame]);
496   EXPECT_TRUE(window->IsMaximized());
497   EXPECT_FALSE(window->IsFullscreen());
499   app_window->Restore();
500   EXPECT_NSEQ(initial_frame, [ns_window frame]);
501   EXPECT_FALSE(window->IsMaximized());
503   // Fullscreen, Maximize, Restore.
504   app_window->Fullscreen();
505   [waiter waitForEnterCount:2 exitCount:1];
506   EXPECT_FALSE(window->IsMaximized());
507   EXPECT_TRUE(window->IsFullscreen());
509   app_window->Maximize();
510   EXPECT_FALSE(window->IsMaximized());
511   EXPECT_TRUE(window->IsFullscreen());
513   app_window->Restore();
514   [waiter waitForEnterCount:2 exitCount:2];
515   EXPECT_NSEQ(initial_frame, [ns_window frame]);
516   EXPECT_FALSE(window->IsMaximized());
517   EXPECT_FALSE(window->IsFullscreen());
520 // Test that, in frameless windows, the web contents has the same size as the
521 // window.
522 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, Frameless) {
523   AppWindow* app_window = CreateTestAppWindow("{\"frame\": \"none\"}");
524   NSWindow* ns_window = app_window->GetNativeWindow();
525   NSView* web_contents = app_window->web_contents()->GetNativeView();
526   EXPECT_TRUE(NSEqualSizes(NSMakeSize(512, 384), [web_contents frame].size));
527   // Move and resize the window.
528   NSRect new_frame = NSMakeRect(50, 50, 200, 200);
529   [ns_window setFrame:new_frame display:YES];
530   EXPECT_TRUE(NSEqualSizes(new_frame.size, [web_contents frame].size));
532   // Windows created with NSBorderlessWindowMask by default don't have shadow,
533   // but packaged apps should always have one.
534   EXPECT_TRUE([ns_window hasShadow]);
536   // Since the window has no constraints, it should have all of the following
537   // style mask bits.
538   NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
539                           NSMiniaturizableWindowMask | NSResizableWindowMask |
540                           NSTexturedBackgroundWindowMask;
541   EXPECT_EQ(style_mask, [ns_window styleMask]);
543   CloseAppWindow(app_window);
546 namespace {
548 // Test that resize and fullscreen controls are correctly enabled/disabled.
549 void TestControls(AppWindow* app_window) {
550   NSWindow* ns_window = app_window->GetNativeWindow();
552   // The window is resizable.
553   EXPECT_TRUE([ns_window styleMask] & NSResizableWindowMask);
554   if (base::mac::IsOSSnowLeopard())
555     EXPECT_TRUE([ns_window showsResizeIndicator]);
557   // Due to this bug: http://crbug.com/362039, which manifests on the Cocoa
558   // implementation but not the views one, frameless windows should have
559   // fullscreen controls disabled.
560   BOOL can_fullscreen =
561       ![NSStringFromClass([ns_window class]) isEqualTo:@"AppFramelessNSWindow"];
562   // The window can fullscreen and maximize.
563   if (base::mac::IsOSLionOrLater()) {
564     EXPECT_EQ(can_fullscreen, !!([ns_window collectionBehavior] &
565                                  NSWindowCollectionBehaviorFullScreenPrimary));
566   }
568   // In OSX 10.10+, the zoom button performs the zoom action rather than the
569   // fullscreen action. The above check that collectionBehavior does not include
570   // NSWindowCollectionBehaviorFullScreenPrimary is sufficient to determine that
571   // the window can't be fullscreened.
572   if (base::mac::IsOSMavericksOrEarlier()) {
573     EXPECT_EQ(can_fullscreen,
574               [[ns_window standardWindowButton:NSWindowZoomButton] isEnabled]);
575   }
577   // Set a maximum size.
578   app_window->SetContentSizeConstraints(gfx::Size(), gfx::Size(200, 201));
579   EXPECT_EQ(200, [ns_window contentMaxSize].width);
580   EXPECT_EQ(201, [ns_window contentMaxSize].height);
581   NSView* web_contents = app_window->web_contents()->GetNativeView();
582   EXPECT_EQ(200, [web_contents frame].size.width);
583   EXPECT_EQ(201, [web_contents frame].size.height);
585   // Still resizable.
586   EXPECT_TRUE([ns_window styleMask] & NSResizableWindowMask);
587   if (base::mac::IsOSSnowLeopard())
588     EXPECT_TRUE([ns_window showsResizeIndicator]);
590   // Fullscreen and maximize are disabled.
591   if (base::mac::IsOSLionOrLater())
592     EXPECT_FALSE([ns_window collectionBehavior] &
593                  NSWindowCollectionBehaviorFullScreenPrimary);
594   EXPECT_FALSE([[ns_window standardWindowButton:NSWindowZoomButton] isEnabled]);
596   // Set a minimum size equal to the maximum size.
597   app_window->SetContentSizeConstraints(gfx::Size(200, 201),
598                                         gfx::Size(200, 201));
599   EXPECT_EQ(200, [ns_window contentMinSize].width);
600   EXPECT_EQ(201, [ns_window contentMinSize].height);
602   // No longer resizable.
603   EXPECT_FALSE([ns_window styleMask] & NSResizableWindowMask);
604   if (base::mac::IsOSSnowLeopard())
605     EXPECT_FALSE([ns_window showsResizeIndicator]);
607   // If a window is made fullscreen by the API, fullscreen should be enabled so
608   // the user can exit fullscreen.
609   if (base::mac::IsOSLionOrLater()) {
610     ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
611     base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter([
612         [NSWindowFullscreenNotificationWaiter alloc] initWithWindow:ns_window]);
613     app_window->SetFullscreen(AppWindow::FULLSCREEN_TYPE_WINDOW_API, true);
614     [waiter waitForEnterCount:1 exitCount:0];
615     EXPECT_TRUE([ns_window collectionBehavior] &
616                 NSWindowCollectionBehaviorFullScreenPrimary);
617     EXPECT_EQ(NSWidth([[ns_window contentView] frame]),
618               NSWidth([ns_window frame]));
619     // Once it leaves fullscreen, it is disabled again.
620     app_window->SetFullscreen(AppWindow::FULLSCREEN_TYPE_WINDOW_API, false);
621     [waiter waitForEnterCount:1 exitCount:1];
622     EXPECT_FALSE([ns_window collectionBehavior] &
623                  NSWindowCollectionBehaviorFullScreenPrimary);
624   }
627 }  // namespace
629 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, Controls) {
630   TestControls(CreateTestAppWindow("{}"));
633 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, ControlsFrameless) {
634   TestControls(CreateTestAppWindow("{\"frame\": \"none\"}"));
637 namespace {
639 // Convert a color constant to an NSColor that can be compared with |bitmap|.
640 NSColor* ColorInBitmapColorSpace(SkColor color, NSBitmapImageRep* bitmap) {
641   return [gfx::SkColorToSRGBNSColor(color)
642       colorUsingColorSpace:[bitmap colorSpace]];
645 // Take a screenshot of the window, including its native frame.
646 NSBitmapImageRep* ScreenshotNSWindow(NSWindow* window) {
647   // When building with 10.10 SDK and running on 10.9, -[NSView
648   // cacheDisplayInRect] does not seem to capture subviews. This seems related
649   // to the frame view having a layer with 10.10 SDK, but is probably a bug
650   // since it doesn't manifest on 10.10. See http://crbug.com/508722.
651   // In this case, take a screenshot using the CGWindowList API instead. The
652   // bitmap is now in the display's color space, so expected colors need to be
653   // converted.
654   // TODO(jackhou): Update this if it is fixed in AppKit, or if other
655   // platform/SDK combinations need it.
656   // NOTE: This doesn't work with Views, but the regular test does, so use that.
657   bool mac_views = base::CommandLine::ForCurrentProcess()->HasSwitch(
658       switches::kEnableMacViewsNativeAppWindows);
659   if (base::mac::IsOSMavericks() && !mac_views) {
660     // -[NSView setNeedsDisplay:YES] doesn't synchronously display the view, it
661     // gets drawn by another event in the queue, so let that run first.
662     content::RunAllPendingInMessageLoop();
663     base::ScopedCFTypeRef<CGImageRef> cg_image(CGWindowListCreateImage(
664         CGRectNull, kCGWindowListOptionIncludingWindow, [window windowNumber],
665         kCGWindowImageBoundsIgnoreFraming));
666     return [[[NSBitmapImageRep alloc] initWithCGImage:cg_image] autorelease];
667   }
669   NSView* frame_view = [[window contentView] superview];
670   NSRect bounds = [frame_view bounds];
671   NSBitmapImageRep* bitmap =
672       [frame_view bitmapImageRepForCachingDisplayInRect:bounds];
673   [frame_view cacheDisplayInRect:bounds toBitmapImageRep:bitmap];
674   return bitmap;
677 }  // namespace
679 // Test that the colored frames have the correct color when active and inactive.
680 IN_PROC_BROWSER_TEST_P(NativeAppWindowCocoaBrowserTest, FrameColor) {
681   // The hex values indicate an RGB color. When we get the NSColor later, the
682   // components are CGFloats in the range [0, 1].
683   extensions::AppWindow* app_window = CreateTestAppWindow(
684       "{\"frame\": {\"color\": \"#FF0000\", \"inactiveColor\": \"#0000FF\"}}");
685   NSWindow* ns_window = app_window->GetNativeWindow();
686   // No color correction in the default case.
687   [ns_window setColorSpace:[NSColorSpace sRGBColorSpace]];
689   int half_width = NSWidth([ns_window frame]) / 2;
691   NSBitmapImageRep* bitmap = ScreenshotNSWindow(ns_window);
692   // The window is currently inactive so it should be blue (#0000FF).
693   NSColor* expected_color = ColorInBitmapColorSpace(0xFF0000FF, bitmap);
694   NSColor* color = [bitmap colorAtX:half_width y:5];
695   CGFloat expected_components[4], color_components[4];
696   [expected_color getComponents:expected_components];
697   [color getComponents:color_components];
698   EXPECT_NEAR(expected_components[0], color_components[0], 0.01);
699   EXPECT_NEAR(expected_components[1], color_components[1], 0.01);
700   EXPECT_NEAR(expected_components[2], color_components[2], 0.01);
702   ui::test::ScopedFakeNSWindowFocus fake_focus;
703   [ns_window makeMainWindow];
705   bitmap = ScreenshotNSWindow(ns_window);
706   // The window is now active so it should be red (#FF0000).
707   expected_color = ColorInBitmapColorSpace(0xFFFF0000, bitmap);
708   color = [bitmap colorAtX:half_width y:5];
709   [expected_color getComponents:expected_components];
710   [color getComponents:color_components];
711   EXPECT_NEAR(expected_components[0], color_components[0], 0.01);
712   EXPECT_NEAR(expected_components[1], color_components[1], 0.01);
713   EXPECT_NEAR(expected_components[2], color_components[2], 0.01);
716 INSTANTIATE_TEST_CASE_P(NativeAppWindowCocoaBrowserTestInstance,
717                         NativeAppWindowCocoaBrowserTest,
718                         ::testing::Bool());