Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / base / test / scoped_fake_nswindow_fullscreen.mm
blob943e97b469d2f70ca5abb9907ea9dddefe95f0de
1 // Copyright 2015 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 #import "ui/base/test/scoped_fake_nswindow_fullscreen.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/bind.h"
10 #import "base/mac/foundation_util.h"
11 #import "base/mac/mac_util.h"
12 #import "base/mac/scoped_nsobject.h"
13 #import "base/mac/scoped_objc_class_swizzler.h"
14 #import "base/mac/sdk_forward_declarations.h"
15 #include "base/message_loop/message_loop.h"
17 // This method exists on NSWindowDelegate on 10.7+.
18 // To build on 10.6, we just need to declare it somewhere. We'll test
19 // -[NSObject respondsToSelector] before calling it.
20 #if !defined(MAC_OS_X_VERSION_10_7) || \
21     MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
22 @protocol NSWindowDelegateLion
23 - (NSSize)window:(NSWindow*)window
24     willUseFullScreenContentSize:(NSSize)proposedSize;
25 @end
26 #endif
28 // Donates a testing implementation of [NSWindow toggleFullScreen:].
29 @interface ToggleFullscreenDonorForWindow : NSObject
30 @end
32 namespace {
34 ui::test::ScopedFakeNSWindowFullscreen::Impl* g_fake_fullscreen_impl = nullptr;
36 }  // namespace
38 namespace ui {
39 namespace test {
41 class ScopedFakeNSWindowFullscreen::Impl {
42  public:
43   Impl()
44       : toggle_fullscreen_swizzler_([NSWindow class],
45                                     [ToggleFullscreenDonorForWindow class],
46                                     @selector(toggleFullScreen:)) {}
48   ~Impl() {
49     // If there's a pending transition, it means there's a task in the queue to
50     // complete it, referencing |this|.
51     DCHECK(!is_in_transition_);
52   }
54   void ToggleFullscreenForWindow(NSWindow* window) {
55     DCHECK(!is_in_transition_);
56     if (window_ == nil) {
57       StartEnterFullscreen(window);
58     } else if (window_ == window) {
59       StartExitFullscreen();
60     } else {
61       // Another window is fullscreen.
62       NOTREACHED();
63     }
64   }
66   void StartEnterFullscreen(NSWindow* window) {
67     // If the window cannot go fullscreen, do nothing.
68     if (!([window collectionBehavior] &
69           NSWindowCollectionBehaviorFullScreenPrimary)) {
70       return;
71     }
73     // This cannot be id<NSWindowDelegate> because on 10.6 it won't have
74     // window:willUseFullScreenContentSize:.
75     id delegate = [window delegate];
77     // Nothing is currently fullscreen. Make this window fullscreen.
78     window_ = window;
79     is_in_transition_ = true;
80     frame_before_fullscreen_ = [window frame];
81     NSSize fullscreen_content_size =
82         [window contentRectForFrameRect:[[window screen] frame]].size;
83     if ([delegate respondsToSelector:@selector(window:
84                                          willUseFullScreenContentSize:)]) {
85       fullscreen_content_size = [delegate window:window
86                     willUseFullScreenContentSize:fullscreen_content_size];
87     }
88     [[NSNotificationCenter defaultCenter]
89         postNotificationName:NSWindowWillEnterFullScreenNotification
90                       object:window];
91     base::MessageLoopForUI::current()->PostTask(
92         FROM_HERE, base::Bind(&Impl::FinishEnterFullscreen,
93                               base::Unretained(this), fullscreen_content_size));
94   }
96   void FinishEnterFullscreen(NSSize fullscreen_content_size) {
97     // The frame should not have changed during the transition.
98     DCHECK(NSEqualRects(frame_before_fullscreen_, [window_ frame]));
99     // Style mask must be set first because -[NSWindow frame] may be different
100     // depending on NSFullScreenWindowMask.
101     [window_ setStyleMask:[window_ styleMask] | NSFullScreenWindowMask];
102     // The origin doesn't matter, NSFullScreenWindowMask means the origin will
103     // be adjusted.
104     NSRect target_fullscreen_frame = [window_
105         frameRectForContentRect:NSMakeRect(0, 0, fullscreen_content_size.width,
106                                            fullscreen_content_size.height)];
107     [window_ setFrame:target_fullscreen_frame display:YES animate:NO];
108     [[NSNotificationCenter defaultCenter]
109         postNotificationName:NSWindowDidEnterFullScreenNotification
110                       object:window_];
111     // Store the actual frame because we check against it when exiting.
112     frame_during_fullscreen_ = [window_ frame];
113     is_in_transition_ = false;
114   }
116   void StartExitFullscreen() {
117     is_in_transition_ = true;
118     [[NSNotificationCenter defaultCenter]
119         postNotificationName:NSWindowWillExitFullScreenNotification
120                       object:window_];
122     base::MessageLoopForUI::current()->PostTask(
123         FROM_HERE,
124         base::Bind(&Impl::FinishExitFullscreen, base::Unretained(this)));
125   }
127   void FinishExitFullscreen() {
128     // The bounds may have changed during the transition. Check for this before
129     // setting the style mask because -[NSWindow frame] may be different
130     // depending on NSFullScreenWindowMask.
131     bool no_frame_change_during_fullscreen =
132         NSEqualRects(frame_during_fullscreen_, [window_ frame]);
133     [window_ setStyleMask:[window_ styleMask] & ~NSFullScreenWindowMask];
134     // Set the original frame after setting the style mask.
135     if (no_frame_change_during_fullscreen)
136       [window_ setFrame:frame_before_fullscreen_ display:YES animate:NO];
137     [[NSNotificationCenter defaultCenter]
138         postNotificationName:NSWindowDidExitFullScreenNotification
139                       object:window_];
140     window_ = nil;
141     is_in_transition_ = false;
142   }
144  private:
145   base::mac::ScopedObjCClassSwizzler toggle_fullscreen_swizzler_;
147   // The currently fullscreen window.
148   NSWindow* window_ = nil;
149   NSRect frame_before_fullscreen_;
150   NSRect frame_during_fullscreen_;
151   bool is_in_transition_ = false;
153   DISALLOW_COPY_AND_ASSIGN(Impl);
156 ScopedFakeNSWindowFullscreen::ScopedFakeNSWindowFullscreen() {
157   // -[NSWindow toggleFullScreen:] does not exist on 10.6, so do nothing.
158   if (base::mac::IsOSSnowLeopard())
159     return;
161   DCHECK(!g_fake_fullscreen_impl);
162   impl_.reset(new Impl);
163   g_fake_fullscreen_impl = impl_.get();
166 ScopedFakeNSWindowFullscreen::~ScopedFakeNSWindowFullscreen() {
167   g_fake_fullscreen_impl = nullptr;
170 }  // namespace test
171 }  // namespace ui
173 @implementation ToggleFullscreenDonorForWindow
175 - (void)toggleFullScreen:(id)sender {
176   NSWindow* window = base::mac::ObjCCastStrict<NSWindow>(self);
177   g_fake_fullscreen_impl->ToggleFullscreenForWindow(window);
180 @end