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 - (NSWindow*)sheetWindow {
60 return [alert_ window];
63 - (void)alertDidEnd:(NSAlert *)alert
64 returnCode:(NSInteger)returnCode
65 ctxInfo:(void *)contextInfo {
66 returnCode_ = returnCode;
71 class ConstrainedWindowSheetControllerTest : public CocoaTest {
73 void SetUp() override {
76 // Center the window so that the sheet doesn't go offscreen.
77 [test_window() center];
79 // The real view setup is quite a few levels deep; recreate something
81 NSRect dummy_rect = NSMakeRect(0, 0, 50, 50);
82 tab_view_parent_ = [test_window() contentView];
83 for (int i = 0; i < 3; ++i) {
84 base::scoped_nsobject<NSView> new_view(
85 [[NSView alloc] initWithFrame:dummy_rect]);
86 [tab_view_parent_ addSubview:new_view.get()];
87 tab_view_parent_ = new_view.get();
90 controller_.reset([[ConstrainedWindowSheetController
91 controllerForParentWindow:test_window()] retain]);
92 EXPECT_TRUE(controller_);
94 // Create two dummy tabs and make the first one active.
95 tab_views_.reset([[NSMutableArray alloc] init]);
96 sheet_windows_.reset([[NSMutableArray alloc] init]);
97 sheets_.reset([[NSMutableArray alloc] init]);
98 for (int i = 0; i < 2; ++i) {
99 base::scoped_nsobject<NSView> view(
100 [[NSView alloc] initWithFrame:dummy_rect]);
101 base::scoped_nsobject<NSWindow> sheet_window(
103 initWithContentRect:dummy_rect
104 styleMask:NSTitledWindowMask
105 backing:NSBackingStoreBuffered
107 [sheet_window setReleasedWhenClosed:NO];
108 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
109 [[CustomConstrainedWindowSheet alloc]
110 initWithCustomWindow:sheet_window]);
111 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet]);
112 [tab_views_ addObject:view];
113 [sheet_windows_ addObject:sheet_window];
114 [sheets_ addObject:sheet];
116 tab0_ = [tab_views_ objectAtIndex:0];
117 tab1_ = [tab_views_ objectAtIndex:1];
118 sheet_window0_ = [sheet_windows_ objectAtIndex:0];
119 sheet_window1_ = [sheet_windows_ objectAtIndex:1];
120 sheet_window_ = sheet_window0_;
121 sheet0_ = [sheets_ objectAtIndex:0];
122 sheet1_ = [sheets_ objectAtIndex:1];
125 active_tab_view_ = tab0_;
126 [tab_view_parent_ addSubview:active_tab_view_];
129 void TearDown() override {
131 sheet_windows_.reset();
132 CocoaTest::TearDown();
135 void ActivateTabView(NSView* tab_view) {
136 for (NSView* view in tab_views_.get())
137 [view removeFromSuperview];
138 [tab_view_parent_ addSubview:tab_view];
139 active_tab_view_ = tab_view;
141 ConstrainedWindowSheetController* controller =
142 [ConstrainedWindowSheetController
143 controllerForParentWindow:test_window()];
144 EXPECT_TRUE(controller);
146 [controller hideSheet];
147 CustomConstrainedWindowSheet* sheet =
148 [sheets_ objectAtIndex:[tab_views_ indexOfObject:active_tab_view_]];
150 [controller showSheet:sheet forParentView:active_tab_view_];
153 NSRect GetViewFrameInScreenCoordinates(NSView* view) {
154 NSRect rect = [view convertRect:[view bounds] toView:nil];
155 rect.origin = [[view window] convertBaseToScreen:rect.origin];
159 void VerifySheetXPosition(NSRect sheet_frame, NSView* parent_view) {
160 NSRect parent_frame = GetViewFrameInScreenCoordinates(parent_view);
161 CGFloat expected_x = NSMinX(parent_frame) +
162 (NSWidth(parent_frame) - NSWidth(sheet_frame)) / 2.0;
163 EXPECT_EQ(expected_x, NSMinX(sheet_frame));
166 CGFloat GetSheetYOffset(NSRect sheet_frame, NSView* parent_view) {
167 return NSMaxY(sheet_frame) -
168 NSMaxY(GetViewFrameInScreenCoordinates(parent_view));
171 base::scoped_nsobject<NSMutableArray> sheet_windows_;
172 NSWindow* sheet_window0_;
173 NSWindow* sheet_window1_;
174 NSWindow* sheet_window_;
175 base::scoped_nsobject<NSMutableArray> sheets_;
176 CustomConstrainedWindowSheet* sheet0_;
177 CustomConstrainedWindowSheet* sheet1_;
178 CustomConstrainedWindowSheet* sheet_;
179 base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
180 base::scoped_nsobject<NSMutableArray> tab_views_;
181 NSView* tab_view_parent_;
182 NSView* active_tab_view_;
187 // Test showing then hiding the sheet.
188 TEST_F(ConstrainedWindowSheetControllerTest, ShowHide) {
189 EXPECT_FALSE([sheet_window_ isVisible]);
190 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
191 EXPECT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
192 EXPECT_TRUE([sheet_window_ isVisible]);
194 [controller_ closeSheet:sheet_];
195 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
196 EXPECT_FALSE([sheet_window_ isVisible]);
199 // Test that switching tabs correctly hides the inactive tab's sheet.
200 TEST_F(ConstrainedWindowSheetControllerTest, SwitchTabs) {
201 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
203 EXPECT_TRUE([sheet_window_ isVisible]);
204 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
205 ActivateTabView([tab_views_ objectAtIndex:1]);
206 EXPECT_TRUE([sheet_window_ isVisible]);
207 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
208 ActivateTabView([tab_views_ objectAtIndex:0]);
209 EXPECT_TRUE([sheet_window_ isVisible]);
210 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
213 // Test that two parent windows with two sheet controllers don't conflict.
214 TEST_F(ConstrainedWindowSheetControllerTest, TwoParentWindows) {
215 base::scoped_nsobject<NSWindow> parent_window2(
216 [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 30, 30)
217 styleMask:NSTitledWindowMask
218 backing:NSBackingStoreBuffered
220 [parent_window2 setReleasedWhenClosed:NO];
222 ConstrainedWindowSheetController* controller2 =
223 [ConstrainedWindowSheetController
224 controllerForParentWindow:parent_window2];
225 EXPECT_TRUE(controller2);
226 EXPECT_NSNE(controller_, controller2);
228 [controller2 showSheet:sheet_ forParentView:[parent_window2 contentView]];
229 EXPECT_NSEQ(controller2,
230 [ConstrainedWindowSheetController controllerForSheet:sheet_]);
232 [parent_window2 close];
235 // Test that resizing sheet works.
236 TEST_F(ConstrainedWindowSheetControllerTest, Resize) {
237 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
239 NSRect old_frame = [sheet_window_ frame];
242 sheet_frame.size = NSMakeSize(NSWidth(old_frame) + 100,
243 NSHeight(old_frame) + 50);
244 sheet_frame.origin = [controller_ originForSheet:sheet_
245 withWindowSize:sheet_frame.size];
247 // Y pos should not have changed.
248 EXPECT_EQ(NSMaxY(sheet_frame), NSMaxY(old_frame));
250 // X pos should be centered on parent view.
251 VerifySheetXPosition(sheet_frame, active_tab_view_);
254 // Test that resizing a hidden sheet works.
255 TEST_F(ConstrainedWindowSheetControllerTest, ResizeHiddenSheet) {
256 [controller_ showSheet:sheet_ forParentView:tab0_];
257 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
258 ActivateTabView(tab1_);
259 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
261 NSRect old_frame = [sheet_window_ frame];
262 NSRect new_inactive_frame = NSInsetRect(old_frame, -30, -40);
263 [sheet_window_ setFrame:new_inactive_frame display:YES];
265 ActivateTabView(tab0_);
266 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
268 NSRect new_active_frame = [sheet_window_ frame];
269 EXPECT_EQ(NSWidth(new_inactive_frame), NSWidth(new_active_frame));
270 EXPECT_EQ(NSHeight(new_inactive_frame), NSHeight(new_active_frame));
273 // Test resizing parent window keeps the sheet anchored.
274 TEST_F(ConstrainedWindowSheetControllerTest, ResizeParentWindow) {
275 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
276 CGFloat sheet_offset =
277 GetSheetYOffset([sheet_window_ frame], active_tab_view_);
279 // Test 3x3 different parent window sizes.
280 CGFloat insets[] = {-10, 0, 10};
281 NSRect old_frame = [test_window() frame];
283 for (size_t x = 0; x < arraysize(insets); x++) {
284 for (size_t y = 0; y < arraysize(insets); y++) {
285 NSRect resized_frame = NSInsetRect(old_frame, insets[x], insets[y]);
286 [test_window() setFrame:resized_frame display:YES];
287 NSRect sheet_frame = [sheet_window_ frame];
289 // Y pos should track parent view's position.
290 EXPECT_EQ(sheet_offset, GetSheetYOffset(sheet_frame, active_tab_view_));
292 // X pos should be centered on parent view.
293 VerifySheetXPosition(sheet_frame, active_tab_view_);
298 // Test system sheets.
299 TEST_F(ConstrainedWindowSheetControllerTest, SystemSheet) {
300 base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
301 [[ConstrainedWindowSystemSheetTest alloc] init]);
302 base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
303 [system_sheet setAlert:alert];
305 EXPECT_FALSE([[alert window] isVisible]);
306 [controller_ showSheet:system_sheet forParentView:active_tab_view_];
307 EXPECT_TRUE([[alert window] isVisible]);
309 [controller_ closeSheet:system_sheet];
310 EXPECT_FALSE([[alert window] isVisible]);
311 EXPECT_EQ(kSystemSheetReturnCode, [system_sheet returnCode]);