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