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