Commit 72ff2bb4 authored by jackhou's avatar jackhou Committed by Commit bot

[MacViews] Fix behavior of non-resizable windows in fullscreen.

They should take up the entire screen, and the fullscreen button must
be enabled to allow the user to leave.

This also factors out NSWindowFullscreenNotificationWaiter.

BUG=459877

Review URL: https://codereview.chromium.org/1109493002

Cr-Commit-Position: refs/heads/master@{#330704}
parent d0b9cf4b
......@@ -21,7 +21,9 @@
#include "content/public/test/test_utils.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/common/constants.h"
#import "ui/base/test/nswindow_fullscreen_notification_waiter.h"
using extensions::AppWindow;
using extensions::PlatformAppBrowserTest;
namespace {
......@@ -60,11 +62,11 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
extensions::AppWindowRegistry::AppWindowList windows =
extensions::AppWindowRegistry::Get(profile())->app_windows();
extensions::AppWindow* app_window = windows.front();
AppWindow* app_window = windows.front();
extensions::NativeAppWindow* native_window = app_window->GetBaseWindow();
NSWindow* ns_window = native_window->GetNativeWindow();
extensions::AppWindow* other_app_window = windows.back();
AppWindow* other_app_window = windows.back();
extensions::NativeAppWindow* other_native_window =
other_app_window->GetBaseWindow();
NSWindow* other_ns_window = other_native_window->GetNativeWindow();
......@@ -72,7 +74,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
// Normal Hide/Show.
app_window->Hide();
EXPECT_FALSE([ns_window isVisible]);
app_window->Show(extensions::AppWindow::SHOW_ACTIVE);
app_window->Show(AppWindow::SHOW_ACTIVE);
EXPECT_TRUE([ns_window isVisible]);
// Normal Hide/ShowWithApp.
......@@ -93,7 +95,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
EXPECT_FALSE([ns_window isVisible]);
// Return to shown state.
app_window->Show(extensions::AppWindow::SHOW_ACTIVE);
app_window->Show(AppWindow::SHOW_ACTIVE);
EXPECT_TRUE([ns_window isVisible]);
// HideWithApp the other window.
......@@ -104,7 +106,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
// HideWithApp, Show shows all windows for this app.
native_window->HideWithApp();
EXPECT_FALSE([ns_window isVisible]);
app_window->Show(extensions::AppWindow::SHOW_ACTIVE);
app_window->Show(AppWindow::SHOW_ACTIVE);
EXPECT_TRUE([ns_window isVisible]);
EXPECT_TRUE([other_ns_window isVisible]);
......@@ -163,12 +165,12 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Fullscreen) {
return;
SetUpAppWithWindows(1);
extensions::AppWindow* app_window = GetFirstAppWindow();
AppWindow* app_window = GetFirstAppWindow();
extensions::NativeAppWindow* window = app_window->GetBaseWindow();
NSWindow* ns_window = app_window->GetNativeWindow();
base::scoped_nsobject<ScopedNotificationWatcher> watcher;
EXPECT_EQ(extensions::AppWindow::FULLSCREEN_TYPE_NONE,
EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
app_window->fullscreen_types_for_test());
EXPECT_FALSE(window->IsFullscreen());
EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
......@@ -179,7 +181,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Fullscreen) {
[ns_window toggleFullScreen:nil];
[watcher waitForNotification];
EXPECT_TRUE(app_window->fullscreen_types_for_test() &
extensions::AppWindow::FULLSCREEN_TYPE_OS);
AppWindow::FULLSCREEN_TYPE_OS);
EXPECT_TRUE(window->IsFullscreen());
EXPECT_TRUE([ns_window styleMask] & NSFullScreenWindowMask);
......@@ -189,7 +191,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Fullscreen) {
app_window->Restore();
EXPECT_FALSE(window->IsFullscreenOrPending());
[watcher waitForNotification];
EXPECT_EQ(extensions::AppWindow::FULLSCREEN_TYPE_NONE,
EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
app_window->fullscreen_types_for_test());
EXPECT_FALSE(window->IsFullscreen());
EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
......@@ -201,7 +203,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Fullscreen) {
EXPECT_TRUE(window->IsFullscreenOrPending());
[watcher waitForNotification];
EXPECT_TRUE(app_window->fullscreen_types_for_test() &
extensions::AppWindow::FULLSCREEN_TYPE_WINDOW_API);
AppWindow::FULLSCREEN_TYPE_WINDOW_API);
EXPECT_TRUE(window->IsFullscreen());
EXPECT_TRUE([ns_window styleMask] & NSFullScreenWindowMask);
......@@ -210,7 +212,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Fullscreen) {
andObject:ns_window]);
[ns_window toggleFullScreen:nil];
[watcher waitForNotification];
EXPECT_EQ(extensions::AppWindow::FULLSCREEN_TYPE_NONE,
EXPECT_EQ(AppWindow::FULLSCREEN_TYPE_NONE,
app_window->fullscreen_types_for_test());
EXPECT_FALSE(window->IsFullscreen());
EXPECT_FALSE([ns_window styleMask] & NSFullScreenWindowMask);
......@@ -219,8 +221,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Fullscreen) {
// Test that, in frameless windows, the web contents has the same size as the
// window.
IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Frameless) {
extensions::AppWindow* app_window =
CreateTestAppWindow("{\"frame\": \"none\"}");
AppWindow* app_window = CreateTestAppWindow("{\"frame\": \"none\"}");
NSWindow* ns_window = app_window->GetNativeWindow();
NSView* web_contents = app_window->web_contents()->GetNativeView();
EXPECT_TRUE(NSEqualSizes(NSMakeSize(512, 384), [web_contents frame].size));
......@@ -246,7 +247,7 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, Frameless) {
namespace {
// Test that resize and fullscreen controls are correctly enabled/disabled.
void TestControls(extensions::AppWindow* app_window) {
void TestControls(AppWindow* app_window) {
NSWindow* ns_window = app_window->GetNativeWindow();
// The window is resizable.
......@@ -295,6 +296,24 @@ void TestControls(extensions::AppWindow* app_window) {
EXPECT_FALSE([ns_window styleMask] & NSResizableWindowMask);
if (base::mac::IsOSSnowLeopard())
EXPECT_FALSE([ns_window showsResizeIndicator]);
// If a window is made fullscreen by the API, fullscreen should be enabled so
// the user can exit fullscreen.
if (base::mac::IsOSLionOrLater()) {
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
......
......@@ -667,6 +667,7 @@ if (!is_android) {
"//third_party/safe_browsing:test_support",
"//third_party/widevine/cdm:version_h",
"//ui/accessibility:test_support",
"//ui/base:test_support",
"//ui/compositor:test_support",
"//ui/resources",
"//ui/web_dialogs:test_support",
......
......@@ -563,6 +563,8 @@ component("base") {
source_set("test_support") {
testonly = true
sources = [
"test/nswindow_fullscreen_notification_waiter.h",
"test/nswindow_fullscreen_notification_waiter.mm",
"test/test_clipboard.cc",
"test/test_clipboard.h",
"test/ui_controls.h",
......
// Copyright 2015 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.
#ifndef UI_BASE_TEST_NSWINDOW_FULLSCREEN_NOTIFICATION_WAITER_H_
#define UI_BASE_TEST_NSWINDOW_FULLSCREEN_NOTIFICATION_WAITER_H_
#import <Cocoa/Cocoa.h>
#import "base/mac/scoped_nsobject.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
// Waits for fullscreen transitions to complete.
@interface NSWindowFullscreenNotificationWaiter : NSObject {
@private
scoped_ptr<base::RunLoop> runLoop_;
base::scoped_nsobject<NSWindow> window_;
int enterCount_;
int exitCount_;
int targetEnterCount_;
int targetExitCount_;
}
@property(readonly, nonatomic) int enterCount;
@property(readonly, nonatomic) int exitCount;
// Initialize for the given window and start tracking notifications.
- (id)initWithWindow:(NSWindow*)window;
// Keep spinning a run loop until the enter and exit counts match.
- (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount;
@end
#endif // UI_BASE_TEST_NSWINDOW_FULLSCREEN_NOTIFICATION_WAITER_H_
// Copyright 2015 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.
#import "ui/base/test/nswindow_fullscreen_notification_waiter.h"
#import "base/mac/sdk_forward_declarations.h"
@interface NSWindowFullscreenNotificationWaiter ()
// Exit the RunLoop if there is one and the counts being tracked match.
- (void)maybeQuitForChangedArg:(int*)changedArg;
- (void)onEnter:(NSNotification*)notification;
- (void)onExit:(NSNotification*)notification;
@end
@implementation NSWindowFullscreenNotificationWaiter
@synthesize enterCount = enterCount_;
@synthesize exitCount = exitCount_;
- (id)initWithWindow:(NSWindow*)window {
if ((self = [super init])) {
window_.reset([window retain]);
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self
selector:@selector(onEnter:)
name:NSWindowDidEnterFullScreenNotification
object:window];
[defaultCenter addObserver:self
selector:@selector(onExit:)
name:NSWindowDidExitFullScreenNotification
object:window];
}
return self;
}
- (void)dealloc {
DCHECK(!runLoop_);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount {
if (enterCount_ >= enterCount && exitCount_ >= exitCount)
return;
targetEnterCount_ = enterCount;
targetExitCount_ = exitCount;
runLoop_.reset(new base::RunLoop);
runLoop_->Run();
runLoop_.reset();
}
- (void)maybeQuitForChangedArg:(int*)changedArg {
++*changedArg;
if (!runLoop_)
return;
if (enterCount_ >= targetEnterCount_ && exitCount_ >= targetExitCount_)
runLoop_->Quit();
}
- (void)onEnter:(NSNotification*)notification {
[self maybeQuitForChangedArg:&enterCount_];
}
- (void)onExit:(NSNotification*)notification {
[self maybeQuitForChangedArg:&exitCount_];
}
@end
......@@ -669,6 +669,8 @@
'ime/dummy_input_method.h',
'ime/dummy_text_input_client.cc',
'ime/dummy_text_input_client.h',
'test/nswindow_fullscreen_notification_waiter.h',
'test/nswindow_fullscreen_notification_waiter.mm',
],
}],
['use_aura==1', {
......
......@@ -284,6 +284,7 @@ if (is_mac) {
"//base/test:test_support",
"//skia",
"//testing/gtest",
"//ui/base:test_support",
"//ui/compositor",
"//ui/resources",
"//ui/resources:ui_test_pak",
......
......@@ -394,8 +394,12 @@ void BridgedNativeWidget::OnFullscreenTransitionStart(
void BridgedNativeWidget::OnFullscreenTransitionComplete(
bool actual_fullscreen_state) {
in_fullscreen_transition_ = false;
if (target_fullscreen_state_ == actual_fullscreen_state)
if (target_fullscreen_state_ == actual_fullscreen_state) {
// Ensure constraints are re-applied when completing a transition.
OnSizeConstraintsChanged();
return;
}
// First update to reflect reality so that OnTargetFullscreenStateChanged()
// expects the change.
......@@ -438,13 +442,13 @@ void BridgedNativeWidget::ToggleDesiredFullscreenState() {
return; // TODO(tapted): Implement this for Snow Leopard.
}
// Since fullscreen requests are ignored if the collection behavior does not
// allow it, save the collection behavior and restore it after.
NSWindowCollectionBehavior behavior = [window_ collectionBehavior];
[window_ setCollectionBehavior:behavior |
NSWindowCollectionBehaviorFullScreenPrimary];
// Enable fullscreen collection behavior because:
// 1: -[NSWindow toggleFullscreen:] would otherwise be ignored,
// 2: the fullscreen button must be enabled so the user can leave fullscreen.
// This will be reset when a transition out of fullscreen completes.
gfx::SetNSWindowCanFullscreen(window_, true);
[window_ toggleFullScreen:nil];
[window_ setCollectionBehavior:behavior];
}
void BridgedNativeWidget::OnSizeChanged() {
......@@ -529,7 +533,12 @@ void BridgedNativeWidget::OnWindowKeyStatusChangedTo(bool is_key) {
}
void BridgedNativeWidget::OnSizeConstraintsChanged() {
NSWindow* window = ns_window();
// Don't modify the size constraints or fullscreen collection behavior while
// in fullscreen or during a transition. OnFullscreenTransitionComplete will
// reset these after leaving fullscreen.
if (target_fullscreen_state_ || in_fullscreen_transition_)
return;
Widget* widget = native_widget_mac()->GetWidget();
gfx::Size min_size = widget->GetMinimumSize();
gfx::Size max_size = widget->GetMaximumSize();
......@@ -539,7 +548,7 @@ void BridgedNativeWidget::OnSizeConstraintsChanged() {
bool shows_fullscreen_controls =
is_resizable && widget->widget_delegate()->CanMaximize();
gfx::ApplyNSWindowSizeConstraints(window, min_size, max_size,
gfx::ApplyNSWindowSizeConstraints(window_, min_size, max_size,
shows_resize_controls,
shows_fullscreen_controls);
}
......
......@@ -8,94 +8,9 @@
#import "base/mac/mac_util.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/run_loop.h"
#import "ui/base/test/nswindow_fullscreen_notification_waiter.h"
#include "ui/views/test/widget_test.h"
@interface NativeWidgetMacNotificationWaiter : NSObject {
@private
scoped_ptr<base::RunLoop> runLoop_;
base::scoped_nsobject<NSWindow> window_;
int enterCount_;
int exitCount_;
int targetEnterCount_;
int targetExitCount_;
}
@property(readonly, nonatomic) int enterCount;
@property(readonly, nonatomic) int exitCount;
// Initialize for the given window and start tracking notifications.
- (id)initWithWindow:(NSWindow*)window;
// Keep spinning a run loop until the enter and exit counts match.
- (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount;
// private:
// Exit the RunLoop if there is one and the counts being tracked match.
- (void)maybeQuitForChangedArg:(int*)changedArg;
- (void)onEnter:(NSNotification*)notification;
- (void)onExit:(NSNotification*)notification;
@end
@implementation NativeWidgetMacNotificationWaiter
@synthesize enterCount = enterCount_;
@synthesize exitCount = exitCount_;
- (id)initWithWindow:(NSWindow*)window {
if ((self = [super init])) {
window_.reset([window retain]);
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self
selector:@selector(onEnter:)
name:NSWindowDidEnterFullScreenNotification
object:window];
[defaultCenter addObserver:self
selector:@selector(onExit:)
name:NSWindowDidExitFullScreenNotification
object:window];
}
return self;
}
- (void)dealloc {
DCHECK(!runLoop_);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount {
if (enterCount_ >= enterCount && exitCount_ >= exitCount)
return;
targetEnterCount_ = enterCount;
targetExitCount_ = exitCount;
runLoop_.reset(new base::RunLoop);
runLoop_->Run();
runLoop_.reset();
}
- (void)maybeQuitForChangedArg:(int*)changedArg {
++*changedArg;
if (!runLoop_)
return;
if (enterCount_ >= targetEnterCount_ && exitCount_ >= targetExitCount_)
runLoop_->Quit();
}
- (void)onEnter:(NSNotification*)notification {
[self maybeQuitForChangedArg:&enterCount_];
}
- (void)onExit:(NSNotification*)notification {
[self maybeQuitForChangedArg:&exitCount_];
}
@end
namespace views {
class BridgedNativeWidgetUITest : public test::WidgetTest {
......@@ -139,8 +54,9 @@ TEST_F(BridgedNativeWidgetUITest, FullscreenSynchronousState) {
setCollectionBehavior:[test_window() collectionBehavior] |
NSWindowCollectionBehaviorFullScreenPrimary];
base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
[[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter(
[[NSWindowFullscreenNotificationWaiter alloc]
initWithWindow:test_window()]);
const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
// First show the widget. A user shouldn't be able to initiate fullscreen
......@@ -186,8 +102,9 @@ TEST_F(BridgedNativeWidgetUITest, FullscreenSynchronousState) {
// Test fullscreen without overlapping calls and without changing collection
// behavior on the test window.
TEST_F(BridgedNativeWidgetUITest, FullscreenEnterAndExit) {
base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
[[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
base::scoped_nsobject<NSWindowFullscreenNotificationWaiter> waiter(
[[NSWindowFullscreenNotificationWaiter alloc]
initWithWindow:test_window()]);
EXPECT_FALSE(widget_->IsFullscreen());
const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
......
......@@ -116,4 +116,14 @@
parent_->OnFullscreenTransitionComplete(false);
}
// Allow non-resizable windows (without NSResizableWindowMask) to fill the
// screen in fullscreen mode. This only happens when
// -[NSWindow toggleFullscreen:] is called since non-resizable windows have no
// fullscreen button. Without this they would only enter fullscreen at their
// current size.
- (NSSize)window:(NSWindow*)window
willUseFullScreenContentSize:(NSSize)proposedSize {
return proposedSize;
}
@end
......@@ -911,6 +911,7 @@
'../../base/base.gyp:test_support_base',
'../../skia/skia.gyp:skia',
'../../testing/gtest.gyp:gtest',
'../base/ui_base.gyp:ui_base_test_support',
'../compositor/compositor.gyp:compositor_test_support',
'../resources/ui_resources.gyp:ui_resources',
'../resources/ui_resources.gyp:ui_test_pak',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment