| Index: ui/views/cocoa/bridged_native_widget_unittest.mm
|
| diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm
|
| index 14a7f9c3c0bfc17fe35a420705f2ce6209b2426a..7c24cf7b4bba00c60ac619e56580433a3e28dc1b 100644
|
| --- a/ui/views/cocoa/bridged_native_widget_unittest.mm
|
| +++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
|
| @@ -6,7 +6,10 @@
|
|
|
| #import <Cocoa/Cocoa.h>
|
|
|
| +#import "base/mac/sdk_forward_declarations.h"
|
| #include "base/memory/scoped_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| #include "base/strings/sys_string_conversions.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| #import "testing/gtest_mac.h"
|
| @@ -37,6 +40,91 @@ NSRange EmptyRange() {
|
|
|
| } // namespace
|
|
|
| +@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 {
|
| namespace test {
|
|
|
| @@ -148,6 +236,9 @@ void BridgedNativeWidgetTest::SetUp() {
|
| view_.reset(new views::View);
|
| base::scoped_nsobject<NSWindow> window([test_window() retain]);
|
|
|
| + // BridgedNativeWidget expects to be initialized with a hidden (deferred)
|
| + // window.
|
| + [window orderOut:nil];
|
| EXPECT_FALSE([window delegate]);
|
| bridge()->Init(window, Widget::InitParams());
|
|
|
| @@ -156,6 +247,8 @@ void BridgedNativeWidgetTest::SetUp() {
|
| bridge()->SetRootView(view_.get());
|
| ns_view_ = bridge()->ns_view();
|
|
|
| + // Pretend it has been shown via NativeWidgetMac::Show().
|
| + [window orderFront:nil];
|
| [test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()];
|
| }
|
|
|
| @@ -246,7 +339,7 @@ TEST_F(BridgedNativeWidgetInitTest, ParentWindowNotNativeWidgetMac) {
|
| [[NSWindow alloc] initWithContentRect:NSMakeRect(50, 50, 400, 300)
|
| styleMask:NSBorderlessWindowMask
|
| backing:NSBackingStoreBuffered
|
| - defer:NO]);
|
| + defer:YES]);
|
| [child_window setReleasedWhenClosed:NO]; // Owned by scoped_nsobject.
|
|
|
| EXPECT_FALSE([child_window parentWindow]);
|
| @@ -415,5 +508,92 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteForward) {
|
| EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]);
|
| }
|
|
|
| +// Tests for correct fullscreen tracking, regardless of whether it is initiated
|
| +// by the Widget code or elsewhere (e.g. by the user).
|
| +TEST_F(BridgedNativeWidgetTest, FullscreenSynchronousState) {
|
| + EXPECT_FALSE(widget_->IsFullscreen());
|
| + // Allow user-initiated fullscreen changes on the Window.
|
| + [test_window()
|
| + setCollectionBehavior:[test_window() collectionBehavior] |
|
| + NSWindowCollectionBehaviorFullScreenPrimary];
|
| +
|
| + base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
|
| + [[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
|
| + const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
|
| +
|
| + // Simualate a user-initiated fullscreen. Note trying to to this again before
|
| + // spinning a runloop will cause Cocoa to emit text to stdio and ignore it.
|
| + [test_window() toggleFullScreen:nil];
|
| + EXPECT_TRUE(widget_->IsFullscreen());
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +
|
| + // Note there's now an animation running. While that's happening, toggling the
|
| + // state should work as expected, but do "nothing".
|
| + widget_->SetFullscreen(false);
|
| + EXPECT_FALSE(widget_->IsFullscreen());
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| + widget_->SetFullscreen(false); // Same request - should no-op.
|
| + EXPECT_FALSE(widget_->IsFullscreen());
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +
|
| + widget_->SetFullscreen(true);
|
| + EXPECT_TRUE(widget_->IsFullscreen());
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +
|
| + // Always finish out of fullscreen. Otherwise there are 4 NSWindow objects
|
| + // that Cocoa creates which don't close themselves and will be seen by the Mac
|
| + // test harness on teardown. Note that the test harness will be waiting until
|
| + // all animations complete, since these temporary animation windows will not
|
| + // be removed from the window list until they do.
|
| + widget_->SetFullscreen(false);
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +
|
| + // Now we must wait for the notifications. Since, if the widget is torn down,
|
| + // the NSWindowDelegate is removed, and the pending request to take out of
|
| + // fullscreen is lost. Since a message loop has not yet spun up in this test
|
| + // we can reliably say there will be one enter and one exit, despite all the
|
| + // toggling above.
|
| + base::MessageLoopForUI message_loop;
|
| + [waiter waitForEnterCount:1 exitCount:1];
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +}
|
| +
|
| +// Test fullscreen without overlapping calls and without changing collection
|
| +// behaviour on the test window.
|
| +TEST_F(BridgedNativeWidgetTest, FullscreenEnterAndExit) {
|
| + base::MessageLoopForUI message_loop;
|
| + base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
|
| + [[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
|
| +
|
| + EXPECT_FALSE(widget_->IsFullscreen());
|
| + const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
|
| + EXPECT_FALSE(restored_bounds.IsEmpty());
|
| +
|
| + // Ensure this works without having to change collection behavior as for the
|
| + // test above.
|
| + widget_->SetFullscreen(true);
|
| + EXPECT_TRUE(widget_->IsFullscreen());
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +
|
| + // Should be zero until the runloop spins.
|
| + EXPECT_EQ(0, [waiter enterCount]);
|
| + [waiter waitForEnterCount:1 exitCount:0];
|
| +
|
| + // Verify it hasn't exceeded.
|
| + EXPECT_EQ(1, [waiter enterCount]);
|
| + EXPECT_EQ(0, [waiter exitCount]);
|
| + EXPECT_TRUE(widget_->IsFullscreen());
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +
|
| + widget_->SetFullscreen(false);
|
| + EXPECT_FALSE(widget_->IsFullscreen());
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +
|
| + [waiter waitForEnterCount:1 exitCount:1];
|
| + EXPECT_EQ(1, [waiter enterCount]);
|
| + EXPECT_EQ(1, [waiter exitCount]);
|
| + EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
|
| +}
|
| +
|
| } // namespace test
|
| } // namespace views
|
|
|