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