Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / bookmarks / bookmark_bar_folder_controller_unittest.mm
blob99e545bb3d716bc6874c56358340297c599fb1c6
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 #include "base/basictypes.h"
6 #include "base/mac/scoped_nsobject.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/bookmarks/bookmark_model.h"
9 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
10 #include "chrome/browser/bookmarks/bookmark_test_helpers.h"
11 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
12 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
13 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_button_cell.h"
14 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h"
15 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h"
16 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
17 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
18 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
19 #include "chrome/test/base/testing_profile.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #import "testing/gtest_mac.h"
22 #include "testing/platform_test.h"
23 #include "ui/base/cocoa/animation_utils.h"
25 using base::ASCIIToUTF16;
27 namespace {
29 const int kLotsOfNodesCount = 150;
31 // Deletes the bookmark corresponding to |button|.
32 void DeleteBookmark(BookmarkButton* button, Profile* profile) {
33   const BookmarkNode* node = [button bookmarkNode];
34   if (node) {
35     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
36     model->Remove(node->parent(), node->parent()->GetIndexOf(node));
37   }
40 }  // namespace
42 // Add a redirect to make testing easier.
43 @interface BookmarkBarFolderController(MakeTestingEasier)
44 - (void)validateMenuSpacing;
45 @end
47 @implementation BookmarkBarFolderController(MakeTestingEasier)
49 // Utility function to verify that the buttons in this folder are all
50 // evenly spaced in a progressive manner.
51 - (void)validateMenuSpacing {
52   BOOL firstButton = YES;
53   CGFloat lastVerticalOffset = 0.0;
54   for (BookmarkButton* button in [self buttons]) {
55     if (firstButton) {
56       firstButton = NO;
57       lastVerticalOffset = [button frame].origin.y;
58     } else {
59       CGFloat nextVerticalOffset = [button frame].origin.y;
60       EXPECT_CGFLOAT_EQ(lastVerticalOffset -
61                             bookmarks::kBookmarkFolderButtonHeight,
62                         nextVerticalOffset);
63       lastVerticalOffset = nextVerticalOffset;
64     }
65   }
67 @end
69 // Don't use a high window level when running unit tests -- it'll
70 // interfere with anything else you are working on.
71 // For testing.
72 @interface BookmarkBarFolderControllerNoLevel : BookmarkBarFolderController
73 @end
75 @implementation BookmarkBarFolderControllerNoLevel
76 - (void)configureWindowLevel {
77   // Intentionally empty.
79 @end
81 @interface BookmarkBarFolderControllerPong : BookmarkBarFolderController {
82   BOOL childFolderWillShow_;
83   BOOL childFolderWillClose_;
85 @property (nonatomic, readonly) BOOL childFolderWillShow;
86 @property (nonatomic, readonly) BOOL childFolderWillClose;
87 @end
89 @implementation BookmarkBarFolderControllerPong
90 @synthesize childFolderWillShow = childFolderWillShow_;
91 @synthesize childFolderWillClose = childFolderWillClose_;
93 - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
94   childFolderWillShow_ = YES;
97 - (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
98   childFolderWillClose_ = YES;
101 // We don't have a real BookmarkBarController as our parent root so
102 // we fake this one out.
103 - (void)closeAllBookmarkFolders {
104   [self closeBookmarkFolder:self];
107 @end
109 // Redirect certain calls so they can be seen by tests.
111 @interface BookmarkBarControllerChildFolderRedirect : BookmarkBarController {
112   BookmarkBarFolderController* childFolderDelegate_;
114 @property (nonatomic, assign) BookmarkBarFolderController* childFolderDelegate;
115 @end
117 @implementation BookmarkBarControllerChildFolderRedirect
119 @synthesize childFolderDelegate = childFolderDelegate_;
121 - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
122   [childFolderDelegate_ childFolderWillShow:child];
125 - (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
126   [childFolderDelegate_ childFolderWillClose:child];
129 @end
132 class BookmarkBarFolderControllerTest : public CocoaProfileTest {
133  public:
134   base::scoped_nsobject<BookmarkBarControllerChildFolderRedirect> bar_;
135   const BookmarkNode* folderA_;  // Owned by model.
136   const BookmarkNode* longTitleNode_;  // Owned by model.
138   virtual void SetUp() {
139     CocoaProfileTest::SetUp();
140     ASSERT_TRUE(profile());
142     CreateModel();
143   }
145   void CreateModel() {
146     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
147     const BookmarkNode* parent = model->bookmark_bar_node();
148     const BookmarkNode* folderA = model->AddFolder(parent,
149                                                    parent->child_count(),
150                                                    ASCIIToUTF16("folder"));
151     folderA_ = folderA;
152     model->AddFolder(parent, parent->child_count(),
153                      ASCIIToUTF16("sibbling folder"));
154     const BookmarkNode* folderB = model->AddFolder(folderA,
155                                                    folderA->child_count(),
156                                                    ASCIIToUTF16("subfolder 1"));
157     model->AddFolder(folderA,
158                      folderA->child_count(),
159                      ASCIIToUTF16("subfolder 2"));
160     model->AddURL(folderA, folderA->child_count(), ASCIIToUTF16("title a"),
161                   GURL("http://www.google.com/a"));
162     longTitleNode_ = model->AddURL(
163       folderA, folderA->child_count(),
164       ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
165       GURL("http://www.google.com/b"));
166     model->AddURL(folderB, folderB->child_count(), ASCIIToUTF16("t"),
167                   GURL("http://www.google.com/c"));
169     bar_.reset(
170       [[BookmarkBarControllerChildFolderRedirect alloc]
171           initWithBrowser:browser()
172              initialWidth:300
173                  delegate:nil
174            resizeDelegate:nil]);
175     [bar_ loaded:model];
176     // Make parent frame for bookmark bar then open it.
177     NSRect frame = [[test_window() contentView] frame];
178     frame = NSMakeRect(frame.origin.x,
179                        frame.size.height - chrome::kNTPBookmarkBarHeight,
180                        frame.size.width, chrome::kNTPBookmarkBarHeight);
181     NSView* fakeToolbarView = [[[NSView alloc] initWithFrame:frame]
182                                 autorelease];
183     [[test_window() contentView] addSubview:fakeToolbarView];
184     [fakeToolbarView addSubview:[bar_ view]];
185     [bar_ setBookmarkBarEnabled:YES];
186   }
188   // Remove the bookmark with the long title.
189   void RemoveLongTitleNode() {
190     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
191     model->Remove(longTitleNode_->parent(),
192                   longTitleNode_->parent()->GetIndexOf(longTitleNode_));
193   }
195   // Add LOTS of nodes to our model if needed (e.g. scrolling).
196   // Returns the number of nodes added.
197   int AddLotsOfNodes() {
198     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
199     for (int i = 0; i < kLotsOfNodesCount; i++) {
200       model->AddURL(folderA_, folderA_->child_count(),
201                     ASCIIToUTF16("repeated title"),
202                     GURL("http://www.google.com/repeated/url"));
203     }
204     return kLotsOfNodesCount;
205   }
207   // Return a simple BookmarkBarFolderController.
208   BookmarkBarFolderControllerPong* SimpleBookmarkBarFolderController() {
209     BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
210     BookmarkBarFolderControllerPong* c =
211       [[BookmarkBarFolderControllerPong alloc]
212                initWithParentButton:parentButton
213                    parentController:nil
214                       barController:bar_
215                             profile:profile()];
216     [c window];  // Force nib load.
217     return c;
218   }
221 TEST_F(BookmarkBarFolderControllerTest, InitCreateAndDelete) {
222   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
223   bbfc.reset(SimpleBookmarkBarFolderController());
225   // Make sure none of the buttons overlap, that all are inside
226   // the content frame, and their cells are of the proper class.
227   NSArray* buttons = [bbfc buttons];
228   EXPECT_TRUE([buttons count]);
229   for (unsigned int i = 0; i < ([buttons count]-1); i++) {
230     EXPECT_FALSE(NSContainsRect([[buttons objectAtIndex:i] frame],
231                               [[buttons objectAtIndex:i+1] frame]));
232   }
233   Class cellClass = [BookmarkBarFolderButtonCell class];
234   for (BookmarkButton* button in buttons) {
235     NSRect r = [[bbfc folderView] convertRect:[button frame] fromView:button];
236     // TODO(jrg): remove this adjustment.
237     NSRect bigger = NSInsetRect([[bbfc folderView] frame], -2, 0);
238     EXPECT_TRUE(NSContainsRect(bigger, r));
239     EXPECT_TRUE([[button cell] isKindOfClass:cellClass]);
240   }
242   // Confirm folder buttons have no tooltip.  The important thing
243   // really is that we insure folders and non-folders are treated
244   // differently; not sure of any other generic way to do this.
245   for (BookmarkButton* button in buttons) {
246     if ([button isFolder])
247       EXPECT_FALSE([button toolTip]);
248     else
249       EXPECT_TRUE([button toolTip]);
250   }
253 // Make sure closing of the window releases the controller.
254 // (e.g. valgrind shouldn't complain if we do this).
255 TEST_F(BookmarkBarFolderControllerTest, ReleaseOnClose) {
256   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
257   bbfc.reset(SimpleBookmarkBarFolderController());
258   EXPECT_TRUE(bbfc.get());
260   [bbfc retain];  // stop the scoped_nsobject from doing anything
261   [[bbfc window] close];  // trigger an autorelease of bbfc.get()
264 TEST_F(BookmarkBarFolderControllerTest, BasicPosition) {
265   BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
266   EXPECT_TRUE(parentButton);
268   // If parent is a BookmarkBarController, grow down.
269   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
270   bbfc.reset([[BookmarkBarFolderController alloc]
271                initWithParentButton:parentButton
272                    parentController:nil
273                       barController:bar_
274                             profile:profile()]);
275   [bbfc window];
276   NSPoint pt = [bbfc windowTopLeftForWidth:0 height:100];  // screen coords
277   NSPoint buttonOriginInWindow =
278       [parentButton convertRect:[parentButton bounds]
279                          toView:nil].origin;
280   NSPoint buttonOriginInScreen =
281       [[parentButton window] convertBaseToScreen:buttonOriginInWindow];
282   // Within margin
283   EXPECT_LE(abs(pt.x - buttonOriginInScreen.x),
284             bookmarks::kBookmarkMenuOverlap + 1);
285   EXPECT_LE(abs(pt.y - buttonOriginInScreen.y),
286             bookmarks::kBookmarkMenuOverlap + 1);
288   // Make sure we see the window shift left if it spills off the screen
289   pt = [bbfc windowTopLeftForWidth:0 height:100];
290   NSPoint shifted = [bbfc windowTopLeftForWidth:9999999 height:100];
291   EXPECT_LT(shifted.x, pt.x);
293   // If parent is a BookmarkBarFolderController, grow right.
294   base::scoped_nsobject<BookmarkBarFolderController> bbfc2;
295   bbfc2.reset([[BookmarkBarFolderController alloc]
296                 initWithParentButton:[[bbfc buttons] objectAtIndex:0]
297                     parentController:bbfc.get()
298                        barController:bar_
299                              profile:profile()]);
300   [bbfc2 window];
301   pt = [bbfc2 windowTopLeftForWidth:0 height:100];
302   // We're now overlapping the window a bit.
303   EXPECT_EQ(pt.x, NSMaxX([[bbfc.get() window] frame]) -
304             bookmarks::kBookmarkMenuOverlap);
307 // Confirm we grow right until end of screen, then start growing left
308 // until end of screen again, then right.
309 TEST_F(BookmarkBarFolderControllerTest, PositionRightLeftRight) {
310   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
311   const BookmarkNode* parent = model->bookmark_bar_node();
312   const BookmarkNode* folder = parent;
314   const int count = 100;
315   int i;
316   // Make some super duper deeply nested folders.
317   for (i = 0; i < count; i++) {
318     folder = model->AddFolder(folder, 0, ASCIIToUTF16("nested folder"));
319   }
321   // Setup initial state for opening all folders.
322   folder = parent;
323   BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
324   BookmarkBarFolderController* parentController = nil;
325   EXPECT_TRUE(parentButton);
327   // Open them all.
328   base::scoped_nsobject<NSMutableArray> folder_controller_array;
329   folder_controller_array.reset([[NSMutableArray array] retain]);
330   for (i=0; i<count; i++) {
331     BookmarkBarFolderControllerNoLevel* bbfcl =
332         [[BookmarkBarFolderControllerNoLevel alloc]
333           initWithParentButton:parentButton
334               parentController:parentController
335                  barController:bar_
336                        profile:profile()];
337     [folder_controller_array addObject:bbfcl];
338     [bbfcl autorelease];
339     [bbfcl window];
340     parentController = bbfcl;
341     parentButton = [[bbfcl buttons] objectAtIndex:0];
342   }
344   // Make vector of all x positions.
345   std::vector<CGFloat> leftPositions;
346   for (i=0; i<count; i++) {
347     CGFloat x = [[[folder_controller_array objectAtIndex:i] window]
348                   frame].origin.x;
349     leftPositions.push_back(x);
350   }
352   // Make sure the first few grow right.
353   for (i=0; i<3; i++)
354     EXPECT_TRUE(leftPositions[i+1] > leftPositions[i]);
356   // Look for the first "grow left".
357   while (leftPositions[i] > leftPositions[i-1])
358     i++;
359   // Confirm the next few also grow left.
360   int j;
361   for (j=i; j<i+3; j++)
362     EXPECT_TRUE(leftPositions[j+1] < leftPositions[j]);
363   i = j;
365   // Finally, confirm we see a "grow right" once more.
366   while (leftPositions[i] < leftPositions[i-1])
367     i++;
368   // (No need to EXPECT a final "grow right"; if we didn't find one
369   // we'd get a C++ array bounds exception).
372 TEST_F(BookmarkBarFolderControllerTest, DropDestination) {
373   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
374   bbfc.reset(SimpleBookmarkBarFolderController());
375   EXPECT_TRUE(bbfc.get());
377   // Confirm "off the top" and "off the bottom" match no buttons.
378   NSPoint p = NSMakePoint(NSMidX([[bbfc folderView] frame]), 10000);
379   EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]);
380   EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]);
381   p = NSMakePoint(NSMidX([[bbfc folderView] frame]), -1);
382   EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]);
383   EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]);
385   // Confirm "right in the center" (give or take a pixel) is a match,
386   // and confirm "just barely in the button" is not.  Anything more
387   // specific seems likely to be tweaked.  We don't loop over all
388   // buttons because the scroll view makes them not visible.
389   for (BookmarkButton* button in [bbfc buttons]) {
390     CGFloat x = NSMidX([button frame]);
391     CGFloat y = NSMidY([button frame]);
392     // Somewhere near the center: a match (but only if a folder!)
393     if ([button isFolder]) {
394       EXPECT_EQ(button,
395                 [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]);
396       EXPECT_EQ(button,
397                 [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]);
398       EXPECT_FALSE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);;
399     } else {
400       // If not a folder we don't drop into it.
401       EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]);
402       EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]);
403       EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);;
404     }
405   }
408 TEST_F(BookmarkBarFolderControllerTest, OpenFolder) {
409   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
410   bbfc.reset(SimpleBookmarkBarFolderController());
411   EXPECT_TRUE(bbfc.get());
413   EXPECT_FALSE([bbfc folderController]);
414   BookmarkButton* button = [[bbfc buttons] objectAtIndex:0];
415   [bbfc openBookmarkFolderFromButton:button];
416   id controller = [bbfc folderController];
417   EXPECT_TRUE(controller);
418   EXPECT_EQ([controller parentButton], button);
420   // Click the same one --> it gets closed.
421   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]];
422   EXPECT_FALSE([bbfc folderController]);
424   // Open a new one --> change.
425   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:1]];
426   EXPECT_NE(controller, [bbfc folderController]);
427   EXPECT_NE([[bbfc folderController] parentButton], button);
429   // Close it --> all gone!
430   [bbfc closeBookmarkFolder:nil];
431   EXPECT_FALSE([bbfc folderController]);
434 TEST_F(BookmarkBarFolderControllerTest, DeleteOpenFolder) {
435   base::scoped_nsobject<BookmarkBarFolderController> parent_controller(
436       SimpleBookmarkBarFolderController());
438   // Open a folder.
439   EXPECT_FALSE([parent_controller folderController]);
440   BookmarkButton* button = [[parent_controller buttons] objectAtIndex:0];
441   [parent_controller openBookmarkFolderFromButton:button];
442   EXPECT_EQ([[parent_controller folderController] parentButton], button);
444   // Delete the folder's button - the folder should close.
445   [parent_controller removeButton:0 animate:NO];
446   EXPECT_FALSE([parent_controller folderController]);
449 TEST_F(BookmarkBarFolderControllerTest, ChildFolderCallbacks) {
450   base::scoped_nsobject<BookmarkBarFolderControllerPong> bbfc;
451   bbfc.reset(SimpleBookmarkBarFolderController());
452   EXPECT_TRUE(bbfc.get());
453   [bar_ setChildFolderDelegate:bbfc.get()];
455   EXPECT_FALSE([bbfc childFolderWillShow]);
456   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]];
457   EXPECT_TRUE([bbfc childFolderWillShow]);
459   EXPECT_FALSE([bbfc childFolderWillClose]);
460   [bbfc closeBookmarkFolder:nil];
461   EXPECT_TRUE([bbfc childFolderWillClose]);
463   [bar_ setChildFolderDelegate:nil];
466 // Make sure bookmark folders have variable widths.
467 TEST_F(BookmarkBarFolderControllerTest, ChildFolderWidth) {
468   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
470   bbfc.reset(SimpleBookmarkBarFolderController());
471   EXPECT_TRUE(bbfc.get());
472   [bbfc showWindow:bbfc.get()];
473   CGFloat wideWidth = NSWidth([[bbfc window] frame]);
475   RemoveLongTitleNode();
476   bbfc.reset(SimpleBookmarkBarFolderController());
477   EXPECT_TRUE(bbfc.get());
478   CGFloat thinWidth = NSWidth([[bbfc window] frame]);
480   // Make sure window size changed as expected.
481   EXPECT_GT(wideWidth, thinWidth);
484 // Simple scrolling tests.
485 // Currently flaky due to a changed definition of the correct menu boundaries.
486 TEST_F(BookmarkBarFolderControllerTest, DISABLED_SimpleScroll) {
487   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
488   NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
489   CGFloat screenHeight = NSHeight(screenFrame);
490   int nodecount = AddLotsOfNodes();
491   bbfc.reset(SimpleBookmarkBarFolderController());
492   EXPECT_TRUE(bbfc.get());
493   [bbfc showWindow:bbfc.get()];
494   NSWindow* window = [bbfc window];
496   // The window should be shorter than the screen but reach exactly to the
497   // bottom of the screen since it's scrollable.
498   EXPECT_LT(NSHeight([window frame]), screenHeight);
499   EXPECT_CGFLOAT_EQ(0.0, [window frame].origin.y);
501   // Initially, should show scroll-up but not scroll-down.
502   EXPECT_TRUE([bbfc canScrollUp]);
503   EXPECT_FALSE([bbfc canScrollDown]);
505   // Scroll up a bit.  Make sure the window has gotten bigger each time.
506   // Also, for each scroll, make sure our hit test finds a new button
507   // (to confirm the content area changed).
508   NSView* savedHit = nil;
509   NSScrollView* scrollView = [bbfc scrollView];
511   // Find the next-to-last button showing at the bottom of the window and
512   // use its center for hit testing.
513   BookmarkButton* targetButton = nil;
514   NSPoint scrollPoint = [scrollView documentVisibleRect].origin;
515   for (BookmarkButton* button in [bbfc buttons]) {
516     NSRect buttonFrame = [button frame];
517     buttonFrame.origin.y -= scrollPoint.y;
518     if (buttonFrame.origin.y < 0.0)
519       break;
520     targetButton = button;
521   }
522   EXPECT_TRUE(targetButton != nil);
523   NSPoint hitPoint = [targetButton frame].origin;
524   hitPoint.x += 50.0;
525   hitPoint.y += (bookmarks::kBookmarkFolderButtonHeight / 2.0) - scrollPoint.y;
526   hitPoint = [targetButton convertPoint:hitPoint toView:scrollView];
528   for (int i = 0; i < 3; i++) {
529     CGFloat height = NSHeight([window frame]);
530     [bbfc performOneScroll:60];
531     EXPECT_GT(NSHeight([window frame]), height);
532     NSView* hit = [scrollView hitTest:hitPoint];
533     // We should hit a bookmark button.
534     EXPECT_TRUE([[hit className] isEqualToString:@"BookmarkButton"]);
535     EXPECT_NE(hit, savedHit);
536     savedHit = hit;
537   }
539   // Keep scrolling up; make sure we never get bigger than the screen.
540   // Also confirm we never scroll the window off the screen.
541   bool bothAtOnce = false;
542   while ([bbfc canScrollUp]) {
543     [bbfc performOneScroll:60];
544     EXPECT_TRUE(NSContainsRect([[NSScreen mainScreen] frame], [window frame]));
545     // Make sure, sometime during our scroll, we have the ability to
546     // scroll in either direction.
547     if ([bbfc canScrollUp] &&
548         [bbfc canScrollDown])
549       bothAtOnce = true;
550   }
551   EXPECT_TRUE(bothAtOnce);
553   // Once we've scrolled to the end, our only option should be to scroll back.
554   EXPECT_FALSE([bbfc canScrollUp]);
555   EXPECT_TRUE([bbfc canScrollDown]);
557   NSRect wholeScreenRect = [[NSScreen mainScreen] frame];
559   // Now scroll down and make sure the window size does not change.
560   // Also confirm we never scroll the window off the screen the other
561   // way.
562   for (int i = 0; i < nodecount+50; ++i) {
563     [bbfc performOneScroll:-60];
564     // Once we can no longer scroll down the window height changes.
565     if (![bbfc canScrollDown])
566       break;
567     EXPECT_TRUE(NSContainsRect(wholeScreenRect, [window frame]));
568   }
570   EXPECT_GT(NSHeight(wholeScreenRect), NSHeight([window frame]));
571   EXPECT_TRUE(NSContainsRect(wholeScreenRect, [window frame]));
574 // Folder menu sizing and placement while deleting bookmarks
575 // and scrolling tests.
576 TEST_F(BookmarkBarFolderControllerTest, MenuPlacementWhileScrollingDeleting) {
577   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
578   AddLotsOfNodes();
579   bbfc.reset(SimpleBookmarkBarFolderController());
580   [bbfc showWindow:bbfc.get()];
581   NSWindow* menuWindow = [bbfc window];
582   BookmarkBarFolderController* folder = [bar_ folderController];
583   NSArray* buttons = [folder buttons];
585   // Before scrolling any, delete a bookmark and make sure the window top has
586   // not moved. Pick a button which is near the top and visible.
587   CGFloat oldTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
588   BookmarkButton* button = [buttons objectAtIndex:3];
589   DeleteBookmark(button, profile());
590   CGFloat newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
591   EXPECT_CGFLOAT_EQ(oldTop, newTop);
593   // Scroll so that both the top and bottom scroll arrows show, make sure
594   // the top of the window has moved up, then delete a visible button and
595   // make sure the top has not moved.
596   oldTop = newTop;
597   const CGFloat scrollOneBookmark = bookmarks::kBookmarkFolderButtonHeight +
598       bookmarks::kBookmarkVerticalPadding;
599   NSUInteger buttonCounter = 0;
600   NSUInteger extraButtonLimit = 3;
601   while (![bbfc canScrollDown] || extraButtonLimit > 0) {
602     [bbfc performOneScroll:scrollOneBookmark];
603     ++buttonCounter;
604     if ([bbfc canScrollDown])
605       --extraButtonLimit;
606   }
607   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
608   EXPECT_NE(oldTop, newTop);
609   oldTop = newTop;
610   button = [buttons objectAtIndex:buttonCounter + 3];
611   DeleteBookmark(button, profile());
612   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
613   EXPECT_CGFLOAT_EQ(oldTop, newTop);
615   // Scroll so that the top scroll arrow is no longer showing, make sure
616   // the top of the window has not moved, then delete a visible button and
617   // make sure the top has not moved.
618   while ([bbfc canScrollDown]) {
619     [bbfc performOneScroll:-scrollOneBookmark];
620     --buttonCounter;
621   }
622   button = [buttons objectAtIndex:buttonCounter + 3];
623   DeleteBookmark(button, profile());
624   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
625   EXPECT_CGFLOAT_EQ(oldTop - bookmarks::kScrollWindowVerticalMargin, newTop);
628 // Make sure that we return the correct browser window.
629 TEST_F(BookmarkBarFolderControllerTest, BrowserWindow) {
630   base::scoped_nsobject<BookmarkBarFolderController> controller(
631       SimpleBookmarkBarFolderController());
632   EXPECT_EQ([bar_ browserWindow], [controller browserWindow]);
635 @interface FakedDragInfo : NSObject {
636 @public
637   NSPoint dropLocation_;
638   NSDragOperation sourceMask_;
640 @property (nonatomic, assign) NSPoint dropLocation;
641 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
642 @end
644 @implementation FakedDragInfo
646 @synthesize dropLocation = dropLocation_;
648 - (id)init {
649   if ((self = [super init])) {
650     dropLocation_ = NSZeroPoint;
651     sourceMask_ = NSDragOperationMove;
652   }
653   return self;
656 // NSDraggingInfo protocol functions.
658 - (id)draggingPasteboard {
659   return self;
662 - (id)draggingSource {
663   return self;
666 - (NSDragOperation)draggingSourceOperationMask {
667   return sourceMask_;
670 - (NSPoint)draggingLocation {
671   return dropLocation_;
674 // Other functions.
676 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
677   sourceMask_ = mask;
680 @end
683 class BookmarkBarFolderControllerMenuTest : public CocoaProfileTest {
684  public:
685   base::scoped_nsobject<NSView> parent_view_;
686   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
687   base::scoped_nsobject<BookmarkBarController> bar_;
689   virtual void SetUp() {
690     CocoaProfileTest::SetUp();
691     ASSERT_TRUE(browser());
693     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
694     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
695     parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
696     [parent_view_ setHidden:YES];
697     bar_.reset([[BookmarkBarController alloc]
698                 initWithBrowser:browser()
699                    initialWidth:NSWidth(parent_frame)
700                        delegate:nil
701                  resizeDelegate:resizeDelegate_.get()]);
702     InstallAndToggleBar(bar_.get());
703   }
705   void InstallAndToggleBar(BookmarkBarController* bar) {
706     // Force loading of the nib.
707     [bar view];
708     // Awkwardness to look like we've been installed.
709     [parent_view_ addSubview:[bar view]];
710     NSRect frame = [[[bar view] superview] frame];
711     frame.origin.y = 400;
712     [[[bar view] superview] setFrame:frame];
714     // Make sure it's on in a window so viewDidMoveToWindow is called
715     [[test_window() contentView] addSubview:parent_view_];
717     // Make sure it's open so certain things aren't no-ops.
718     [bar updateState:BookmarkBar::SHOW
719           changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
720   }
723 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToFolder) {
724   WithNoAnimation at_all;
725   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
726   const BookmarkNode* root = model->bookmark_bar_node();
727   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
728       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
729       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
730   test::AddNodesFromModelString(model, root, model_string);
732   // Validate initial model.
733   std::string actualModelString = test::ModelStringFromNode(root);
734   EXPECT_EQ(model_string, actualModelString);
736   // Pop up a folder menu and drag in a button from the bar.
737   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
738   NSRect oldToFolderFrame = [toFolder frame];
739   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
740                           withObject:toFolder];
741   BookmarkBarFolderController* folderController = [bar_ folderController];
742   EXPECT_TRUE(folderController);
743   NSWindow* toWindow = [folderController window];
744   EXPECT_TRUE(toWindow);
745   NSRect oldToWindowFrame = [toWindow frame];
746   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
747   // so it should end up below the target bookmark.
748   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"];
749   ASSERT_TRUE(draggedButton);
750   CGFloat horizontalShift =
751       NSWidth([draggedButton frame]) + bookmarks::kBookmarkHorizontalPadding;
752   BookmarkButton* targetButton =
753       [folderController buttonWithTitleEqualTo:@"2f1b"];
754   ASSERT_TRUE(targetButton);
755   [folderController dragButton:draggedButton
756                             to:[targetButton center]
757                           copy:NO];
758   // The button should have landed just after "2f1b".
759   const std::string expected_string("2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
760       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
761       "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
762   EXPECT_EQ(expected_string, test::ModelStringFromNode(root));
764   // Verify the window still appears by looking for its controller.
765   EXPECT_TRUE([bar_ folderController]);
767   // Gather the new frames.
768   NSRect newToFolderFrame = [toFolder frame];
769   NSRect newToWindowFrame = [toWindow frame];
770   // The toFolder should have shifted left horizontally but not vertically.
771   NSRect expectedToFolderFrame =
772       NSOffsetRect(oldToFolderFrame, -horizontalShift, 0);
773   EXPECT_NSRECT_EQ(expectedToFolderFrame, newToFolderFrame);
774   // The toWindow should have shifted left horizontally, down vertically,
775   // and grown vertically.
776   NSRect expectedToWindowFrame = oldToWindowFrame;
777   expectedToWindowFrame.origin.x -= horizontalShift;
778   expectedToWindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
779   expectedToWindowFrame.size.height += bookmarks::kBookmarkFolderButtonHeight;
780   EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame);
782   // Check button spacing.
783   [folderController validateMenuSpacing];
785   // Move the button back to the bar at the beginning.
786   draggedButton = [folderController buttonWithTitleEqualTo:@"1b"];
787   ASSERT_TRUE(draggedButton);
788   targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
789   ASSERT_TRUE(targetButton);
790   [bar_ dragButton:draggedButton
791                 to:[targetButton left]
792               copy:NO];
793   EXPECT_EQ(model_string, test::ModelStringFromNode(root));
794   // Don't check the folder window since it's not supposed to be showing.
797 TEST_F(BookmarkBarFolderControllerMenuTest, DragCopyBarBookmarkToFolder) {
798   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
799   const BookmarkNode* root = model->bookmark_bar_node();
800   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
801       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
802       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
803   test::AddNodesFromModelString(model, root, model_string);
805   // Validate initial model.
806   std::string actualModelString = test::ModelStringFromNode(root);
807   EXPECT_EQ(model_string, actualModelString);
809   // Pop up a folder menu and copy in a button from the bar.
810   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
811   ASSERT_TRUE(toFolder);
812   NSRect oldToFolderFrame = [toFolder frame];
813   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
814                           withObject:toFolder];
815   BookmarkBarFolderController* folderController = [bar_ folderController];
816   EXPECT_TRUE(folderController);
817   NSWindow* toWindow = [folderController window];
818   EXPECT_TRUE(toWindow);
819   NSRect oldToWindowFrame = [toWindow frame];
820   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
821   // so it should end up below the target bookmark.
822   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"];
823   ASSERT_TRUE(draggedButton);
824   BookmarkButton* targetButton =
825       [folderController buttonWithTitleEqualTo:@"2f1b"];
826   ASSERT_TRUE(targetButton);
827   [folderController dragButton:draggedButton
828                             to:[targetButton center]
829                           copy:YES];
830   // The button should have landed just after "2f1b".
831   const std::string expected_1("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
832     "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
833     "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
834   EXPECT_EQ(expected_1, test::ModelStringFromNode(root));
836   // Gather the new frames.
837   NSRect newToFolderFrame = [toFolder frame];
838   NSRect newToWindowFrame = [toWindow frame];
839   // The toFolder should have shifted.
840   EXPECT_NSRECT_EQ(oldToFolderFrame, newToFolderFrame);
841   // The toWindow should have shifted down vertically and grown vertically.
842   NSRect expectedToWindowFrame = oldToWindowFrame;
843   expectedToWindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
844   expectedToWindowFrame.size.height += bookmarks::kBookmarkFolderButtonHeight;
845   EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame);
847   // Copy the button back to the bar after "3b".
848   draggedButton = [folderController buttonWithTitleEqualTo:@"1b"];
849   ASSERT_TRUE(draggedButton);
850   targetButton = [bar_ buttonWithTitleEqualTo:@"4f"];
851   ASSERT_TRUE(targetButton);
852   [bar_ dragButton:draggedButton
853                 to:[targetButton left]
854               copy:YES];
855   const std::string expected_2("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
856       "2f2f2b 2f2f3b ] 2f3b ] 3b 1b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
857       "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
858   EXPECT_EQ(expected_2, test::ModelStringFromNode(root));
861 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToSubfolder) {
862   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
863   const BookmarkNode* root = model->bookmark_bar_node();
864   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
865       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
866       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
867   test::AddNodesFromModelString(model, root, model_string);
869   // Validate initial model.
870   std::string actualModelString = test::ModelStringFromNode(root);
871   EXPECT_EQ(model_string, actualModelString);
873   // Pop up a folder menu and a subfolder menu.
874   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
875   ASSERT_TRUE(toFolder);
876   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
877                           withObject:toFolder];
878   BookmarkBarFolderController* folderController = [bar_ folderController];
879   EXPECT_TRUE(folderController);
880   NSWindow* toWindow = [folderController window];
881   EXPECT_TRUE(toWindow);
882   NSRect oldToWindowFrame = [toWindow frame];
883   BookmarkButton* toSubfolder =
884       [folderController buttonWithTitleEqualTo:@"4f2f"];
885   ASSERT_TRUE(toSubfolder);
886   [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:)
887                              withObject:toSubfolder];
888   BookmarkBarFolderController* subfolderController =
889       [folderController folderController];
890   EXPECT_TRUE(subfolderController);
891   NSWindow* toSubwindow = [subfolderController window];
892   EXPECT_TRUE(toSubwindow);
893   NSRect oldToSubwindowFrame = [toSubwindow frame];
894   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
895   // so it should end up below the target bookmark.
896   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"5b"];
897   ASSERT_TRUE(draggedButton);
898   BookmarkButton* targetButton =
899       [subfolderController buttonWithTitleEqualTo:@"4f2f3b"];
900   ASSERT_TRUE(targetButton);
901   [subfolderController dragButton:draggedButton
902                                to:[targetButton center]
903                              copy:NO];
904   // The button should have landed just after "2f".
905   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b "
906       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
907       "4f2f1b 4f2f2b 4f2f3b 5b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] ");
908   EXPECT_EQ(expected_string, test::ModelStringFromNode(root));
910   // Check button spacing.
911   [folderController validateMenuSpacing];
912   [subfolderController validateMenuSpacing];
914   // Check the window layouts. The folder window should not have changed,
915   // but the subfolder window should have shifted vertically and grown.
916   NSRect newToWindowFrame = [toWindow frame];
917   EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame);
918   NSRect newToSubwindowFrame = [toSubwindow frame];
919   NSRect expectedToSubwindowFrame = oldToSubwindowFrame;
920   expectedToSubwindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
921   expectedToSubwindowFrame.size.height +=
922       bookmarks::kBookmarkFolderButtonHeight;
923   EXPECT_NSRECT_EQ(expectedToSubwindowFrame, newToSubwindowFrame);
926 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveWithinFolder) {
927   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
928   const BookmarkNode* root = model->bookmark_bar_node();
929   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
930       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
931       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
932   test::AddNodesFromModelString(model, root, model_string);
934   // Validate initial model.
935   std::string actualModelString = test::ModelStringFromNode(root);
936   EXPECT_EQ(model_string, actualModelString);
938   // Pop up a folder menu.
939   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
940   ASSERT_TRUE(toFolder);
941   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
942                           withObject:toFolder];
943   BookmarkBarFolderController* folderController = [bar_ folderController];
944   EXPECT_TRUE(folderController);
945   NSWindow* toWindow = [folderController window];
946   EXPECT_TRUE(toWindow);
947   NSRect oldToWindowFrame = [toWindow frame];
948   // Drag a folder button to the top within the same parent.
949   BookmarkButton* draggedButton =
950       [folderController buttonWithTitleEqualTo:@"4f2f"];
951   ASSERT_TRUE(draggedButton);
952   BookmarkButton* targetButton =
953       [folderController buttonWithTitleEqualTo:@"4f1f"];
954   ASSERT_TRUE(targetButton);
955   [folderController dragButton:draggedButton
956                             to:[targetButton top]
957                           copy:NO];
958   // The button should have landed above "4f1f".
959   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b "
960       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f2f:[ 4f2f1b 4f2f2b 4f2f3b ] "
961       "4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
962   EXPECT_EQ(expected_string, test::ModelStringFromNode(root));
964   // The window should not have gone away.
965   EXPECT_TRUE([bar_ folderController]);
967   // The folder window should not have changed.
968   NSRect newToWindowFrame = [toWindow frame];
969   EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame);
971   // Check button spacing.
972   [folderController validateMenuSpacing];
975 TEST_F(BookmarkBarFolderControllerMenuTest, DragParentOntoChild) {
976   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
977   const BookmarkNode* root = model->bookmark_bar_node();
978   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
979       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
980       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
981   test::AddNodesFromModelString(model, root, model_string);
983   // Validate initial model.
984   std::string actualModelString = test::ModelStringFromNode(root);
985   EXPECT_EQ(model_string, actualModelString);
987   // Pop up a folder menu.
988   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
989   ASSERT_TRUE(toFolder);
990   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
991                           withObject:toFolder];
992   BookmarkBarFolderController* folderController = [bar_ folderController];
993   EXPECT_TRUE(folderController);
994   NSWindow* toWindow = [folderController window];
995   EXPECT_TRUE(toWindow);
996   // Drag a folder button to one of its children.
997   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"4f"];
998   ASSERT_TRUE(draggedButton);
999   BookmarkButton* targetButton =
1000       [folderController buttonWithTitleEqualTo:@"4f3f"];
1001   ASSERT_TRUE(targetButton);
1002   [folderController dragButton:draggedButton
1003                             to:[targetButton top]
1004                           copy:NO];
1005   // The model should not have changed.
1006   EXPECT_EQ(model_string, test::ModelStringFromNode(root));
1008   // Check button spacing.
1009   [folderController validateMenuSpacing];
1012 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveChildToParent) {
1013   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1014   const BookmarkNode* root = model->bookmark_bar_node();
1015   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1016       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
1017       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1018   test::AddNodesFromModelString(model, root, model_string);
1020   // Validate initial model.
1021   std::string actualModelString = test::ModelStringFromNode(root);
1022   EXPECT_EQ(model_string, actualModelString);
1024   // Pop up a folder menu and a subfolder menu.
1025   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
1026   ASSERT_TRUE(toFolder);
1027   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1028                           withObject:toFolder];
1029   BookmarkBarFolderController* folderController = [bar_ folderController];
1030   EXPECT_TRUE(folderController);
1031   BookmarkButton* toSubfolder =
1032       [folderController buttonWithTitleEqualTo:@"4f2f"];
1033   ASSERT_TRUE(toSubfolder);
1034   [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1035                              withObject:toSubfolder];
1036   BookmarkBarFolderController* subfolderController =
1037       [folderController folderController];
1038   EXPECT_TRUE(subfolderController);
1040   // Drag a subfolder bookmark to the parent folder.
1041   BookmarkButton* draggedButton =
1042       [subfolderController buttonWithTitleEqualTo:@"4f2f3b"];
1043   ASSERT_TRUE(draggedButton);
1044   BookmarkButton* targetButton =
1045       [folderController buttonWithTitleEqualTo:@"4f2f"];
1046   ASSERT_TRUE(targetButton);
1047   [folderController dragButton:draggedButton
1048                             to:[targetButton top]
1049                           copy:NO];
1050   // The button should have landed above "4f2f".
1051   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1052       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f3b 4f2f:[ "
1053       "4f2f1b 4f2f2b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1054   EXPECT_EQ(expected_string, test::ModelStringFromNode(root));
1056   // Check button spacing.
1057   [folderController validateMenuSpacing];
1058   // The window should not have gone away.
1059   EXPECT_TRUE([bar_ folderController]);
1060   // The subfolder should have gone away.
1061   EXPECT_FALSE([folderController folderController]);
1064 TEST_F(BookmarkBarFolderControllerMenuTest, DragWindowResizing) {
1065   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1066   const BookmarkNode* root = model->bookmark_bar_node();
1067   const std::string model_string(
1068       "a b:[ b1 b2 b3 ] reallyReallyLongBookmarkName c ");
1069   test::AddNodesFromModelString(model, root, model_string);
1071   // Validate initial model.
1072   std::string actualModelString = test::ModelStringFromNode(root);
1073   EXPECT_EQ(model_string, actualModelString);
1075   // Pop up a folder menu.
1076   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"b"];
1077   ASSERT_TRUE(toFolder);
1078   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1079                           withObject:toFolder];
1080   BookmarkBarFolderController* folderController = [bar_ folderController];
1081   EXPECT_TRUE(folderController);
1082   NSWindow* toWindow = [folderController window];
1083   EXPECT_TRUE(toWindow);
1084   CGFloat oldWidth = NSWidth([toWindow frame]);
1085   // Drag the bookmark with a long name to the folder.
1086   BookmarkButton* draggedButton =
1087       [bar_ buttonWithTitleEqualTo:@"reallyReallyLongBookmarkName"];
1088   ASSERT_TRUE(draggedButton);
1089   BookmarkButton* targetButton =
1090       [folderController buttonWithTitleEqualTo:@"b1"];
1091   ASSERT_TRUE(targetButton);
1092   [folderController dragButton:draggedButton
1093                             to:[targetButton center]
1094                           copy:NO];
1095   // Verify the model change.
1096   const std::string expected_string(
1097       "a b:[ b1 reallyReallyLongBookmarkName b2 b3 ] c ");
1098   EXPECT_EQ(expected_string, test::ModelStringFromNode(root));
1099   // Verify the window grew. Just test a reasonable width gain.
1100   CGFloat newWidth = NSWidth([toWindow frame]);
1101   EXPECT_LT(oldWidth + 30.0, newWidth);
1104 TEST_F(BookmarkBarFolderControllerMenuTest, MoveRemoveAddButtons) {
1105   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1106   const BookmarkNode* root = model->bookmark_bar_node();
1107   const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
1108   test::AddNodesFromModelString(model, root, model_string);
1110   // Validate initial model.
1111   std::string actualModelString = test::ModelStringFromNode(root);
1112   EXPECT_EQ(model_string, actualModelString);
1114   // Pop up a folder menu.
1115   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
1116   ASSERT_TRUE(toFolder);
1117   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1118                           withObject:toFolder];
1119   BookmarkBarFolderController* folder = [bar_ folderController];
1120   EXPECT_TRUE(folder);
1122   // Remember how many buttons are showing.
1123   NSArray* buttons = [folder buttons];
1124   NSUInteger oldDisplayedButtons = [buttons count];
1126   // Move a button around a bit.
1127   [folder moveButtonFromIndex:0 toIndex:2];
1128   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:0] title]);
1129   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:1] title]);
1130   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:2] title]);
1131   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1132   [folder moveButtonFromIndex:2 toIndex:0];
1133   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1134   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]);
1135   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]);
1136   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1138   // Add a couple of buttons.
1139   const BookmarkNode* node = root->GetChild(2); // Purloin an existing node.
1140   [folder addButtonForNode:node atIndex:0];
1141   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1142   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]);
1143   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]);
1144   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]);
1145   EXPECT_EQ(oldDisplayedButtons + 1, [buttons count]);
1146   node = root->GetChild(3);
1147   [folder addButtonForNode:node atIndex:-1];
1148   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1149   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]);
1150   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]);
1151   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]);
1152   EXPECT_NSEQ(@"4b", [[buttons objectAtIndex:4] title]);
1153   EXPECT_EQ(oldDisplayedButtons + 2, [buttons count]);
1155   // Remove a couple of buttons.
1156   [folder removeButton:4 animate:NO];
1157   [folder removeButton:1 animate:NO];
1158   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1159   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]);
1160   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]);
1161   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1163   // Check button spacing.
1164   [folder validateMenuSpacing];
1167 TEST_F(BookmarkBarFolderControllerMenuTest, ControllerForNode) {
1168   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1169   const BookmarkNode* root = model->bookmark_bar_node();
1170   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1171   test::AddNodesFromModelString(model, root, model_string);
1173   // Validate initial model.
1174   std::string actualModelString = test::ModelStringFromNode(root);
1175   EXPECT_EQ(model_string, actualModelString);
1177   // Find the main bar controller.
1178   const void* expectedController = bar_;
1179   const void* actualController = [bar_ controllerForNode:root];
1180   EXPECT_EQ(expectedController, actualController);
1182   // Pop up the folder menu.
1183   BookmarkButton* targetFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
1184   ASSERT_TRUE(targetFolder);
1185   [[targetFolder target]
1186    performSelector:@selector(openBookmarkFolderFromButton:)
1187    withObject:targetFolder];
1188   BookmarkBarFolderController* folder = [bar_ folderController];
1189   EXPECT_TRUE(folder);
1191   // Find the folder controller using the folder controller.
1192   const BookmarkNode* targetNode = root->GetChild(1);
1193   expectedController = folder;
1194   actualController = [bar_ controllerForNode:targetNode];
1195   EXPECT_EQ(expectedController, actualController);
1197   // Find the folder controller from the bar.
1198   actualController = [folder controllerForNode:targetNode];
1199   EXPECT_EQ(expectedController, actualController);
1202 TEST_F(BookmarkBarFolderControllerMenuTest, MenuSizingAndScrollArrows) {
1203   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1204   const BookmarkNode* root = model->bookmark_bar_node();
1205   const std::string model_string("1b 2b 3b ");
1206   test::AddNodesFromModelString(model, root, model_string);
1208   // Validate initial model.
1209   std::string actualModelString = test::ModelStringFromNode(root);
1210   EXPECT_EQ(model_string, actualModelString);
1212   const BookmarkNode* parent = model->bookmark_bar_node();
1213   const BookmarkNode* folder = model->AddFolder(parent,
1214                                                 parent->child_count(),
1215                                                 ASCIIToUTF16("BIG"));
1217   // Pop open the new folder window and verify it has one (empty) item.
1218   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"];
1219   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1220                         withObject:button];
1221   BookmarkBarFolderController* folderController = [bar_ folderController];
1222   EXPECT_TRUE(folderController);
1223   NSWindow* folderWindow = [folderController window];
1224   EXPECT_TRUE(folderWindow);
1225   CGFloat expectedHeight = (CGFloat)bookmarks::kBookmarkFolderButtonHeight +
1226       (2*bookmarks::kBookmarkVerticalPadding);
1227   NSRect windowFrame = [folderWindow frame];
1228   CGFloat windowHeight = NSHeight(windowFrame);
1229   EXPECT_CGFLOAT_EQ(expectedHeight, windowHeight);
1230   EXPECT_FALSE([folderController canScrollUp]);
1231   EXPECT_FALSE([folderController canScrollDown]);
1233   // Now add a real bookmark and reopen.
1234   model->AddURL(folder, folder->child_count(), ASCIIToUTF16("a"),
1235                 GURL("http://a.com/"));
1236   folderController = [bar_ folderController];
1237   EXPECT_TRUE(folderController);
1238   NSView* folderView = [folderController folderView];
1239   EXPECT_TRUE(folderView);
1240   NSRect menuFrame = [folderView frame];
1241   NSView* visibleView = [folderController visibleView];
1242   NSRect visibleFrame = [visibleView frame];
1243   NSScrollView* scrollView = [folderController scrollView];
1244   NSRect scrollFrame = [scrollView frame];
1246   // Determine the margins between the scroll frame and the visible frame.
1247   CGFloat widthDelta = NSWidth(visibleFrame) - NSWidth(scrollFrame);
1249   CGFloat menuHeight = NSHeight(menuFrame);
1250   EXPECT_CGFLOAT_EQ(expectedHeight, menuHeight);
1251   CGFloat scrollerWidth = NSWidth(scrollFrame);
1252   button = [folderController buttonWithTitleEqualTo:@"a"];
1253   CGFloat buttonWidth = NSWidth([button frame]);
1254   EXPECT_CGFLOAT_EQ(scrollerWidth, buttonWidth);
1255   CGFloat visibleWidth = NSWidth(visibleFrame);
1256   EXPECT_CGFLOAT_EQ(visibleWidth - widthDelta, buttonWidth);
1257   EXPECT_LT(scrollerWidth, NSWidth([folderView frame]));
1259   // Add a wider bookmark and make sure the button widths match.
1260   int reallyWideButtonNumber = folder->child_count();
1261   model->AddURL(folder, reallyWideButtonNumber,
1262                 ASCIIToUTF16("A really, really, really, really, really, "
1263                             "really long name"),
1264                 GURL("http://www.google.com/a"));
1265   BookmarkButton* bigButton =
1266       [folderController buttonWithTitleEqualTo:
1267        @"A really, really, really, really, really, really long name"];
1268   EXPECT_TRUE(bigButton);
1269   CGFloat buttonWidthB = NSWidth([bigButton frame]);
1270   EXPECT_LT(buttonWidth, buttonWidthB);
1271   // Add a bunch of bookmarks until the window becomes scrollable, then check
1272   // for a scroll up arrow.
1273   NSUInteger tripWire = 0;  // Prevent a runaway.
1274   while (![folderController canScrollUp] && ++tripWire < 1000) {
1275     model->AddURL(folder, folder->child_count(), ASCIIToUTF16("B"),
1276                   GURL("http://b.com/"));
1277   }
1278   EXPECT_TRUE([folderController canScrollUp]);
1280   // Remove one bookmark and make sure the scroll down arrow has been removed.
1281   // We'll remove the really long node so we can see if the buttons get resized.
1282   scrollerWidth = NSWidth([folderView frame]);
1283   buttonWidth = NSWidth([button frame]);
1284   model->Remove(folder, reallyWideButtonNumber);
1285   EXPECT_FALSE([folderController canScrollUp]);
1286   EXPECT_FALSE([folderController canScrollDown]);
1288   // Check the size. It should have reduced.
1289   EXPECT_GT(scrollerWidth, NSWidth([folderView frame]));
1290   EXPECT_GT(buttonWidth, NSWidth([button frame]));
1292   // Check button spacing.
1293   [folderController validateMenuSpacing];
1296 // See http://crbug.com/46101
1297 TEST_F(BookmarkBarFolderControllerMenuTest, HoverThenDeleteBookmark) {
1298   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1299   const BookmarkNode* root = model->bookmark_bar_node();
1300   const BookmarkNode* folder = model->AddFolder(root,
1301                                                 root->child_count(),
1302                                                 ASCIIToUTF16("BIG"));
1303   for (int i = 0; i < kLotsOfNodesCount; i++)
1304     model->AddURL(folder, folder->child_count(), ASCIIToUTF16("kid"),
1305                   GURL("http://kid.com/smile"));
1307   // Pop open the new folder window and hover one of its kids.
1308   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"];
1309   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1310                         withObject:button];
1311   BookmarkBarFolderController* bbfc = [bar_ folderController];
1312   NSArray* buttons = [bbfc buttons];
1314   // Hover over a button and verify that it is now known.
1315   button = [buttons objectAtIndex:3];
1316   BookmarkButton* buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1317   EXPECT_FALSE(buttonThatMouseIsIn);
1318   [bbfc mouseEnteredButton:button event:nil];
1319   buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1320   EXPECT_EQ(button, buttonThatMouseIsIn);
1322   // Delete the bookmark and verify that it is now not known.
1323   model->Remove(folder, 3);
1324   buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1325   EXPECT_FALSE(buttonThatMouseIsIn);
1328 // Just like a BookmarkBarFolderController but intercedes when providing
1329 // pasteboard drag data.
1330 @interface BookmarkBarFolderControllerDragData : BookmarkBarFolderController {
1331   const BookmarkNode* dragDataNode_;  // Weak
1333 - (void)setDragDataNode:(const BookmarkNode*)node;
1334 @end
1336 @implementation BookmarkBarFolderControllerDragData
1338 - (id)initWithParentButton:(BookmarkButton*)button
1339           parentController:(BookmarkBarFolderController*)parentController
1340              barController:(BookmarkBarController*)barController
1341                    profile:(Profile*)profile {
1342   if ((self = [super initWithParentButton:button
1343                          parentController:parentController
1344                             barController:barController
1345                                   profile:profile])) {
1346     dragDataNode_ = NULL;
1347   }
1348   return self;
1351 - (void)setDragDataNode:(const BookmarkNode*)node {
1352   dragDataNode_ = node;
1355 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
1356   std::vector<const BookmarkNode*> dragDataNodes;
1357   if(dragDataNode_) {
1358     dragDataNodes.push_back(dragDataNode_);
1359   }
1360   return dragDataNodes;
1363 @end
1365 TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkData) {
1366   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1367   const BookmarkNode* root = model->bookmark_bar_node();
1368   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1369                                  "2f3b ] 3b 4b ");
1370   test::AddNodesFromModelString(model, root, model_string);
1371   const BookmarkNode* other = model->other_node();
1372   const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1373                                  "O4f:[ O4f1b O4f2f ] 05b ");
1374   test::AddNodesFromModelString(model, other, other_string);
1376   // Validate initial model.
1377   std::string actual = test::ModelStringFromNode(root);
1378   EXPECT_EQ(model_string, actual);
1379   actual = test::ModelStringFromNode(other);
1380   EXPECT_EQ(other_string, actual);
1382   // Pop open a folder.
1383   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1384   base::scoped_nsobject<BookmarkBarFolderControllerDragData> folderController;
1385   folderController.reset([[BookmarkBarFolderControllerDragData alloc]
1386                           initWithParentButton:button
1387                               parentController:nil
1388                                  barController:bar_
1389                                        profile:profile()]);
1390   BookmarkButton* targetButton =
1391       [folderController buttonWithTitleEqualTo:@"2f1b"];
1392   ASSERT_TRUE(targetButton);
1394   // Gen up some dragging data.
1395   const BookmarkNode* newNode = other->GetChild(2);
1396   [folderController setDragDataNode:newNode];
1397   base::scoped_nsobject<FakedDragInfo> dragInfo([[FakedDragInfo alloc] init]);
1398   [dragInfo setDropLocation:[targetButton top]];
1399   [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1401   // Verify the model.
1402   const std::string expected("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ 2f2f1b "
1403                              "2f2f2b 2f2f3b ] 2f3b ] 3b 4b ");
1404   actual = test::ModelStringFromNode(root);
1405   EXPECT_EQ(expected, actual);
1407   // Now drag over a folder button.
1408   targetButton = [folderController buttonWithTitleEqualTo:@"2f2f"];
1409   ASSERT_TRUE(targetButton);
1410   newNode = other->GetChild(2);  // Should be O4f.
1411   EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1412   [folderController setDragDataNode:newNode];
1413   [dragInfo setDropLocation:[targetButton center]];
1414   [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1416   // Verify the model.
1417   const std::string expectedA("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ "
1418                               "2f2f1b 2f2f2b 2f2f3b O4f:[ O4f1b O4f2f ] ] "
1419                               "2f3b ] 3b 4b ");
1420   actual = test::ModelStringFromNode(root);
1421   EXPECT_EQ(expectedA, actual);
1423   // Check button spacing.
1424   [folderController validateMenuSpacing];
1427 TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkDataToTrash) {
1428   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1429   const BookmarkNode* root = model->bookmark_bar_node();
1430   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1431                                  "2f3b ] 3b 4b ");
1432   test::AddNodesFromModelString(model, root, model_string);
1434   // Validate initial model.
1435   std::string actual = test::ModelStringFromNode(root);
1436   EXPECT_EQ(model_string, actual);
1438   const BookmarkNode* folderNode = root->GetChild(1);
1439   int oldFolderChildCount = folderNode->child_count();
1441   // Pop open a folder.
1442   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1443   base::scoped_nsobject<BookmarkBarFolderControllerDragData> folderController;
1444   folderController.reset([[BookmarkBarFolderControllerDragData alloc]
1445                           initWithParentButton:button
1446                               parentController:nil
1447                                  barController:bar_
1448                                        profile:profile()]);
1450   // Drag a button to the trash.
1451   BookmarkButton* buttonToDelete =
1452       [folderController buttonWithTitleEqualTo:@"2f1b"];
1453   ASSERT_TRUE(buttonToDelete);
1454   EXPECT_TRUE([folderController canDragBookmarkButtonToTrash:buttonToDelete]);
1455   [folderController didDragBookmarkToTrash:buttonToDelete];
1457   // There should be one less button in the folder.
1458   int newFolderChildCount = folderNode->child_count();
1459   EXPECT_EQ(oldFolderChildCount - 1, newFolderChildCount);
1460   // Verify the model.
1461   const std::string expected("1b 2f:[ 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1462                              "2f3b ] 3b 4b ");
1463   actual = test::ModelStringFromNode(root);
1464   EXPECT_EQ(expected, actual);
1466   // Check button spacing.
1467   [folderController validateMenuSpacing];
1470 TEST_F(BookmarkBarFolderControllerMenuTest, AddURLs) {
1471   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1472   const BookmarkNode* root = model->bookmark_bar_node();
1473   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1474                                  "2f3b ] 3b 4b ");
1475   test::AddNodesFromModelString(model, root, model_string);
1477   // Validate initial model.
1478   std::string actual = test::ModelStringFromNode(root);
1479   EXPECT_EQ(model_string, actual);
1481   // Pop open a folder.
1482   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1483   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1484                         withObject:button];
1485   BookmarkBarFolderController* folderController = [bar_ folderController];
1486   EXPECT_TRUE(folderController);
1487   NSArray* buttons = [folderController buttons];
1488   EXPECT_TRUE(buttons);
1490   // Remember how many buttons are showing.
1491   int oldDisplayedButtons = [buttons count];
1493   BookmarkButton* targetButton =
1494       [folderController buttonWithTitleEqualTo:@"2f1b"];
1495   ASSERT_TRUE(targetButton);
1497   NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
1498                    @"http://www.b.com/", nil];
1499   NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
1500   [folderController addURLs:urls withTitles:titles at:[targetButton top]];
1502   // There should two more buttons in the folder.
1503   int newDisplayedButtons = [buttons count];
1504   EXPECT_EQ(oldDisplayedButtons + 2, newDisplayedButtons);
1505   // Verify the model.
1506   const std::string expected("1b 2f:[ SiteA SiteB 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1507                              "2f2f3b ] 2f3b ] 3b 4b ");
1508   actual = test::ModelStringFromNode(root);
1509   EXPECT_EQ(expected, actual);
1511   // Check button spacing.
1512   [folderController validateMenuSpacing];
1515 TEST_F(BookmarkBarFolderControllerMenuTest, DropPositionIndicator) {
1516   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1517   const BookmarkNode* root = model->bookmark_bar_node();
1518   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1519                                  "2f3b ] 3b 4b ");
1520   test::AddNodesFromModelString(model, root, model_string);
1522   // Validate initial model.
1523   std::string actual = test::ModelStringFromNode(root);
1524   EXPECT_EQ(model_string, actual);
1526   // Pop open the folder.
1527   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1528   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1529                         withObject:button];
1530   BookmarkBarFolderController* folder = [bar_ folderController];
1531   EXPECT_TRUE(folder);
1533   // Test a series of points starting at the top of the folder.
1534   const CGFloat yOffset = 0.5 * bookmarks::kBookmarkVerticalPadding;
1535   BookmarkButton* targetButton = [folder buttonWithTitleEqualTo:@"2f1b"];
1536   ASSERT_TRUE(targetButton);
1537   NSPoint targetPoint = [targetButton top];
1538   CGFloat pos = [folder indicatorPosForDragToPoint:targetPoint];
1539   EXPECT_CGFLOAT_EQ(targetPoint.y + yOffset, pos);
1540   pos = [folder indicatorPosForDragToPoint:[targetButton bottom]];
1541   targetButton = [folder buttonWithTitleEqualTo:@"2f2f"];
1542   EXPECT_CGFLOAT_EQ([targetButton top].y + yOffset, pos);
1543   pos = [folder indicatorPosForDragToPoint:NSMakePoint(10,0)];
1544   targetButton = [folder buttonWithTitleEqualTo:@"2f3b"];
1545   EXPECT_CGFLOAT_EQ([targetButton bottom].y - yOffset, pos);
1548 @interface BookmarkBarControllerNoDelete : BookmarkBarController
1549 - (IBAction)deleteBookmark:(id)sender;
1550 @end
1552 @implementation BookmarkBarControllerNoDelete
1553 - (IBAction)deleteBookmark:(id)sender {
1554   // NOP
1556 @end
1558 class BookmarkBarFolderControllerClosingTest : public
1559     BookmarkBarFolderControllerMenuTest {
1560  public:
1561   virtual void SetUp() {
1562     BookmarkBarFolderControllerMenuTest::SetUp();
1563     ASSERT_TRUE(browser());
1565     bar_.reset([[BookmarkBarControllerNoDelete alloc]
1566                 initWithBrowser:browser()
1567                    initialWidth:NSWidth([parent_view_ frame])
1568                        delegate:nil
1569                  resizeDelegate:resizeDelegate_.get()]);
1570     InstallAndToggleBar(bar_.get());
1571   }
1574 TEST_F(BookmarkBarFolderControllerClosingTest, DeleteClosesFolder) {
1575   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1576   const BookmarkNode* root = model->bookmark_bar_node();
1577   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b ] "
1578                                  "2f3b ] 3b ");
1579   test::AddNodesFromModelString(model, root, model_string);
1581   // Validate initial model.
1582   std::string actualModelString = test::ModelStringFromNode(root);
1583   EXPECT_EQ(model_string, actualModelString);
1585   // Open the folder menu and submenu.
1586   BookmarkButton* target = [bar_ buttonWithTitleEqualTo:@"2f"];
1587   ASSERT_TRUE(target);
1588   [[target target] performSelector:@selector(openBookmarkFolderFromButton:)
1589                               withObject:target];
1590   BookmarkBarFolderController* folder = [bar_ folderController];
1591   EXPECT_TRUE(folder);
1592   BookmarkButton* subTarget = [folder buttonWithTitleEqualTo:@"2f2f"];
1593   ASSERT_TRUE(subTarget);
1594   [[subTarget target] performSelector:@selector(openBookmarkFolderFromButton:)
1595                            withObject:subTarget];
1596   BookmarkBarFolderController* subFolder = [folder folderController];
1597   EXPECT_TRUE(subFolder);
1599   // Delete the folder node and verify the window closed down by looking
1600   // for its controller again.
1601   DeleteBookmark([folder parentButton], profile());
1602   EXPECT_FALSE([folder folderController]);
1605 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1606 // they are hard to test.  Factor out "fire timers" into routines
1607 // which can be overridden to fire immediately to make behavior
1608 // confirmable.
1609 // There is a similar problem with mouseEnteredButton: and
1610 // mouseExitedButton:.