Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / bookmarks / bookmark_bar_folder_controller_unittest.mm
blob203b6f892e870bc3045011a99faab36a5cec0570
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_factory.h"
9 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
10 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
11 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_button_cell.h"
12 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h"
13 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h"
14 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
15 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
16 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/bookmarks/browser/bookmark_model.h"
19 #include "components/bookmarks/test/bookmark_test_helpers.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 #include <cmath>
27 using base::ASCIIToUTF16;
28 using bookmarks::BookmarkModel;
29 using bookmarks::BookmarkNode;
31 namespace {
33 const int kLotsOfNodesCount = 150;
35 // Deletes the bookmark corresponding to |button|.
36 void DeleteBookmark(BookmarkButton* button, Profile* profile) {
37   const BookmarkNode* node = [button bookmarkNode];
38   if (node) {
39     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
40     model->Remove(node);
41   }
44 }  // namespace
46 // Add a redirect to make testing easier.
47 @interface BookmarkBarFolderController(MakeTestingEasier)
48 - (void)validateMenuSpacing;
49 @end
51 @implementation BookmarkBarFolderController(MakeTestingEasier)
53 // Utility function to verify that the buttons in this folder are all
54 // evenly spaced in a progressive manner.
55 - (void)validateMenuSpacing {
56   BOOL firstButton = YES;
57   CGFloat lastVerticalOffset = 0.0;
58   for (BookmarkButton* button in [self buttons]) {
59     if (firstButton) {
60       firstButton = NO;
61       lastVerticalOffset = [button frame].origin.y;
62     } else {
63       CGFloat nextVerticalOffset = [button frame].origin.y;
64       EXPECT_CGFLOAT_EQ(lastVerticalOffset -
65                             bookmarks::kBookmarkFolderButtonHeight,
66                         nextVerticalOffset);
67       lastVerticalOffset = nextVerticalOffset;
68     }
69   }
71 @end
73 // Don't use a high window level when running unit tests -- it'll
74 // interfere with anything else you are working on.
75 // For testing.
76 @interface BookmarkBarFolderControllerNoLevel : BookmarkBarFolderController
77 @end
79 @implementation BookmarkBarFolderControllerNoLevel
80 - (void)configureWindowLevel {
81   // Intentionally empty.
83 @end
85 @interface BookmarkBarFolderControllerPong : BookmarkBarFolderController {
86   BOOL childFolderWillShow_;
87   BOOL childFolderWillClose_;
89 @property (nonatomic, readonly) BOOL childFolderWillShow;
90 @property (nonatomic, readonly) BOOL childFolderWillClose;
91 @end
93 @implementation BookmarkBarFolderControllerPong
94 @synthesize childFolderWillShow = childFolderWillShow_;
95 @synthesize childFolderWillClose = childFolderWillClose_;
97 - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
98   childFolderWillShow_ = YES;
101 - (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
102   childFolderWillClose_ = YES;
105 // We don't have a real BookmarkBarController as our parent root so
106 // we fake this one out.
107 - (void)closeAllBookmarkFolders {
108   [self closeBookmarkFolder:self];
111 @end
113 // Redirect certain calls so they can be seen by tests.
115 @interface BookmarkBarControllerChildFolderRedirect : BookmarkBarController {
116   BookmarkBarFolderController* childFolderDelegate_;
118 @property (nonatomic, assign) BookmarkBarFolderController* childFolderDelegate;
119 @end
121 @implementation BookmarkBarControllerChildFolderRedirect
123 @synthesize childFolderDelegate = childFolderDelegate_;
125 - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
126   [childFolderDelegate_ childFolderWillShow:child];
129 - (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
130   [childFolderDelegate_ childFolderWillClose:child];
133 @end
136 class BookmarkBarFolderControllerTest : public CocoaProfileTest {
137  public:
138   base::scoped_nsobject<BookmarkBarControllerChildFolderRedirect> bar_;
139   const BookmarkNode* folderA_;  // Owned by model.
140   const BookmarkNode* longTitleNode_;  // Owned by model.
142   void SetUp() override {
143     CocoaProfileTest::SetUp();
144     ASSERT_TRUE(profile());
146     CreateModel();
147   }
149   void CreateModel() {
150     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
151     const BookmarkNode* parent = model->bookmark_bar_node();
152     const BookmarkNode* folderA = model->AddFolder(parent,
153                                                    parent->child_count(),
154                                                    ASCIIToUTF16("folder"));
155     folderA_ = folderA;
156     model->AddFolder(parent, parent->child_count(),
157                      ASCIIToUTF16("sibbling folder"));
158     const BookmarkNode* folderB = model->AddFolder(folderA,
159                                                    folderA->child_count(),
160                                                    ASCIIToUTF16("subfolder 1"));
161     model->AddFolder(folderA,
162                      folderA->child_count(),
163                      ASCIIToUTF16("subfolder 2"));
164     model->AddURL(folderA, folderA->child_count(), ASCIIToUTF16("title a"),
165                   GURL("http://www.google.com/a"));
166     longTitleNode_ = model->AddURL(
167       folderA, folderA->child_count(),
168       ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
169       GURL("http://www.google.com/b"));
170     model->AddURL(folderB, folderB->child_count(), ASCIIToUTF16("t"),
171                   GURL("http://www.google.com/c"));
173     bar_.reset(
174       [[BookmarkBarControllerChildFolderRedirect alloc]
175           initWithBrowser:browser()
176              initialWidth:300
177                  delegate:nil
178            resizeDelegate:nil]);
179     [bar_ loaded:model];
180     // Make parent frame for bookmark bar then open it.
181     NSRect frame = [[test_window() contentView] frame];
182     frame = NSMakeRect(frame.origin.x,
183                        frame.size.height - chrome::kNTPBookmarkBarHeight,
184                        frame.size.width, chrome::kNTPBookmarkBarHeight);
185     NSView* fakeToolbarView = [[[NSView alloc] initWithFrame:frame]
186                                 autorelease];
187     [[test_window() contentView] addSubview:fakeToolbarView];
188     [fakeToolbarView addSubview:[bar_ view]];
189     [bar_ setBookmarkBarEnabled:YES];
190   }
192   // Remove the bookmark with the long title.
193   void RemoveLongTitleNode() {
194     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
195     model->Remove(longTitleNode_);
196   }
198   // Add LOTS of nodes to our model if needed (e.g. scrolling).
199   // Returns the number of nodes added.
200   int AddLotsOfNodes() {
201     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
202     for (int i = 0; i < kLotsOfNodesCount; i++) {
203       model->AddURL(folderA_, folderA_->child_count(),
204                     ASCIIToUTF16("repeated title"),
205                     GURL("http://www.google.com/repeated/url"));
206     }
207     return kLotsOfNodesCount;
208   }
210   // Return a simple BookmarkBarFolderController.
211   BookmarkBarFolderControllerPong* SimpleBookmarkBarFolderController() {
212     BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
213     BookmarkBarFolderControllerPong* c =
214       [[BookmarkBarFolderControllerPong alloc]
215                initWithParentButton:parentButton
216                    parentController:nil
217                       barController:bar_
218                             profile:profile()];
219     [c window];  // Force nib load.
220     return c;
221   }
224 TEST_F(BookmarkBarFolderControllerTest, InitCreateAndDelete) {
225   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
226   bbfc.reset(SimpleBookmarkBarFolderController());
228   // Make sure none of the buttons overlap, that all are inside
229   // the content frame, and their cells are of the proper class.
230   NSArray* buttons = [bbfc buttons];
231   EXPECT_TRUE([buttons count]);
232   for (unsigned int i = 0; i < ([buttons count]-1); i++) {
233     EXPECT_FALSE(NSContainsRect([[buttons objectAtIndex:i] frame],
234                               [[buttons objectAtIndex:i+1] frame]));
235   }
236   Class cellClass = [BookmarkBarFolderButtonCell class];
237   for (BookmarkButton* button in buttons) {
238     NSRect r = [[bbfc folderView] convertRect:[button frame] fromView:button];
239     // TODO(jrg): remove this adjustment.
240     NSRect bigger = NSInsetRect([[bbfc folderView] frame], -2, 0);
241     EXPECT_TRUE(NSContainsRect(bigger, r));
242     EXPECT_TRUE([[button cell] isKindOfClass:cellClass]);
243   }
245   // Confirm folder buttons have no tooltip.  The important thing
246   // really is that we insure folders and non-folders are treated
247   // differently; not sure of any other generic way to do this.
248   for (BookmarkButton* button in buttons) {
249     if ([button isFolder])
250       EXPECT_FALSE([button toolTip]);
251     else
252       EXPECT_TRUE([button toolTip]);
253   }
256 // Make sure closing of the window releases the controller.
257 // (e.g. valgrind shouldn't complain if we do this).
258 TEST_F(BookmarkBarFolderControllerTest, ReleaseOnClose) {
259   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
260   bbfc.reset(SimpleBookmarkBarFolderController());
261   EXPECT_TRUE(bbfc.get());
263   [bbfc retain];  // stop the scoped_nsobject from doing anything
264   [[bbfc window] close];  // trigger an autorelease of bbfc.get()
267 TEST_F(BookmarkBarFolderControllerTest, BasicPosition) {
268   BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
269   EXPECT_TRUE(parentButton);
271   // If parent is a BookmarkBarController, grow down.
272   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
273   bbfc.reset([[BookmarkBarFolderController alloc]
274                initWithParentButton:parentButton
275                    parentController:nil
276                       barController:bar_
277                             profile:profile()]);
278   [bbfc window];
279   NSPoint pt = [bbfc windowTopLeftForWidth:0 height:100];  // screen coords
280   NSPoint buttonOriginInWindow =
281       [parentButton convertRect:[parentButton bounds]
282                          toView:nil].origin;
283   NSPoint buttonOriginInScreen =
284       [[parentButton window] convertBaseToScreen:buttonOriginInWindow];
285   // Within margin
286   EXPECT_LE(std::abs(pt.x - buttonOriginInScreen.x),
287             bookmarks::kBookmarkMenuOverlap + 1);
288   EXPECT_LE(std::abs(pt.y - buttonOriginInScreen.y),
289             bookmarks::kBookmarkMenuOverlap + 1);
291   // Make sure we see the window shift left if it spills off the screen
292   pt = [bbfc windowTopLeftForWidth:0 height:100];
293   NSPoint shifted = [bbfc windowTopLeftForWidth:9999999 height:100];
294   EXPECT_LT(shifted.x, pt.x);
296   // If parent is a BookmarkBarFolderController, grow right.
297   base::scoped_nsobject<BookmarkBarFolderController> bbfc2;
298   bbfc2.reset([[BookmarkBarFolderController alloc]
299                 initWithParentButton:[[bbfc buttons] objectAtIndex:0]
300                     parentController:bbfc.get()
301                        barController:bar_
302                              profile:profile()]);
303   [bbfc2 window];
304   pt = [bbfc2 windowTopLeftForWidth:0 height:100];
305   // We're now overlapping the window a bit.
306   EXPECT_EQ(pt.x, NSMaxX([[bbfc.get() window] frame]) -
307             bookmarks::kBookmarkMenuOverlap);
310 // Confirm we grow right until end of screen, then start growing left
311 // until end of screen again, then right.
312 TEST_F(BookmarkBarFolderControllerTest, PositionRightLeftRight) {
313   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
314   const BookmarkNode* parent = model->bookmark_bar_node();
315   const BookmarkNode* folder = parent;
317   const int count = 100;
318   int i;
319   // Make some super duper deeply nested folders.
320   for (i = 0; i < count; i++) {
321     folder = model->AddFolder(folder, 0, ASCIIToUTF16("nested folder"));
322   }
324   // Setup initial state for opening all folders.
325   folder = parent;
326   BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
327   BookmarkBarFolderController* parentController = nil;
328   EXPECT_TRUE(parentButton);
330   // Open them all.
331   base::scoped_nsobject<NSMutableArray> folder_controller_array;
332   folder_controller_array.reset([[NSMutableArray array] retain]);
333   for (i=0; i<count; i++) {
334     BookmarkBarFolderControllerNoLevel* bbfcl =
335         [[BookmarkBarFolderControllerNoLevel alloc]
336           initWithParentButton:parentButton
337               parentController:parentController
338                  barController:bar_
339                        profile:profile()];
340     [folder_controller_array addObject:bbfcl];
341     [bbfcl autorelease];
342     [bbfcl window];
343     parentController = bbfcl;
344     parentButton = [[bbfcl buttons] objectAtIndex:0];
345   }
347   // Make vector of all x positions.
348   std::vector<CGFloat> leftPositions;
349   for (i=0; i<count; i++) {
350     CGFloat x = [[[folder_controller_array objectAtIndex:i] window]
351                   frame].origin.x;
352     leftPositions.push_back(x);
353   }
355   // Make sure the first few grow right.
356   for (i=0; i<3; i++)
357     EXPECT_TRUE(leftPositions[i+1] > leftPositions[i]);
359   // Look for the first "grow left".
360   while (leftPositions[i] > leftPositions[i-1])
361     i++;
362   // Confirm the next few also grow left.
363   int j;
364   for (j=i; j<i+3; j++)
365     EXPECT_TRUE(leftPositions[j+1] < leftPositions[j]);
366   i = j;
368   // Finally, confirm we see a "grow right" once more.
369   while (leftPositions[i] < leftPositions[i-1])
370     i++;
371   // (No need to EXPECT a final "grow right"; if we didn't find one
372   // we'd get a C++ array bounds exception).
375 TEST_F(BookmarkBarFolderControllerTest, DropDestination) {
376   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
377   bbfc.reset(SimpleBookmarkBarFolderController());
378   EXPECT_TRUE(bbfc.get());
380   // Confirm "off the top" and "off the bottom" match no buttons.
381   NSPoint p = NSMakePoint(NSMidX([[bbfc folderView] frame]), 10000);
382   EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]);
383   EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]);
384   p = NSMakePoint(NSMidX([[bbfc folderView] frame]), -1);
385   EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]);
386   EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]);
388   // Confirm "right in the center" (give or take a pixel) is a match,
389   // and confirm "just barely in the button" is not.  Anything more
390   // specific seems likely to be tweaked.  We don't loop over all
391   // buttons because the scroll view makes them not visible.
392   for (BookmarkButton* button in [bbfc buttons]) {
393     CGFloat x = NSMidX([button frame]);
394     CGFloat y = NSMidY([button frame]);
395     // Somewhere near the center: a match (but only if a folder!)
396     if ([button isFolder]) {
397       EXPECT_EQ(button,
398                 [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]);
399       EXPECT_EQ(button,
400                 [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]);
401       EXPECT_FALSE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);;
402     } else {
403       // If not a folder we don't drop into it.
404       EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]);
405       EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]);
406       EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);;
407     }
408   }
411 TEST_F(BookmarkBarFolderControllerTest, OpenFolder) {
412   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
413   bbfc.reset(SimpleBookmarkBarFolderController());
414   EXPECT_TRUE(bbfc.get());
416   EXPECT_FALSE([bbfc folderController]);
417   BookmarkButton* button = [[bbfc buttons] objectAtIndex:0];
418   [bbfc openBookmarkFolderFromButton:button];
419   id controller = [bbfc folderController];
420   EXPECT_TRUE(controller);
421   EXPECT_EQ([controller parentButton], button);
423   // Click the same one --> it gets closed.
424   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]];
425   EXPECT_FALSE([bbfc folderController]);
427   // Open a new one --> change.
428   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:1]];
429   EXPECT_NE(controller, [bbfc folderController]);
430   EXPECT_NE([[bbfc folderController] parentButton], button);
432   // Close it --> all gone!
433   [bbfc closeBookmarkFolder:nil];
434   EXPECT_FALSE([bbfc folderController]);
437 TEST_F(BookmarkBarFolderControllerTest, DeleteOpenFolder) {
438   base::scoped_nsobject<BookmarkBarFolderController> parent_controller(
439       SimpleBookmarkBarFolderController());
441   // Open a folder.
442   EXPECT_FALSE([parent_controller folderController]);
443   BookmarkButton* button = [[parent_controller buttons] objectAtIndex:0];
444   [parent_controller openBookmarkFolderFromButton:button];
445   EXPECT_EQ([[parent_controller folderController] parentButton], button);
447   // Delete the folder's button - the folder should close.
448   [parent_controller removeButton:0 animate:NO];
449   EXPECT_FALSE([parent_controller folderController]);
452 TEST_F(BookmarkBarFolderControllerTest, ChildFolderCallbacks) {
453   base::scoped_nsobject<BookmarkBarFolderControllerPong> bbfc;
454   bbfc.reset(SimpleBookmarkBarFolderController());
455   EXPECT_TRUE(bbfc.get());
456   [bar_ setChildFolderDelegate:bbfc.get()];
458   EXPECT_FALSE([bbfc childFolderWillShow]);
459   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]];
460   EXPECT_TRUE([bbfc childFolderWillShow]);
462   EXPECT_FALSE([bbfc childFolderWillClose]);
463   [bbfc closeBookmarkFolder:nil];
464   EXPECT_TRUE([bbfc childFolderWillClose]);
466   [bar_ setChildFolderDelegate:nil];
469 // Make sure bookmark folders have variable widths.
470 TEST_F(BookmarkBarFolderControllerTest, ChildFolderWidth) {
471   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
473   bbfc.reset(SimpleBookmarkBarFolderController());
474   EXPECT_TRUE(bbfc.get());
475   [bbfc showWindow:bbfc.get()];
476   CGFloat wideWidth = NSWidth([[bbfc window] frame]);
478   RemoveLongTitleNode();
479   bbfc.reset(SimpleBookmarkBarFolderController());
480   EXPECT_TRUE(bbfc.get());
481   CGFloat thinWidth = NSWidth([[bbfc window] frame]);
483   // Make sure window size changed as expected.
484   EXPECT_GT(wideWidth, thinWidth);
487 // Scrolling (in this case using keyboard up/down buttons) should
488 // not be a cause of item hovering change where the mouse is pointing to.
489 // Here we are simulating scrolling by calling -performOneScroll: which
490 // indirectly is called by keyboard up/down buttons.
491 TEST_F(BookmarkBarFolderControllerTest, ScrollingBehaviorAndMouseMovement){
492   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
493   AddLotsOfNodes();
494   bbfc.reset(SimpleBookmarkBarFolderController());
495   [bbfc showWindow:bbfc.get()];
496   // We should be able to scroll-up otherwise the rest of the test is pointless.
497   ASSERT_TRUE([bbfc canScrollUp]);
498   NSArray* buttons = [bbfc buttons];
499   BookmarkButton* currentButton = [bbfc buttonThatMouseIsIn];
500   // Mouse cursor is not pointing to any button.
501   EXPECT_FALSE(currentButton);
502   BookmarkButton* firstButton = [buttons objectAtIndex:0];
503   [bbfc mouseEnteredButton:firstButton event:nil];
504   // Mouse cursor should be over the first button.
505   EXPECT_EQ(firstButton, [bbfc buttonThatMouseIsIn]);
506   while ([bbfc canScrollUp]) {
507     [bbfc performOneScroll:200 updateMouseSelection:NO];
508     [bbfc mouseEnteredButton:[buttons objectAtIndex:2] event:nil];
509     // -buttonThatMouseIsIn: must return firstButton because we
510     // are still scrolling. i.e. should not be changed when
511     // -mouseEnteredButton: is called.
512     EXPECT_EQ(firstButton,[bbfc buttonThatMouseIsIn]);
513     // We are scrolling unless mouse movement happens.
514     EXPECT_TRUE([bbfc isScrolling]);
515   }
516   [bbfc mouseExitedButton:firstButton event:nil];
517   // If mouse exit from a button scrolling should be stopped.
518   EXPECT_FALSE([bbfc isScrolling]);
519   [bbfc mouseEnteredButton:nil event:nil];
522 // Simple scrolling tests.
523 // Currently flaky due to a changed definition of the correct menu boundaries.
524 TEST_F(BookmarkBarFolderControllerTest, DISABLED_SimpleScroll) {
525   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
526   NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
527   CGFloat screenHeight = NSHeight(screenFrame);
528   int nodecount = AddLotsOfNodes();
529   bbfc.reset(SimpleBookmarkBarFolderController());
530   EXPECT_TRUE(bbfc.get());
531   [bbfc showWindow:bbfc.get()];
532   NSWindow* window = [bbfc window];
534   // The window should be shorter than the screen but reach exactly to the
535   // bottom of the screen since it's scrollable.
536   EXPECT_LT(NSHeight([window frame]), screenHeight);
537   EXPECT_CGFLOAT_EQ(0.0, [window frame].origin.y);
539   // Initially, should show scroll-up but not scroll-down.
540   EXPECT_TRUE([bbfc canScrollUp]);
541   EXPECT_FALSE([bbfc canScrollDown]);
543   // Scroll up a bit.  Make sure the window has gotten bigger each time.
544   // Also, for each scroll, make sure our hit test finds a new button
545   // (to confirm the content area changed).
546   NSView* savedHit = nil;
547   NSScrollView* scrollView = [bbfc scrollView];
549   // Find the next-to-last button showing at the bottom of the window and
550   // use its center for hit testing.
551   BookmarkButton* targetButton = nil;
552   NSPoint scrollPoint = [scrollView documentVisibleRect].origin;
553   for (BookmarkButton* button in [bbfc buttons]) {
554     NSRect buttonFrame = [button frame];
555     buttonFrame.origin.y -= scrollPoint.y;
556     if (buttonFrame.origin.y < 0.0)
557       break;
558     targetButton = button;
559   }
560   EXPECT_TRUE(targetButton != nil);
561   NSPoint hitPoint = [targetButton frame].origin;
562   hitPoint.x += 50.0;
563   hitPoint.y += (bookmarks::kBookmarkFolderButtonHeight / 2.0) - scrollPoint.y;
564   hitPoint = [targetButton convertPoint:hitPoint toView:scrollView];
566   for (int i = 0; i < 3; i++) {
567     CGFloat height = NSHeight([window frame]);
568     [bbfc performOneScroll:60 updateMouseSelection:NO];
569     EXPECT_GT(NSHeight([window frame]), height);
570     NSView* hit = [scrollView hitTest:hitPoint];
571     // We should hit a bookmark button.
572     EXPECT_TRUE([[hit className] isEqualToString:@"BookmarkButton"]);
573     EXPECT_NE(hit, savedHit);
574     savedHit = hit;
575   }
577   // Keep scrolling up; make sure we never get bigger than the screen.
578   // Also confirm we never scroll the window off the screen.
579   bool bothAtOnce = false;
580   while ([bbfc canScrollUp]) {
581     [bbfc performOneScroll:60 updateMouseSelection:NO];
582     EXPECT_TRUE(NSContainsRect([[NSScreen mainScreen] frame], [window frame]));
583     // Make sure, sometime during our scroll, we have the ability to
584     // scroll in either direction.
585     if ([bbfc canScrollUp] &&
586         [bbfc canScrollDown])
587       bothAtOnce = true;
588   }
589   EXPECT_TRUE(bothAtOnce);
591   // Once we've scrolled to the end, our only option should be to scroll back.
592   EXPECT_FALSE([bbfc canScrollUp]);
593   EXPECT_TRUE([bbfc canScrollDown]);
595   NSRect wholeScreenRect = [[NSScreen mainScreen] frame];
597   // Now scroll down and make sure the window size does not change.
598   // Also confirm we never scroll the window off the screen the other
599   // way.
600   for (int i = 0; i < nodecount+50; ++i) {
601     [bbfc performOneScroll:-60 updateMouseSelection:NO];
602     // Once we can no longer scroll down the window height changes.
603     if (![bbfc canScrollDown])
604       break;
605     EXPECT_TRUE(NSContainsRect(wholeScreenRect, [window frame]));
606   }
608   EXPECT_GT(NSHeight(wholeScreenRect), NSHeight([window frame]));
609   EXPECT_TRUE(NSContainsRect(wholeScreenRect, [window frame]));
612 // Folder menu sizing and placement while deleting bookmarks
613 // and scrolling tests.
614 TEST_F(BookmarkBarFolderControllerTest, MenuPlacementWhileScrollingDeleting) {
615   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
616   AddLotsOfNodes();
617   bbfc.reset(SimpleBookmarkBarFolderController());
618   [bbfc showWindow:bbfc.get()];
619   NSWindow* menuWindow = [bbfc window];
620   BookmarkBarFolderController* folder = [bar_ folderController];
621   NSArray* buttons = [folder buttons];
623   // Before scrolling any, delete a bookmark and make sure the window top has
624   // not moved. Pick a button which is near the top and visible.
625   CGFloat oldTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
626   BookmarkButton* button = [buttons objectAtIndex:3];
627   DeleteBookmark(button, profile());
628   CGFloat newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
629   EXPECT_CGFLOAT_EQ(oldTop, newTop);
631   // Scroll so that both the top and bottom scroll arrows show, make sure
632   // the top of the window has moved up, then delete a visible button and
633   // make sure the top has not moved.
634   oldTop = newTop;
635   const CGFloat scrollOneBookmark = bookmarks::kBookmarkFolderButtonHeight +
636       bookmarks::kBookmarkVerticalPadding;
637   NSUInteger buttonCounter = 0;
638   NSUInteger extraButtonLimit = 3;
639   while (![bbfc canScrollDown] || extraButtonLimit > 0) {
640     [bbfc performOneScroll:scrollOneBookmark updateMouseSelection:NO];
641     ++buttonCounter;
642     if ([bbfc canScrollDown])
643       --extraButtonLimit;
644   }
645   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
646   EXPECT_NE(oldTop, newTop);
647   oldTop = newTop;
648   button = [buttons objectAtIndex:buttonCounter + 3];
649   DeleteBookmark(button, profile());
650   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
651   EXPECT_CGFLOAT_EQ(oldTop, newTop);
653   // Scroll so that the top scroll arrow is no longer showing, make sure
654   // the top of the window has not moved, then delete a visible button and
655   // make sure the top has not moved.
656   while ([bbfc canScrollDown]) {
657     [bbfc performOneScroll:-scrollOneBookmark updateMouseSelection:NO];
658     --buttonCounter;
659   }
660   button = [buttons objectAtIndex:buttonCounter + 3];
661   DeleteBookmark(button, profile());
662   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
663   EXPECT_CGFLOAT_EQ(oldTop - bookmarks::kScrollWindowVerticalMargin, newTop);
666 // Make sure that we return the correct browser window.
667 TEST_F(BookmarkBarFolderControllerTest, BrowserWindow) {
668   base::scoped_nsobject<BookmarkBarFolderController> controller(
669       SimpleBookmarkBarFolderController());
670   EXPECT_EQ([bar_ browserWindow], [controller browserWindow]);
673 @interface FakedDragInfo : NSObject {
674 @public
675   NSPoint dropLocation_;
676   NSDragOperation sourceMask_;
678 @property (nonatomic, assign) NSPoint dropLocation;
679 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
680 @end
682 @implementation FakedDragInfo
684 @synthesize dropLocation = dropLocation_;
686 - (id)init {
687   if ((self = [super init])) {
688     dropLocation_ = NSZeroPoint;
689     sourceMask_ = NSDragOperationMove;
690   }
691   return self;
694 // NSDraggingInfo protocol functions.
696 - (id)draggingPasteboard {
697   return self;
700 - (id)draggingSource {
701   return self;
704 - (NSDragOperation)draggingSourceOperationMask {
705   return sourceMask_;
708 - (NSPoint)draggingLocation {
709   return dropLocation_;
712 // Other functions.
714 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
715   sourceMask_ = mask;
718 @end
721 class BookmarkBarFolderControllerMenuTest : public CocoaProfileTest {
722  public:
723   base::scoped_nsobject<NSView> parent_view_;
724   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
725   base::scoped_nsobject<BookmarkBarController> bar_;
727   void SetUp() override {
728     CocoaProfileTest::SetUp();
729     ASSERT_TRUE(browser());
731     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
732     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
733     parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
734     [parent_view_ setHidden:YES];
735     bar_.reset([[BookmarkBarController alloc]
736                 initWithBrowser:browser()
737                    initialWidth:NSWidth(parent_frame)
738                        delegate:nil
739                  resizeDelegate:resizeDelegate_.get()]);
740     InstallAndToggleBar(bar_.get());
741   }
743   void InstallAndToggleBar(BookmarkBarController* bar) {
744     // Force loading of the nib.
745     [bar view];
746     // Awkwardness to look like we've been installed.
747     [parent_view_ addSubview:[bar view]];
748     NSRect frame = [[[bar view] superview] frame];
749     frame.origin.y = 400;
750     [[[bar view] superview] setFrame:frame];
752     // Make sure it's on in a window so viewDidMoveToWindow is called
753     [[test_window() contentView] addSubview:parent_view_];
755     // Make sure it's open so certain things aren't no-ops.
756     [bar updateState:BookmarkBar::SHOW
757           changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
758   }
761 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToFolder) {
762   WithNoAnimation at_all;
763   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
764   const BookmarkNode* root = model->bookmark_bar_node();
765   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
766       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
767       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
768   bookmarks::test::AddNodesFromModelString(model, root, model_string);
770   // Validate initial model.
771   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
772   EXPECT_EQ(model_string, actualModelString);
774   // Pop up a folder menu and drag in a button from the bar.
775   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
776   NSRect oldToFolderFrame = [toFolder frame];
777   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
778                           withObject:toFolder];
779   BookmarkBarFolderController* folderController = [bar_ folderController];
780   EXPECT_TRUE(folderController);
781   NSWindow* toWindow = [folderController window];
782   EXPECT_TRUE(toWindow);
783   NSRect oldToWindowFrame = [toWindow frame];
784   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
785   // so it should end up below the target bookmark.
786   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"];
787   ASSERT_TRUE(draggedButton);
788   CGFloat horizontalShift =
789       NSWidth([draggedButton frame]) + bookmarks::kBookmarkHorizontalPadding;
790   BookmarkButton* targetButton =
791       [folderController buttonWithTitleEqualTo:@"2f1b"];
792   ASSERT_TRUE(targetButton);
793   [folderController dragButton:draggedButton
794                             to:[targetButton center]
795                           copy:NO];
796   // The button should have landed just after "2f1b".
797   const std::string expected_string("2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
798       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
799       "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
800   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
802   // Verify the window still appears by looking for its controller.
803   EXPECT_TRUE([bar_ folderController]);
805   // Gather the new frames.
806   NSRect newToFolderFrame = [toFolder frame];
807   NSRect newToWindowFrame = [toWindow frame];
808   // The toFolder should have shifted left horizontally but not vertically.
809   NSRect expectedToFolderFrame =
810       NSOffsetRect(oldToFolderFrame, -horizontalShift, 0);
811   EXPECT_NSRECT_EQ(expectedToFolderFrame, newToFolderFrame);
812   // The toWindow should have shifted left horizontally, down vertically,
813   // and grown vertically.
814   NSRect expectedToWindowFrame = oldToWindowFrame;
815   expectedToWindowFrame.origin.x -= horizontalShift;
816   expectedToWindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
817   expectedToWindowFrame.size.height += bookmarks::kBookmarkFolderButtonHeight;
818   EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame);
820   // Check button spacing.
821   [folderController validateMenuSpacing];
823   // Move the button back to the bar at the beginning.
824   draggedButton = [folderController buttonWithTitleEqualTo:@"1b"];
825   ASSERT_TRUE(draggedButton);
826   targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
827   ASSERT_TRUE(targetButton);
828   [bar_ dragButton:draggedButton
829                 to:[targetButton left]
830               copy:NO];
831   EXPECT_EQ(model_string, bookmarks::test::ModelStringFromNode(root));
832   // Don't check the folder window since it's not supposed to be showing.
835 TEST_F(BookmarkBarFolderControllerMenuTest, DragCopyBarBookmarkToFolder) {
836   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
837   const BookmarkNode* root = model->bookmark_bar_node();
838   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
839       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
840       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
841   bookmarks::test::AddNodesFromModelString(model, root, model_string);
843   // Validate initial model.
844   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
845   EXPECT_EQ(model_string, actualModelString);
847   // Pop up a folder menu and copy in a button from the bar.
848   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
849   ASSERT_TRUE(toFolder);
850   NSRect oldToFolderFrame = [toFolder frame];
851   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
852                           withObject:toFolder];
853   BookmarkBarFolderController* folderController = [bar_ folderController];
854   EXPECT_TRUE(folderController);
855   NSWindow* toWindow = [folderController window];
856   EXPECT_TRUE(toWindow);
857   NSRect oldToWindowFrame = [toWindow frame];
858   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
859   // so it should end up below the target bookmark.
860   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"];
861   ASSERT_TRUE(draggedButton);
862   BookmarkButton* targetButton =
863       [folderController buttonWithTitleEqualTo:@"2f1b"];
864   ASSERT_TRUE(targetButton);
865   [folderController dragButton:draggedButton
866                             to:[targetButton center]
867                           copy:YES];
868   // The button should have landed just after "2f1b".
869   const std::string expected_1("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
870     "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
871     "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
872   EXPECT_EQ(expected_1, bookmarks::test::ModelStringFromNode(root));
874   // Gather the new frames.
875   NSRect newToFolderFrame = [toFolder frame];
876   NSRect newToWindowFrame = [toWindow frame];
877   // The toFolder should have shifted.
878   EXPECT_NSRECT_EQ(oldToFolderFrame, newToFolderFrame);
879   // The toWindow should have shifted down vertically and grown vertically.
880   NSRect expectedToWindowFrame = oldToWindowFrame;
881   expectedToWindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
882   expectedToWindowFrame.size.height += bookmarks::kBookmarkFolderButtonHeight;
883   EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame);
885   // Copy the button back to the bar after "3b".
886   draggedButton = [folderController buttonWithTitleEqualTo:@"1b"];
887   ASSERT_TRUE(draggedButton);
888   targetButton = [bar_ buttonWithTitleEqualTo:@"4f"];
889   ASSERT_TRUE(targetButton);
890   [bar_ dragButton:draggedButton
891                 to:[targetButton left]
892               copy:YES];
893   const std::string expected_2("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
894       "2f2f2b 2f2f3b ] 2f3b ] 3b 1b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
895       "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
896   EXPECT_EQ(expected_2, bookmarks::test::ModelStringFromNode(root));
899 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToSubfolder) {
900   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
901   const BookmarkNode* root = model->bookmark_bar_node();
902   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
903       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
904       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
905   bookmarks::test::AddNodesFromModelString(model, root, model_string);
907   // Validate initial model.
908   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
909   EXPECT_EQ(model_string, actualModelString);
911   // Pop up a folder menu and a subfolder menu.
912   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
913   ASSERT_TRUE(toFolder);
914   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
915                           withObject:toFolder];
916   BookmarkBarFolderController* folderController = [bar_ folderController];
917   EXPECT_TRUE(folderController);
918   NSWindow* toWindow = [folderController window];
919   EXPECT_TRUE(toWindow);
920   NSRect oldToWindowFrame = [toWindow frame];
921   BookmarkButton* toSubfolder =
922       [folderController buttonWithTitleEqualTo:@"4f2f"];
923   ASSERT_TRUE(toSubfolder);
924   [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:)
925                              withObject:toSubfolder];
926   BookmarkBarFolderController* subfolderController =
927       [folderController folderController];
928   EXPECT_TRUE(subfolderController);
929   NSWindow* toSubwindow = [subfolderController window];
930   EXPECT_TRUE(toSubwindow);
931   NSRect oldToSubwindowFrame = [toSubwindow frame];
932   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
933   // so it should end up below the target bookmark.
934   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"5b"];
935   ASSERT_TRUE(draggedButton);
936   BookmarkButton* targetButton =
937       [subfolderController buttonWithTitleEqualTo:@"4f2f3b"];
938   ASSERT_TRUE(targetButton);
939   [subfolderController dragButton:draggedButton
940                                to:[targetButton center]
941                              copy:NO];
942   // The button should have landed just after "2f".
943   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b "
944       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
945       "4f2f1b 4f2f2b 4f2f3b 5b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] ");
946   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
948   // Check button spacing.
949   [folderController validateMenuSpacing];
950   [subfolderController validateMenuSpacing];
952   // Check the window layouts. The folder window should not have changed,
953   // but the subfolder window should have shifted vertically and grown.
954   NSRect newToWindowFrame = [toWindow frame];
955   EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame);
956   NSRect newToSubwindowFrame = [toSubwindow frame];
957   NSRect expectedToSubwindowFrame = oldToSubwindowFrame;
958   expectedToSubwindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
959   expectedToSubwindowFrame.size.height +=
960       bookmarks::kBookmarkFolderButtonHeight;
961   EXPECT_NSRECT_EQ(expectedToSubwindowFrame, newToSubwindowFrame);
964 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveWithinFolder) {
965   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
966   const BookmarkNode* root = model->bookmark_bar_node();
967   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
968       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
969       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
970   bookmarks::test::AddNodesFromModelString(model, root, model_string);
972   // Validate initial model.
973   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
974   EXPECT_EQ(model_string, actualModelString);
976   // Pop up a folder menu.
977   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
978   ASSERT_TRUE(toFolder);
979   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
980                           withObject:toFolder];
981   BookmarkBarFolderController* folderController = [bar_ folderController];
982   EXPECT_TRUE(folderController);
983   NSWindow* toWindow = [folderController window];
984   EXPECT_TRUE(toWindow);
985   NSRect oldToWindowFrame = [toWindow frame];
986   // Drag a folder button to the top within the same parent.
987   BookmarkButton* draggedButton =
988       [folderController buttonWithTitleEqualTo:@"4f2f"];
989   ASSERT_TRUE(draggedButton);
990   BookmarkButton* targetButton =
991       [folderController buttonWithTitleEqualTo:@"4f1f"];
992   ASSERT_TRUE(targetButton);
993   [folderController dragButton:draggedButton
994                             to:[targetButton top]
995                           copy:NO];
996   // The button should have landed above "4f1f".
997   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b "
998       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f2f:[ 4f2f1b 4f2f2b 4f2f3b ] "
999       "4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1000   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
1002   // The window should not have gone away.
1003   EXPECT_TRUE([bar_ folderController]);
1005   // The folder window should not have changed.
1006   NSRect newToWindowFrame = [toWindow frame];
1007   EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame);
1009   // Check button spacing.
1010   [folderController validateMenuSpacing];
1013 TEST_F(BookmarkBarFolderControllerMenuTest, DragParentOntoChild) {
1014   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1015   const BookmarkNode* root = model->bookmark_bar_node();
1016   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1017       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
1018       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1019   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1021   // Validate initial model.
1022   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1023   EXPECT_EQ(model_string, actualModelString);
1025   // Pop up a folder menu.
1026   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
1027   ASSERT_TRUE(toFolder);
1028   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1029                           withObject:toFolder];
1030   BookmarkBarFolderController* folderController = [bar_ folderController];
1031   EXPECT_TRUE(folderController);
1032   NSWindow* toWindow = [folderController window];
1033   EXPECT_TRUE(toWindow);
1034   // Drag a folder button to one of its children.
1035   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"4f"];
1036   ASSERT_TRUE(draggedButton);
1037   BookmarkButton* targetButton =
1038       [folderController buttonWithTitleEqualTo:@"4f3f"];
1039   ASSERT_TRUE(targetButton);
1040   [folderController dragButton:draggedButton
1041                             to:[targetButton top]
1042                           copy:NO];
1043   // The model should not have changed.
1044   EXPECT_EQ(model_string, bookmarks::test::ModelStringFromNode(root));
1046   // Check button spacing.
1047   [folderController validateMenuSpacing];
1050 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveChildToParent) {
1051   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1052   const BookmarkNode* root = model->bookmark_bar_node();
1053   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1054       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
1055       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1056   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1058   // Validate initial model.
1059   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1060   EXPECT_EQ(model_string, actualModelString);
1062   // Pop up a folder menu and a subfolder menu.
1063   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
1064   ASSERT_TRUE(toFolder);
1065   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1066                           withObject:toFolder];
1067   BookmarkBarFolderController* folderController = [bar_ folderController];
1068   EXPECT_TRUE(folderController);
1069   BookmarkButton* toSubfolder =
1070       [folderController buttonWithTitleEqualTo:@"4f2f"];
1071   ASSERT_TRUE(toSubfolder);
1072   [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1073                              withObject:toSubfolder];
1074   BookmarkBarFolderController* subfolderController =
1075       [folderController folderController];
1076   EXPECT_TRUE(subfolderController);
1078   // Drag a subfolder bookmark to the parent folder.
1079   BookmarkButton* draggedButton =
1080       [subfolderController buttonWithTitleEqualTo:@"4f2f3b"];
1081   ASSERT_TRUE(draggedButton);
1082   BookmarkButton* targetButton =
1083       [folderController buttonWithTitleEqualTo:@"4f2f"];
1084   ASSERT_TRUE(targetButton);
1085   [folderController dragButton:draggedButton
1086                             to:[targetButton top]
1087                           copy:NO];
1088   // The button should have landed above "4f2f".
1089   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1090       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f3b 4f2f:[ "
1091       "4f2f1b 4f2f2b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1092   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
1094   // Check button spacing.
1095   [folderController validateMenuSpacing];
1096   // The window should not have gone away.
1097   EXPECT_TRUE([bar_ folderController]);
1098   // The subfolder should have gone away.
1099   EXPECT_FALSE([folderController folderController]);
1102 TEST_F(BookmarkBarFolderControllerMenuTest, DragWindowResizing) {
1103   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1104   const BookmarkNode* root = model->bookmark_bar_node();
1105   const std::string model_string(
1106       "a b:[ b1 b2 b3 ] reallyReallyLongBookmarkName c ");
1107   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1109   // Validate initial model.
1110   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1111   EXPECT_EQ(model_string, actualModelString);
1113   // Pop up a folder menu.
1114   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"b"];
1115   ASSERT_TRUE(toFolder);
1116   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1117                           withObject:toFolder];
1118   BookmarkBarFolderController* folderController = [bar_ folderController];
1119   EXPECT_TRUE(folderController);
1120   NSWindow* toWindow = [folderController window];
1121   EXPECT_TRUE(toWindow);
1122   CGFloat oldWidth = NSWidth([toWindow frame]);
1123   // Drag the bookmark with a long name to the folder.
1124   BookmarkButton* draggedButton =
1125       [bar_ buttonWithTitleEqualTo:@"reallyReallyLongBookmarkName"];
1126   ASSERT_TRUE(draggedButton);
1127   BookmarkButton* targetButton =
1128       [folderController buttonWithTitleEqualTo:@"b1"];
1129   ASSERT_TRUE(targetButton);
1130   [folderController dragButton:draggedButton
1131                             to:[targetButton center]
1132                           copy:NO];
1133   // Verify the model change.
1134   const std::string expected_string(
1135       "a b:[ b1 reallyReallyLongBookmarkName b2 b3 ] c ");
1136   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
1137   // Verify the window grew. Just test a reasonable width gain.
1138   CGFloat newWidth = NSWidth([toWindow frame]);
1139   EXPECT_LT(oldWidth + 30.0, newWidth);
1142 TEST_F(BookmarkBarFolderControllerMenuTest, MoveRemoveAddButtons) {
1143   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1144   const BookmarkNode* root = model->bookmark_bar_node();
1145   const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
1146   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1148   // Validate initial model.
1149   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1150   EXPECT_EQ(model_string, actualModelString);
1152   // Pop up a folder menu.
1153   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
1154   ASSERT_TRUE(toFolder);
1155   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1156                           withObject:toFolder];
1157   BookmarkBarFolderController* folder = [bar_ folderController];
1158   EXPECT_TRUE(folder);
1160   // Remember how many buttons are showing.
1161   NSArray* buttons = [folder buttons];
1162   NSUInteger oldDisplayedButtons = [buttons count];
1164   // Move a button around a bit.
1165   [folder moveButtonFromIndex:0 toIndex:2];
1166   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:0] title]);
1167   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:1] title]);
1168   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:2] title]);
1169   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1170   [folder moveButtonFromIndex:2 toIndex:0];
1171   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1172   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]);
1173   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]);
1174   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1176   // Add a couple of buttons.
1177   const BookmarkNode* node = root->GetChild(2); // Purloin an existing node.
1178   [folder addButtonForNode:node atIndex:0];
1179   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1180   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]);
1181   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]);
1182   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]);
1183   EXPECT_EQ(oldDisplayedButtons + 1, [buttons count]);
1184   node = root->GetChild(3);
1185   [folder addButtonForNode:node atIndex:-1];
1186   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1187   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]);
1188   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]);
1189   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]);
1190   EXPECT_NSEQ(@"4b", [[buttons objectAtIndex:4] title]);
1191   EXPECT_EQ(oldDisplayedButtons + 2, [buttons count]);
1193   // Remove a couple of buttons.
1194   [folder removeButton:4 animate:NO];
1195   [folder removeButton:1 animate:NO];
1196   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1197   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]);
1198   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]);
1199   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1201   // Check button spacing.
1202   [folder validateMenuSpacing];
1205 TEST_F(BookmarkBarFolderControllerMenuTest, RemoveLastButtonOtherBookmarks) {
1206   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1207   const BookmarkNode* otherBookmarks = model->other_node();
1209   BookmarkButton* otherButton = [bar_ otherBookmarksButton];
1210   ASSERT_TRUE(otherButton);
1212   // Open the folder to get the folderController_.
1213   [[otherButton target] openBookmarkFolderFromButton:otherButton];
1214   BookmarkBarFolderController* folder = [bar_ folderController];
1215   EXPECT_TRUE(folder);
1217   // Initially there is only (empty) placeholder button, hence buttonCount
1218   // should be 1.
1219   NSArray* buttons = [folder buttons];
1220   EXPECT_TRUE(buttons);
1221   EXPECT_EQ(1U, [buttons count]);
1223   // Add a new bookmark into 'Other bookmarks' folder.
1224   model->AddURL(otherBookmarks, otherBookmarks->child_count(),
1225                 ASCIIToUTF16("TheOther"),
1226                 GURL("http://www.other.com"));
1228   // buttonCount still should be 1, as we remove the (empty) placeholder button
1229   // when adding a new button to an empty folder.
1230   EXPECT_EQ(1U, [buttons count]);
1232   // Now we have only 1 button; remove it so that 'Other bookmarks' folder
1233   // is hidden.
1234   [folder removeButton:0 animate:NO];
1235   EXPECT_EQ(0U, [buttons count]);
1237   // 'Other bookmarks' folder gets closed once we remove the last button. Hence
1238   // folderController_ should be NULL.
1239   EXPECT_FALSE([bar_ folderController]);
1242 TEST_F(BookmarkBarFolderControllerMenuTest, ControllerForNode) {
1243   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1244   const BookmarkNode* root = model->bookmark_bar_node();
1245   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1246   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1248   // Validate initial model.
1249   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1250   EXPECT_EQ(model_string, actualModelString);
1252   // Find the main bar controller.
1253   const void* expectedController = bar_;
1254   const void* actualController = [bar_ controllerForNode:root];
1255   EXPECT_EQ(expectedController, actualController);
1257   // Pop up the folder menu.
1258   BookmarkButton* targetFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
1259   ASSERT_TRUE(targetFolder);
1260   [[targetFolder target]
1261    performSelector:@selector(openBookmarkFolderFromButton:)
1262    withObject:targetFolder];
1263   BookmarkBarFolderController* folder = [bar_ folderController];
1264   EXPECT_TRUE(folder);
1266   // Find the folder controller using the folder controller.
1267   const BookmarkNode* targetNode = root->GetChild(1);
1268   expectedController = folder;
1269   actualController = [bar_ controllerForNode:targetNode];
1270   EXPECT_EQ(expectedController, actualController);
1272   // Find the folder controller from the bar.
1273   actualController = [folder controllerForNode:targetNode];
1274   EXPECT_EQ(expectedController, actualController);
1277 TEST_F(BookmarkBarFolderControllerMenuTest, MenuSizingAndScrollArrows) {
1278   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1279   const BookmarkNode* root = model->bookmark_bar_node();
1280   const std::string model_string("1b 2b 3b ");
1281   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1283   // Validate initial model.
1284   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1285   EXPECT_EQ(model_string, actualModelString);
1287   const BookmarkNode* parent = model->bookmark_bar_node();
1288   const BookmarkNode* folder = model->AddFolder(parent,
1289                                                 parent->child_count(),
1290                                                 ASCIIToUTF16("BIG"));
1292   // Pop open the new folder window and verify it has one (empty) item.
1293   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"];
1294   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1295                         withObject:button];
1296   BookmarkBarFolderController* folderController = [bar_ folderController];
1297   EXPECT_TRUE(folderController);
1298   NSWindow* folderWindow = [folderController window];
1299   EXPECT_TRUE(folderWindow);
1300   CGFloat expectedHeight = (CGFloat)bookmarks::kBookmarkFolderButtonHeight +
1301       (2*bookmarks::kBookmarkVerticalPadding);
1302   NSRect windowFrame = [folderWindow frame];
1303   CGFloat windowHeight = NSHeight(windowFrame);
1304   EXPECT_CGFLOAT_EQ(expectedHeight, windowHeight);
1305   EXPECT_FALSE([folderController canScrollUp]);
1306   EXPECT_FALSE([folderController canScrollDown]);
1308   // Now add a real bookmark and reopen.
1309   model->AddURL(folder, folder->child_count(), ASCIIToUTF16("a"),
1310                 GURL("http://a.com/"));
1311   folderController = [bar_ folderController];
1312   EXPECT_TRUE(folderController);
1313   NSView* folderView = [folderController folderView];
1314   EXPECT_TRUE(folderView);
1315   NSRect menuFrame = [folderView frame];
1316   NSView* visibleView = [folderController visibleView];
1317   NSRect visibleFrame = [visibleView frame];
1318   NSScrollView* scrollView = [folderController scrollView];
1319   NSRect scrollFrame = [scrollView frame];
1321   // Determine the margins between the scroll frame and the visible frame.
1322   CGFloat widthDelta = NSWidth(visibleFrame) - NSWidth(scrollFrame);
1324   CGFloat menuHeight = NSHeight(menuFrame);
1325   EXPECT_CGFLOAT_EQ(expectedHeight, menuHeight);
1326   CGFloat scrollerWidth = NSWidth(scrollFrame);
1327   button = [folderController buttonWithTitleEqualTo:@"a"];
1328   CGFloat buttonWidth = NSWidth([button frame]);
1329   EXPECT_CGFLOAT_EQ(scrollerWidth, buttonWidth);
1330   CGFloat visibleWidth = NSWidth(visibleFrame);
1331   EXPECT_CGFLOAT_EQ(visibleWidth - widthDelta, buttonWidth);
1332   EXPECT_LT(scrollerWidth, NSWidth([folderView frame]));
1334   // Add a wider bookmark and make sure the button widths match.
1335   int reallyWideButtonNumber = folder->child_count();
1336   model->AddURL(folder, reallyWideButtonNumber,
1337                 ASCIIToUTF16("A really, really, really, really, really, "
1338                             "really long name"),
1339                 GURL("http://www.google.com/a"));
1340   BookmarkButton* bigButton =
1341       [folderController buttonWithTitleEqualTo:
1342        @"A really, really, really, really, really, really long name"];
1343   EXPECT_TRUE(bigButton);
1344   CGFloat buttonWidthB = NSWidth([bigButton frame]);
1345   EXPECT_LT(buttonWidth, buttonWidthB);
1346   // Add a bunch of bookmarks until the window becomes scrollable, then check
1347   // for a scroll up arrow.
1348   NSUInteger tripWire = 0;  // Prevent a runaway.
1349   while (![folderController canScrollUp] && ++tripWire < 1000) {
1350     model->AddURL(folder, folder->child_count(), ASCIIToUTF16("B"),
1351                   GURL("http://b.com/"));
1352   }
1353   EXPECT_TRUE([folderController canScrollUp]);
1355   // Remove one bookmark and make sure the scroll down arrow has been removed.
1356   // We'll remove the really long node so we can see if the buttons get resized.
1357   scrollerWidth = NSWidth([folderView frame]);
1358   buttonWidth = NSWidth([button frame]);
1359   model->Remove(folder->GetChild(reallyWideButtonNumber));
1360   EXPECT_FALSE([folderController canScrollUp]);
1361   EXPECT_FALSE([folderController canScrollDown]);
1363   // Check the size. It should have reduced.
1364   EXPECT_GT(scrollerWidth, NSWidth([folderView frame]));
1365   EXPECT_GT(buttonWidth, NSWidth([button frame]));
1367   // Check button spacing.
1368   [folderController validateMenuSpacing];
1371 // See http://crbug.com/46101
1372 TEST_F(BookmarkBarFolderControllerMenuTest, HoverThenDeleteBookmark) {
1373   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1374   const BookmarkNode* root = model->bookmark_bar_node();
1375   const BookmarkNode* folder = model->AddFolder(root,
1376                                                 root->child_count(),
1377                                                 ASCIIToUTF16("BIG"));
1378   for (int i = 0; i < kLotsOfNodesCount; i++)
1379     model->AddURL(folder, folder->child_count(), ASCIIToUTF16("kid"),
1380                   GURL("http://kid.com/smile"));
1382   // Pop open the new folder window and hover one of its kids.
1383   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"];
1384   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1385                         withObject:button];
1386   BookmarkBarFolderController* bbfc = [bar_ folderController];
1387   NSArray* buttons = [bbfc buttons];
1389   // Hover over a button and verify that it is now known.
1390   button = [buttons objectAtIndex:3];
1391   BookmarkButton* buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1392   EXPECT_FALSE(buttonThatMouseIsIn);
1393   [bbfc mouseEnteredButton:button event:nil];
1394   buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1395   EXPECT_EQ(button, buttonThatMouseIsIn);
1397   // Delete the bookmark and verify that it is now not known.
1398   model->Remove(folder->GetChild(3));
1399   buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1400   EXPECT_FALSE(buttonThatMouseIsIn);
1403 // Just like a BookmarkBarFolderController but intercedes when providing
1404 // pasteboard drag data.
1405 @interface BookmarkBarFolderControllerDragData : BookmarkBarFolderController {
1406   const BookmarkNode* dragDataNode_;  // Weak
1408 - (void)setDragDataNode:(const BookmarkNode*)node;
1409 @end
1411 @implementation BookmarkBarFolderControllerDragData
1413 - (id)initWithParentButton:(BookmarkButton*)button
1414           parentController:(BookmarkBarFolderController*)parentController
1415              barController:(BookmarkBarController*)barController
1416                    profile:(Profile*)profile {
1417   if ((self = [super initWithParentButton:button
1418                          parentController:parentController
1419                             barController:barController
1420                                   profile:profile])) {
1421     dragDataNode_ = NULL;
1422   }
1423   return self;
1426 - (void)setDragDataNode:(const BookmarkNode*)node {
1427   dragDataNode_ = node;
1430 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
1431   std::vector<const BookmarkNode*> dragDataNodes;
1432   if(dragDataNode_) {
1433     dragDataNodes.push_back(dragDataNode_);
1434   }
1435   return dragDataNodes;
1438 @end
1440 TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkData) {
1441   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1442   const BookmarkNode* root = model->bookmark_bar_node();
1443   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1444                                  "2f3b ] 3b 4b ");
1445   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1446   const BookmarkNode* other = model->other_node();
1447   const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1448                                  "O4f:[ O4f1b O4f2f ] 05b ");
1449   bookmarks::test::AddNodesFromModelString(model, other, other_string);
1451   // Validate initial model.
1452   std::string actual = bookmarks::test::ModelStringFromNode(root);
1453   EXPECT_EQ(model_string, actual);
1454   actual = bookmarks::test::ModelStringFromNode(other);
1455   EXPECT_EQ(other_string, actual);
1457   // Pop open a folder.
1458   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1459   base::scoped_nsobject<BookmarkBarFolderControllerDragData> folderController;
1460   folderController.reset([[BookmarkBarFolderControllerDragData alloc]
1461                           initWithParentButton:button
1462                               parentController:nil
1463                                  barController:bar_
1464                                        profile:profile()]);
1465   BookmarkButton* targetButton =
1466       [folderController buttonWithTitleEqualTo:@"2f1b"];
1467   ASSERT_TRUE(targetButton);
1469   // Gen up some dragging data.
1470   const BookmarkNode* newNode = other->GetChild(2);
1471   [folderController setDragDataNode:newNode];
1472   base::scoped_nsobject<FakedDragInfo> dragInfo([[FakedDragInfo alloc] init]);
1473   [dragInfo setDropLocation:[targetButton top]];
1474   [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1476   // Verify the model.
1477   const std::string expected("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ 2f2f1b "
1478                              "2f2f2b 2f2f3b ] 2f3b ] 3b 4b ");
1479   actual = bookmarks::test::ModelStringFromNode(root);
1480   EXPECT_EQ(expected, actual);
1482   // Now drag over a folder button.
1483   targetButton = [folderController buttonWithTitleEqualTo:@"2f2f"];
1484   ASSERT_TRUE(targetButton);
1485   newNode = other->GetChild(2);  // Should be O4f.
1486   EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1487   [folderController setDragDataNode:newNode];
1488   [dragInfo setDropLocation:[targetButton center]];
1489   [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1491   // Verify the model.
1492   const std::string expectedA("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ "
1493                               "2f2f1b 2f2f2b 2f2f3b O4f:[ O4f1b O4f2f ] ] "
1494                               "2f3b ] 3b 4b ");
1495   actual = bookmarks::test::ModelStringFromNode(root);
1496   EXPECT_EQ(expectedA, actual);
1498   // Check button spacing.
1499   [folderController validateMenuSpacing];
1502 TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkDataToTrash) {
1503   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1504   const BookmarkNode* root = model->bookmark_bar_node();
1505   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1506                                  "2f3b ] 3b 4b ");
1507   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1509   // Validate initial model.
1510   std::string actual = bookmarks::test::ModelStringFromNode(root);
1511   EXPECT_EQ(model_string, actual);
1513   const BookmarkNode* folderNode = root->GetChild(1);
1514   int oldFolderChildCount = folderNode->child_count();
1516   // Pop open a folder.
1517   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1518   base::scoped_nsobject<BookmarkBarFolderControllerDragData> folderController;
1519   folderController.reset([[BookmarkBarFolderControllerDragData alloc]
1520                           initWithParentButton:button
1521                               parentController:nil
1522                                  barController:bar_
1523                                        profile:profile()]);
1525   // Drag a button to the trash.
1526   BookmarkButton* buttonToDelete =
1527       [folderController buttonWithTitleEqualTo:@"2f1b"];
1528   ASSERT_TRUE(buttonToDelete);
1529   EXPECT_TRUE([folderController canDragBookmarkButtonToTrash:buttonToDelete]);
1530   [folderController didDragBookmarkToTrash:buttonToDelete];
1532   // There should be one less button in the folder.
1533   int newFolderChildCount = folderNode->child_count();
1534   EXPECT_EQ(oldFolderChildCount - 1, newFolderChildCount);
1535   // Verify the model.
1536   const std::string expected("1b 2f:[ 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1537                              "2f3b ] 3b 4b ");
1538   actual = bookmarks::test::ModelStringFromNode(root);
1539   EXPECT_EQ(expected, actual);
1541   // Check button spacing.
1542   [folderController validateMenuSpacing];
1545 TEST_F(BookmarkBarFolderControllerMenuTest, AddURLs) {
1546   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1547   const BookmarkNode* root = model->bookmark_bar_node();
1548   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1549                                  "2f3b ] 3b 4b ");
1550   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1552   // Validate initial model.
1553   std::string actual = bookmarks::test::ModelStringFromNode(root);
1554   EXPECT_EQ(model_string, actual);
1556   // Pop open a folder.
1557   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1558   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1559                         withObject:button];
1560   BookmarkBarFolderController* folderController = [bar_ folderController];
1561   EXPECT_TRUE(folderController);
1562   NSArray* buttons = [folderController buttons];
1563   EXPECT_TRUE(buttons);
1565   // Remember how many buttons are showing.
1566   int oldDisplayedButtons = [buttons count];
1568   BookmarkButton* targetButton =
1569       [folderController buttonWithTitleEqualTo:@"2f1b"];
1570   ASSERT_TRUE(targetButton);
1572   NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
1573                    @"http://www.b.com/", nil];
1574   NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
1575   [folderController addURLs:urls withTitles:titles at:[targetButton top]];
1577   // There should two more buttons in the folder.
1578   int newDisplayedButtons = [buttons count];
1579   EXPECT_EQ(oldDisplayedButtons + 2, newDisplayedButtons);
1580   // Verify the model.
1581   const std::string expected("1b 2f:[ SiteA SiteB 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1582                              "2f2f3b ] 2f3b ] 3b 4b ");
1583   actual = bookmarks::test::ModelStringFromNode(root);
1584   EXPECT_EQ(expected, actual);
1586   // Check button spacing.
1587   [folderController validateMenuSpacing];
1590 TEST_F(BookmarkBarFolderControllerMenuTest, DropPositionIndicator) {
1591   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1592   const BookmarkNode* root = model->bookmark_bar_node();
1593   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1594                                  "2f3b ] 3b 4b ");
1595   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1597   // Validate initial model.
1598   std::string actual = bookmarks::test::ModelStringFromNode(root);
1599   EXPECT_EQ(model_string, actual);
1601   // Pop open the folder.
1602   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1603   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1604                         withObject:button];
1605   BookmarkBarFolderController* folder = [bar_ folderController];
1606   EXPECT_TRUE(folder);
1608   // Test a series of points starting at the top of the folder.
1609   const CGFloat yOffset = 0.5 * bookmarks::kBookmarkVerticalPadding;
1610   BookmarkButton* targetButton = [folder buttonWithTitleEqualTo:@"2f1b"];
1611   ASSERT_TRUE(targetButton);
1612   NSPoint targetPoint = [targetButton top];
1613   CGFloat pos = [folder indicatorPosForDragToPoint:targetPoint];
1614   EXPECT_CGFLOAT_EQ(targetPoint.y + yOffset, pos);
1615   pos = [folder indicatorPosForDragToPoint:[targetButton bottom]];
1616   targetButton = [folder buttonWithTitleEqualTo:@"2f2f"];
1617   EXPECT_CGFLOAT_EQ([targetButton top].y + yOffset, pos);
1618   pos = [folder indicatorPosForDragToPoint:NSMakePoint(10,0)];
1619   targetButton = [folder buttonWithTitleEqualTo:@"2f3b"];
1620   EXPECT_CGFLOAT_EQ([targetButton bottom].y - yOffset, pos);
1623 @interface BookmarkBarControllerNoDelete : BookmarkBarController
1624 - (IBAction)deleteBookmark:(id)sender;
1625 @end
1627 @implementation BookmarkBarControllerNoDelete
1628 - (IBAction)deleteBookmark:(id)sender {
1629   // NOP
1631 @end
1633 class BookmarkBarFolderControllerClosingTest : public
1634     BookmarkBarFolderControllerMenuTest {
1635  public:
1636   void SetUp() override {
1637     BookmarkBarFolderControllerMenuTest::SetUp();
1638     ASSERT_TRUE(browser());
1640     bar_.reset([[BookmarkBarControllerNoDelete alloc]
1641                 initWithBrowser:browser()
1642                    initialWidth:NSWidth([parent_view_ frame])
1643                        delegate:nil
1644                  resizeDelegate:resizeDelegate_.get()]);
1645     InstallAndToggleBar(bar_.get());
1646   }
1649 TEST_F(BookmarkBarFolderControllerClosingTest, DeleteClosesFolder) {
1650   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1651   const BookmarkNode* root = model->bookmark_bar_node();
1652   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b ] "
1653                                  "2f3b ] 3b ");
1654   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1656   // Validate initial model.
1657   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1658   EXPECT_EQ(model_string, actualModelString);
1660   // Open the folder menu and submenu.
1661   BookmarkButton* target = [bar_ buttonWithTitleEqualTo:@"2f"];
1662   ASSERT_TRUE(target);
1663   [[target target] performSelector:@selector(openBookmarkFolderFromButton:)
1664                               withObject:target];
1665   BookmarkBarFolderController* folder = [bar_ folderController];
1666   EXPECT_TRUE(folder);
1667   BookmarkButton* subTarget = [folder buttonWithTitleEqualTo:@"2f2f"];
1668   ASSERT_TRUE(subTarget);
1669   [[subTarget target] performSelector:@selector(openBookmarkFolderFromButton:)
1670                            withObject:subTarget];
1671   BookmarkBarFolderController* subFolder = [folder folderController];
1672   EXPECT_TRUE(subFolder);
1674   // Delete the folder node and verify the window closed down by looking
1675   // for its controller again.
1676   DeleteBookmark([folder parentButton], profile());
1677   EXPECT_FALSE([folder folderController]);
1680 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1681 // they are hard to test.  Factor out "fire timers" into routines
1682 // which can be overridden to fire immediately to make behavior
1683 // confirmable.
1684 // There is a similar problem with mouseEnteredButton: and
1685 // mouseExitedButton:.