1 // Copyright (c) 2012 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 "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
7 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
8 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
9 #import "testing/gtest_mac.h"
13 const int kSystemSheetReturnCode = 77;
17 @interface ConstrainedWindowSystemSheetTest
18 : NSObject <ConstrainedWindowSheet> {
20 NSAlert* alert_; // weak
23 @property(nonatomic, readonly) int returnCode;
24 @property(nonatomic, assign) NSAlert* alert;
28 @implementation ConstrainedWindowSystemSheetTest
30 @synthesize returnCode = returnCode_;
31 @synthesize alert = alert_;
33 - (void)showSheetForWindow:(NSWindow*)window {
34 [alert_ beginSheetModalForWindow:window
36 didEndSelector:@selector(alertDidEnd:returnCode:ctxInfo:)
40 - (void)closeSheetWithAnimation:(BOOL)withAnimation {
41 [NSApp endSheet:[alert_ window] returnCode:kSystemSheetReturnCode];
53 - (void)makeSheetKeyAndOrderFront {
56 - (void)updateSheetPosition {
59 - (void)alertDidEnd:(NSAlert *)alert
60 returnCode:(NSInteger)returnCode
61 ctxInfo:(void *)contextInfo {
62 returnCode_ = returnCode;
67 class ConstrainedWindowSheetControllerTest : public CocoaTest {
69 virtual ~ConstrainedWindowSheetControllerTest() {
72 virtual void SetUp() override {
75 // Center the window so that the sheet doesn't go offscreen.
76 [test_window() center];
78 // The real view setup is quite a few levels deep; recreate something
80 NSRect dummy_rect = NSMakeRect(0, 0, 50, 50);
81 tab_view_parent_ = [test_window() contentView];
82 for (int i = 0; i < 3; ++i) {
83 base::scoped_nsobject<NSView> new_view(
84 [[NSView alloc] initWithFrame:dummy_rect]);
85 [tab_view_parent_ addSubview:new_view.get()];
86 tab_view_parent_ = new_view.get();
89 // Create two dummy tabs and make the first one active.
90 tab_views_.reset([[NSMutableArray alloc] init]);
91 for (int i = 0; i < 2; ++i) {
92 base::scoped_nsobject<NSView> view(
93 [[NSView alloc] initWithFrame:dummy_rect]);
94 [tab_views_ addObject:view];
96 tab0_ = [tab_views_ objectAtIndex:0];
97 tab1_ = [tab_views_ objectAtIndex:1];
98 ActivateTabView(tab0_);
100 // Create a test sheet.
101 sheet_window_.reset([[NSWindow alloc]
102 initWithContentRect:dummy_rect
103 styleMask:NSTitledWindowMask
104 backing:NSBackingStoreBuffered
106 [sheet_window_ setReleasedWhenClosed:NO];
107 sheet_.reset([[CustomConstrainedWindowSheet alloc]
108 initWithCustomWindow:sheet_window_]);
110 controller_.reset([[ConstrainedWindowSheetController
111 controllerForParentWindow:test_window()] retain]);
112 EXPECT_TRUE(controller_);
113 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
116 virtual void TearDown() override {
118 sheet_window_.reset();
119 CocoaTest::TearDown();
122 void ActivateTabView(NSView* tab_view) {
123 for (NSView* view in tab_views_.get())
124 [view removeFromSuperview];
125 [tab_view_parent_ addSubview:tab_view];
126 active_tab_view_ = tab_view;
128 ConstrainedWindowSheetController* controller =
129 [ConstrainedWindowSheetController
130 controllerForParentWindow:test_window()];
131 EXPECT_TRUE(controller);
132 [controller parentViewDidBecomeActive:active_tab_view_];
135 NSRect GetViewFrameInScreenCoordinates(NSView* view) {
136 NSRect rect = [view convertRect:[view bounds] toView:nil];
137 rect.origin = [[view window] convertBaseToScreen:rect.origin];
141 void VerifySheetXPosition(NSRect sheet_frame, NSView* parent_view) {
142 NSRect parent_frame = GetViewFrameInScreenCoordinates(parent_view);
143 CGFloat expected_x = NSMinX(parent_frame) +
144 (NSWidth(parent_frame) - NSWidth(sheet_frame)) / 2.0;
145 EXPECT_EQ(expected_x, NSMinX(sheet_frame));
148 CGFloat GetSheetYOffset(NSRect sheet_frame, NSView* parent_view) {
149 return NSMaxY(sheet_frame) -
150 NSMaxY(GetViewFrameInScreenCoordinates(parent_view));
153 base::scoped_nsobject<NSWindow> sheet_window_;
154 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet_;
155 base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
156 base::scoped_nsobject<NSMutableArray> tab_views_;
157 NSView* tab_view_parent_;
158 NSView* active_tab_view_;
163 // Test showing then hiding the sheet.
164 TEST_F(ConstrainedWindowSheetControllerTest, ShowHide) {
165 EXPECT_FALSE([sheet_window_ isVisible]);
166 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
167 EXPECT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
168 EXPECT_TRUE([sheet_window_ isVisible]);
170 [controller_ closeSheet:sheet_];
171 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
172 EXPECT_FALSE([sheet_window_ isVisible]);
175 // Test that switching tabs correctly hides the inactive tab's sheet.
176 TEST_F(ConstrainedWindowSheetControllerTest, SwitchTabs) {
177 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
179 EXPECT_TRUE([sheet_window_ isVisible]);
180 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
181 ActivateTabView([tab_views_ objectAtIndex:1]);
182 EXPECT_TRUE([sheet_window_ isVisible]);
183 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
184 ActivateTabView([tab_views_ objectAtIndex:0]);
185 EXPECT_TRUE([sheet_window_ isVisible]);
186 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
189 // Test that adding a sheet to an inactive view doesn't show it.
190 TEST_F(ConstrainedWindowSheetControllerTest, AddToInactiveTab) {
191 ActivateTabView(tab0_);
192 [controller_ showSheet:sheet_ forParentView:tab1_];
193 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
195 ActivateTabView(tab1_);
196 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
197 VerifySheetXPosition([sheet_window_ frame], tab1_);
200 // Test that two parent windows with two sheet controllers don't conflict.
201 TEST_F(ConstrainedWindowSheetControllerTest, TwoParentWindows) {
202 base::scoped_nsobject<NSWindow> parent_window2(
203 [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 30, 30)
204 styleMask:NSTitledWindowMask
205 backing:NSBackingStoreBuffered
207 [parent_window2 setReleasedWhenClosed:NO];
209 ConstrainedWindowSheetController* controller2 =
210 [ConstrainedWindowSheetController
211 controllerForParentWindow:parent_window2];
212 EXPECT_TRUE(controller2);
213 EXPECT_NSNE(controller_, controller2);
215 [controller2 showSheet:sheet_ forParentView:[parent_window2 contentView]];
216 EXPECT_NSEQ(controller2,
217 [ConstrainedWindowSheetController controllerForSheet:sheet_]);
219 [parent_window2 close];
222 // Test that resizing sheet works.
223 TEST_F(ConstrainedWindowSheetControllerTest, Resize) {
224 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
226 NSRect old_frame = [sheet_window_ frame];
229 sheet_frame.size = NSMakeSize(NSWidth(old_frame) + 100,
230 NSHeight(old_frame) + 50);
231 sheet_frame.origin = [controller_ originForSheet:sheet_
232 withWindowSize:sheet_frame.size];
234 // Y pos should not have changed.
235 EXPECT_EQ(NSMaxY(sheet_frame), NSMaxY(old_frame));
237 // X pos should be centered on parent view.
238 VerifySheetXPosition(sheet_frame, active_tab_view_);
241 // Test that resizing a hidden sheet works.
242 TEST_F(ConstrainedWindowSheetControllerTest, ResizeHiddenSheet) {
243 [controller_ showSheet:sheet_ forParentView:tab0_];
244 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
245 ActivateTabView(tab1_);
246 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
248 NSRect old_frame = [sheet_window_ frame];
249 NSRect new_inactive_frame = NSInsetRect(old_frame, -30, -40);
250 [sheet_window_ setFrame:new_inactive_frame display:YES];
252 ActivateTabView(tab0_);
253 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
255 NSRect new_active_frame = [sheet_window_ frame];
256 EXPECT_EQ(NSWidth(new_inactive_frame), NSWidth(new_active_frame));
257 EXPECT_EQ(NSHeight(new_inactive_frame), NSHeight(new_active_frame));
260 // Test resizing parent window keeps the sheet anchored.
261 TEST_F(ConstrainedWindowSheetControllerTest, ResizeParentWindow) {
262 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
263 CGFloat sheet_offset =
264 GetSheetYOffset([sheet_window_ frame], active_tab_view_);
266 // Test 3x3 different parent window sizes.
267 CGFloat insets[] = {-10, 0, 10};
268 NSRect old_frame = [test_window() frame];
270 for (size_t x = 0; x < arraysize(insets); x++) {
271 for (size_t y = 0; y < arraysize(insets); y++) {
272 NSRect resized_frame = NSInsetRect(old_frame, insets[x], insets[y]);
273 [test_window() setFrame:resized_frame display:YES];
274 NSRect sheet_frame = [sheet_window_ frame];
276 // Y pos should track parent view's position.
277 EXPECT_EQ(sheet_offset, GetSheetYOffset(sheet_frame, active_tab_view_));
279 // X pos should be centered on parent view.
280 VerifySheetXPosition(sheet_frame, active_tab_view_);
285 // Test system sheets.
286 TEST_F(ConstrainedWindowSheetControllerTest, SystemSheet) {
287 base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
288 [[ConstrainedWindowSystemSheetTest alloc] init]);
289 base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
290 [system_sheet setAlert:alert];
292 EXPECT_FALSE([[alert window] isVisible]);
293 [controller_ showSheet:system_sheet forParentView:active_tab_view_];
294 EXPECT_TRUE([[alert window] isVisible]);
296 [controller_ closeSheet:system_sheet];
297 EXPECT_FALSE([[alert window] isVisible]);
298 EXPECT_EQ(kSystemSheetReturnCode, [system_sheet returnCode]);
301 // Test showing a system sheet on an inactive tab.
302 TEST_F(ConstrainedWindowSheetControllerTest, SystemSheetAddToInactiveTab) {
303 base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
304 [[ConstrainedWindowSystemSheetTest alloc] init]);
305 base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
306 [system_sheet setAlert:alert];
308 EXPECT_FALSE([[alert window] isVisible]);
309 [controller_ showSheet:system_sheet forParentView:tab1_];
310 EXPECT_FALSE([[alert window] isVisible]);
312 ActivateTabView(tab1_);
313 EXPECT_TRUE([[alert window] isVisible]);
314 EXPECT_EQ(1.0, [[alert window] alphaValue]);
316 [controller_ closeSheet:system_sheet];