Home | History | Annotate | Download | only in bookmarks
      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 #include "base/basictypes.h"
      8 #include "base/command_line.h"
      9 #include "base/mac/scoped_nsobject.h"
     10 #include "base/strings/string16.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/sys_string_conversions.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
     15 #include "chrome/browser/extensions/test_extension_system.h"
     16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
     17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
     18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h"
     19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h"
     20 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h"
     21 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
     22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"
     23 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
     24 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
     25 #include "chrome/common/chrome_switches.h"
     26 #include "chrome/common/pref_names.h"
     27 #include "chrome/test/base/testing_pref_service_syncable.h"
     28 #include "chrome/test/base/testing_profile.h"
     29 #include "components/bookmarks/browser/bookmark_model.h"
     30 #include "components/bookmarks/browser/bookmark_utils.h"
     31 #include "components/bookmarks/test/bookmark_test_helpers.h"
     32 #include "testing/gtest/include/gtest/gtest.h"
     33 #import "testing/gtest_mac.h"
     34 #include "testing/platform_test.h"
     35 #import "third_party/ocmock/OCMock/OCMock.h"
     36 #include "third_party/ocmock/gtest_support.h"
     37 #include "ui/base/cocoa/animation_utils.h"
     38 #include "ui/base/theme_provider.h"
     39 #include "ui/events/test/cocoa_test_event_utils.h"
     40 #include "ui/gfx/image/image_skia.h"
     41 
     42 using base::ASCIIToUTF16;
     43 
     44 // Unit tests don't need time-consuming asynchronous animations.
     45 @interface BookmarkBarControllerTestable : BookmarkBarController {
     46 }
     47 
     48 @end
     49 
     50 @implementation BookmarkBarControllerTestable
     51 
     52 - (id)initWithBrowser:(Browser*)browser
     53          initialWidth:(CGFloat)initialWidth
     54              delegate:(id<BookmarkBarControllerDelegate>)delegate
     55        resizeDelegate:(id<ViewResizer>)resizeDelegate {
     56   if ((self = [super initWithBrowser:browser
     57                         initialWidth:initialWidth
     58                             delegate:delegate
     59                       resizeDelegate:resizeDelegate])) {
     60     [self setStateAnimationsEnabled:NO];
     61     [self setInnerContentAnimationsEnabled:NO];
     62   }
     63   return self;
     64 }
     65 
     66 @end
     67 
     68 // Just like a BookmarkBarController but openURL: is stubbed out.
     69 @interface BookmarkBarControllerNoOpen : BookmarkBarControllerTestable {
     70  @public
     71   std::vector<GURL> urls_;
     72   std::vector<WindowOpenDisposition> dispositions_;
     73 }
     74 @end
     75 
     76 @implementation BookmarkBarControllerNoOpen
     77 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition {
     78   urls_.push_back(url);
     79   dispositions_.push_back(disposition);
     80 }
     81 - (void)clear {
     82   urls_.clear();
     83   dispositions_.clear();
     84 }
     85 @end
     86 
     87 
     88 // NSCell that is pre-provided with a desired size that becomes the
     89 // return value for -(NSSize)cellSize:.
     90 @interface CellWithDesiredSize : NSCell {
     91  @private
     92   NSSize cellSize_;
     93 }
     94 @property (nonatomic, readonly) NSSize cellSize;
     95 @end
     96 
     97 @implementation CellWithDesiredSize
     98 
     99 @synthesize cellSize = cellSize_;
    100 
    101 - (id)initTextCell:(NSString*)string desiredSize:(NSSize)size {
    102   if ((self = [super initTextCell:string])) {
    103     cellSize_ = size;
    104   }
    105   return self;
    106 }
    107 
    108 @end
    109 
    110 // Remember the number of times we've gotten a frameDidChange notification.
    111 @interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen {
    112  @private
    113   int toggles_;
    114 }
    115 @property (nonatomic, readonly) int toggles;
    116 @end
    117 
    118 @implementation BookmarkBarControllerTogglePong
    119 
    120 @synthesize toggles = toggles_;
    121 
    122 - (void)frameDidChange {
    123   toggles_++;
    124 }
    125 
    126 @end
    127 
    128 // Remembers if a notification callback was called.
    129 @interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen {
    130   BOOL windowWillCloseReceived_;
    131   BOOL windowDidResignKeyReceived_;
    132 }
    133 @property (nonatomic, readonly) BOOL windowWillCloseReceived;
    134 @property (nonatomic, readonly) BOOL windowDidResignKeyReceived;
    135 @end
    136 
    137 @implementation BookmarkBarControllerNotificationPong
    138 @synthesize windowWillCloseReceived = windowWillCloseReceived_;
    139 @synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_;
    140 
    141 // Override NSNotificationCenter callback.
    142 - (void)parentWindowWillClose:(NSNotification*)notification {
    143   windowWillCloseReceived_ = YES;
    144 }
    145 
    146 // NSNotificationCenter callback.
    147 - (void)parentWindowDidResignKey:(NSNotification*)notification {
    148   windowDidResignKeyReceived_ = YES;
    149 }
    150 @end
    151 
    152 // Remembers if and what kind of openAll was performed.
    153 @interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen {
    154   WindowOpenDisposition dispositionDetected_;
    155 }
    156 @property (nonatomic) WindowOpenDisposition dispositionDetected;
    157 @end
    158 
    159 @implementation BookmarkBarControllerOpenAllPong
    160 @synthesize dispositionDetected = dispositionDetected_;
    161 
    162 // Intercede for the openAll:disposition: method.
    163 - (void)openAll:(const BookmarkNode*)node
    164     disposition:(WindowOpenDisposition)disposition {
    165   [self setDispositionDetected:disposition];
    166 }
    167 
    168 @end
    169 
    170 // Just like a BookmarkBarController but intercedes when providing
    171 // pasteboard drag data.
    172 @interface BookmarkBarControllerDragData : BookmarkBarControllerTestable {
    173   const BookmarkNode* dragDataNode_;  // Weak
    174 }
    175 - (void)setDragDataNode:(const BookmarkNode*)node;
    176 @end
    177 
    178 @implementation BookmarkBarControllerDragData
    179 
    180 - (id)initWithBrowser:(Browser*)browser
    181          initialWidth:(CGFloat)initialWidth
    182              delegate:(id<BookmarkBarControllerDelegate>)delegate
    183        resizeDelegate:(id<ViewResizer>)resizeDelegate {
    184   if ((self = [super initWithBrowser:browser
    185                         initialWidth:initialWidth
    186                             delegate:delegate
    187                       resizeDelegate:resizeDelegate])) {
    188     dragDataNode_ = NULL;
    189   }
    190   return self;
    191 }
    192 
    193 - (void)setDragDataNode:(const BookmarkNode*)node {
    194   dragDataNode_ = node;
    195 }
    196 
    197 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
    198   std::vector<const BookmarkNode*> dragDataNodes;
    199   if(dragDataNode_) {
    200     dragDataNodes.push_back(dragDataNode_);
    201   }
    202   return dragDataNodes;
    203 }
    204 
    205 @end
    206 
    207 
    208 class FakeTheme : public ui::ThemeProvider {
    209  public:
    210   FakeTheme(NSColor* color) : color_(color) {}
    211   base::scoped_nsobject<NSColor> color_;
    212 
    213   virtual bool UsingSystemTheme() const OVERRIDE {
    214     return true;
    215   }
    216   virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE {
    217     return NULL;
    218   }
    219   virtual SkColor GetColor(int id) const OVERRIDE { return SkColor(); }
    220   virtual int GetDisplayProperty(int id) const OVERRIDE {
    221     return -1;
    222   }
    223   virtual bool ShouldUseNativeFrame() const OVERRIDE { return false; }
    224   virtual bool HasCustomImage(int id) const OVERRIDE { return false; }
    225   virtual base::RefCountedMemory* GetRawData(
    226       int id,
    227       ui::ScaleFactor scale_factor) const OVERRIDE {
    228     return NULL;
    229   }
    230   virtual NSImage* GetNSImageNamed(int id) const OVERRIDE {
    231     return nil;
    232   }
    233   virtual NSColor* GetNSImageColorNamed(int id) const OVERRIDE {
    234     return nil;
    235   }
    236   virtual NSColor* GetNSColor(int id) const OVERRIDE {
    237     return color_.get();
    238   }
    239   virtual NSColor* GetNSColorTint(int id) const OVERRIDE {
    240     return nil;
    241   }
    242   virtual NSGradient* GetNSGradient(int id) const OVERRIDE {
    243     return nil;
    244   }
    245 };
    246 
    247 
    248 @interface FakeDragInfo : NSObject {
    249  @public
    250   NSPoint dropLocation_;
    251   NSDragOperation sourceMask_;
    252 }
    253 @property (nonatomic, assign) NSPoint dropLocation;
    254 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
    255 @end
    256 
    257 @implementation FakeDragInfo
    258 
    259 @synthesize dropLocation = dropLocation_;
    260 
    261 - (id)init {
    262   if ((self = [super init])) {
    263     dropLocation_ = NSZeroPoint;
    264     sourceMask_ = NSDragOperationMove;
    265   }
    266   return self;
    267 }
    268 
    269 // NSDraggingInfo protocol functions.
    270 
    271 - (id)draggingPasteboard {
    272   return self;
    273 }
    274 
    275 - (id)draggingSource {
    276   return self;
    277 }
    278 
    279 - (NSDragOperation)draggingSourceOperationMask {
    280   return sourceMask_;
    281 }
    282 
    283 - (NSPoint)draggingLocation {
    284   return dropLocation_;
    285 }
    286 
    287 // Other functions.
    288 
    289 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
    290   sourceMask_ = mask;
    291 }
    292 
    293 @end
    294 
    295 
    296 namespace {
    297 
    298 class BookmarkBarControllerTestBase : public CocoaProfileTest {
    299  public:
    300   base::scoped_nsobject<NSView> parent_view_;
    301   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
    302 
    303   virtual void SetUp() {
    304     CocoaProfileTest::SetUp();
    305     ASSERT_TRUE(profile());
    306 
    307     base::FilePath extension_dir;
    308     static_cast<extensions::TestExtensionSystem*>(
    309         extensions::ExtensionSystem::Get(profile()))->
    310         CreateExtensionService(
    311             CommandLine::ForCurrentProcess(),
    312             extension_dir, false);
    313     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
    314     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
    315     parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
    316     [parent_view_ setHidden:YES];
    317   }
    318 
    319   void InstallAndToggleBar(BookmarkBarController* bar) {
    320     // Force loading of the nib.
    321     [bar view];
    322     // Awkwardness to look like we've been installed.
    323     for (NSView* subView in [parent_view_ subviews])
    324       [subView removeFromSuperview];
    325     [parent_view_ addSubview:[bar view]];
    326     NSRect frame = [[[bar view] superview] frame];
    327     frame.origin.y = 100;
    328     [[[bar view] superview] setFrame:frame];
    329 
    330     // Make sure it's on in a window so viewDidMoveToWindow is called
    331     NSView* contentView = [test_window() contentView];
    332     if (![parent_view_ isDescendantOf:contentView])
    333       [contentView addSubview:parent_view_];
    334 
    335     // Make sure it's open so certain things aren't no-ops.
    336     [bar updateState:BookmarkBar::SHOW
    337           changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    338   }
    339 };
    340 
    341 class BookmarkBarControllerTest : public BookmarkBarControllerTestBase {
    342  public:
    343   base::scoped_nsobject<BookmarkBarControllerNoOpen> bar_;
    344 
    345   virtual void SetUp() OVERRIDE {
    346     BookmarkBarControllerTestBase::SetUp();
    347     ASSERT_TRUE(browser());
    348     AddCommandLineSwitches();
    349 
    350     bar_.reset(
    351       [[BookmarkBarControllerNoOpen alloc]
    352           initWithBrowser:browser()
    353              initialWidth:NSWidth([parent_view_ frame])
    354                  delegate:nil
    355            resizeDelegate:resizeDelegate_.get()]);
    356 
    357     InstallAndToggleBar(bar_.get());
    358   }
    359 
    360   virtual void AddCommandLineSwitches() {}
    361 
    362   BookmarkBarControllerNoOpen* noOpenBar() {
    363     return (BookmarkBarControllerNoOpen*)bar_.get();
    364   }
    365 };
    366 
    367 TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) {
    368   [bar_ updateState:BookmarkBar::SHOW
    369          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    370   EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
    371   EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
    372   EXPECT_TRUE([bar_ isVisible]);
    373   EXPECT_FALSE([bar_ isAnimationRunning]);
    374   EXPECT_FALSE([[bar_ view] isHidden]);
    375   EXPECT_GT([resizeDelegate_ height], 0);
    376   EXPECT_GT([[bar_ view] frame].size.height, 0);
    377 }
    378 
    379 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) {
    380   [bar_ updateState:BookmarkBar::HIDDEN
    381          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    382   EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
    383   EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
    384   EXPECT_FALSE([bar_ isVisible]);
    385   EXPECT_FALSE([bar_ isAnimationRunning]);
    386   EXPECT_TRUE([[bar_ view] isHidden]);
    387   EXPECT_EQ(0, [resizeDelegate_ height]);
    388   EXPECT_EQ(0, [[bar_ view] frame].size.height);
    389 }
    390 
    391 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) {
    392   [bar_ setBookmarkBarEnabled:NO];
    393   [bar_ updateState:BookmarkBar::SHOW
    394          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    395   EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
    396   EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
    397   EXPECT_FALSE([bar_ isVisible]);
    398   EXPECT_FALSE([bar_ isAnimationRunning]);
    399   EXPECT_TRUE([[bar_ view] isHidden]);
    400   EXPECT_EQ(0, [resizeDelegate_ height]);
    401   EXPECT_EQ(0, [[bar_ view] frame].size.height);
    402 }
    403 
    404 TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) {
    405   [bar_ updateState:BookmarkBar::DETACHED
    406          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    407   EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
    408   EXPECT_TRUE([bar_ isInState:BookmarkBar::DETACHED]);
    409   EXPECT_TRUE([bar_ isVisible]);
    410   EXPECT_FALSE([bar_ isAnimationRunning]);
    411   EXPECT_FALSE([[bar_ view] isHidden]);
    412   EXPECT_GT([resizeDelegate_ height], 0);
    413   EXPECT_GT([[bar_ view] frame].size.height, 0);
    414 
    415   // Make sure no buttons fall off the bar, either now or when resized
    416   // bigger or smaller.
    417   CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 };
    418   CGFloat previousX = 0.0;
    419   for (unsigned x = 0; x < arraysize(sizes); x++) {
    420     // Confirm the buttons moved from the last check (which may be
    421     // init but that's fine).
    422     CGFloat newX = [[bar_ offTheSideButton] frame].origin.x;
    423     EXPECT_NE(previousX, newX);
    424     previousX = newX;
    425 
    426     // Confirm the buttons have a reasonable bounds. Recall that |-frame|
    427     // returns rectangles in the superview's coordinates.
    428     NSRect buttonViewFrame =
    429         [[bar_ buttonView] convertRect:[[bar_ buttonView] frame]
    430                               fromView:[[bar_ buttonView] superview]];
    431     EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]);
    432     EXPECT_TRUE(NSContainsRect(buttonViewFrame,
    433                                [[bar_ offTheSideButton] frame]));
    434     EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]);
    435     EXPECT_TRUE(NSContainsRect(buttonViewFrame,
    436                                [[bar_ otherBookmarksButton] frame]));
    437 
    438     // Now move them implicitly.
    439     // We confirm FrameChangeNotification works in the next unit test;
    440     // we simply assume it works here to resize or reposition the
    441     // buttons above.
    442     NSRect frame = [[bar_ view] frame];
    443     frame.size.width += sizes[x];
    444     [[bar_ view] setFrame:frame];
    445   }
    446 }
    447 
    448 // Test whether |-updateState:...| sets currentState as expected. Make
    449 // sure things don't crash.
    450 TEST_F(BookmarkBarControllerTest, StateChanges) {
    451   // First, go in one-at-a-time cycle.
    452   [bar_ updateState:BookmarkBar::HIDDEN
    453          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    454   EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
    455   EXPECT_FALSE([bar_ isVisible]);
    456   EXPECT_FALSE([bar_ isAnimationRunning]);
    457 
    458   [bar_ updateState:BookmarkBar::SHOW
    459          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    460   EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
    461   EXPECT_TRUE([bar_ isVisible]);
    462   EXPECT_FALSE([bar_ isAnimationRunning]);
    463 
    464   [bar_ updateState:BookmarkBar::DETACHED
    465          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    466   EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
    467   EXPECT_TRUE([bar_ isVisible]);
    468   EXPECT_FALSE([bar_ isAnimationRunning]);
    469 
    470   // Now try some "jumps".
    471   for (int i = 0; i < 2; i++) {
    472   [bar_ updateState:BookmarkBar::HIDDEN
    473          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    474     EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
    475     EXPECT_FALSE([bar_ isVisible]);
    476     EXPECT_FALSE([bar_ isAnimationRunning]);
    477 
    478     [bar_ updateState:BookmarkBar::SHOW
    479            changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    480     EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
    481     EXPECT_TRUE([bar_ isVisible]);
    482     EXPECT_FALSE([bar_ isAnimationRunning]);
    483   }
    484 
    485   // Now try some "jumps".
    486   for (int i = 0; i < 2; i++) {
    487     [bar_ updateState:BookmarkBar::SHOW
    488            changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    489     EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
    490     EXPECT_TRUE([bar_ isVisible]);
    491     EXPECT_FALSE([bar_ isAnimationRunning]);
    492 
    493     [bar_ updateState:BookmarkBar::DETACHED
    494            changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    495     EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
    496     EXPECT_TRUE([bar_ isVisible]);
    497     EXPECT_FALSE([bar_ isAnimationRunning]);
    498   }
    499 }
    500 
    501 // Make sure we're watching for frame change notifications.
    502 TEST_F(BookmarkBarControllerTest, FrameChangeNotification) {
    503   base::scoped_nsobject<BookmarkBarControllerTogglePong> bar;
    504   bar.reset(
    505     [[BookmarkBarControllerTogglePong alloc]
    506           initWithBrowser:browser()
    507              initialWidth:100  // arbitrary
    508                  delegate:nil
    509            resizeDelegate:resizeDelegate_.get()]);
    510   InstallAndToggleBar(bar.get());
    511 
    512   // Send a frame did change notification for the pong's view.
    513   [[NSNotificationCenter defaultCenter]
    514     postNotificationName:NSViewFrameDidChangeNotification
    515                   object:[bar view]];
    516 
    517   EXPECT_GT([bar toggles], 0);
    518 }
    519 
    520 // Confirm our "no items" container goes away when we add the 1st
    521 // bookmark, and comes back when we delete the bookmark.
    522 TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) {
    523   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    524   const BookmarkNode* bar = model->bookmark_bar_node();
    525 
    526   [bar_ loaded:model];
    527   BookmarkBarView* view = [bar_ buttonView];
    528   DCHECK(view);
    529   NSView* noItemContainer = [view noItemContainer];
    530   DCHECK(noItemContainer);
    531 
    532   EXPECT_FALSE([noItemContainer isHidden]);
    533   const BookmarkNode* node = model->AddURL(bar, bar->child_count(),
    534                                            ASCIIToUTF16("title"),
    535                                            GURL("http://www.google.com"));
    536   EXPECT_TRUE([noItemContainer isHidden]);
    537   model->Remove(bar, bar->GetIndexOf(node));
    538   EXPECT_FALSE([noItemContainer isHidden]);
    539 
    540   // Now try it using a bookmark from the Other Bookmarks.
    541   const BookmarkNode* otherBookmarks = model->other_node();
    542   node = model->AddURL(otherBookmarks, otherBookmarks->child_count(),
    543                        ASCIIToUTF16("TheOther"),
    544                        GURL("http://www.other.com"));
    545   EXPECT_FALSE([noItemContainer isHidden]);
    546   // Move it from Other Bookmarks to the bar.
    547   model->Move(node, bar, 0);
    548   EXPECT_TRUE([noItemContainer isHidden]);
    549   // Move it back to Other Bookmarks from the bar.
    550   model->Move(node, otherBookmarks, 0);
    551   EXPECT_FALSE([noItemContainer isHidden]);
    552 }
    553 
    554 // Confirm off the side button only enabled when reasonable.
    555 TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
    556   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    557 
    558   [bar_ loaded:model];
    559   EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
    560 
    561   for (int i = 0; i < 2; i++) {
    562     bookmark_utils::AddIfNotBookmarked(
    563         model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
    564     EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
    565   }
    566 
    567   const BookmarkNode* parent = model->bookmark_bar_node();
    568   for (int i = 0; i < 20; i++) {
    569     model->AddURL(parent, parent->child_count(),
    570                   ASCIIToUTF16("super duper wide title"),
    571                   GURL("http://superfriends.hall-of-justice.edu"));
    572   }
    573   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
    574 
    575   // Open the "off the side" and start deleting nodes.  Make sure
    576   // deletion of the last node in "off the side" causes the folder to
    577   // close.
    578   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
    579   NSButton* offTheSideButton = [bar_ offTheSideButton];
    580   // Open "off the side" menu.
    581   [bar_ openOffTheSideFolderFromButton:offTheSideButton];
    582   BookmarkBarFolderController* bbfc = [bar_ folderController];
    583   EXPECT_TRUE(bbfc);
    584   [bbfc setIgnoreAnimations:YES];
    585   while (!parent->empty()) {
    586     // We've completed the job so we're done.
    587     if ([bar_ offTheSideButtonIsHidden])
    588       break;
    589     // Delete the last button.
    590     model->Remove(parent, parent->child_count() - 1);
    591     // If last one make sure the menu is closed and the button is hidden.
    592     // Else make sure menu stays open.
    593     if ([bar_ offTheSideButtonIsHidden]) {
    594       EXPECT_FALSE([bar_ folderController]);
    595     } else {
    596       EXPECT_TRUE([bar_ folderController]);
    597     }
    598   }
    599 }
    600 
    601 // http://crbug.com/46175 is a crash when deleting bookmarks from the
    602 // off-the-side menu while it is open.  This test tries to bang hard
    603 // in this area to reproduce the crash.
    604 TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) {
    605   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    606   [bar_ loaded:model];
    607 
    608   // Add a lot of bookmarks (per the bug).
    609   const BookmarkNode* parent = model->bookmark_bar_node();
    610   for (int i = 0; i < 100; i++) {
    611     std::ostringstream title;
    612     title << "super duper wide title " << i;
    613     model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()),
    614                   GURL("http://superfriends.hall-of-justice.edu"));
    615   }
    616   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
    617 
    618   // Open "off the side" menu.
    619   NSButton* offTheSideButton = [bar_ offTheSideButton];
    620   [bar_ openOffTheSideFolderFromButton:offTheSideButton];
    621   BookmarkBarFolderController* bbfc = [bar_ folderController];
    622   EXPECT_TRUE(bbfc);
    623   [bbfc setIgnoreAnimations:YES];
    624 
    625   // Start deleting items; try and delete randomish ones in case it
    626   // makes a difference.
    627   int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 };
    628   while (!parent->empty()) {
    629     for (unsigned int i = 0; i < arraysize(indices); i++) {
    630       if (indices[i] < parent->child_count()) {
    631         // First we mouse-enter the button to make things harder.
    632         NSArray* buttons = [bbfc buttons];
    633         for (BookmarkButton* button in buttons) {
    634           if ([button bookmarkNode] == parent->GetChild(indices[i])) {
    635             [bbfc mouseEnteredButton:button event:nil];
    636             break;
    637           }
    638         }
    639         // Then we remove the node.  This triggers the button to get
    640         // deleted.
    641         model->Remove(parent, indices[i]);
    642         // Force visual update which is otherwise delayed.
    643         [[bbfc window] displayIfNeeded];
    644       }
    645     }
    646   }
    647 }
    648 
    649 // Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is
    650 // detached.
    651 TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) {
    652   [bar_ updateState:BookmarkBar::HIDDEN
    653          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    654   EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
    655 
    656   [bar_ updateState:BookmarkBar::SHOW
    657          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    658   EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
    659 
    660   [bar_ updateState:BookmarkBar::DETACHED
    661          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
    662   EXPECT_FALSE([bar_ dragShouldLockBarVisibility]);
    663 }
    664 
    665 TEST_F(BookmarkBarControllerTest, TagMap) {
    666   int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 };
    667   std::vector<int32> tags;
    668 
    669   // Generate some tags
    670   for (unsigned int i = 0; i < arraysize(ids); i++) {
    671     tags.push_back([bar_ menuTagFromNodeId:ids[i]]);
    672   }
    673 
    674   // Confirm reverse mapping.
    675   for (unsigned int i = 0; i < arraysize(ids); i++) {
    676     EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]);
    677   }
    678 
    679   // Confirm uniqueness.
    680   std::sort(tags.begin(), tags.end());
    681   for (unsigned int i=0; i<(tags.size()-1); i++) {
    682     EXPECT_NE(tags[i], tags[i+1]);
    683   }
    684 }
    685 
    686 TEST_F(BookmarkBarControllerTest, MenuForFolderNode) {
    687   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    688 
    689   // First make sure something (e.g. "(empty)" string) is always present.
    690   NSMenu* menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
    691   EXPECT_GT([menu numberOfItems], 0);
    692 
    693   // Test two bookmarks.
    694   GURL gurl("http://www.foo.com");
    695   bookmark_utils::AddIfNotBookmarked(model, gurl, ASCIIToUTF16("small"));
    696   bookmark_utils::AddIfNotBookmarked(
    697       model, GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title"));
    698   menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
    699   EXPECT_EQ([menu numberOfItems], 2);
    700   NSMenuItem *item = [menu itemWithTitle:@"bigger title"];
    701   EXPECT_TRUE(item);
    702   item = [menu itemWithTitle:@"small"];
    703   EXPECT_TRUE(item);
    704   if (item) {
    705     int64 tag = [bar_ nodeIdFromMenuTag:[item tag]];
    706     const BookmarkNode* node = GetBookmarkNodeByID(model, tag);
    707     EXPECT_TRUE(node);
    708     EXPECT_EQ(gurl, node->url());
    709   }
    710 
    711   // Test with an actual folder as well
    712   const BookmarkNode* parent = model->bookmark_bar_node();
    713   const BookmarkNode* folder = model->AddFolder(parent,
    714                                                 parent->child_count(),
    715                                                 ASCIIToUTF16("folder"));
    716   model->AddURL(folder, folder->child_count(),
    717                 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
    718   model->AddURL(folder, folder->child_count(),
    719                 ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com"));
    720   menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
    721   EXPECT_EQ([menu numberOfItems], 3);
    722 
    723   item = [menu itemWithTitle:@"folder"];
    724   EXPECT_TRUE(item);
    725   EXPECT_TRUE([item hasSubmenu]);
    726   NSMenu *submenu = [item submenu];
    727   EXPECT_TRUE(submenu);
    728   EXPECT_EQ(2, [submenu numberOfItems]);
    729   EXPECT_TRUE([submenu itemWithTitle:@"f1"]);
    730   EXPECT_TRUE([submenu itemWithTitle:@"f2"]);
    731 }
    732 
    733 // Confirm openBookmark: forwards the request to the controller's delegate
    734 TEST_F(BookmarkBarControllerTest, OpenBookmark) {
    735   GURL gurl("http://walla.walla.ding.dong.com");
    736   scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl));
    737 
    738   base::scoped_nsobject<BookmarkButtonCell> cell(
    739       [[BookmarkButtonCell alloc] init]);
    740   [cell setBookmarkNode:node.get()];
    741   base::scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]);
    742   [button setCell:cell.get()];
    743   [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]];
    744 
    745   [bar_ openBookmark:button];
    746   EXPECT_EQ(noOpenBar()->urls_[0], node->url());
    747   EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB);
    748 }
    749 
    750 TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
    751   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    752   NSView* buttonView = [bar_ buttonView];
    753   EXPECT_EQ(0U, [[bar_ buttons] count]);
    754   unsigned int initial_subview_count = [[buttonView subviews] count];
    755 
    756   // Make sure a redundant call doesn't choke
    757   [bar_ clearBookmarkBar];
    758   EXPECT_EQ(0U, [[bar_ buttons] count]);
    759   EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
    760 
    761   GURL gurl1("http://superfriends.hall-of-justice.edu");
    762   // Short titles increase the chances of this test succeeding if the view is
    763   // narrow.
    764   // TODO(viettrungluu): make the test independent of window/view size, font
    765   // metrics, button size and spacing, and everything else.
    766   base::string16 title1(ASCIIToUTF16("x"));
    767   bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
    768   EXPECT_EQ(1U, [[bar_ buttons] count]);
    769   EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
    770 
    771   GURL gurl2("http://legion-of-doom.gov");
    772   base::string16 title2(ASCIIToUTF16("y"));
    773   bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
    774   EXPECT_EQ(2U, [[bar_ buttons] count]);
    775   EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
    776 
    777   for (int i = 0; i < 3; i++) {
    778     bookmark_utils::RemoveAllBookmarks(model, gurl2);
    779     EXPECT_EQ(1U, [[bar_ buttons] count]);
    780     EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
    781 
    782     // and bring it back
    783     bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
    784     EXPECT_EQ(2U, [[bar_ buttons] count]);
    785     EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
    786   }
    787 
    788   [bar_ clearBookmarkBar];
    789   EXPECT_EQ(0U, [[bar_ buttons] count]);
    790   EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
    791 
    792   // Explicit test of loaded: since this is a convenient spot
    793   [bar_ loaded:model];
    794   EXPECT_EQ(2U, [[bar_ buttons] count]);
    795   EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
    796 }
    797 
    798 // Make sure we don't create too many buttons; we only really need
    799 // ones that will be visible.
    800 TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
    801   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    802   EXPECT_EQ(0U, [[bar_ buttons] count]);
    803   // Add one; make sure we see it.
    804   const BookmarkNode* parent = model->bookmark_bar_node();
    805   model->AddURL(parent, parent->child_count(),
    806                 ASCIIToUTF16("title"), GURL("http://www.google.com"));
    807   EXPECT_EQ(1U, [[bar_ buttons] count]);
    808 
    809   // Add 30 which we expect to be 'too many'.  Make sure we don't see
    810   // 30 buttons.
    811   model->Remove(parent, 0);
    812   EXPECT_EQ(0U, [[bar_ buttons] count]);
    813   for (int i=0; i<30; i++) {
    814     model->AddURL(parent, parent->child_count(),
    815                   ASCIIToUTF16("title"), GURL("http://www.google.com"));
    816   }
    817   int count = [[bar_ buttons] count];
    818   EXPECT_LT(count, 30L);
    819 
    820   // Add 10 more (to the front of the list so the on-screen buttons
    821   // would change) and make sure the count stays the same.
    822   for (int i=0; i<10; i++) {
    823     model->AddURL(parent, 0,  /* index is 0, so front, not end */
    824                   ASCIIToUTF16("title"), GURL("http://www.google.com"));
    825   }
    826 
    827   // Finally, grow the view and make sure the button count goes up.
    828   NSRect frame = [[bar_ view] frame];
    829   frame.size.width += 600;
    830   [[bar_ view] setFrame:frame];
    831   int finalcount = [[bar_ buttons] count];
    832   EXPECT_GT(finalcount, count);
    833 }
    834 
    835 // Make sure that each button we add marches to the right and does not
    836 // overlap with the previous one.
    837 TEST_F(BookmarkBarControllerTest, TestButtonMarch) {
    838   base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]);
    839 
    840   CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 };
    841   for (unsigned int i = 0; i < arraysize(widths); i++) {
    842     NSCell* cell = [[CellWithDesiredSize alloc]
    843                      initTextCell:@"foo"
    844                       desiredSize:NSMakeSize(widths[i], 30)];
    845     [cells addObject:cell];
    846     [cell release];
    847   }
    848 
    849   int x_offset = 0;
    850   CGFloat x_end = x_offset;  // end of the previous button
    851   for (unsigned int i = 0; i < arraysize(widths); i++) {
    852     NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i]
    853                                             xOffset:&x_offset];
    854     EXPECT_GE(r.origin.x, x_end);
    855     x_end = NSMaxX(r);
    856   }
    857 }
    858 
    859 TEST_F(BookmarkBarControllerTest, CheckForGrowth) {
    860   WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope.
    861   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    862   GURL gurl1("http://www.google.com");
    863   base::string16 title1(ASCIIToUTF16("x"));
    864   bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
    865 
    866   GURL gurl2("http://www.google.com/blah");
    867   base::string16 title2(ASCIIToUTF16("y"));
    868   bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
    869 
    870   EXPECT_EQ(2U, [[bar_ buttons] count]);
    871   CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width;
    872   CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x;
    873 
    874   NSButton* first = [[bar_ buttons] objectAtIndex:0];
    875   [[first cell] setTitle:@"This is a really big title; watch out mom!"];
    876   [bar_ checkForBookmarkButtonGrowth:first];
    877 
    878   // Make sure the 1st button is now wider, the 2nd one is moved over,
    879   // and they don't overlap.
    880   NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame];
    881   NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame];
    882   EXPECT_GT(frame_1.size.width, width_1);
    883   EXPECT_GT(frame_2.origin.x, x_2);
    884   EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width);
    885 }
    886 
    887 TEST_F(BookmarkBarControllerTest, DeleteBookmark) {
    888   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    889 
    890   const char* urls[] = { "https://secret.url.com",
    891                          "http://super.duper.web.site.for.doodz.gov",
    892                          "http://www.foo-bar-baz.com/" };
    893   const BookmarkNode* parent = model->bookmark_bar_node();
    894   for (unsigned int i = 0; i < arraysize(urls); i++) {
    895     model->AddURL(parent, parent->child_count(),
    896                   ASCIIToUTF16("title"), GURL(urls[i]));
    897   }
    898   EXPECT_EQ(3, parent->child_count());
    899   const BookmarkNode* middle_node = parent->GetChild(1);
    900   model->Remove(middle_node->parent(),
    901                 middle_node->parent()->GetIndexOf(middle_node));
    902 
    903   EXPECT_EQ(2, parent->child_count());
    904   EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0]));
    905   // node 2 moved into spot 1
    906   EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2]));
    907 }
    908 
    909 // TODO(jrg): write a test to confirm that nodeFaviconLoaded calls
    910 // checkForBookmarkButtonGrowth:.
    911 
    912 TEST_F(BookmarkBarControllerTest, Cell) {
    913   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    914   [bar_ loaded:model];
    915 
    916   const BookmarkNode* parent = model->bookmark_bar_node();
    917   model->AddURL(parent, parent->child_count(),
    918                 ASCIIToUTF16("supertitle"),
    919                 GURL("http://superfriends.hall-of-justice.edu"));
    920   const BookmarkNode* node = parent->GetChild(0);
    921 
    922   NSCell* cell = [bar_ cellForBookmarkNode:node];
    923   EXPECT_TRUE(cell);
    924   EXPECT_NSEQ(@"supertitle", [cell title]);
    925   EXPECT_EQ(node, [[cell representedObject] pointerValue]);
    926   EXPECT_TRUE([cell menu]);
    927 
    928   // Empty cells still have a menu.
    929   cell = [bar_ cellForBookmarkNode:nil];
    930   EXPECT_TRUE([cell menu]);
    931   // Even empty cells have a title (of "(empty)")
    932   EXPECT_TRUE([cell title]);
    933 
    934   // cell is autoreleased; no need to release here
    935 }
    936 
    937 // Test drawing, mostly to ensure nothing leaks or crashes.
    938 TEST_F(BookmarkBarControllerTest, Display) {
    939   [[bar_ view] display];
    940 }
    941 
    942 // Test that middle clicking on a bookmark button results in an open action.
    943 TEST_F(BookmarkBarControllerTest, MiddleClick) {
    944   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    945   GURL gurl1("http://www.google.com/");
    946   base::string16 title1(ASCIIToUTF16("x"));
    947   bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
    948 
    949   EXPECT_EQ(1U, [[bar_ buttons] count]);
    950   NSButton* first = [[bar_ buttons] objectAtIndex:0];
    951   EXPECT_TRUE(first);
    952 
    953   [first otherMouseUp:
    954       cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
    955   EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
    956 }
    957 
    958 TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) {
    959   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    960   [bar_ loaded:model];
    961   EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]);
    962 }
    963 
    964 TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) {
    965   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    966 
    967   const BookmarkNode* parent = model->bookmark_bar_node();
    968   model->AddURL(parent, parent->child_count(),
    969                 ASCIIToUTF16("title"), GURL("http://one.com"));
    970 
    971   [bar_ loaded:model];
    972   EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]);
    973 }
    974 
    975 TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) {
    976   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
    977 
    978   const BookmarkNode* parent = model->bookmark_bar_node();
    979   model->AddURL(parent, parent->child_count(),
    980                 ASCIIToUTF16("title"), GURL("http://one.com"));
    981 
    982   [bar_ loaded:model];
    983 
    984   // Make sure the internal bookmark button also is the correct height.
    985   NSArray* buttons = [bar_ buttons];
    986   EXPECT_GT([buttons count], 0u);
    987   for (NSButton* button in buttons) {
    988     EXPECT_FLOAT_EQ(
    989         (chrome::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) -
    990             2 * bookmarks::kBookmarkVerticalPadding,
    991         [button frame].size.height);
    992   }
    993 }
    994 
    995 TEST_F(BookmarkBarControllerTest, DropBookmarks) {
    996   const char* urls[] = {
    997     "http://qwantz.com",
    998     "http://xkcd.com",
    999     "javascript:alert('lolwut')",
   1000     "file://localhost/tmp/local-file.txt"  // As if dragged from the desktop.
   1001   };
   1002   const char* titles[] = {
   1003     "Philosophoraptor",
   1004     "Can't draw",
   1005     "Inspiration",
   1006     "Frum stuf"
   1007   };
   1008   EXPECT_EQ(arraysize(urls), arraysize(titles));
   1009 
   1010   NSMutableArray* nsurls = [NSMutableArray array];
   1011   NSMutableArray* nstitles = [NSMutableArray array];
   1012   for (size_t i = 0; i < arraysize(urls); ++i) {
   1013     [nsurls addObject:base::SysUTF8ToNSString(urls[i])];
   1014     [nstitles addObject:base::SysUTF8ToNSString(titles[i])];
   1015   }
   1016 
   1017   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1018   const BookmarkNode* parent = model->bookmark_bar_node();
   1019   [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint];
   1020   EXPECT_EQ(4, parent->child_count());
   1021   for (int i = 0; i < parent->child_count(); ++i) {
   1022     GURL gurl = parent->GetChild(i)->url();
   1023     if (gurl.scheme() == "http" ||
   1024         gurl.scheme() == "javascript") {
   1025       EXPECT_EQ(parent->GetChild(i)->url(), GURL(urls[i]));
   1026     } else {
   1027       // Be flexible if the scheme needed to be added.
   1028       std::string gurl_string = gurl.spec();
   1029       std::string my_string = parent->GetChild(i)->url().spec();
   1030       EXPECT_NE(gurl_string.find(my_string), std::string::npos);
   1031     }
   1032     EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i]));
   1033   }
   1034 }
   1035 
   1036 TEST_F(BookmarkBarControllerTest, TestDragButton) {
   1037   WithNoAnimation at_all;
   1038   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1039 
   1040   GURL gurls[] = { GURL("http://www.google.com/a"),
   1041                    GURL("http://www.google.com/b"),
   1042                    GURL("http://www.google.com/c") };
   1043   base::string16 titles[] = { ASCIIToUTF16("a"),
   1044                               ASCIIToUTF16("b"),
   1045                               ASCIIToUTF16("c") };
   1046   for (unsigned i = 0; i < arraysize(titles); i++)
   1047     bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
   1048 
   1049   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
   1050   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
   1051 
   1052   [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
   1053                 to:NSZeroPoint
   1054               copy:NO];
   1055   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
   1056   // Make sure a 'copy' did not happen.
   1057   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
   1058 
   1059   [bar_ dragButton:[[bar_ buttons] objectAtIndex:1]
   1060                 to:NSMakePoint(1000, 0)
   1061               copy:NO];
   1062   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
   1063   EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
   1064   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
   1065   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
   1066 
   1067   // A drop of the 1st between the next 2.
   1068   CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
   1069   x += [[bar_ view] frame].origin.x;
   1070   [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
   1071                 to:NSMakePoint(x, 0)
   1072               copy:NO];
   1073   EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]);
   1074   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]);
   1075   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
   1076   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
   1077 
   1078   // A drop on a non-folder button.  (Shouldn't try and go in it.)
   1079   x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
   1080   x += [[bar_ view] frame].origin.x;
   1081   [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
   1082                 to:NSMakePoint(x, 0)
   1083               copy:NO];
   1084   EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
   1085 
   1086   // A drop on a folder button.
   1087   const BookmarkNode* folder = model->AddFolder(
   1088       model->bookmark_bar_node(), 0, ASCIIToUTF16("awesome folder"));
   1089   DCHECK(folder);
   1090   model->AddURL(folder, 0, ASCIIToUTF16("already"),
   1091                 GURL("http://www.google.com"));
   1092   EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]);
   1093   EXPECT_EQ(1, folder->child_count());
   1094   x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
   1095   x += [[bar_ view] frame].origin.x;
   1096   base::string16 title =
   1097       [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle();
   1098   [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
   1099                 to:NSMakePoint(x, 0)
   1100               copy:NO];
   1101   // Gone from the bar
   1102   EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
   1103   // In the folder
   1104   EXPECT_EQ(2, folder->child_count());
   1105   // At the end
   1106   EXPECT_EQ(title, folder->GetChild(1)->GetTitle());
   1107 }
   1108 
   1109 TEST_F(BookmarkBarControllerTest, TestCopyButton) {
   1110   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1111 
   1112   GURL gurls[] = { GURL("http://www.google.com/a"),
   1113                    GURL("http://www.google.com/b"),
   1114                    GURL("http://www.google.com/c") };
   1115   base::string16 titles[] = { ASCIIToUTF16("a"),
   1116                               ASCIIToUTF16("b"),
   1117                               ASCIIToUTF16("c") };
   1118   for (unsigned i = 0; i < arraysize(titles); i++)
   1119     bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
   1120 
   1121   EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
   1122   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
   1123 
   1124   // Drag 'a' between 'b' and 'c'.
   1125   CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
   1126   x += [[bar_ view] frame].origin.x;
   1127   [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
   1128                 to:NSMakePoint(x, 0)
   1129               copy:YES];
   1130   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
   1131   EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
   1132   EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
   1133   EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]);
   1134   EXPECT_EQ([[bar_ buttons] count], 4U);
   1135 }
   1136 
   1137 // Fake a theme with colored text.  Apply it and make sure bookmark
   1138 // buttons have the same colored text.  Repeat more than once.
   1139 TEST_F(BookmarkBarControllerTest, TestThemedButton) {
   1140   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1141   bookmark_utils::AddIfNotBookmarked(
   1142       model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
   1143   BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
   1144   EXPECT_TRUE(button);
   1145 
   1146   NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor],
   1147                                               [NSColor blueColor],
   1148                                               nil];
   1149   for (NSColor* color in colors) {
   1150     FakeTheme theme(color);
   1151     [bar_ updateTheme:&theme];
   1152     NSAttributedString* astr = [button attributedTitle];
   1153     EXPECT_TRUE(astr);
   1154     EXPECT_NSEQ(@"small", [astr string]);
   1155     // Pick a char in the middle to test (index 3)
   1156     NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL];
   1157     NSColor* newColor =
   1158         [attributes objectForKey:NSForegroundColorAttributeName];
   1159     EXPECT_NSEQ(newColor, color);
   1160   }
   1161 }
   1162 
   1163 // Test that delegates and targets of buttons are cleared on dealloc.
   1164 TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) {
   1165   // Make some bookmark buttons.
   1166   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1167   GURL gurls[] = { GURL("http://www.foo.com/"),
   1168                    GURL("http://www.bar.com/"),
   1169                    GURL("http://www.baz.com/") };
   1170   base::string16 titles[] = { ASCIIToUTF16("a"),
   1171                               ASCIIToUTF16("b"),
   1172                               ASCIIToUTF16("c") };
   1173   for (size_t i = 0; i < arraysize(titles); i++)
   1174     bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
   1175 
   1176   // Get and retain the buttons so we can examine them after dealloc.
   1177   base::scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]);
   1178   EXPECT_EQ([buttons count], arraysize(titles));
   1179 
   1180   // Make sure that everything is set.
   1181   for (BookmarkButton* button in buttons.get()) {
   1182     ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]);
   1183     EXPECT_TRUE([button delegate]);
   1184     EXPECT_TRUE([button target]);
   1185     EXPECT_TRUE([button action]);
   1186   }
   1187 
   1188   // This will dealloc....
   1189   bar_.reset();
   1190 
   1191   // Make sure that everything is cleared.
   1192   for (BookmarkButton* button in buttons.get()) {
   1193     EXPECT_FALSE([button delegate]);
   1194     EXPECT_FALSE([button target]);
   1195     EXPECT_FALSE([button action]);
   1196   }
   1197 }
   1198 
   1199 TEST_F(BookmarkBarControllerTest, TestFolders) {
   1200   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1201 
   1202   // Create some folder buttons.
   1203   const BookmarkNode* parent = model->bookmark_bar_node();
   1204   const BookmarkNode* folder = model->AddFolder(parent,
   1205                                                 parent->child_count(),
   1206                                                 ASCIIToUTF16("folder"));
   1207   model->AddURL(folder, folder->child_count(),
   1208                 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
   1209   folder = model->AddFolder(parent, parent->child_count(),
   1210                             ASCIIToUTF16("empty"));
   1211 
   1212   EXPECT_EQ([[bar_ buttons] count], 2U);
   1213 
   1214   // First confirm mouseEntered does nothing if "menus" aren't active.
   1215   NSEvent* event =
   1216       cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0);
   1217   [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
   1218   EXPECT_FALSE([bar_ folderController]);
   1219 
   1220   // Make one active.  Entering it is now a no-op.
   1221   [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]];
   1222   BookmarkBarFolderController* bbfc = [bar_ folderController];
   1223   EXPECT_TRUE(bbfc);
   1224   [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
   1225   EXPECT_EQ(bbfc, [bar_ folderController]);
   1226 
   1227   // Enter a different one; a new folderController is active.
   1228   [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event];
   1229   EXPECT_NE(bbfc, [bar_ folderController]);
   1230 
   1231   // Confirm exited is a no-op.
   1232   [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event];
   1233   EXPECT_NE(bbfc, [bar_ folderController]);
   1234 
   1235   // Clean up.
   1236   [bar_ closeBookmarkFolder:nil];
   1237 }
   1238 
   1239 // Verify that the folder menu presentation properly tracks mouse movements
   1240 // over the bar. Until there is a click no folder menus should show. After a
   1241 // click on a folder folder menus should show until another click on a folder
   1242 // button, and a click outside the bar and its folder menus.
   1243 TEST_F(BookmarkBarControllerTest, TestFolderButtons) {
   1244   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1245   const BookmarkNode* root = model->bookmark_bar_node();
   1246   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] ");
   1247   test::AddNodesFromModelString(model, root, model_string);
   1248 
   1249   // Validate initial model and that we do not have a folder controller.
   1250   std::string actualModelString = test::ModelStringFromNode(root);
   1251   EXPECT_EQ(model_string, actualModelString);
   1252   EXPECT_FALSE([bar_ folderController]);
   1253 
   1254   // Add a real bookmark so we can click on it.
   1255   const BookmarkNode* folder = root->GetChild(3);
   1256   model->AddURL(folder, folder->child_count(), ASCIIToUTF16("CLICK ME"),
   1257                 GURL("http://www.google.com/"));
   1258 
   1259   // Click on a folder button.
   1260   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"];
   1261   EXPECT_TRUE(button);
   1262   [bar_ openBookmarkFolderFromButton:button];
   1263   BookmarkBarFolderController* bbfc = [bar_ folderController];
   1264   EXPECT_TRUE(bbfc);
   1265 
   1266   // Make sure a 2nd click on the same button closes things.
   1267   [bar_ openBookmarkFolderFromButton:button];
   1268   EXPECT_FALSE([bar_ folderController]);
   1269 
   1270   // Next open is a different button.
   1271   button = [bar_ buttonWithTitleEqualTo:@"2f"];
   1272   EXPECT_TRUE(button);
   1273   [bar_ openBookmarkFolderFromButton:button];
   1274   EXPECT_TRUE([bar_ folderController]);
   1275 
   1276   // Mouse over a non-folder button and confirm controller has gone away.
   1277   button = [bar_ buttonWithTitleEqualTo:@"1b"];
   1278   EXPECT_TRUE(button);
   1279   NSEvent* event = cocoa_test_event_utils::MouseEventAtPoint([button center],
   1280                                                              NSMouseMoved, 0);
   1281   [bar_ mouseEnteredButton:button event:event];
   1282   EXPECT_FALSE([bar_ folderController]);
   1283 
   1284   // Mouse over the original folder and confirm a new controller.
   1285   button = [bar_ buttonWithTitleEqualTo:@"2f"];
   1286   EXPECT_TRUE(button);
   1287   [bar_ mouseEnteredButton:button event:event];
   1288   BookmarkBarFolderController* oldBBFC = [bar_ folderController];
   1289   EXPECT_TRUE(oldBBFC);
   1290 
   1291   // 'Jump' over to a different folder and confirm a new controller.
   1292   button = [bar_ buttonWithTitleEqualTo:@"4f"];
   1293   EXPECT_TRUE(button);
   1294   [bar_ mouseEnteredButton:button event:event];
   1295   BookmarkBarFolderController* newBBFC = [bar_ folderController];
   1296   EXPECT_TRUE(newBBFC);
   1297   EXPECT_NE(oldBBFC, newBBFC);
   1298 }
   1299 
   1300 // Make sure the "off the side" folder looks like a bookmark folder
   1301 // but only contains "off the side" items.
   1302 TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
   1303 
   1304   // It starts hidden.
   1305   EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
   1306 
   1307   // Create some buttons.
   1308   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1309   const BookmarkNode* parent = model->bookmark_bar_node();
   1310   for (int x = 0; x < 30; x++) {
   1311     model->AddURL(parent, parent->child_count(),
   1312                   ASCIIToUTF16("medium-size-title"),
   1313                   GURL("http://framma-lamma.com"));
   1314   }
   1315   // Add a couple more so we can delete one and make sure its button goes away.
   1316   model->AddURL(parent, parent->child_count(),
   1317                 ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com"));
   1318   model->AddURL(parent, parent->child_count(),
   1319                 ASCIIToUTF16("medium-size-title"),
   1320                 GURL("http://framma-lamma.com"));
   1321 
   1322   // Should no longer be hidden.
   1323   EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
   1324 
   1325   // Open it; make sure we have a folder controller.
   1326   EXPECT_FALSE([bar_ folderController]);
   1327   [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]];
   1328   BookmarkBarFolderController* bbfc = [bar_ folderController];
   1329   EXPECT_TRUE(bbfc);
   1330 
   1331   // Confirm the contents are only buttons which fell off the side by
   1332   // making sure that none of the nodes in the off-the-side folder are
   1333   // found in bar buttons.  Be careful since not all the bar buttons
   1334   // may be currently displayed.
   1335   NSArray* folderButtons = [bbfc buttons];
   1336   NSArray* barButtons = [bar_ buttons];
   1337   for (BookmarkButton* folderButton in folderButtons) {
   1338     for (BookmarkButton* barButton in barButtons) {
   1339       if ([barButton superview]) {
   1340         EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]);
   1341       }
   1342     }
   1343   }
   1344 
   1345   // Delete a bookmark in the off-the-side and verify it's gone.
   1346   BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
   1347   EXPECT_TRUE(button);
   1348   model->Remove(parent, parent->child_count() - 2);
   1349   button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
   1350   EXPECT_FALSE(button);
   1351 }
   1352 
   1353 TEST_F(BookmarkBarControllerTest, EventToExitCheck) {
   1354   NSEvent* event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0);
   1355   EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
   1356 
   1357   BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc]
   1358                                              init] autorelease];
   1359   [[[bar_ view] window] addChildWindow:folderWindow
   1360                                ordered:NSWindowAbove];
   1361   event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1),
   1362                                                                folderWindow);
   1363   EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
   1364 
   1365   event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
   1366       NSMakePoint(100,100), test_window());
   1367   EXPECT_TRUE([bar_ isEventAnExitEvent:event]);
   1368 
   1369   // Many components are arbitrary (e.g. location, keycode).
   1370   event = [NSEvent keyEventWithType:NSKeyDown
   1371                            location:NSMakePoint(1,1)
   1372                       modifierFlags:0
   1373                           timestamp:0
   1374                        windowNumber:0
   1375                             context:nil
   1376                          characters:@"x"
   1377         charactersIgnoringModifiers:@"x"
   1378                           isARepeat:NO
   1379                             keyCode:87];
   1380   EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
   1381 
   1382   [[[bar_ view] window] removeChildWindow:folderWindow];
   1383 }
   1384 
   1385 TEST_F(BookmarkBarControllerTest, DropDestination) {
   1386   // Make some buttons.
   1387   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1388   const BookmarkNode* parent = model->bookmark_bar_node();
   1389   model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 1"));
   1390   model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 2"));
   1391   EXPECT_EQ([[bar_ buttons] count], 2U);
   1392 
   1393   // Confirm "off to left" and "off to right" match nothing.
   1394   NSPoint p = NSMakePoint(-1, 2);
   1395   EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
   1396   EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
   1397   p = NSMakePoint(50000, 10);
   1398   EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
   1399   EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
   1400 
   1401   // Confirm "right in the center" (give or take a pixel) is a match,
   1402   // and confirm "just barely in the button" is not.  Anything more
   1403   // specific seems likely to be tweaked.
   1404   CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x;
   1405   for (BookmarkButton* button in [bar_ buttons]) {
   1406     CGFloat x = NSMidX([button frame]) + viewFrameXOffset;
   1407     // Somewhere near the center: a match
   1408     EXPECT_EQ(button,
   1409               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]);
   1410     EXPECT_EQ(button,
   1411               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]);
   1412     EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);;
   1413 
   1414     // On the very edges: NOT a match
   1415     x = NSMinX([button frame]) + viewFrameXOffset;
   1416     EXPECT_NE(button,
   1417               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]);
   1418     x = NSMaxX([button frame]) + viewFrameXOffset;
   1419     EXPECT_NE(button,
   1420               [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]);
   1421   }
   1422 }
   1423 
   1424 TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) {
   1425   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1426   [bar_ setStateAnimationsEnabled:YES];
   1427   const BookmarkNode* parent = model->bookmark_bar_node();
   1428   const BookmarkNode* folder = model->AddFolder(parent,
   1429                                                 parent->child_count(),
   1430                                                 ASCIIToUTF16("folder"));
   1431   model->AddFolder(parent, parent->child_count(),
   1432                   ASCIIToUTF16("sibbling folder"));
   1433   model->AddURL(folder, folder->child_count(), ASCIIToUTF16("title a"),
   1434                 GURL("http://www.google.com/a"));
   1435   model->AddURL(folder, folder->child_count(),
   1436       ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
   1437       GURL("http://www.google.com/b"));
   1438   BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
   1439   EXPECT_FALSE([bar_ folderController]);
   1440   [bar_ openBookmarkFolderFromButton:button];
   1441   BookmarkBarFolderController* bbfc = [bar_ folderController];
   1442   // The following tells us that the folder menu is showing. We want to make
   1443   // sure the folder menu goes away if the bookmark bar is hidden.
   1444   EXPECT_TRUE(bbfc);
   1445   EXPECT_TRUE([bar_ isVisible]);
   1446 
   1447   // Hide the bookmark bar.
   1448   [bar_ updateState:BookmarkBar::DETACHED
   1449          changeType:BookmarkBar::ANIMATE_STATE_CHANGE];
   1450   EXPECT_TRUE([bar_ isAnimationRunning]);
   1451 
   1452   // Now that we've closed the bookmark bar (with animation) the folder menu
   1453   // should have been closed thus releasing the folderController.
   1454   EXPECT_FALSE([bar_ folderController]);
   1455 
   1456   // Stop the pending animation to tear down cleanly.
   1457   [bar_ updateState:BookmarkBar::DETACHED
   1458          changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
   1459   EXPECT_FALSE([bar_ isAnimationRunning]);
   1460 }
   1461 
   1462 TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) {
   1463   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1464   const BookmarkNode* root = model->bookmark_bar_node();
   1465   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
   1466   test::AddNodesFromModelString(model, root, model_string);
   1467 
   1468   // Validate initial model.
   1469   std::string actualModelString = test::ModelStringFromNode(root);
   1470   EXPECT_EQ(model_string, actualModelString);
   1471 
   1472   // Remember how many buttons are showing.
   1473   int oldDisplayedButtons = [bar_ displayedButtonCount];
   1474   NSArray* buttons = [bar_ buttons];
   1475 
   1476   // Move a button around a bit.
   1477   [bar_ moveButtonFromIndex:0 toIndex:2];
   1478   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]);
   1479   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]);
   1480   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]);
   1481   EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
   1482   [bar_ moveButtonFromIndex:2 toIndex:0];
   1483   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]);
   1484   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
   1485   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
   1486   EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
   1487 
   1488   // Add a couple of buttons.
   1489   const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node.
   1490   const BookmarkNode* node = parent->GetChild(0);
   1491   [bar_ addButtonForNode:node atIndex:0];
   1492   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
   1493   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
   1494   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
   1495   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
   1496   EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]);
   1497   node = parent->GetChild(1);
   1498   [bar_ addButtonForNode:node atIndex:-1];
   1499   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
   1500   EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
   1501   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
   1502   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
   1503   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]);
   1504   EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]);
   1505 
   1506   // Remove a couple of buttons.
   1507   [bar_ removeButton:4 animate:NO];
   1508   [bar_ removeButton:1 animate:NO];
   1509   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
   1510   EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
   1511   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
   1512   EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
   1513 }
   1514 
   1515 TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) {
   1516   NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0);
   1517   NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease];
   1518   EXPECT_FALSE([view isHidden]);
   1519   [bar_ shrinkOrHideView:view forMaxX:500.0];
   1520   EXPECT_EQ(500.0, NSWidth([view frame]));
   1521   EXPECT_FALSE([view isHidden]);
   1522   [bar_ shrinkOrHideView:view forMaxX:450.0];
   1523   EXPECT_EQ(450.0, NSWidth([view frame]));
   1524   EXPECT_FALSE([view isHidden]);
   1525   [bar_ shrinkOrHideView:view forMaxX:40.0];
   1526   EXPECT_EQ(40.0, NSWidth([view frame]));
   1527   EXPECT_FALSE([view isHidden]);
   1528   [bar_ shrinkOrHideView:view forMaxX:31.0];
   1529   EXPECT_EQ(31.0, NSWidth([view frame]));
   1530   EXPECT_FALSE([view isHidden]);
   1531   [bar_ shrinkOrHideView:view forMaxX:29.0];
   1532   EXPECT_TRUE([view isHidden]);
   1533 }
   1534 
   1535 TEST_F(BookmarkBarControllerTest, LastBookmarkResizeBehavior) {
   1536   // Hide the apps shortcut.
   1537   profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
   1538                                     false);
   1539   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
   1540 
   1541   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1542   const BookmarkNode* root = model->bookmark_bar_node();
   1543   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
   1544   test::AddNodesFromModelString(model, root, model_string);
   1545   [bar_ frameDidChange];
   1546 
   1547   CGFloat viewWidths[] = { 123.0, 124.0, 151.0, 152.0, 153.0, 154.0, 155.0,
   1548                            200.0, 155.0, 154.0, 153.0, 152.0, 151.0, 124.0,
   1549                            123.0 };
   1550   BOOL offTheSideButtonIsHiddenResults[] = { NO, NO, NO, NO, YES, YES, YES, YES,
   1551                                              YES, YES, YES, NO, NO, NO, NO};
   1552   int displayedButtonCountResults[] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2,
   1553                                         2, 1 };
   1554 
   1555   for (unsigned int i = 0; i < sizeof(viewWidths) / sizeof(viewWidths[0]);
   1556        ++i) {
   1557     NSRect frame = [[bar_ view] frame];
   1558     frame.size.width = viewWidths[i] + bookmarks::kBookmarkRightMargin;
   1559     [[bar_ view] setFrame:frame];
   1560     EXPECT_EQ(offTheSideButtonIsHiddenResults[i],
   1561               [bar_ offTheSideButtonIsHidden]);
   1562     EXPECT_EQ(displayedButtonCountResults[i], [bar_ displayedButtonCount]);
   1563   }
   1564 }
   1565 
   1566 TEST_F(BookmarkBarControllerTest, BookmarksWithAppsPageShortcut) {
   1567   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1568   const BookmarkNode* root = model->bookmark_bar_node();
   1569   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
   1570   test::AddNodesFromModelString(model, root, model_string);
   1571   [bar_ frameDidChange];
   1572 
   1573   // Apps page shortcut button should be visible.
   1574   ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
   1575 
   1576   // Bookmarks should be to the right of the Apps page shortcut button.
   1577   CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
   1578   CGFloat right = apps_button_right;
   1579   NSArray* buttons = [bar_ buttons];
   1580   for (size_t i = 0; i < [buttons count]; ++i) {
   1581     EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame]));
   1582     right = NSMaxX([[buttons objectAtIndex:i] frame]);
   1583   }
   1584 
   1585   // Removing the Apps button should move every bookmark to the left.
   1586   profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
   1587                                     false);
   1588   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
   1589   EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame]));
   1590   for (size_t i = 1; i < [buttons count]; ++i) {
   1591     EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]),
   1592               NSMinX([[buttons objectAtIndex:i] frame]));
   1593   }
   1594 }
   1595 
   1596 TEST_F(BookmarkBarControllerTest, BookmarksWithoutAppsPageShortcut) {
   1597   // The no item containers should be to the right of the Apps button.
   1598   ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
   1599   CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
   1600   EXPECT_LE(apps_button_right,
   1601             NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
   1602   EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
   1603             NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
   1604 
   1605   // Removing the Apps button should move the no item containers to the left.
   1606   profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
   1607                                     false);
   1608   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
   1609   EXPECT_GT(apps_button_right,
   1610             NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
   1611   EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
   1612             NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
   1613 }
   1614 
   1615 TEST_F(BookmarkBarControllerTest, ManagedShowAppsShortcutInBookmarksBar) {
   1616   // By default the pref is not managed and the apps shortcut is shown.
   1617   TestingPrefServiceSyncable* prefs = profile()->GetTestingPrefService();
   1618   EXPECT_FALSE(
   1619       prefs->IsManagedPreference(prefs::kShowAppsShortcutInBookmarkBar));
   1620   EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
   1621 
   1622   // Hide the apps shortcut by policy, via the managed pref.
   1623   prefs->SetManagedPref(prefs::kShowAppsShortcutInBookmarkBar,
   1624                         new base::FundamentalValue(false));
   1625   EXPECT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
   1626 
   1627   // And try showing it via policy too.
   1628   prefs->SetManagedPref(prefs::kShowAppsShortcutInBookmarkBar,
   1629                         new base::FundamentalValue(true));
   1630   EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
   1631 }
   1632 
   1633 class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest {
   1634 public:
   1635   virtual void SetUp() {
   1636     BookmarkBarControllerTest::SetUp();
   1637     ASSERT_TRUE(profile());
   1638 
   1639     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
   1640     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
   1641     bar_.reset(
   1642                [[BookmarkBarControllerOpenAllPong alloc]
   1643                 initWithBrowser:browser()
   1644                    initialWidth:NSWidth(parent_frame)
   1645                        delegate:nil
   1646                  resizeDelegate:resizeDelegate_.get()]);
   1647     [bar_ view];
   1648     // Awkwardness to look like we've been installed.
   1649     [parent_view_ addSubview:[bar_ view]];
   1650     NSRect frame = [[[bar_ view] superview] frame];
   1651     frame.origin.y = 100;
   1652     [[[bar_ view] superview] setFrame:frame];
   1653 
   1654     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1655     parent_ = model->bookmark_bar_node();
   1656     // { one, { two-one, two-two }, three }
   1657     model->AddURL(parent_, parent_->child_count(), ASCIIToUTF16("title"),
   1658                   GURL("http://one.com"));
   1659     folder_ = model->AddFolder(parent_, parent_->child_count(),
   1660                                ASCIIToUTF16("folder"));
   1661     model->AddURL(folder_, folder_->child_count(),
   1662                   ASCIIToUTF16("title"), GURL("http://two-one.com"));
   1663     model->AddURL(folder_, folder_->child_count(),
   1664                   ASCIIToUTF16("title"), GURL("http://two-two.com"));
   1665     model->AddURL(parent_, parent_->child_count(),
   1666                   ASCIIToUTF16("title"), GURL("https://three.com"));
   1667   }
   1668   const BookmarkNode* parent_;  // Weak
   1669   const BookmarkNode* folder_;  // Weak
   1670 };
   1671 
   1672 // Command-click on a folder should open all the bookmarks in it.
   1673 TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) {
   1674   NSButton* first = [[bar_ buttons] objectAtIndex:0];
   1675   EXPECT_TRUE(first);
   1676 
   1677   // Create the right kind of event; mock NSApp so [NSApp
   1678   // currentEvent] finds it.
   1679   NSEvent* commandClick =
   1680       cocoa_test_event_utils::MouseEventAtPoint(NSZeroPoint,
   1681                                                 NSLeftMouseDown,
   1682                                                 NSCommandKeyMask);
   1683   id fakeApp = [OCMockObject partialMockForObject:NSApp];
   1684   [[[fakeApp stub] andReturn:commandClick] currentEvent];
   1685   id oldApp = NSApp;
   1686   NSApp = fakeApp;
   1687   size_t originalDispositionCount = noOpenBar()->dispositions_.size();
   1688 
   1689   // Click!
   1690   [first performClick:first];
   1691 
   1692   size_t dispositionCount = noOpenBar()->dispositions_.size();
   1693   EXPECT_EQ(originalDispositionCount+1, dispositionCount);
   1694   EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB);
   1695 
   1696   // Replace NSApp
   1697   NSApp = oldApp;
   1698 }
   1699 
   1700 class BookmarkBarControllerNotificationTest : public CocoaProfileTest {
   1701  public:
   1702   virtual void SetUp() {
   1703     CocoaProfileTest::SetUp();
   1704     ASSERT_TRUE(browser());
   1705 
   1706     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
   1707     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
   1708     parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
   1709     [parent_view_ setHidden:YES];
   1710     bar_.reset(
   1711       [[BookmarkBarControllerNotificationPong alloc]
   1712           initWithBrowser:browser()
   1713              initialWidth:NSWidth(parent_frame)
   1714                  delegate:nil
   1715            resizeDelegate:resizeDelegate_.get()]);
   1716 
   1717     // Force loading of the nib.
   1718     [bar_ view];
   1719     // Awkwardness to look like we've been installed.
   1720     [parent_view_ addSubview:[bar_ view]];
   1721     NSRect frame = [[[bar_ view] superview] frame];
   1722     frame.origin.y = 100;
   1723     [[[bar_ view] superview] setFrame:frame];
   1724 
   1725     // Do not add the bar to a window, yet.
   1726   }
   1727 
   1728   base::scoped_nsobject<NSView> parent_view_;
   1729   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
   1730   base::scoped_nsobject<BookmarkBarControllerNotificationPong> bar_;
   1731 };
   1732 
   1733 TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) {
   1734   NSWindow* window = [[CocoaTestHelperWindow alloc] init];
   1735   [window setReleasedWhenClosed:YES];
   1736 
   1737   // First add the bookmark bar to the temp window, then to another window.
   1738   [[window contentView] addSubview:parent_view_];
   1739   [[test_window() contentView] addSubview:parent_view_];
   1740 
   1741   // Post a fake windowDidResignKey notification for the temp window and make
   1742   // sure the bookmark bar controller wasn't listening.
   1743   [[NSNotificationCenter defaultCenter]
   1744       postNotificationName:NSWindowDidResignKeyNotification
   1745                     object:window];
   1746   EXPECT_FALSE([bar_ windowDidResignKeyReceived]);
   1747 
   1748   // Close the temp window and make sure no notification was received.
   1749   [window close];
   1750   EXPECT_FALSE([bar_ windowWillCloseReceived]);
   1751 }
   1752 
   1753 
   1754 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
   1755 // they are hard to test.  Factor out "fire timers" into routines
   1756 // which can be overridden to fire immediately to make behavior
   1757 // confirmable.
   1758 
   1759 // TODO(jrg): add unit test to make sure "Other Bookmarks" responds
   1760 // properly to a hover open.
   1761 
   1762 // TODO(viettrungluu): figure out how to test animations.
   1763 
   1764 class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase {
   1765  public:
   1766   base::scoped_nsobject<BookmarkBarControllerDragData> bar_;
   1767 
   1768   virtual void SetUp() {
   1769     BookmarkBarControllerTestBase::SetUp();
   1770     ASSERT_TRUE(browser());
   1771 
   1772     bar_.reset(
   1773                [[BookmarkBarControllerDragData alloc]
   1774                 initWithBrowser:browser()
   1775                    initialWidth:NSWidth([parent_view_ frame])
   1776                        delegate:nil
   1777                  resizeDelegate:resizeDelegate_.get()]);
   1778     InstallAndToggleBar(bar_.get());
   1779   }
   1780 };
   1781 
   1782 TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
   1783   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1784   const BookmarkNode* root = model->bookmark_bar_node();
   1785   const std::string model_string("1bWithLongName 2fWithLongName:[ "
   1786       "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName "
   1787       "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] "
   1788       "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
   1789       "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
   1790       "11bWithLongName 12bWithLongName 13b ");
   1791   test::AddNodesFromModelString(model, root, model_string);
   1792 
   1793   // Validate initial model.
   1794   std::string actualModelString = test::ModelStringFromNode(root);
   1795   EXPECT_EQ(model_string, actualModelString);
   1796 
   1797   // Insure that the off-the-side is not showing.
   1798   ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
   1799 
   1800   // Remember how many buttons are showing and are available.
   1801   int oldDisplayedButtons = [bar_ displayedButtonCount];
   1802   int oldChildCount = root->child_count();
   1803 
   1804   // Pop up the off-the-side menu.
   1805   BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
   1806   ASSERT_TRUE(otsButton);
   1807   [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
   1808                            withObject:otsButton];
   1809   BookmarkBarFolderController* otsController = [bar_ folderController];
   1810   EXPECT_TRUE(otsController);
   1811   NSWindow* toWindow = [otsController window];
   1812   EXPECT_TRUE(toWindow);
   1813   BookmarkButton* draggedButton =
   1814       [bar_ buttonWithTitleEqualTo:@"3bWithLongName"];
   1815   ASSERT_TRUE(draggedButton);
   1816   int oldOTSCount = (int)[[otsController buttons] count];
   1817   EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons);
   1818   BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0];
   1819   ASSERT_TRUE(targetButton);
   1820   [otsController dragButton:draggedButton
   1821                          to:[targetButton center]
   1822                        copy:YES];
   1823   // There should still be the same number of buttons in the bar
   1824   // and off-the-side should have one more.
   1825   int newDisplayedButtons = [bar_ displayedButtonCount];
   1826   int newChildCount = root->child_count();
   1827   int newOTSCount = (int)[[otsController buttons] count];
   1828   EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons);
   1829   EXPECT_EQ(oldChildCount + 1, newChildCount);
   1830   EXPECT_EQ(oldOTSCount + 1, newOTSCount);
   1831   EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons);
   1832 }
   1833 
   1834 TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) {
   1835   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1836   const BookmarkNode* root = model->bookmark_bar_node();
   1837   const std::string model_string("1bWithLongName 2bWithLongName "
   1838       "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
   1839       "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
   1840       "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName "
   1841       "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName "
   1842       "19bWithLongName 20bWithLongName ");
   1843   test::AddNodesFromModelString(model, root, model_string);
   1844 
   1845   const BookmarkNode* other = model->other_node();
   1846   const std::string other_string("1other 2other 3other ");
   1847   test::AddNodesFromModelString(model, other, other_string);
   1848 
   1849   // Validate initial model.
   1850   std::string actualModelString = test::ModelStringFromNode(root);
   1851   EXPECT_EQ(model_string, actualModelString);
   1852   std::string actualOtherString = test::ModelStringFromNode(other);
   1853   EXPECT_EQ(other_string, actualOtherString);
   1854 
   1855   // Insure that the off-the-side is showing.
   1856   ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
   1857 
   1858   // Remember how many buttons are showing and are available.
   1859   int oldDisplayedButtons = [bar_ displayedButtonCount];
   1860   int oldRootCount = root->child_count();
   1861   int oldOtherCount = other->child_count();
   1862 
   1863   // Pop up the off-the-side menu.
   1864   BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
   1865   ASSERT_TRUE(otsButton);
   1866   [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
   1867                            withObject:otsButton];
   1868   BookmarkBarFolderController* otsController = [bar_ folderController];
   1869   EXPECT_TRUE(otsController);
   1870   int oldOTSCount = (int)[[otsController buttons] count];
   1871   EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons);
   1872 
   1873   // Pick an off-the-side button and drag it to the other bookmarks.
   1874   BookmarkButton* draggedButton =
   1875       [otsController buttonWithTitleEqualTo:@"20bWithLongName"];
   1876   ASSERT_TRUE(draggedButton);
   1877   BookmarkButton* targetButton = [bar_ otherBookmarksButton];
   1878   ASSERT_TRUE(targetButton);
   1879   [bar_ dragButton:draggedButton to:[targetButton center] copy:NO];
   1880 
   1881   // There should one less button in the bar, one less in off-the-side,
   1882   // and one more in other bookmarks.
   1883   int newRootCount = root->child_count();
   1884   int newOTSCount = (int)[[otsController buttons] count];
   1885   int newOtherCount = other->child_count();
   1886   EXPECT_EQ(oldRootCount - 1, newRootCount);
   1887   EXPECT_EQ(oldOTSCount - 1, newOTSCount);
   1888   EXPECT_EQ(oldOtherCount + 1, newOtherCount);
   1889 }
   1890 
   1891 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) {
   1892   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1893   const BookmarkNode* root = model->bookmark_bar_node();
   1894   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
   1895                                   "2f3b ] 3b 4b ");
   1896   test::AddNodesFromModelString(model, root, model_string);
   1897   const BookmarkNode* other = model->other_node();
   1898   const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
   1899                                  "O4f:[ O4f1b O4f2f ] 05b ");
   1900   test::AddNodesFromModelString(model, other, other_string);
   1901 
   1902   // Validate initial model.
   1903   std::string actual = test::ModelStringFromNode(root);
   1904   EXPECT_EQ(model_string, actual);
   1905   actual = test::ModelStringFromNode(other);
   1906   EXPECT_EQ(other_string, actual);
   1907 
   1908   // Remember the little ones.
   1909   int oldChildCount = root->child_count();
   1910 
   1911   BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
   1912   ASSERT_TRUE(targetButton);
   1913 
   1914   // Gen up some dragging data.
   1915   const BookmarkNode* newNode = other->GetChild(2);
   1916   [bar_ setDragDataNode:newNode];
   1917   base::scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]);
   1918   [dragInfo setDropLocation:[targetButton center]];
   1919   [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
   1920 
   1921   // There should one more button in the bar.
   1922   int newChildCount = root->child_count();
   1923   EXPECT_EQ(oldChildCount + 1, newChildCount);
   1924   // Verify the model.
   1925   const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
   1926                              "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b ");
   1927   actual = test::ModelStringFromNode(root);
   1928   EXPECT_EQ(expected, actual);
   1929   oldChildCount = newChildCount;
   1930 
   1931   // Now do it over a folder button.
   1932   targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
   1933   ASSERT_TRUE(targetButton);
   1934   NSPoint targetPoint = [targetButton center];
   1935   newNode = other->GetChild(2);  // Should be O4f.
   1936   EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
   1937   [bar_ setDragDataNode:newNode];
   1938   [dragInfo setDropLocation:targetPoint];
   1939   [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
   1940 
   1941   newChildCount = root->child_count();
   1942   EXPECT_EQ(oldChildCount, newChildCount);
   1943   // Verify the model.
   1944   const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
   1945                               "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] "
   1946                               "3b 4b ");
   1947   actual = test::ModelStringFromNode(root);
   1948   EXPECT_EQ(expected1, actual);
   1949 }
   1950 
   1951 TEST_F(BookmarkBarControllerDragDropTest, AddURLs) {
   1952   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1953   const BookmarkNode* root = model->bookmark_bar_node();
   1954   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
   1955                                  "2f3b ] 3b 4b ");
   1956   test::AddNodesFromModelString(model, root, model_string);
   1957 
   1958   // Validate initial model.
   1959   std::string actual = test::ModelStringFromNode(root);
   1960   EXPECT_EQ(model_string, actual);
   1961 
   1962   // Remember the children.
   1963   int oldChildCount = root->child_count();
   1964 
   1965   BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
   1966   ASSERT_TRUE(targetButton);
   1967 
   1968   NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
   1969                    @"http://www.b.com/", nil];
   1970   NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
   1971   [bar_ addURLs:urls withTitles:titles at:[targetButton center]];
   1972 
   1973   // There should two more nodes in the bar.
   1974   int newChildCount = root->child_count();
   1975   EXPECT_EQ(oldChildCount + 2, newChildCount);
   1976   // Verify the model.
   1977   const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
   1978                              "2f3b ] SiteA SiteB 3b 4b ");
   1979   actual = test::ModelStringFromNode(root);
   1980   EXPECT_EQ(expected, actual);
   1981 }
   1982 
   1983 TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) {
   1984   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   1985   const BookmarkNode* root = model->bookmark_bar_node();
   1986   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
   1987   test::AddNodesFromModelString(model, root, model_string);
   1988 
   1989   // Validate initial model.
   1990   std::string actualModelString = test::ModelStringFromNode(root);
   1991   EXPECT_EQ(model_string, actualModelString);
   1992 
   1993   // Find the main bar controller.
   1994   const void* expectedController = bar_;
   1995   const void* actualController = [bar_ controllerForNode:root];
   1996   EXPECT_EQ(expectedController, actualController);
   1997 }
   1998 
   1999 TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
   2000   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   2001   const BookmarkNode* root = model->bookmark_bar_node();
   2002   const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
   2003   test::AddNodesFromModelString(model, root, model_string);
   2004 
   2005   // Hide the apps shortcut.
   2006   profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
   2007                                     false);
   2008   ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
   2009 
   2010   // Validate initial model.
   2011   std::string actualModel = test::ModelStringFromNode(root);
   2012   EXPECT_EQ(model_string, actualModel);
   2013 
   2014   // Test a series of points starting at the right edge of the bar.
   2015   BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"];
   2016   ASSERT_TRUE(targetButton);
   2017   NSPoint targetPoint = [targetButton left];
   2018   CGFloat leftMarginIndicatorPosition = bookmarks::kBookmarkLeftMargin - 0.5 *
   2019                                         bookmarks::kBookmarkHorizontalPadding;
   2020   const CGFloat baseOffset = targetPoint.x;
   2021   CGFloat expected = leftMarginIndicatorPosition;
   2022   CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint];
   2023   EXPECT_CGFLOAT_EQ(expected, actual);
   2024   targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
   2025   actual = [bar_ indicatorPosForDragToPoint:[targetButton right]];
   2026   targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
   2027   expected = [targetButton left].x - baseOffset + leftMarginIndicatorPosition;
   2028   EXPECT_CGFLOAT_EQ(expected, actual);
   2029   targetButton = [bar_ buttonWithTitleEqualTo:@"4b"];
   2030   targetPoint = [targetButton right];
   2031   targetPoint.x += 100;  // Somewhere off to the right.
   2032   CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding;
   2033   expected = NSMaxX([targetButton frame]) + xDelta;
   2034   actual = [bar_ indicatorPosForDragToPoint:targetPoint];
   2035   EXPECT_CGFLOAT_EQ(expected, actual);
   2036 }
   2037 
   2038 TEST_F(BookmarkBarControllerDragDropTest, PulseButton) {
   2039   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   2040   const BookmarkNode* root = model->bookmark_bar_node();
   2041   GURL gurl("http://www.google.com");
   2042   const BookmarkNode* node = model->AddURL(root, root->child_count(),
   2043                                            ASCIIToUTF16("title"), gurl);
   2044 
   2045   BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
   2046   EXPECT_FALSE([button isContinuousPulsing]);
   2047 
   2048   NSValue *value = [NSValue valueWithPointer:node];
   2049   NSDictionary *dict = [NSDictionary
   2050                          dictionaryWithObjectsAndKeys:value,
   2051                          bookmark_button::kBookmarkKey,
   2052                          [NSNumber numberWithBool:YES],
   2053                          bookmark_button::kBookmarkPulseFlagKey,
   2054                          nil];
   2055   [[NSNotificationCenter defaultCenter]
   2056         postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
   2057                       object:nil
   2058                     userInfo:dict];
   2059   EXPECT_TRUE([button isContinuousPulsing]);
   2060 
   2061   dict = [NSDictionary dictionaryWithObjectsAndKeys:value,
   2062                        bookmark_button::kBookmarkKey,
   2063                        [NSNumber numberWithBool:NO],
   2064                        bookmark_button::kBookmarkPulseFlagKey,
   2065                        nil];
   2066   [[NSNotificationCenter defaultCenter]
   2067         postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
   2068                       object:nil
   2069                     userInfo:dict];
   2070   EXPECT_FALSE([button isContinuousPulsing]);
   2071 }
   2072 
   2073 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) {
   2074   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
   2075   const BookmarkNode* root = model->bookmark_bar_node();
   2076   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
   2077                                   "2f3b ] 3b 4b ");
   2078   test::AddNodesFromModelString(model, root, model_string);
   2079 
   2080   // Validate initial model.
   2081   std::string actual = test::ModelStringFromNode(root);
   2082   EXPECT_EQ(model_string, actual);
   2083 
   2084   int oldChildCount = root->child_count();
   2085 
   2086   // Drag a button to the trash.
   2087   BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"];
   2088   ASSERT_TRUE(buttonToDelete);
   2089   EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]);
   2090   [bar_ didDragBookmarkToTrash:buttonToDelete];
   2091 
   2092   // There should be one less button in the bar.
   2093   int newChildCount = root->child_count();
   2094   EXPECT_EQ(oldChildCount - 1, newChildCount);
   2095   // Verify the model.
   2096   const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
   2097                              "2f3b ] 4b ");
   2098   actual = test::ModelStringFromNode(root);
   2099   EXPECT_EQ(expected, actual);
   2100 
   2101   // Verify that the other bookmark folder can't be deleted.
   2102   BookmarkButton *otherButton = [bar_ otherBookmarksButton];
   2103   EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]);
   2104 }
   2105 
   2106 }  // namespace
   2107