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