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