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