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