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 // Create two dummy tabs and make the first one active.
91 tab_views_.reset([[NSMutableArray alloc] init]);
92 for (int i = 0; i < 2; ++i) {
93 base::scoped_nsobject<NSView> view(
94 [[NSView alloc] initWithFrame:dummy_rect]);
95 [tab_views_ addObject:view];
97 tab0_ = [tab_views_ objectAtIndex:0];
98 tab1_ = [tab_views_ objectAtIndex:1];
99 ActivateTabView(tab0_);
101 // Create a test sheet.
102 sheet_window_.reset([[NSWindow alloc]
103 initWithContentRect:dummy_rect
104 styleMask:NSTitledWindowMask
105 backing:NSBackingStoreBuffered
107 [sheet_window_ setReleasedWhenClosed:NO];
108 sheet_.reset([[CustomConstrainedWindowSheet alloc]
109 initWithCustomWindow:sheet_window_]);
111 controller_.reset([[ConstrainedWindowSheetController
112 controllerForParentWindow:test_window()] retain]);
113 EXPECT_TRUE(controller_);
114 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
117 void TearDown() override {
119 sheet_window_.reset();
120 CocoaTest::TearDown();
123 void ActivateTabView(NSView* tab_view) {
124 for (NSView* view in tab_views_.get())
125 [view removeFromSuperview];
126 [tab_view_parent_ addSubview:tab_view];
127 active_tab_view_ = tab_view;
129 ConstrainedWindowSheetController* controller =
130 [ConstrainedWindowSheetController
131 controllerForParentWindow:test_window()];
132 EXPECT_TRUE(controller);
133 [controller parentViewDidBecomeActive:active_tab_view_];
136 NSRect GetViewFrameInScreenCoordinates(NSView* view) {
137 NSRect rect = [view convertRect:[view bounds] toView:nil];
138 rect.origin = [[view window] convertBaseToScreen:rect.origin];
142 void VerifySheetXPosition(NSRect sheet_frame, NSView* parent_view) {
143 NSRect parent_frame = GetViewFrameInScreenCoordinates(parent_view);
144 CGFloat expected_x = NSMinX(parent_frame) +
145 (NSWidth(parent_frame) - NSWidth(sheet_frame)) / 2.0;
146 EXPECT_EQ(expected_x, NSMinX(sheet_frame));
149 CGFloat GetSheetYOffset(NSRect sheet_frame, NSView* parent_view) {
150 return NSMaxY(sheet_frame) -
151 NSMaxY(GetViewFrameInScreenCoordinates(parent_view));
154 base::scoped_nsobject<NSWindow> sheet_window_;
155 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet_;
156 base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
157 base::scoped_nsobject<NSMutableArray> tab_views_;
158 NSView* tab_view_parent_;
159 NSView* active_tab_view_;
164 // Test showing then hiding the sheet.
165 TEST_F(ConstrainedWindowSheetControllerTest, ShowHide) {
166 EXPECT_FALSE([sheet_window_ isVisible]);
167 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
168 EXPECT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
169 EXPECT_TRUE([sheet_window_ isVisible]);
171 [controller_ closeSheet:sheet_];
172 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
173 EXPECT_FALSE([sheet_window_ isVisible]);
176 // Test that switching tabs correctly hides the inactive tab's sheet.
177 TEST_F(ConstrainedWindowSheetControllerTest, SwitchTabs) {
178 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
180 EXPECT_TRUE([sheet_window_ isVisible]);
181 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
182 ActivateTabView([tab_views_ objectAtIndex:1]);
183 EXPECT_TRUE([sheet_window_ isVisible]);
184 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
185 ActivateTabView([tab_views_ objectAtIndex:0]);
186 EXPECT_TRUE([sheet_window_ isVisible]);
187 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
190 // Test that adding a sheet to an inactive view doesn't show it.
191 TEST_F(ConstrainedWindowSheetControllerTest, AddToInactiveTab) {
192 ActivateTabView(tab0_);
193 [controller_ showSheet:sheet_ forParentView:tab1_];
194 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
196 ActivateTabView(tab1_);
197 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
198 VerifySheetXPosition([sheet_window_ frame], tab1_);
201 // Test that two parent windows with two sheet controllers don't conflict.
202 TEST_F(ConstrainedWindowSheetControllerTest, TwoParentWindows) {
203 base::scoped_nsobject<NSWindow> parent_window2(
204 [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 30, 30)
205 styleMask:NSTitledWindowMask
206 backing:NSBackingStoreBuffered
208 [parent_window2 setReleasedWhenClosed:NO];
210 ConstrainedWindowSheetController* controller2 =
211 [ConstrainedWindowSheetController
212 controllerForParentWindow:parent_window2];
213 EXPECT_TRUE(controller2);
214 EXPECT_NSNE(controller_, controller2);
216 [controller2 showSheet:sheet_ forParentView:[parent_window2 contentView]];
217 EXPECT_NSEQ(controller2,
218 [ConstrainedWindowSheetController controllerForSheet:sheet_]);
220 [parent_window2 close];
223 // Test that resizing sheet works.
224 TEST_F(ConstrainedWindowSheetControllerTest, Resize) {
225 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
227 NSRect old_frame = [sheet_window_ frame];
230 sheet_frame.size = NSMakeSize(NSWidth(old_frame) + 100,
231 NSHeight(old_frame) + 50);
232 sheet_frame.origin = [controller_ originForSheet:sheet_
233 withWindowSize:sheet_frame.size];
235 // Y pos should not have changed.
236 EXPECT_EQ(NSMaxY(sheet_frame), NSMaxY(old_frame));
238 // X pos should be centered on parent view.
239 VerifySheetXPosition(sheet_frame, active_tab_view_);
242 // Test that resizing a hidden sheet works.
243 TEST_F(ConstrainedWindowSheetControllerTest, ResizeHiddenSheet) {
244 [controller_ showSheet:sheet_ forParentView:tab0_];
245 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
246 ActivateTabView(tab1_);
247 EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
249 NSRect old_frame = [sheet_window_ frame];
250 NSRect new_inactive_frame = NSInsetRect(old_frame, -30, -40);
251 [sheet_window_ setFrame:new_inactive_frame display:YES];
253 ActivateTabView(tab0_);
254 EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
256 NSRect new_active_frame = [sheet_window_ frame];
257 EXPECT_EQ(NSWidth(new_inactive_frame), NSWidth(new_active_frame));
258 EXPECT_EQ(NSHeight(new_inactive_frame), NSHeight(new_active_frame));
261 // Test resizing parent window keeps the sheet anchored.
262 TEST_F(ConstrainedWindowSheetControllerTest, ResizeParentWindow) {
263 [controller_ showSheet:sheet_ forParentView:active_tab_view_];
264 CGFloat sheet_offset =
265 GetSheetYOffset([sheet_window_ frame], active_tab_view_);
267 // Test 3x3 different parent window sizes.
268 CGFloat insets[] = {-10, 0, 10};
269 NSRect old_frame = [test_window() frame];
271 for (size_t x = 0; x < arraysize(insets); x++) {
272 for (size_t y = 0; y < arraysize(insets); y++) {
273 NSRect resized_frame = NSInsetRect(old_frame, insets[x], insets[y]);
274 [test_window() setFrame:resized_frame display:YES];
275 NSRect sheet_frame = [sheet_window_ frame];
277 // Y pos should track parent view's position.
278 EXPECT_EQ(sheet_offset, GetSheetYOffset(sheet_frame, active_tab_view_));
280 // X pos should be centered on parent view.
281 VerifySheetXPosition(sheet_frame, active_tab_view_);
286 // Test system sheets.
287 TEST_F(ConstrainedWindowSheetControllerTest, SystemSheet) {
288 base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
289 [[ConstrainedWindowSystemSheetTest alloc] init]);
290 base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
291 [system_sheet setAlert:alert];
293 EXPECT_FALSE([[alert window] isVisible]);
294 [controller_ showSheet:system_sheet forParentView:active_tab_view_];
295 EXPECT_TRUE([[alert window] isVisible]);
297 [controller_ closeSheet:system_sheet];
298 EXPECT_FALSE([[alert window] isVisible]);
299 EXPECT_EQ(kSystemSheetReturnCode, [system_sheet returnCode]);
302 // Test showing a system sheet on an inactive tab.
303 TEST_F(ConstrainedWindowSheetControllerTest, SystemSheetAddToInactiveTab) {
304 base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
305 [[ConstrainedWindowSystemSheetTest alloc] init]);
306 base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
307 [system_sheet setAlert:alert];
309 EXPECT_FALSE([[alert window] isVisible]);
310 [controller_ showSheet:system_sheet forParentView:tab1_];
311 EXPECT_FALSE([[alert window] isVisible]);
313 ActivateTabView(tab1_);
314 EXPECT_TRUE([[alert window] isVisible]);
315 EXPECT_EQ(1.0, [[alert window] alphaValue]);
317 [controller_ closeSheet:system_sheet];