Home | History | Annotate | Download | only in constrained_window
      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 
     11 namespace {
     12 
     13 const 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 
     67 class 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     // Create two dummy tabs and make the first one active.
     79     NSRect dummyRect = NSMakeRect(0, 0, 50, 50);
     80     tab_views_.reset([[NSMutableArray alloc] init]);
     81     for (int i = 0; i < 2; ++i) {
     82       base::scoped_nsobject<NSView> view(
     83           [[NSView alloc] initWithFrame:dummyRect]);
     84       [tab_views_ addObject:view];
     85     }
     86     tab0_.reset([[tab_views_ objectAtIndex:0] retain]);
     87     tab1_.reset([[tab_views_ objectAtIndex:1] retain]);
     88     ActivateTabView(tab0_);
     89 
     90     // Create a test sheet.
     91     sheet_window_.reset([[NSWindow alloc]
     92         initWithContentRect:dummyRect
     93                   styleMask:NSTitledWindowMask
     94                     backing:NSBackingStoreBuffered
     95                       defer:NO]);
     96     [sheet_window_ setReleasedWhenClosed:NO];
     97     sheet_.reset([[CustomConstrainedWindowSheet alloc]
     98         initWithCustomWindow:sheet_window_]);
     99 
    100     controller_.reset([[ConstrainedWindowSheetController
    101             controllerForParentWindow:test_window()] retain]);
    102     EXPECT_TRUE(controller_);
    103     EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
    104   }
    105 
    106   virtual void TearDown() OVERRIDE {
    107     sheet_.reset();
    108     sheet_window_.reset();
    109     CocoaTest::TearDown();
    110   }
    111 
    112   void ActivateTabView(NSView* tab_view) {
    113     for (NSView* view in tab_views_.get()) {
    114       [view removeFromSuperview];
    115     }
    116     [[test_window() contentView] addSubview:tab_view];
    117     active_tab_view_.reset([tab_view retain]);
    118 
    119     ConstrainedWindowSheetController* controller =
    120         [ConstrainedWindowSheetController
    121             controllerForParentWindow:test_window()];
    122     EXPECT_TRUE(controller);
    123     [controller parentViewDidBecomeActive:active_tab_view_];
    124   }
    125 
    126   NSRect GetViewFrameInScreenCoordinates(NSView* view) {
    127     NSRect rect = [view convertRect:[view bounds] toView:nil];
    128     rect.origin = [[view window] convertBaseToScreen:rect.origin];
    129     return rect;
    130   }
    131 
    132   void VerifySheetXPosition(NSRect sheet_frame, NSView* parent_view) {
    133     NSRect parent_frame = GetViewFrameInScreenCoordinates(parent_view);
    134     CGFloat expected_x = NSMinX(parent_frame) +
    135         (NSWidth(parent_frame) - NSWidth(sheet_frame)) / 2.0;
    136     EXPECT_EQ(expected_x, NSMinX(sheet_frame));
    137   }
    138 
    139   CGFloat GetSheetYOffset(NSRect sheet_frame, NSView* parent_view) {
    140     return NSMaxY(sheet_frame) -
    141            NSMaxY(GetViewFrameInScreenCoordinates(parent_view));
    142   }
    143 
    144   base::scoped_nsobject<NSWindow> sheet_window_;
    145   base::scoped_nsobject<CustomConstrainedWindowSheet> sheet_;
    146   base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
    147   base::scoped_nsobject<NSMutableArray> tab_views_;
    148   base::scoped_nsobject<NSView> active_tab_view_;
    149   base::scoped_nsobject<NSView> tab0_;
    150   base::scoped_nsobject<NSView> tab1_;
    151 };
    152 
    153 // Test showing then hiding the sheet.
    154 TEST_F(ConstrainedWindowSheetControllerTest, ShowHide) {
    155   EXPECT_FALSE([sheet_window_ isVisible]);
    156   [controller_ showSheet:sheet_ forParentView:active_tab_view_];
    157   EXPECT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
    158   EXPECT_TRUE([sheet_window_ isVisible]);
    159 
    160   [controller_ closeSheet:sheet_];
    161   EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
    162   EXPECT_FALSE([sheet_window_ isVisible]);
    163 }
    164 
    165 // Test that switching tabs correctly hides the inactive tab's sheet.
    166 TEST_F(ConstrainedWindowSheetControllerTest, SwitchTabs) {
    167   [controller_ showSheet:sheet_ forParentView:active_tab_view_];
    168 
    169   EXPECT_TRUE([sheet_window_ isVisible]);
    170   EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
    171   ActivateTabView([tab_views_ objectAtIndex:1]);
    172   EXPECT_TRUE([sheet_window_ isVisible]);
    173   EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
    174   ActivateTabView([tab_views_ objectAtIndex:0]);
    175   EXPECT_TRUE([sheet_window_ isVisible]);
    176   EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
    177 }
    178 
    179 // Test that adding a sheet to an inactive view doesn't show it.
    180 TEST_F(ConstrainedWindowSheetControllerTest, AddToInactiveTab) {
    181   ActivateTabView(tab0_);
    182   [controller_ showSheet:sheet_ forParentView:tab1_];
    183   EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
    184 
    185   ActivateTabView(tab1_);
    186   EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
    187   VerifySheetXPosition([sheet_window_ frame], tab1_);
    188 }
    189 
    190 // Test that two parent windows with two sheet controllers don't conflict.
    191 TEST_F(ConstrainedWindowSheetControllerTest, TwoParentWindows) {
    192   base::scoped_nsobject<NSWindow> parent_window2(
    193       [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 30, 30)
    194                                   styleMask:NSTitledWindowMask
    195                                     backing:NSBackingStoreBuffered
    196                                       defer:NO]);
    197   [parent_window2 setReleasedWhenClosed:NO];
    198 
    199   ConstrainedWindowSheetController* controller2 =
    200       [ConstrainedWindowSheetController
    201           controllerForParentWindow:parent_window2];
    202   EXPECT_TRUE(controller2);
    203   EXPECT_NSNE(controller_, controller2);
    204 
    205   [controller2 showSheet:sheet_ forParentView:[parent_window2 contentView]];
    206   EXPECT_NSEQ(controller2,
    207               [ConstrainedWindowSheetController controllerForSheet:sheet_]);
    208 
    209   [parent_window2 close];
    210 }
    211 
    212 // Test that using a top level parent view works.
    213 TEST_F(ConstrainedWindowSheetControllerTest, TopLevelView) {
    214   NSView* parentView = [[test_window() contentView] superview];
    215   [controller_ parentViewDidBecomeActive:parentView];
    216 
    217   EXPECT_FALSE([sheet_window_ isVisible]);
    218   [controller_ showSheet:sheet_ forParentView:parentView];
    219   EXPECT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
    220   EXPECT_TRUE([sheet_window_ isVisible]);
    221   VerifySheetXPosition([sheet_window_ frame], parentView);
    222 }
    223 
    224 // Test that resizing sheet works.
    225 TEST_F(ConstrainedWindowSheetControllerTest, Resize) {
    226   [controller_ showSheet:sheet_ forParentView:active_tab_view_];
    227 
    228   NSRect old_frame = [sheet_window_ frame];
    229 
    230   NSRect sheet_frame;
    231   sheet_frame.size = NSMakeSize(NSWidth(old_frame) + 100,
    232                                 NSHeight(old_frame) + 50);
    233   sheet_frame.origin = [controller_ originForSheet:sheet_
    234                                     withWindowSize:sheet_frame.size];
    235 
    236   // Y pos should not have changed.
    237   EXPECT_EQ(NSMaxY(sheet_frame), NSMaxY(old_frame));
    238 
    239   // X pos should be centered on parent view.
    240   VerifySheetXPosition(sheet_frame, active_tab_view_);
    241 }
    242 
    243 // Test that resizing a hidden sheet works.
    244 TEST_F(ConstrainedWindowSheetControllerTest, ResizeHiddenSheet) {
    245   [controller_ showSheet:sheet_ forParentView:tab0_];
    246   EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
    247   ActivateTabView(tab1_);
    248   EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
    249 
    250   NSRect old_frame = [sheet_window_ frame];
    251   NSRect new_inactive_frame = NSInsetRect(old_frame, -30, -40);
    252   [sheet_window_ setFrame:new_inactive_frame display:YES];
    253 
    254   ActivateTabView(tab0_);
    255   EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
    256 
    257   NSRect new_active_frame = [sheet_window_ frame];
    258   EXPECT_EQ(NSWidth(new_inactive_frame), NSWidth(new_active_frame));
    259   EXPECT_EQ(NSHeight(new_inactive_frame), NSHeight(new_active_frame));
    260 }
    261 
    262 // Test resizing parent window keeps the sheet anchored.
    263 TEST_F(ConstrainedWindowSheetControllerTest, ResizeParentWindow) {
    264   [controller_ showSheet:sheet_ forParentView:active_tab_view_];
    265   CGFloat sheet_offset =
    266       GetSheetYOffset([sheet_window_ frame], active_tab_view_);
    267 
    268   // Test 3x3 different parent window sizes.
    269   CGFloat insets[] = {-10, 0, 10};
    270   NSRect old_frame = [test_window() frame];
    271 
    272   for (size_t x = 0; x < sizeof(insets) / sizeof(CGFloat); x++) {
    273     for (size_t y = 0; y < sizeof(insets) / sizeof(CGFloat); y++) {
    274       NSRect resized_frame = NSInsetRect(old_frame, insets[x], insets[y]);
    275       [test_window() setFrame:resized_frame display:YES];
    276       NSRect sheet_frame = [sheet_window_ frame];
    277 
    278       // Y pos should track parent view's position.
    279       EXPECT_EQ(sheet_offset, GetSheetYOffset(sheet_frame, active_tab_view_));
    280 
    281       // X pos should be centered on parent view.
    282       VerifySheetXPosition(sheet_frame, active_tab_view_);
    283     }
    284   }
    285 }
    286 
    287 // Test system sheets.
    288 TEST_F(ConstrainedWindowSheetControllerTest, SystemSheet) {
    289   base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
    290       [[ConstrainedWindowSystemSheetTest alloc] init]);
    291   base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
    292   [system_sheet setAlert:alert];
    293 
    294   EXPECT_FALSE([[alert window] isVisible]);
    295   [controller_ showSheet:system_sheet
    296                  forParentView:active_tab_view_];
    297   EXPECT_TRUE([[alert window] isVisible]);
    298 
    299   [controller_ closeSheet:system_sheet];
    300   EXPECT_FALSE([[alert window] isVisible]);
    301   EXPECT_EQ(kSystemSheetReturnCode, [system_sheet returnCode]);
    302 }
    303 
    304 // Test showing a system sheet on an inactive tab.
    305 TEST_F(ConstrainedWindowSheetControllerTest, SystemSheetAddToInactiveTab) {
    306   base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
    307       [[ConstrainedWindowSystemSheetTest alloc] init]);
    308   base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
    309   [system_sheet setAlert:alert];
    310 
    311   EXPECT_FALSE([[alert window] isVisible]);
    312   [controller_ showSheet:system_sheet
    313                  forParentView:tab1_];
    314   EXPECT_FALSE([[alert window] isVisible]);
    315 
    316   ActivateTabView(tab1_);
    317   EXPECT_TRUE([[alert window] isVisible]);
    318   EXPECT_EQ(1.0, [[alert window] alphaValue]);
    319 
    320   [controller_ closeSheet:system_sheet];
    321 }
    322