blob: 9947e956c963db8a4e3c827000e6d0eda8fb607c [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
#import "testing/gtest_mac.h"
namespace {
const int kSystemSheetReturnCode = 77;
} // namespace
@interface ConstrainedWindowSystemSheetTest
: NSObject <ConstrainedWindowSheet> {
int returnCode_;
NSAlert* alert_; // weak
}
@property(nonatomic, readonly) int returnCode;
@property(nonatomic, assign) NSAlert* alert;
@end
@implementation ConstrainedWindowSystemSheetTest
@synthesize returnCode = returnCode_;
@synthesize alert = alert_;
- (void)showSheetForWindow:(NSWindow*)window {
[alert_ beginSheetModalForWindow:window
modalDelegate:self
didEndSelector:@selector(alertDidEnd:returnCode:ctxInfo:)
contextInfo:NULL];
}
- (void)closeSheetWithAnimation:(BOOL)withAnimation {
[NSApp endSheet:[alert_ window] returnCode:kSystemSheetReturnCode];
}
- (void)hideSheet {
}
- (void)unhideSheet {
}
- (void)pulseSheet {
}
- (void)makeSheetKeyAndOrderFront {
}
- (void)updateSheetPosition {
}
- (void)alertDidEnd:(NSAlert *)alert
returnCode:(NSInteger)returnCode
ctxInfo:(void *)contextInfo {
returnCode_ = returnCode;
}
@end
class ConstrainedWindowSheetControllerTest : public CocoaTest {
protected:
virtual ~ConstrainedWindowSheetControllerTest() {
}
virtual void SetUp() OVERRIDE {
CocoaTest::SetUp();
// Center the window so that the sheet doesn't go offscreen.
[test_window() center];
// The real view setup is quite a few levels deep; recreate something
// similar.
NSRect dummy_rect = NSMakeRect(0, 0, 50, 50);
tab_view_parent_ = [test_window() contentView];
for (int i = 0; i < 3; ++i) {
base::scoped_nsobject<NSView> new_view(
[[NSView alloc] initWithFrame:dummy_rect]);
[tab_view_parent_ addSubview:new_view.get()];
tab_view_parent_ = new_view.get();
}
// Create two dummy tabs and make the first one active.
tab_views_.reset([[NSMutableArray alloc] init]);
for (int i = 0; i < 2; ++i) {
base::scoped_nsobject<NSView> view(
[[NSView alloc] initWithFrame:dummy_rect]);
[tab_views_ addObject:view];
}
tab0_ = [tab_views_ objectAtIndex:0];
tab1_ = [tab_views_ objectAtIndex:1];
ActivateTabView(tab0_);
// Create a test sheet.
sheet_window_.reset([[NSWindow alloc]
initWithContentRect:dummy_rect
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO]);
[sheet_window_ setReleasedWhenClosed:NO];
sheet_.reset([[CustomConstrainedWindowSheet alloc]
initWithCustomWindow:sheet_window_]);
controller_.reset([[ConstrainedWindowSheetController
controllerForParentWindow:test_window()] retain]);
EXPECT_TRUE(controller_);
EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
}
virtual void TearDown() OVERRIDE {
sheet_.reset();
sheet_window_.reset();
CocoaTest::TearDown();
}
void ActivateTabView(NSView* tab_view) {
for (NSView* view in tab_views_.get())
[view removeFromSuperview];
[tab_view_parent_ addSubview:tab_view];
active_tab_view_ = tab_view;
ConstrainedWindowSheetController* controller =
[ConstrainedWindowSheetController
controllerForParentWindow:test_window()];
EXPECT_TRUE(controller);
[controller parentViewDidBecomeActive:active_tab_view_];
}
NSRect GetViewFrameInScreenCoordinates(NSView* view) {
NSRect rect = [view convertRect:[view bounds] toView:nil];
rect.origin = [[view window] convertBaseToScreen:rect.origin];
return rect;
}
void VerifySheetXPosition(NSRect sheet_frame, NSView* parent_view) {
NSRect parent_frame = GetViewFrameInScreenCoordinates(parent_view);
CGFloat expected_x = NSMinX(parent_frame) +
(NSWidth(parent_frame) - NSWidth(sheet_frame)) / 2.0;
EXPECT_EQ(expected_x, NSMinX(sheet_frame));
}
CGFloat GetSheetYOffset(NSRect sheet_frame, NSView* parent_view) {
return NSMaxY(sheet_frame) -
NSMaxY(GetViewFrameInScreenCoordinates(parent_view));
}
base::scoped_nsobject<NSWindow> sheet_window_;
base::scoped_nsobject<CustomConstrainedWindowSheet> sheet_;
base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
base::scoped_nsobject<NSMutableArray> tab_views_;
NSView* tab_view_parent_;
NSView* active_tab_view_;
NSView* tab0_;
NSView* tab1_;
};
// Test showing then hiding the sheet.
TEST_F(ConstrainedWindowSheetControllerTest, ShowHide) {
EXPECT_FALSE([sheet_window_ isVisible]);
[controller_ showSheet:sheet_ forParentView:active_tab_view_];
EXPECT_TRUE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
EXPECT_TRUE([sheet_window_ isVisible]);
[controller_ closeSheet:sheet_];
EXPECT_FALSE([ConstrainedWindowSheetController controllerForSheet:sheet_]);
EXPECT_FALSE([sheet_window_ isVisible]);
}
// Test that switching tabs correctly hides the inactive tab's sheet.
TEST_F(ConstrainedWindowSheetControllerTest, SwitchTabs) {
[controller_ showSheet:sheet_ forParentView:active_tab_view_];
EXPECT_TRUE([sheet_window_ isVisible]);
EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
ActivateTabView([tab_views_ objectAtIndex:1]);
EXPECT_TRUE([sheet_window_ isVisible]);
EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
ActivateTabView([tab_views_ objectAtIndex:0]);
EXPECT_TRUE([sheet_window_ isVisible]);
EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
}
// Test that adding a sheet to an inactive view doesn't show it.
TEST_F(ConstrainedWindowSheetControllerTest, AddToInactiveTab) {
ActivateTabView(tab0_);
[controller_ showSheet:sheet_ forParentView:tab1_];
EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
ActivateTabView(tab1_);
EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
VerifySheetXPosition([sheet_window_ frame], tab1_);
}
// Test that two parent windows with two sheet controllers don't conflict.
TEST_F(ConstrainedWindowSheetControllerTest, TwoParentWindows) {
base::scoped_nsobject<NSWindow> parent_window2(
[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 30, 30)
styleMask:NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:NO]);
[parent_window2 setReleasedWhenClosed:NO];
ConstrainedWindowSheetController* controller2 =
[ConstrainedWindowSheetController
controllerForParentWindow:parent_window2];
EXPECT_TRUE(controller2);
EXPECT_NSNE(controller_, controller2);
[controller2 showSheet:sheet_ forParentView:[parent_window2 contentView]];
EXPECT_NSEQ(controller2,
[ConstrainedWindowSheetController controllerForSheet:sheet_]);
[parent_window2 close];
}
// Test that resizing sheet works.
TEST_F(ConstrainedWindowSheetControllerTest, Resize) {
[controller_ showSheet:sheet_ forParentView:active_tab_view_];
NSRect old_frame = [sheet_window_ frame];
NSRect sheet_frame;
sheet_frame.size = NSMakeSize(NSWidth(old_frame) + 100,
NSHeight(old_frame) + 50);
sheet_frame.origin = [controller_ originForSheet:sheet_
withWindowSize:sheet_frame.size];
// Y pos should not have changed.
EXPECT_EQ(NSMaxY(sheet_frame), NSMaxY(old_frame));
// X pos should be centered on parent view.
VerifySheetXPosition(sheet_frame, active_tab_view_);
}
// Test that resizing a hidden sheet works.
TEST_F(ConstrainedWindowSheetControllerTest, ResizeHiddenSheet) {
[controller_ showSheet:sheet_ forParentView:tab0_];
EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
ActivateTabView(tab1_);
EXPECT_EQ(0.0, [sheet_window_ alphaValue]);
NSRect old_frame = [sheet_window_ frame];
NSRect new_inactive_frame = NSInsetRect(old_frame, -30, -40);
[sheet_window_ setFrame:new_inactive_frame display:YES];
ActivateTabView(tab0_);
EXPECT_EQ(1.0, [sheet_window_ alphaValue]);
NSRect new_active_frame = [sheet_window_ frame];
EXPECT_EQ(NSWidth(new_inactive_frame), NSWidth(new_active_frame));
EXPECT_EQ(NSHeight(new_inactive_frame), NSHeight(new_active_frame));
}
// Test resizing parent window keeps the sheet anchored.
TEST_F(ConstrainedWindowSheetControllerTest, ResizeParentWindow) {
[controller_ showSheet:sheet_ forParentView:active_tab_view_];
CGFloat sheet_offset =
GetSheetYOffset([sheet_window_ frame], active_tab_view_);
// Test 3x3 different parent window sizes.
CGFloat insets[] = {-10, 0, 10};
NSRect old_frame = [test_window() frame];
for (size_t x = 0; x < arraysize(insets); x++) {
for (size_t y = 0; y < arraysize(insets); y++) {
NSRect resized_frame = NSInsetRect(old_frame, insets[x], insets[y]);
[test_window() setFrame:resized_frame display:YES];
NSRect sheet_frame = [sheet_window_ frame];
// Y pos should track parent view's position.
EXPECT_EQ(sheet_offset, GetSheetYOffset(sheet_frame, active_tab_view_));
// X pos should be centered on parent view.
VerifySheetXPosition(sheet_frame, active_tab_view_);
}
}
}
// Test system sheets.
TEST_F(ConstrainedWindowSheetControllerTest, SystemSheet) {
base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
[[ConstrainedWindowSystemSheetTest alloc] init]);
base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
[system_sheet setAlert:alert];
EXPECT_FALSE([[alert window] isVisible]);
[controller_ showSheet:system_sheet forParentView:active_tab_view_];
EXPECT_TRUE([[alert window] isVisible]);
[controller_ closeSheet:system_sheet];
EXPECT_FALSE([[alert window] isVisible]);
EXPECT_EQ(kSystemSheetReturnCode, [system_sheet returnCode]);
}
// Test showing a system sheet on an inactive tab.
TEST_F(ConstrainedWindowSheetControllerTest, SystemSheetAddToInactiveTab) {
base::scoped_nsobject<ConstrainedWindowSystemSheetTest> system_sheet(
[[ConstrainedWindowSystemSheetTest alloc] init]);
base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
[system_sheet setAlert:alert];
EXPECT_FALSE([[alert window] isVisible]);
[controller_ showSheet:system_sheet forParentView:tab1_];
EXPECT_FALSE([[alert window] isVisible]);
ActivateTabView(tab1_);
EXPECT_TRUE([[alert window] isVisible]);
EXPECT_EQ(1.0, [[alert window] alphaValue]);
[controller_ closeSheet:system_sheet];
}