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. 4 5#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h" 6 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" 10 11namespace { 12 13const int kSystemSheetReturnCode = 77; 14 15} // namespace 16 17@interface ConstrainedWindowSystemSheetTest 18 : NSObject <ConstrainedWindowSheet> { 19 int returnCode_; 20 NSAlert* alert_; // weak 21} 22 23@property(nonatomic, readonly) int returnCode; 24@property(nonatomic, assign) NSAlert* alert; 25 26@end 27 28@implementation ConstrainedWindowSystemSheetTest 29 30@synthesize returnCode = returnCode_; 31@synthesize alert = alert_; 32 33- (void)showSheetForWindow:(NSWindow*)window { 34 [alert_ beginSheetModalForWindow:window 35 modalDelegate:self 36 didEndSelector:@selector(alertDidEnd:returnCode:ctxInfo:) 37 contextInfo:NULL]; 38} 39 40- (void)closeSheetWithAnimation:(BOOL)withAnimation { 41 [NSApp endSheet:[alert_ window] returnCode:kSystemSheetReturnCode]; 42} 43 44- (void)hideSheet { 45} 46 47- (void)unhideSheet { 48} 49 50- (void)pulseSheet { 51} 52 53- (void)makeSheetKeyAndOrderFront { 54} 55 56- (void)updateSheetPosition { 57} 58 59- (void)alertDidEnd:(NSAlert *)alert 60 returnCode:(NSInteger)returnCode 61 ctxInfo:(void *)contextInfo { 62 returnCode_ = returnCode; 63} 64 65@end 66 67class ConstrainedWindowSheetControllerTest : public CocoaTest { 68 protected: 69 virtual ~ConstrainedWindowSheetControllerTest() { 70 } 71 72 virtual void SetUp() OVERRIDE { 73 CocoaTest::SetUp(); 74 75 // Center the window so that the sheet doesn't go offscreen. 76 [test_window() center]; 77 78 // The real view setup is quite a few levels deep; recreate something 79 // similar. 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(); 87 } 88 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]; 95 } 96 tab0_ = [tab_views_ objectAtIndex:0]; 97 tab1_ = [tab_views_ objectAtIndex:1]; 98 ActivateTabView(tab0_); 99 100 // Create a test sheet. 101 sheet_window_.reset([[NSWindow alloc] 102 initWithContentRect:dummy_rect 103 styleMask:NSTitledWindowMask 104 backing:NSBackingStoreBuffered 105 defer:NO]); 106 [sheet_window_ setReleasedWhenClosed:NO]; 107 sheet_.reset([[CustomConstrainedWindowSheet alloc] 108 initWithCustomWindow:sheet_window_]); 109 110 controller_.reset([[ConstrainedWindowSheetController 111 controllerForParentWindow:test_window()] retain]); 112 EXPECT_TRUE(controller_); 113 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]); 114 } 115 116 virtual void TearDown() OVERRIDE { 117 sheet_.reset(); 118 sheet_window_.reset(); 119 CocoaTest::TearDown(); 120 } 121 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; 127 128 ConstrainedWindowSheetController* controller = 129 [ConstrainedWindowSheetController 130 controllerForParentWindow:test_window()]; 131 EXPECT_TRUE(controller); 132 [controller parentViewDidBecomeActive:active_tab_view_]; 133 } 134 135 NSRect GetViewFrameInScreenCoordinates(NSView* view) { 136 NSRect rect = [view convertRect:[view bounds] toView:nil]; 137 rect.origin = [[view window] convertBaseToScreen:rect.origin]; 138 return rect; 139 } 140 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)); 146 } 147 148 CGFloat GetSheetYOffset(NSRect sheet_frame, NSView* parent_view) { 149 return NSMaxY(sheet_frame) - 150 NSMaxY(GetViewFrameInScreenCoordinates(parent_view)); 151 } 152 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_; 159 NSView* tab0_; 160 NSView* tab1_; 161}; 162 163// Test showing then hiding the sheet. 164TEST_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]); 169 170 [controller_ closeSheet:sheet_]; 171 EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]); 172 EXPECT_FALSE([sheet_window_ isVisible]); 173} 174 175// Test that switching tabs correctly hides the inactive tab's sheet. 176TEST_F(ConstrainedWindowSheetControllerTest, SwitchTabs) { 177 [controller_ showSheet:sheet_ forParentView:active_tab_view_]; 178 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]); 187} 188 189// Test that adding a sheet to an inactive view doesn't show it. 190TEST_F(ConstrainedWindowSheetControllerTest, AddToInactiveTab) { 191 ActivateTabView(tab0_); 192 [controller_ showSheet:sheet_ forParentView:tab1_]; 193 EXPECT_EQ(0.0, [sheet_window_ alphaValue]); 194 195 ActivateTabView(tab1_); 196 EXPECT_EQ(1.0, [sheet_window_ alphaValue]); 197 VerifySheetXPosition([sheet_window_ frame], tab1_); 198} 199 200// Test that two parent windows with two sheet controllers don't conflict. 201TEST_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 206 defer:NO]); 207 [parent_window2 setReleasedWhenClosed:NO]; 208 209 ConstrainedWindowSheetController* controller2 = 210 [ConstrainedWindowSheetController 211 controllerForParentWindow:parent_window2]; 212 EXPECT_TRUE(controller2); 213 EXPECT_NSNE(controller_, controller2); 214 215 [controller2 showSheet:sheet_ forParentView:[parent_window2 contentView]]; 216 EXPECT_NSEQ(controller2, 217 [ConstrainedWindowSheetController controllerForSheet:sheet_]); 218 219 [parent_window2 close]; 220} 221 222// Test that resizing sheet works. 223TEST_F(ConstrainedWindowSheetControllerTest, Resize) { 224 [controller_ showSheet:sheet_ forParentView:active_tab_view_]; 225 226 NSRect old_frame = [sheet_window_ frame]; 227 228 NSRect sheet_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]; 233 234 // Y pos should not have changed. 235 EXPECT_EQ(NSMaxY(sheet_frame), NSMaxY(old_frame)); 236 237 // X pos should be centered on parent view. 238 VerifySheetXPosition(sheet_frame, active_tab_view_); 239} 240 241// Test that resizing a hidden sheet works. 242TEST_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]); 247 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]; 251 252 ActivateTabView(tab0_); 253 EXPECT_EQ(1.0, [sheet_window_ alphaValue]); 254 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)); 258} 259 260// Test resizing parent window keeps the sheet anchored. 261TEST_F(ConstrainedWindowSheetControllerTest, ResizeParentWindow) { 262 [controller_ showSheet:sheet_ forParentView:active_tab_view_]; 263 CGFloat sheet_offset = 264 GetSheetYOffset([sheet_window_ frame], active_tab_view_); 265 266 // Test 3x3 different parent window sizes. 267 CGFloat insets[] = {-10, 0, 10}; 268 NSRect old_frame = [test_window() frame]; 269 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]; 275 276 // Y pos should track parent view's position. 277 EXPECT_EQ(sheet_offset, GetSheetYOffset(sheet_frame, active_tab_view_)); 278 279 // X pos should be centered on parent view. 280 VerifySheetXPosition(sheet_frame, active_tab_view_); 281 } 282 } 283} 284 285// Test system sheets. 286TEST_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]; 291 292 EXPECT_FALSE([[alert window] isVisible]); 293 [controller_ showSheet:system_sheet forParentView:active_tab_view_]; 294 EXPECT_TRUE([[alert window] isVisible]); 295 296 [controller_ closeSheet:system_sheet]; 297 EXPECT_FALSE([[alert window] isVisible]); 298 EXPECT_EQ(kSystemSheetReturnCode, [system_sheet returnCode]); 299} 300 301// Test showing a system sheet on an inactive tab. 302TEST_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]; 307 308 EXPECT_FALSE([[alert window] isVisible]); 309 [controller_ showSheet:system_sheet forParentView:tab1_]; 310 EXPECT_FALSE([[alert window] isVisible]); 311 312 ActivateTabView(tab1_); 313 EXPECT_TRUE([[alert window] isVisible]); 314 EXPECT_EQ(1.0, [[alert window] alphaValue]); 315 316 [controller_ closeSheet:system_sheet]; 317} 318