1 // Copyright (c) 2011 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 <Cocoa/Cocoa.h> 6 7 #import "base/memory/scoped_nsobject.h" 8 #include "chrome/app/chrome_command_ids.h" 9 #include "chrome/browser/ui/cocoa/browser_test_helper.h" 10 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" 11 #import "chrome/browser/ui/cocoa/gradient_button_cell.h" 12 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" 13 #import "chrome/browser/ui/cocoa/view_resizer_pong.h" 14 #include "chrome/browser/prefs/pref_service.h" 15 #include "chrome/common/pref_names.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 #include "testing/platform_test.h" 18 19 // An NSView that fakes out hitTest:. 20 @interface HitView : NSView { 21 id hitTestReturn_; 22 } 23 @end 24 25 @implementation HitView 26 27 - (void)setHitTestReturn:(id)rtn { 28 hitTestReturn_ = rtn; 29 } 30 31 - (NSView *)hitTest:(NSPoint)aPoint { 32 return hitTestReturn_; 33 } 34 35 @end 36 37 38 namespace { 39 40 class ToolbarControllerTest : public CocoaTest { 41 public: 42 43 // Indexes that match the ordering returned by the private ToolbarController 44 // |-toolbarViews| method. 45 enum { 46 kBackIndex, kForwardIndex, kReloadIndex, kHomeIndex, 47 kWrenchIndex, kLocationIndex, kBrowserActionContainerViewIndex 48 }; 49 50 ToolbarControllerTest() { 51 Browser* browser = helper_.browser(); 52 CommandUpdater* updater = browser->command_updater(); 53 // The default state for the commands is true, set a couple to false to 54 // ensure they get picked up correct on initialization 55 updater->UpdateCommandEnabled(IDC_BACK, false); 56 updater->UpdateCommandEnabled(IDC_FORWARD, false); 57 resizeDelegate_.reset([[ViewResizerPong alloc] init]); 58 bar_.reset( 59 [[ToolbarController alloc] initWithModel:browser->toolbar_model() 60 commands:browser->command_updater() 61 profile:helper_.profile() 62 browser:browser 63 resizeDelegate:resizeDelegate_.get()]); 64 EXPECT_TRUE([bar_ view]); 65 NSView* parent = [test_window() contentView]; 66 [parent addSubview:[bar_ view]]; 67 } 68 69 // Make sure the enabled state of the view is the same as the corresponding 70 // command in the updater. The views are in the declaration order of outlets. 71 void CompareState(CommandUpdater* updater, NSArray* views) { 72 EXPECT_EQ(updater->IsCommandEnabled(IDC_BACK), 73 [[views objectAtIndex:kBackIndex] isEnabled] ? true : false); 74 EXPECT_EQ(updater->IsCommandEnabled(IDC_FORWARD), 75 [[views objectAtIndex:kForwardIndex] isEnabled] ? true : false); 76 EXPECT_EQ(updater->IsCommandEnabled(IDC_RELOAD), 77 [[views objectAtIndex:kReloadIndex] isEnabled] ? true : false); 78 EXPECT_EQ(updater->IsCommandEnabled(IDC_HOME), 79 [[views objectAtIndex:kHomeIndex] isEnabled] ? true : false); 80 } 81 82 BrowserTestHelper helper_; 83 scoped_nsobject<ViewResizerPong> resizeDelegate_; 84 scoped_nsobject<ToolbarController> bar_; 85 }; 86 87 TEST_VIEW(ToolbarControllerTest, [bar_ view]) 88 89 // Test the initial state that everything is sync'd up 90 TEST_F(ToolbarControllerTest, InitialState) { 91 CommandUpdater* updater = helper_.browser()->command_updater(); 92 CompareState(updater, [bar_ toolbarViews]); 93 } 94 95 // Make sure a "titlebar only" toolbar with location bar works. 96 TEST_F(ToolbarControllerTest, TitlebarOnly) { 97 NSView* view = [bar_ view]; 98 99 [bar_ setHasToolbar:NO hasLocationBar:YES]; 100 EXPECT_NE(view, [bar_ view]); 101 102 // Simulate a popup going fullscreen and back by performing the reparenting 103 // that happens during fullscreen transitions 104 NSView* superview = [view superview]; 105 [view removeFromSuperview]; 106 [superview addSubview:view]; 107 108 [bar_ setHasToolbar:YES hasLocationBar:YES]; 109 EXPECT_EQ(view, [bar_ view]); 110 111 // Leave it off to make sure that's fine 112 [bar_ setHasToolbar:NO hasLocationBar:YES]; 113 } 114 115 // Make sure it works in the completely undecorated case. 116 TEST_F(ToolbarControllerTest, NoLocationBar) { 117 NSView* view = [bar_ view]; 118 119 [bar_ setHasToolbar:NO hasLocationBar:NO]; 120 EXPECT_NE(view, [bar_ view]); 121 EXPECT_TRUE([[bar_ view] isHidden]); 122 123 // Simulate a popup going fullscreen and back by performing the reparenting 124 // that happens during fullscreen transitions 125 NSView* superview = [view superview]; 126 [view removeFromSuperview]; 127 [superview addSubview:view]; 128 } 129 130 // Make some changes to the enabled state of a few of the buttons and ensure 131 // that we're still in sync. 132 TEST_F(ToolbarControllerTest, UpdateEnabledState) { 133 CommandUpdater* updater = helper_.browser()->command_updater(); 134 EXPECT_FALSE(updater->IsCommandEnabled(IDC_BACK)); 135 EXPECT_FALSE(updater->IsCommandEnabled(IDC_FORWARD)); 136 updater->UpdateCommandEnabled(IDC_BACK, true); 137 updater->UpdateCommandEnabled(IDC_FORWARD, true); 138 CompareState(updater, [bar_ toolbarViews]); 139 } 140 141 // Focus the location bar and make sure that it's the first responder. 142 TEST_F(ToolbarControllerTest, FocusLocation) { 143 NSWindow* window = test_window(); 144 [window makeFirstResponder:[window contentView]]; 145 EXPECT_EQ([window firstResponder], [window contentView]); 146 [bar_ focusLocationBar:YES]; 147 EXPECT_NE([window firstResponder], [window contentView]); 148 NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex]; 149 EXPECT_EQ([window firstResponder], [(id)locationBar currentEditor]); 150 } 151 152 TEST_F(ToolbarControllerTest, LoadingState) { 153 // In its initial state, the reload button has a tag of 154 // IDC_RELOAD. When loading, it should be IDC_STOP. 155 NSButton* reload = [[bar_ toolbarViews] objectAtIndex:kReloadIndex]; 156 EXPECT_EQ([reload tag], IDC_RELOAD); 157 [bar_ setIsLoading:YES force:YES]; 158 EXPECT_EQ([reload tag], IDC_STOP); 159 [bar_ setIsLoading:NO force:YES]; 160 EXPECT_EQ([reload tag], IDC_RELOAD); 161 } 162 163 // Check that toggling the state of the home button changes the visible 164 // state of the home button and moves the other items accordingly. 165 TEST_F(ToolbarControllerTest, ToggleHome) { 166 PrefService* prefs = helper_.profile()->GetPrefs(); 167 bool showHome = prefs->GetBoolean(prefs::kShowHomeButton); 168 NSView* homeButton = [[bar_ toolbarViews] objectAtIndex:kHomeIndex]; 169 EXPECT_EQ(showHome, ![homeButton isHidden]); 170 171 NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex]; 172 NSRect originalLocationBarFrame = [locationBar frame]; 173 174 // Toggle the pref and make sure the button changed state and the other 175 // views moved. 176 prefs->SetBoolean(prefs::kShowHomeButton, !showHome); 177 EXPECT_EQ(showHome, [homeButton isHidden]); 178 EXPECT_NE(NSMinX(originalLocationBarFrame), NSMinX([locationBar frame])); 179 EXPECT_NE(NSWidth(originalLocationBarFrame), NSWidth([locationBar frame])); 180 } 181 182 // Ensure that we don't toggle the buttons when we have a strip marked as not 183 // having the full toolbar. Also ensure that the location bar doesn't change 184 // size. 185 TEST_F(ToolbarControllerTest, DontToggleWhenNoToolbar) { 186 [bar_ setHasToolbar:NO hasLocationBar:YES]; 187 NSView* homeButton = [[bar_ toolbarViews] objectAtIndex:kHomeIndex]; 188 NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex]; 189 NSRect locationBarFrame = [locationBar frame]; 190 EXPECT_EQ([homeButton isHidden], YES); 191 [bar_ showOptionalHomeButton]; 192 EXPECT_EQ([homeButton isHidden], YES); 193 NSRect newLocationBarFrame = [locationBar frame]; 194 EXPECT_TRUE(NSEqualRects(locationBarFrame, newLocationBarFrame)); 195 newLocationBarFrame = [locationBar frame]; 196 EXPECT_TRUE(NSEqualRects(locationBarFrame, newLocationBarFrame)); 197 } 198 199 TEST_F(ToolbarControllerTest, BookmarkBubblePoint) { 200 const NSPoint starPoint = [bar_ bookmarkBubblePoint]; 201 const NSRect barFrame = 202 [[bar_ view] convertRect:[[bar_ view] bounds] toView:nil]; 203 204 // Make sure the star is completely inside the location bar. 205 EXPECT_TRUE(NSPointInRect(starPoint, barFrame)); 206 } 207 208 TEST_F(ToolbarControllerTest, HoverButtonForEvent) { 209 scoped_nsobject<HitView> view([[HitView alloc] 210 initWithFrame:NSMakeRect(0,0,100,100)]); 211 [bar_ setView:view]; 212 NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved 213 location:NSMakePoint(10,10) 214 modifierFlags:0 215 timestamp:0 216 windowNumber:0 217 context:nil 218 eventNumber:0 219 clickCount:0 220 pressure:0.0]; 221 222 // NOT a match. 223 [view setHitTestReturn:bar_.get()]; 224 EXPECT_FALSE([bar_ hoverButtonForEvent:event]); 225 226 // Not yet... 227 scoped_nsobject<NSButton> button([[NSButton alloc] init]); 228 [view setHitTestReturn:button]; 229 EXPECT_FALSE([bar_ hoverButtonForEvent:event]); 230 231 // Now! 232 scoped_nsobject<GradientButtonCell> cell([[GradientButtonCell alloc] init]); 233 [button setCell:cell.get()]; 234 EXPECT_TRUE([bar_ hoverButtonForEvent:nil]); 235 } 236 237 } // namespace 238