[go: nahoru, domu]

blob: 30a4180eb2a69d712307a0368f399610103c0574 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/app_window/native_app_window.h"
#import <Cocoa/Cocoa.h>
#include <memory>
#import "base/mac/foundation_util.h"
#import "base/mac/mac_util.h"
#import "base/mac/scoped_cftyperef.h"
#import "base/mac/scoped_nsobject.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/macros.h"
#include "chrome/browser/apps/app_shim/app_shim_host_bootstrap_mac.h"
#include "chrome/browser/apps/app_shim/app_shim_host_mac.h"
#include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
#include "chrome/browser/apps/app_shim/test/app_shim_host_manager_test_api_mac.h"
#include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/common/constants.h"
#include "skia/ext/skia_utils_mac.h"
#include "testing/gmock/include/gmock/gmock.h"
#import "testing/gtest_mac.h"
#import "ui/base/test/nswindow_fullscreen_notification_waiter.h"
#import "ui/base/test/scoped_fake_nswindow_focus.h"
#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
#import "ui/base/test/windowed_nsnotification_observer.h"
using extensions::AppWindow;
using extensions::PlatformAppBrowserTest;
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
namespace {
bool IsNSWindowFloating(NSWindow* window) {
return [window level] != NSNormalWindowLevel;
}
class NativeAppWindowCocoaBrowserTest : public PlatformAppBrowserTest {
protected:
NativeAppWindowCocoaBrowserTest() {}
void SetUpAppWithWindows(int num_windows) {
app_ = InstallExtension(
test_data_dir_.AppendASCII("platform_apps").AppendASCII("minimal"), 1);
EXPECT_TRUE(app_);
for (int i = 0; i < num_windows; ++i) {
content::WindowedNotificationObserver app_loaded_observer(
content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
content::NotificationService::AllSources());
OpenApplication(
AppLaunchParams(profile(), app_->id(),
extensions::LaunchContainer::kLaunchContainerNone,
WindowOpenDisposition::NEW_WINDOW,
extensions::AppLaunchSource::kSourceTest));
app_loaded_observer.Wait();
}
}
const extensions::Extension* app_;
private:
DISALLOW_COPY_AND_ASSIGN(NativeAppWindowCocoaBrowserTest);
};
} // namespace
// Test interaction of Hide/Show() with Hide/ShowWithApp().
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
SetUpAppWithWindows(2);
extensions::AppWindowRegistry::AppWindowList windows =
extensions::AppWindowRegistry::Get(profile())->app_windows();
AppWindow* app_window = windows.front();
extensions::NativeAppWindow* native_window = app_window->GetBaseWindow();
NSWindow* ns_window = native_window->GetNativeWindow().GetNativeNSWindow();
AppWindow* other_app_window = windows.back();
extensions::NativeAppWindow* other_native_window =
other_app_window->GetBaseWindow();
NSWindow* other_ns_window =
other_native_window->GetNativeWindow().GetNativeNSWindow();
// Normal Hide/Show.
app_window->Hide();
EXPECT_FALSE([ns_window isVisible]);
app_window->Show(AppWindow::SHOW_ACTIVE);
EXPECT_TRUE([ns_window isVisible]);
// Normal Hide/ShowWithApp.
native_window->HideWithApp();
EXPECT_FALSE([ns_window isVisible]);
native_window->ShowWithApp();
EXPECT_TRUE([ns_window isVisible]);
// HideWithApp, Hide, ShowWithApp does not show.
native_window->HideWithApp();
app_window->Hide();
native_window->ShowWithApp();
EXPECT_FALSE([ns_window isVisible]);
// Hide, HideWithApp, ShowWithApp does not show.
native_window->HideWithApp();
native_window->ShowWithApp();
EXPECT_FALSE([ns_window isVisible]);
// Return to shown state.
app_window->Show(AppWindow::SHOW_ACTIVE);
EXPECT_TRUE([ns_window isVisible]);
// HideWithApp the other window.
EXPECT_TRUE([other_ns_window isVisible]);
other_native_window->HideWithApp();
EXPECT_FALSE([other_ns_window isVisible]);
// HideWithApp, Show shows just one window since there's no shim.
native_window->HideWithApp();
EXPECT_FALSE([ns_window isVisible]);
app_window->Show(AppWindow::SHOW_ACTIVE);
EXPECT_TRUE([ns_window isVisible]);
EXPECT_FALSE([other_ns_window isVisible]);
// Hide the other window.
other_app_window->Hide();
EXPECT_FALSE([other_ns_window isVisible]);
// HideWithApp, ShowWithApp does not show the other window.
native_window->HideWithApp();
EXPECT_FALSE([ns_window isVisible]);
native_window->ShowWithApp();
EXPECT_TRUE([ns_window isVisible]);
EXPECT_FALSE([other_ns_window isVisible]);
}
namespace {
class MockAppShimHost : public AppShimHost {
public:
MockAppShimHost()
: AppShimHost("app",
base::FilePath("Profile"),
false /* uses_remote_views */),
weak_factory_(this) {}
~MockAppShimHost() override {}
MOCK_METHOD0(OnAppUnhideWithoutActivation, void());
base::WeakPtr<MockAppShimHost> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<MockAppShimHost> weak_factory_;
};
class MockExtensionAppShimHandler : public apps::ExtensionAppShimHandler {
public:
MockExtensionAppShimHandler() {
ON_CALL(*this, FindHost(_, _))
.WillByDefault(Invoke(this, &apps::ExtensionAppShimHandler::FindHost));
}
~MockExtensionAppShimHandler() override {}
MOCK_METHOD2(FindHost, AppShimHost*(Profile*, const std::string&));
};
} // namespace
// Test Hide/Show and Hide/ShowWithApp() behavior when shims are enabled.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest,
HideShowWithAppWithShim) {
test::AppShimHostManagerTestApi test_api(
g_browser_process->platform_part()->app_shim_host_manager());
MockExtensionAppShimHandler* mock = new MockExtensionAppShimHandler();
test_api.SetExtensionAppShimHandler(
std::unique_ptr<apps::ExtensionAppShimHandler>(
mock)); // Takes ownership.
base::WeakPtr<MockAppShimHost> mock_host =
(new MockAppShimHost)->GetWeakPtr();
SetUpAppWithWindows(1);
extensions::AppWindowRegistry::AppWindowList windows =
extensions::AppWindowRegistry::Get(profile())->app_windows();
extensions::AppWindow* app_window = windows.front();
extensions::NativeAppWindow* native_window = app_window->GetBaseWindow();
NSWindow* ns_window = native_window->GetNativeWindow().GetNativeNSWindow();
// HideWithApp.
native_window->HideWithApp();
EXPECT_FALSE([ns_window isVisible]);
// Show notifies the shim to unhide.
EXPECT_CALL(*mock_host, OnAppUnhideWithoutActivation());
EXPECT_CALL(*mock, FindHost(_, _)).WillOnce(Return(mock_host.get()));
app_window->Show(extensions::AppWindow::SHOW_ACTIVE);
EXPECT_TRUE([ns_window isVisible]);
testing::Mock::VerifyAndClearExpectations(mock);
testing::Mock::VerifyAndClearExpectations(mock_host.get());
// HideWithApp
native_window->HideWithApp();
EXPECT_FALSE([ns_window isVisible]);
// Activate does the same.
EXPECT_CALL(*mock_host, OnAppUnhideWithoutActivation());
EXPECT_CALL(*mock, FindHost(_, _)).WillOnce(Return(mock_host.get()));
native_window->Activate();
EXPECT_TRUE([ns_window isVisible]);
testing::Mock::VerifyAndClearExpectations(mock);
testing::Mock::VerifyAndClearExpectations(mock_host.get());
// Ensure that the mock object be deleted.
mock_host->OnAppClosed();
DCHECK(!mock_host);
}
// Test that NativeAppWindow and AppWindow fullscreen state is updated when
// the window is fullscreened natively.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Fullscreen) {
extensions::AppWindow* app_window =
CreateTestAppWindow("{\"alwaysOnTop\": true }");
extensions::NativeAppWindow* window = app_window->GetBaseWindow();
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter(
[[NSWindowFullscreenNotificationWaiter alloc] initWithWindow:ns_window]);
EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
app_window->fullscreen_types_for_test());
EXPECT_FALSE(window->IsFullscreen());
EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
EXPECT_TRUE(IsNSWindowFloating(ns_window));
[ns_window toggleFullScreen:nil];
[waiter waitForEnterCount:1 exitCount:0];
EXPECT_TRUE(app_window->fullscreen_types_for_test() &
AppWindow::FULLSCREEN_TYPE_OS);
EXPECT_TRUE(window->IsFullscreen());
EXPECT_TRUE([ns_window styleMask] & NSFullScreenWindowMask);
EXPECT_FALSE(IsNSWindowFloating(ns_window));
app_window->Restore();
EXPECT_FALSE(window->IsFullscreenOrPending());
[waiter waitForEnterCount:1 exitCount:1];
EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
app_window->fullscreen_types_for_test());
EXPECT_FALSE(window->IsFullscreen());
EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
EXPECT_TRUE(IsNSWindowFloating(ns_window));
app_window->Fullscreen();
EXPECT_TRUE(window->IsFullscreenOrPending());
[waiter waitForEnterCount:2 exitCount:1];
EXPECT_TRUE(app_window->fullscreen_types_for_test() &
AppWindow::FULLSCREEN_TYPE_WINDOW_API);
EXPECT_TRUE(window->IsFullscreen());
EXPECT_TRUE([ns_window styleMask] & NSFullScreenWindowMask);
EXPECT_FALSE(IsNSWindowFloating(ns_window));
[ns_window toggleFullScreen:nil];
[waiter waitForEnterCount:2 exitCount:2];
EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
app_window->fullscreen_types_for_test());
EXPECT_FALSE(window->IsFullscreen());
EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
EXPECT_TRUE(IsNSWindowFloating(ns_window));
}
// Test Minimize, Restore combinations with their native equivalents.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Minimize) {
if (base::mac::IsOS10_10())
return; // Fails when swarmed. http://crbug.com/660582
SetUpAppWithWindows(1);
AppWindow* app_window = GetFirstAppWindow();
extensions::NativeAppWindow* window = app_window->GetBaseWindow();
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
NSRect initial_frame = [ns_window frame];
EXPECT_FALSE(window->IsMinimized());
EXPECT_FALSE([ns_window isMiniaturized]);
// Native minimize, Restore.
[ns_window miniaturize:nil];
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMinimized());
EXPECT_TRUE([ns_window isMiniaturized]);
app_window->Restore();
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMinimized());
EXPECT_FALSE([ns_window isMiniaturized]);
// Minimize, native restore.
app_window->Minimize();
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMinimized());
EXPECT_TRUE([ns_window isMiniaturized]);
[ns_window deminiaturize:nil];
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMinimized());
EXPECT_FALSE([ns_window isMiniaturized]);
}
// Test Maximize, Restore combinations with their native equivalents.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Maximize) {
SetUpAppWithWindows(1);
AppWindow* app_window = GetFirstAppWindow();
extensions::NativeAppWindow* window = app_window->GetBaseWindow();
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
gfx::Rect initial_restored_bounds = window->GetRestoredBounds();
NSRect initial_frame = [ns_window frame];
NSRect maximized_frame = [[ns_window screen] visibleFrame];
EXPECT_FALSE(window->IsMaximized());
// Native maximize, Restore.
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
[ns_window zoom:nil];
[watcher wait];
EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMaximized());
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
app_window->Restore();
[watcher wait];
EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMaximized());
// Maximize, native restore.
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
app_window->Maximize();
[watcher wait];
EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMaximized());
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
[ns_window zoom:nil];
[watcher wait];
EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMaximized());
}
// Test Maximize when the window has a maximum size. The maximum size means that
// the window is not user-maximizable. However, calling Maximize() via the
// javascript API should still maximize and since the zoom button is removed,
// the codepath changes.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, MaximizeConstrained) {
AppWindow* app_window = CreateTestAppWindow(
"{\"outerBounds\": {\"maxWidth\":200, \"maxHeight\":300}}");
extensions::NativeAppWindow* window = app_window->GetBaseWindow();
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
gfx::Rect initial_restored_bounds = window->GetRestoredBounds();
NSRect initial_frame = [ns_window frame];
NSRect maximized_frame = [[ns_window screen] visibleFrame];
EXPECT_FALSE(window->IsMaximized());
// Maximize, Restore.
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
app_window->Maximize();
[watcher wait];
EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMaximized());
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
app_window->Restore();
[watcher wait];
EXPECT_EQ(initial_restored_bounds, window->GetRestoredBounds());
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMaximized());
}
// Test Minimize, Maximize, Restore combinations with their native equivalents.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, MinimizeMaximize) {
if (base::mac::IsOS10_10())
return; // Fails when swarmed. http://crbug.com/660582
SetUpAppWithWindows(1);
AppWindow* app_window = GetFirstAppWindow();
extensions::NativeAppWindow* window = app_window->GetBaseWindow();
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
NSRect initial_frame = [ns_window frame];
NSRect maximized_frame = [[ns_window screen] visibleFrame];
EXPECT_FALSE(window->IsMaximized());
EXPECT_FALSE(window->IsMinimized());
EXPECT_FALSE([ns_window isMiniaturized]);
// Maximize, Minimize, Restore.
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
app_window->Maximize();
[watcher wait];
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMaximized());
app_window->Minimize();
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMaximized());
EXPECT_TRUE(window->IsMinimized());
EXPECT_TRUE([ns_window isMiniaturized]);
app_window->Restore();
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMaximized());
EXPECT_FALSE(window->IsMinimized());
EXPECT_FALSE([ns_window isMiniaturized]);
// Minimize, Maximize.
app_window->Minimize();
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMinimized());
EXPECT_TRUE([ns_window isMiniaturized]);
app_window->Maximize();
EXPECT_TRUE([ns_window isVisible]);
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMaximized());
EXPECT_FALSE(window->IsMinimized());
EXPECT_FALSE([ns_window isMiniaturized]);
}
// Test Maximize, Fullscreen, Restore combinations.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, MaximizeFullscreen) {
ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
SetUpAppWithWindows(1);
AppWindow* app_window = GetFirstAppWindow();
extensions::NativeAppWindow* window = app_window->GetBaseWindow();
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
base::scoped_nsobject<WindowedNSNotificationObserver> watcher;
base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter(
[[NSWindowFullscreenNotificationWaiter alloc] initWithWindow:ns_window]);
NSRect initial_frame = [ns_window frame];
NSRect maximized_frame = [[ns_window screen] visibleFrame];
EXPECT_FALSE(window->IsMaximized());
EXPECT_FALSE(window->IsFullscreen());
// Maximize, Fullscreen, Restore, Restore.
watcher.reset([[WindowedNSNotificationObserver alloc]
initForNotification:NSWindowDidResizeNotification
object:ns_window]);
app_window->Maximize();
[watcher wait];
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMaximized());
EXPECT_EQ(0, [waiter enterCount]);
app_window->Fullscreen();
[waiter waitForEnterCount:1 exitCount:0];
EXPECT_FALSE(window->IsMaximized());
EXPECT_TRUE(window->IsFullscreen());
app_window->Restore();
[waiter waitForEnterCount:1 exitCount:1];
EXPECT_NSEQ(maximized_frame, [ns_window frame]);
EXPECT_TRUE(window->IsMaximized());
EXPECT_FALSE(window->IsFullscreen());
app_window->Restore();
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMaximized());
// Fullscreen, Maximize, Restore.
app_window->Fullscreen();
[waiter waitForEnterCount:2 exitCount:1];
EXPECT_FALSE(window->IsMaximized());
EXPECT_TRUE(window->IsFullscreen());
app_window->Maximize();
EXPECT_FALSE(window->IsMaximized());
EXPECT_TRUE(window->IsFullscreen());
app_window->Restore();
[waiter waitForEnterCount:2 exitCount:2];
EXPECT_NSEQ(initial_frame, [ns_window frame]);
EXPECT_FALSE(window->IsMaximized());
EXPECT_FALSE(window->IsFullscreen());
}
// Test that, in frameless windows, the web contents has the same size as the
// window.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Frameless) {
AppWindow* app_window = CreateTestAppWindow("{\"frame\": \"none\"}");
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
NSView* web_contents =
app_window->web_contents()->GetNativeView().GetNativeNSView();
EXPECT_TRUE(NSEqualSizes(NSMakeSize(512, 384), [web_contents frame].size));
// Move and resize the window.
NSRect new_frame = NSMakeRect(50, 50, 200, 200);
[ns_window setFrame:new_frame display:YES];
EXPECT_TRUE(NSEqualSizes(new_frame.size, [web_contents frame].size));
// Windows created with NSBorderlessWindowMask by default don't have shadow,
// but packaged apps should always have one.
EXPECT_TRUE([ns_window hasShadow]);
// Since the window has no constraints, it should have all of the following
// style mask bits.
NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask | NSResizableWindowMask;
EXPECT_EQ(style_mask, [ns_window styleMask] & style_mask);
CloseAppWindow(app_window);
}
namespace {
// Test that resize and fullscreen controls are correctly enabled/disabled.
void TestControls(AppWindow* app_window) {
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
// The window is resizable.
EXPECT_TRUE([ns_window styleMask] & NSResizableWindowMask);
// Due to this bug: http://crbug.com/362039, which manifests on the Cocoa
// implementation but not the views one, frameless windows should have
// fullscreen controls disabled.
BOOL can_fullscreen =
![NSStringFromClass([ns_window class]) isEqualTo:@"AppFramelessNSWindow"];
// The window can fullscreen and maximize.
EXPECT_EQ(can_fullscreen, !!([ns_window collectionBehavior] &
NSWindowCollectionBehaviorFullScreenPrimary));
// Set a maximum size.
app_window->SetContentSizeConstraints(gfx::Size(), gfx::Size(200, 201));
EXPECT_EQ(200, [ns_window contentMaxSize].width);
EXPECT_EQ(201, [ns_window contentMaxSize].height);
NSView* web_contents =
app_window->web_contents()->GetNativeView().GetNativeNSView();
EXPECT_EQ(200, [web_contents frame].size.width);
EXPECT_EQ(201, [web_contents frame].size.height);
// Still resizable.
EXPECT_TRUE([ns_window styleMask] & NSResizableWindowMask);
// Fullscreen and maximize are disabled.
EXPECT_FALSE([ns_window collectionBehavior] &
NSWindowCollectionBehaviorFullScreenPrimary);
EXPECT_FALSE([[ns_window standardWindowButton:NSWindowZoomButton] isEnabled]);
// Set a minimum size equal to the maximum size.
app_window->SetContentSizeConstraints(gfx::Size(200, 201),
gfx::Size(200, 201));
EXPECT_EQ(200, [ns_window contentMinSize].width);
EXPECT_EQ(201, [ns_window contentMinSize].height);
// No longer resizable.
EXPECT_FALSE([ns_window styleMask] & NSResizableWindowMask);
// If a window is made fullscreen by the API, fullscreen should be enabled so
// the user can exit fullscreen.
base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter(
[[NSWindowFullscreenNotificationWaiter alloc] initWithWindow:ns_window]);
app_window->SetFullscreen(AppWindow::FULLSCREEN_TYPE_WINDOW_API, true);
[waiter waitForEnterCount:1 exitCount:0];
EXPECT_TRUE([ns_window collectionBehavior] &
NSWindowCollectionBehaviorFullScreenPrimary);
EXPECT_EQ(NSWidth([[ns_window contentView] frame]),
NSWidth([ns_window frame]));
// Once it leaves fullscreen, it is disabled again.
app_window->SetFullscreen(AppWindow::FULLSCREEN_TYPE_WINDOW_API, false);
[waiter waitForEnterCount:1 exitCount:1];
EXPECT_FALSE([ns_window collectionBehavior] &
NSWindowCollectionBehaviorFullScreenPrimary);
}
} // namespace
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Controls) {
TestControls(CreateTestAppWindow("{}"));
}
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, ControlsFrameless) {
TestControls(CreateTestAppWindow("{\"frame\": \"none\"}"));
}
namespace {
// Convert a color constant to an NSColor that can be compared with |bitmap|.
NSColor* ColorInBitmapColorSpace(SkColor color, NSBitmapImageRep* bitmap) {
return [skia::SkColorToSRGBNSColor(color)
colorUsingColorSpace:[bitmap colorSpace]];
}
// Take a screenshot of the window, including its native frame.
NSBitmapImageRep* ScreenshotNSWindow(NSWindow* window) {
NSView* frame_view = [[window contentView] superview];
NSRect bounds = [frame_view bounds];
NSBitmapImageRep* bitmap =
[frame_view bitmapImageRepForCachingDisplayInRect:bounds];
[frame_view cacheDisplayInRect:bounds toBitmapImageRep:bitmap];
return bitmap;
}
} // namespace
// Test that the colored frames have the correct color when active and inactive.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, FrameColor) {
// The hex values indicate an RGB color. When we get the NSColor later, the
// components are CGFloats in the range [0, 1].
extensions::AppWindow* app_window = CreateTestAppWindow(
"{\"frame\": {\"color\": \"#FF0000\", \"inactiveColor\": \"#0000FF\"}}");
NSWindow* ns_window = app_window->GetNativeWindow().GetNativeNSWindow();
// No color correction in the default case.
[ns_window setColorSpace:[NSColorSpace sRGBColorSpace]];
int half_width = NSWidth([ns_window frame]) / 2;
NSBitmapImageRep* bitmap = ScreenshotNSWindow(ns_window);
// The window is currently inactive so it should be blue (#0000FF).
NSColor* expected_color = ColorInBitmapColorSpace(0xFF0000FF, bitmap);
NSColor* color = [bitmap colorAtX:half_width y:5];
CGFloat expected_components[4], color_components[4];
[expected_color getComponents:expected_components];
[color getComponents:color_components];
EXPECT_NEAR(expected_components[0], color_components[0], 0.01);
EXPECT_NEAR(expected_components[1], color_components[1], 0.01);
EXPECT_NEAR(expected_components[2], color_components[2], 0.01);
ui::test::ScopedFakeNSWindowFocus fake_focus;
[ns_window makeMainWindow];
bitmap = ScreenshotNSWindow(ns_window);
// The window is now active so it should be red (#FF0000).
expected_color = ColorInBitmapColorSpace(0xFFFF0000, bitmap);
color = [bitmap colorAtX:half_width y:5];
[expected_color getComponents:expected_components];
[color getComponents:color_components];
EXPECT_NEAR(expected_components[0], color_components[0], 0.01);
EXPECT_NEAR(expected_components[1], color_components[1], 0.01);
EXPECT_NEAR(expected_components[2], color_components[2], 0.01);
}