Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / bookmarks / bookmark_bar_folder_controller_unittest.mm
blobe5161690f03433c124b658f9edd79a9cc35de640
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->parent(), node->parent()->GetIndexOf(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_->parent(),
196                   longTitleNode_->parent()->GetIndexOf(longTitleNode_));
197   }
199   // Add LOTS of nodes to our model if needed (e.g. scrolling).
200   // Returns the number of nodes added.
201   int AddLotsOfNodes() {
202     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
203     for (int i = 0; i < kLotsOfNodesCount; i++) {
204       model->AddURL(folderA_, folderA_->child_count(),
205                     ASCIIToUTF16("repeated title"),
206                     GURL("http://www.google.com/repeated/url"));
207     }
208     return kLotsOfNodesCount;
209   }
211   // Return a simple BookmarkBarFolderController.
212   BookmarkBarFolderControllerPong* SimpleBookmarkBarFolderController() {
213     BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
214     BookmarkBarFolderControllerPong* c =
215       [[BookmarkBarFolderControllerPong alloc]
216                initWithParentButton:parentButton
217                    parentController:nil
218                       barController:bar_
219                             profile:profile()];
220     [c window];  // Force nib load.
221     return c;
222   }
225 TEST_F(BookmarkBarFolderControllerTest, InitCreateAndDelete) {
226   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
227   bbfc.reset(SimpleBookmarkBarFolderController());
229   // Make sure none of the buttons overlap, that all are inside
230   // the content frame, and their cells are of the proper class.
231   NSArray* buttons = [bbfc buttons];
232   EXPECT_TRUE([buttons count]);
233   for (unsigned int i = 0; i < ([buttons count]-1); i++) {
234     EXPECT_FALSE(NSContainsRect([[buttons objectAtIndex:i] frame],
235                               [[buttons objectAtIndex:i+1] frame]));
236   }
237   Class cellClass = [BookmarkBarFolderButtonCell class];
238   for (BookmarkButton* button in buttons) {
239     NSRect r = [[bbfc folderView] convertRect:[button frame] fromView:button];
240     // TODO(jrg): remove this adjustment.
241     NSRect bigger = NSInsetRect([[bbfc folderView] frame], -2, 0);
242     EXPECT_TRUE(NSContainsRect(bigger, r));
243     EXPECT_TRUE([[button cell] isKindOfClass:cellClass]);
244   }
246   // Confirm folder buttons have no tooltip.  The important thing
247   // really is that we insure folders and non-folders are treated
248   // differently; not sure of any other generic way to do this.
249   for (BookmarkButton* button in buttons) {
250     if ([button isFolder])
251       EXPECT_FALSE([button toolTip]);
252     else
253       EXPECT_TRUE([button toolTip]);
254   }
257 // Make sure closing of the window releases the controller.
258 // (e.g. valgrind shouldn't complain if we do this).
259 TEST_F(BookmarkBarFolderControllerTest, ReleaseOnClose) {
260   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
261   bbfc.reset(SimpleBookmarkBarFolderController());
262   EXPECT_TRUE(bbfc.get());
264   [bbfc retain];  // stop the scoped_nsobject from doing anything
265   [[bbfc window] close];  // trigger an autorelease of bbfc.get()
268 TEST_F(BookmarkBarFolderControllerTest, BasicPosition) {
269   BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
270   EXPECT_TRUE(parentButton);
272   // If parent is a BookmarkBarController, grow down.
273   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
274   bbfc.reset([[BookmarkBarFolderController alloc]
275                initWithParentButton:parentButton
276                    parentController:nil
277                       barController:bar_
278                             profile:profile()]);
279   [bbfc window];
280   NSPoint pt = [bbfc windowTopLeftForWidth:0 height:100];  // screen coords
281   NSPoint buttonOriginInWindow =
282       [parentButton convertRect:[parentButton bounds]
283                          toView:nil].origin;
284   NSPoint buttonOriginInScreen =
285       [[parentButton window] convertBaseToScreen:buttonOriginInWindow];
286   // Within margin
287   EXPECT_LE(std::abs(pt.x - buttonOriginInScreen.x),
288             bookmarks::kBookmarkMenuOverlap + 1);
289   EXPECT_LE(std::abs(pt.y - buttonOriginInScreen.y),
290             bookmarks::kBookmarkMenuOverlap + 1);
292   // Make sure we see the window shift left if it spills off the screen
293   pt = [bbfc windowTopLeftForWidth:0 height:100];
294   NSPoint shifted = [bbfc windowTopLeftForWidth:9999999 height:100];
295   EXPECT_LT(shifted.x, pt.x);
297   // If parent is a BookmarkBarFolderController, grow right.
298   base::scoped_nsobject<BookmarkBarFolderController> bbfc2;
299   bbfc2.reset([[BookmarkBarFolderController alloc]
300                 initWithParentButton:[[bbfc buttons] objectAtIndex:0]
301                     parentController:bbfc.get()
302                        barController:bar_
303                              profile:profile()]);
304   [bbfc2 window];
305   pt = [bbfc2 windowTopLeftForWidth:0 height:100];
306   // We're now overlapping the window a bit.
307   EXPECT_EQ(pt.x, NSMaxX([[bbfc.get() window] frame]) -
308             bookmarks::kBookmarkMenuOverlap);
311 // Confirm we grow right until end of screen, then start growing left
312 // until end of screen again, then right.
313 TEST_F(BookmarkBarFolderControllerTest, PositionRightLeftRight) {
314   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
315   const BookmarkNode* parent = model->bookmark_bar_node();
316   const BookmarkNode* folder = parent;
318   const int count = 100;
319   int i;
320   // Make some super duper deeply nested folders.
321   for (i = 0; i < count; i++) {
322     folder = model->AddFolder(folder, 0, ASCIIToUTF16("nested folder"));
323   }
325   // Setup initial state for opening all folders.
326   folder = parent;
327   BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0];
328   BookmarkBarFolderController* parentController = nil;
329   EXPECT_TRUE(parentButton);
331   // Open them all.
332   base::scoped_nsobject<NSMutableArray> folder_controller_array;
333   folder_controller_array.reset([[NSMutableArray array] retain]);
334   for (i=0; i<count; i++) {
335     BookmarkBarFolderControllerNoLevel* bbfcl =
336         [[BookmarkBarFolderControllerNoLevel alloc]
337           initWithParentButton:parentButton
338               parentController:parentController
339                  barController:bar_
340                        profile:profile()];
341     [folder_controller_array addObject:bbfcl];
342     [bbfcl autorelease];
343     [bbfcl window];
344     parentController = bbfcl;
345     parentButton = [[bbfcl buttons] objectAtIndex:0];
346   }
348   // Make vector of all x positions.
349   std::vector<CGFloat> leftPositions;
350   for (i=0; i<count; i++) {
351     CGFloat x = [[[folder_controller_array objectAtIndex:i] window]
352                   frame].origin.x;
353     leftPositions.push_back(x);
354   }
356   // Make sure the first few grow right.
357   for (i=0; i<3; i++)
358     EXPECT_TRUE(leftPositions[i+1] > leftPositions[i]);
360   // Look for the first "grow left".
361   while (leftPositions[i] > leftPositions[i-1])
362     i++;
363   // Confirm the next few also grow left.
364   int j;
365   for (j=i; j<i+3; j++)
366     EXPECT_TRUE(leftPositions[j+1] < leftPositions[j]);
367   i = j;
369   // Finally, confirm we see a "grow right" once more.
370   while (leftPositions[i] < leftPositions[i-1])
371     i++;
372   // (No need to EXPECT a final "grow right"; if we didn't find one
373   // we'd get a C++ array bounds exception).
376 TEST_F(BookmarkBarFolderControllerTest, DropDestination) {
377   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
378   bbfc.reset(SimpleBookmarkBarFolderController());
379   EXPECT_TRUE(bbfc.get());
381   // Confirm "off the top" and "off the bottom" match no buttons.
382   NSPoint p = NSMakePoint(NSMidX([[bbfc folderView] frame]), 10000);
383   EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]);
384   EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]);
385   p = NSMakePoint(NSMidX([[bbfc folderView] frame]), -1);
386   EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]);
387   EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]);
389   // Confirm "right in the center" (give or take a pixel) is a match,
390   // and confirm "just barely in the button" is not.  Anything more
391   // specific seems likely to be tweaked.  We don't loop over all
392   // buttons because the scroll view makes them not visible.
393   for (BookmarkButton* button in [bbfc buttons]) {
394     CGFloat x = NSMidX([button frame]);
395     CGFloat y = NSMidY([button frame]);
396     // Somewhere near the center: a match (but only if a folder!)
397     if ([button isFolder]) {
398       EXPECT_EQ(button,
399                 [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]);
400       EXPECT_EQ(button,
401                 [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]);
402       EXPECT_FALSE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);;
403     } else {
404       // If not a folder we don't drop into it.
405       EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]);
406       EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]);
407       EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);;
408     }
409   }
412 TEST_F(BookmarkBarFolderControllerTest, OpenFolder) {
413   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
414   bbfc.reset(SimpleBookmarkBarFolderController());
415   EXPECT_TRUE(bbfc.get());
417   EXPECT_FALSE([bbfc folderController]);
418   BookmarkButton* button = [[bbfc buttons] objectAtIndex:0];
419   [bbfc openBookmarkFolderFromButton:button];
420   id controller = [bbfc folderController];
421   EXPECT_TRUE(controller);
422   EXPECT_EQ([controller parentButton], button);
424   // Click the same one --> it gets closed.
425   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]];
426   EXPECT_FALSE([bbfc folderController]);
428   // Open a new one --> change.
429   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:1]];
430   EXPECT_NE(controller, [bbfc folderController]);
431   EXPECT_NE([[bbfc folderController] parentButton], button);
433   // Close it --> all gone!
434   [bbfc closeBookmarkFolder:nil];
435   EXPECT_FALSE([bbfc folderController]);
438 TEST_F(BookmarkBarFolderControllerTest, DeleteOpenFolder) {
439   base::scoped_nsobject<BookmarkBarFolderController> parent_controller(
440       SimpleBookmarkBarFolderController());
442   // Open a folder.
443   EXPECT_FALSE([parent_controller folderController]);
444   BookmarkButton* button = [[parent_controller buttons] objectAtIndex:0];
445   [parent_controller openBookmarkFolderFromButton:button];
446   EXPECT_EQ([[parent_controller folderController] parentButton], button);
448   // Delete the folder's button - the folder should close.
449   [parent_controller removeButton:0 animate:NO];
450   EXPECT_FALSE([parent_controller folderController]);
453 TEST_F(BookmarkBarFolderControllerTest, ChildFolderCallbacks) {
454   base::scoped_nsobject<BookmarkBarFolderControllerPong> bbfc;
455   bbfc.reset(SimpleBookmarkBarFolderController());
456   EXPECT_TRUE(bbfc.get());
457   [bar_ setChildFolderDelegate:bbfc.get()];
459   EXPECT_FALSE([bbfc childFolderWillShow]);
460   [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]];
461   EXPECT_TRUE([bbfc childFolderWillShow]);
463   EXPECT_FALSE([bbfc childFolderWillClose]);
464   [bbfc closeBookmarkFolder:nil];
465   EXPECT_TRUE([bbfc childFolderWillClose]);
467   [bar_ setChildFolderDelegate:nil];
470 // Make sure bookmark folders have variable widths.
471 TEST_F(BookmarkBarFolderControllerTest, ChildFolderWidth) {
472   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
474   bbfc.reset(SimpleBookmarkBarFolderController());
475   EXPECT_TRUE(bbfc.get());
476   [bbfc showWindow:bbfc.get()];
477   CGFloat wideWidth = NSWidth([[bbfc window] frame]);
479   RemoveLongTitleNode();
480   bbfc.reset(SimpleBookmarkBarFolderController());
481   EXPECT_TRUE(bbfc.get());
482   CGFloat thinWidth = NSWidth([[bbfc window] frame]);
484   // Make sure window size changed as expected.
485   EXPECT_GT(wideWidth, thinWidth);
488 // Simple scrolling tests.
489 // Currently flaky due to a changed definition of the correct menu boundaries.
490 TEST_F(BookmarkBarFolderControllerTest, DISABLED_SimpleScroll) {
491   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
492   NSRect screenFrame = [[NSScreen mainScreen] visibleFrame];
493   CGFloat screenHeight = NSHeight(screenFrame);
494   int nodecount = AddLotsOfNodes();
495   bbfc.reset(SimpleBookmarkBarFolderController());
496   EXPECT_TRUE(bbfc.get());
497   [bbfc showWindow:bbfc.get()];
498   NSWindow* window = [bbfc window];
500   // The window should be shorter than the screen but reach exactly to the
501   // bottom of the screen since it's scrollable.
502   EXPECT_LT(NSHeight([window frame]), screenHeight);
503   EXPECT_CGFLOAT_EQ(0.0, [window frame].origin.y);
505   // Initially, should show scroll-up but not scroll-down.
506   EXPECT_TRUE([bbfc canScrollUp]);
507   EXPECT_FALSE([bbfc canScrollDown]);
509   // Scroll up a bit.  Make sure the window has gotten bigger each time.
510   // Also, for each scroll, make sure our hit test finds a new button
511   // (to confirm the content area changed).
512   NSView* savedHit = nil;
513   NSScrollView* scrollView = [bbfc scrollView];
515   // Find the next-to-last button showing at the bottom of the window and
516   // use its center for hit testing.
517   BookmarkButton* targetButton = nil;
518   NSPoint scrollPoint = [scrollView documentVisibleRect].origin;
519   for (BookmarkButton* button in [bbfc buttons]) {
520     NSRect buttonFrame = [button frame];
521     buttonFrame.origin.y -= scrollPoint.y;
522     if (buttonFrame.origin.y < 0.0)
523       break;
524     targetButton = button;
525   }
526   EXPECT_TRUE(targetButton != nil);
527   NSPoint hitPoint = [targetButton frame].origin;
528   hitPoint.x += 50.0;
529   hitPoint.y += (bookmarks::kBookmarkFolderButtonHeight / 2.0) - scrollPoint.y;
530   hitPoint = [targetButton convertPoint:hitPoint toView:scrollView];
532   for (int i = 0; i < 3; i++) {
533     CGFloat height = NSHeight([window frame]);
534     [bbfc performOneScroll:60];
535     EXPECT_GT(NSHeight([window frame]), height);
536     NSView* hit = [scrollView hitTest:hitPoint];
537     // We should hit a bookmark button.
538     EXPECT_TRUE([[hit className] isEqualToString:@"BookmarkButton"]);
539     EXPECT_NE(hit, savedHit);
540     savedHit = hit;
541   }
543   // Keep scrolling up; make sure we never get bigger than the screen.
544   // Also confirm we never scroll the window off the screen.
545   bool bothAtOnce = false;
546   while ([bbfc canScrollUp]) {
547     [bbfc performOneScroll:60];
548     EXPECT_TRUE(NSContainsRect([[NSScreen mainScreen] frame], [window frame]));
549     // Make sure, sometime during our scroll, we have the ability to
550     // scroll in either direction.
551     if ([bbfc canScrollUp] &&
552         [bbfc canScrollDown])
553       bothAtOnce = true;
554   }
555   EXPECT_TRUE(bothAtOnce);
557   // Once we've scrolled to the end, our only option should be to scroll back.
558   EXPECT_FALSE([bbfc canScrollUp]);
559   EXPECT_TRUE([bbfc canScrollDown]);
561   NSRect wholeScreenRect = [[NSScreen mainScreen] frame];
563   // Now scroll down and make sure the window size does not change.
564   // Also confirm we never scroll the window off the screen the other
565   // way.
566   for (int i = 0; i < nodecount+50; ++i) {
567     [bbfc performOneScroll:-60];
568     // Once we can no longer scroll down the window height changes.
569     if (![bbfc canScrollDown])
570       break;
571     EXPECT_TRUE(NSContainsRect(wholeScreenRect, [window frame]));
572   }
574   EXPECT_GT(NSHeight(wholeScreenRect), NSHeight([window frame]));
575   EXPECT_TRUE(NSContainsRect(wholeScreenRect, [window frame]));
578 // Folder menu sizing and placement while deleting bookmarks
579 // and scrolling tests.
580 TEST_F(BookmarkBarFolderControllerTest, MenuPlacementWhileScrollingDeleting) {
581   base::scoped_nsobject<BookmarkBarFolderController> bbfc;
582   AddLotsOfNodes();
583   bbfc.reset(SimpleBookmarkBarFolderController());
584   [bbfc showWindow:bbfc.get()];
585   NSWindow* menuWindow = [bbfc window];
586   BookmarkBarFolderController* folder = [bar_ folderController];
587   NSArray* buttons = [folder buttons];
589   // Before scrolling any, delete a bookmark and make sure the window top has
590   // not moved. Pick a button which is near the top and visible.
591   CGFloat oldTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
592   BookmarkButton* button = [buttons objectAtIndex:3];
593   DeleteBookmark(button, profile());
594   CGFloat newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
595   EXPECT_CGFLOAT_EQ(oldTop, newTop);
597   // Scroll so that both the top and bottom scroll arrows show, make sure
598   // the top of the window has moved up, then delete a visible button and
599   // make sure the top has not moved.
600   oldTop = newTop;
601   const CGFloat scrollOneBookmark = bookmarks::kBookmarkFolderButtonHeight +
602       bookmarks::kBookmarkVerticalPadding;
603   NSUInteger buttonCounter = 0;
604   NSUInteger extraButtonLimit = 3;
605   while (![bbfc canScrollDown] || extraButtonLimit > 0) {
606     [bbfc performOneScroll:scrollOneBookmark];
607     ++buttonCounter;
608     if ([bbfc canScrollDown])
609       --extraButtonLimit;
610   }
611   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
612   EXPECT_NE(oldTop, newTop);
613   oldTop = newTop;
614   button = [buttons objectAtIndex:buttonCounter + 3];
615   DeleteBookmark(button, profile());
616   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
617   EXPECT_CGFLOAT_EQ(oldTop, newTop);
619   // Scroll so that the top scroll arrow is no longer showing, make sure
620   // the top of the window has not moved, then delete a visible button and
621   // make sure the top has not moved.
622   while ([bbfc canScrollDown]) {
623     [bbfc performOneScroll:-scrollOneBookmark];
624     --buttonCounter;
625   }
626   button = [buttons objectAtIndex:buttonCounter + 3];
627   DeleteBookmark(button, profile());
628   newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]);
629   EXPECT_CGFLOAT_EQ(oldTop - bookmarks::kScrollWindowVerticalMargin, newTop);
632 // Make sure that we return the correct browser window.
633 TEST_F(BookmarkBarFolderControllerTest, BrowserWindow) {
634   base::scoped_nsobject<BookmarkBarFolderController> controller(
635       SimpleBookmarkBarFolderController());
636   EXPECT_EQ([bar_ browserWindow], [controller browserWindow]);
639 @interface FakedDragInfo : NSObject {
640 @public
641   NSPoint dropLocation_;
642   NSDragOperation sourceMask_;
644 @property (nonatomic, assign) NSPoint dropLocation;
645 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
646 @end
648 @implementation FakedDragInfo
650 @synthesize dropLocation = dropLocation_;
652 - (id)init {
653   if ((self = [super init])) {
654     dropLocation_ = NSZeroPoint;
655     sourceMask_ = NSDragOperationMove;
656   }
657   return self;
660 // NSDraggingInfo protocol functions.
662 - (id)draggingPasteboard {
663   return self;
666 - (id)draggingSource {
667   return self;
670 - (NSDragOperation)draggingSourceOperationMask {
671   return sourceMask_;
674 - (NSPoint)draggingLocation {
675   return dropLocation_;
678 // Other functions.
680 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
681   sourceMask_ = mask;
684 @end
687 class BookmarkBarFolderControllerMenuTest : public CocoaProfileTest {
688  public:
689   base::scoped_nsobject<NSView> parent_view_;
690   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
691   base::scoped_nsobject<BookmarkBarController> bar_;
693   void SetUp() override {
694     CocoaProfileTest::SetUp();
695     ASSERT_TRUE(browser());
697     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
698     NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
699     parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
700     [parent_view_ setHidden:YES];
701     bar_.reset([[BookmarkBarController alloc]
702                 initWithBrowser:browser()
703                    initialWidth:NSWidth(parent_frame)
704                        delegate:nil
705                  resizeDelegate:resizeDelegate_.get()]);
706     InstallAndToggleBar(bar_.get());
707   }
709   void InstallAndToggleBar(BookmarkBarController* bar) {
710     // Force loading of the nib.
711     [bar view];
712     // Awkwardness to look like we've been installed.
713     [parent_view_ addSubview:[bar view]];
714     NSRect frame = [[[bar view] superview] frame];
715     frame.origin.y = 400;
716     [[[bar view] superview] setFrame:frame];
718     // Make sure it's on in a window so viewDidMoveToWindow is called
719     [[test_window() contentView] addSubview:parent_view_];
721     // Make sure it's open so certain things aren't no-ops.
722     [bar updateState:BookmarkBar::SHOW
723           changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
724   }
727 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToFolder) {
728   WithNoAnimation at_all;
729   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
730   const BookmarkNode* root = model->bookmark_bar_node();
731   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
732       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
733       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
734   bookmarks::test::AddNodesFromModelString(model, root, model_string);
736   // Validate initial model.
737   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
738   EXPECT_EQ(model_string, actualModelString);
740   // Pop up a folder menu and drag in a button from the bar.
741   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
742   NSRect oldToFolderFrame = [toFolder frame];
743   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
744                           withObject:toFolder];
745   BookmarkBarFolderController* folderController = [bar_ folderController];
746   EXPECT_TRUE(folderController);
747   NSWindow* toWindow = [folderController window];
748   EXPECT_TRUE(toWindow);
749   NSRect oldToWindowFrame = [toWindow frame];
750   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
751   // so it should end up below the target bookmark.
752   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"];
753   ASSERT_TRUE(draggedButton);
754   CGFloat horizontalShift =
755       NSWidth([draggedButton frame]) + bookmarks::kBookmarkHorizontalPadding;
756   BookmarkButton* targetButton =
757       [folderController buttonWithTitleEqualTo:@"2f1b"];
758   ASSERT_TRUE(targetButton);
759   [folderController dragButton:draggedButton
760                             to:[targetButton center]
761                           copy:NO];
762   // The button should have landed just after "2f1b".
763   const std::string expected_string("2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
764       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
765       "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
766   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
768   // Verify the window still appears by looking for its controller.
769   EXPECT_TRUE([bar_ folderController]);
771   // Gather the new frames.
772   NSRect newToFolderFrame = [toFolder frame];
773   NSRect newToWindowFrame = [toWindow frame];
774   // The toFolder should have shifted left horizontally but not vertically.
775   NSRect expectedToFolderFrame =
776       NSOffsetRect(oldToFolderFrame, -horizontalShift, 0);
777   EXPECT_NSRECT_EQ(expectedToFolderFrame, newToFolderFrame);
778   // The toWindow should have shifted left horizontally, down vertically,
779   // and grown vertically.
780   NSRect expectedToWindowFrame = oldToWindowFrame;
781   expectedToWindowFrame.origin.x -= horizontalShift;
782   expectedToWindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
783   expectedToWindowFrame.size.height += bookmarks::kBookmarkFolderButtonHeight;
784   EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame);
786   // Check button spacing.
787   [folderController validateMenuSpacing];
789   // Move the button back to the bar at the beginning.
790   draggedButton = [folderController buttonWithTitleEqualTo:@"1b"];
791   ASSERT_TRUE(draggedButton);
792   targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
793   ASSERT_TRUE(targetButton);
794   [bar_ dragButton:draggedButton
795                 to:[targetButton left]
796               copy:NO];
797   EXPECT_EQ(model_string, bookmarks::test::ModelStringFromNode(root));
798   // Don't check the folder window since it's not supposed to be showing.
801 TEST_F(BookmarkBarFolderControllerMenuTest, DragCopyBarBookmarkToFolder) {
802   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
803   const BookmarkNode* root = model->bookmark_bar_node();
804   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
805       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
806       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
807   bookmarks::test::AddNodesFromModelString(model, root, model_string);
809   // Validate initial model.
810   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
811   EXPECT_EQ(model_string, actualModelString);
813   // Pop up a folder menu and copy in a button from the bar.
814   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
815   ASSERT_TRUE(toFolder);
816   NSRect oldToFolderFrame = [toFolder frame];
817   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
818                           withObject:toFolder];
819   BookmarkBarFolderController* folderController = [bar_ folderController];
820   EXPECT_TRUE(folderController);
821   NSWindow* toWindow = [folderController window];
822   EXPECT_TRUE(toWindow);
823   NSRect oldToWindowFrame = [toWindow frame];
824   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
825   // so it should end up below the target bookmark.
826   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"];
827   ASSERT_TRUE(draggedButton);
828   BookmarkButton* targetButton =
829       [folderController buttonWithTitleEqualTo:@"2f1b"];
830   ASSERT_TRUE(targetButton);
831   [folderController dragButton:draggedButton
832                             to:[targetButton center]
833                           copy:YES];
834   // The button should have landed just after "2f1b".
835   const std::string expected_1("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
836     "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
837     "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
838   EXPECT_EQ(expected_1, bookmarks::test::ModelStringFromNode(root));
840   // Gather the new frames.
841   NSRect newToFolderFrame = [toFolder frame];
842   NSRect newToWindowFrame = [toWindow frame];
843   // The toFolder should have shifted.
844   EXPECT_NSRECT_EQ(oldToFolderFrame, newToFolderFrame);
845   // The toWindow should have shifted down vertically and grown vertically.
846   NSRect expectedToWindowFrame = oldToWindowFrame;
847   expectedToWindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
848   expectedToWindowFrame.size.height += bookmarks::kBookmarkFolderButtonHeight;
849   EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame);
851   // Copy the button back to the bar after "3b".
852   draggedButton = [folderController buttonWithTitleEqualTo:@"1b"];
853   ASSERT_TRUE(draggedButton);
854   targetButton = [bar_ buttonWithTitleEqualTo:@"4f"];
855   ASSERT_TRUE(targetButton);
856   [bar_ dragButton:draggedButton
857                 to:[targetButton left]
858               copy:YES];
859   const std::string expected_2("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b "
860       "2f2f2b 2f2f3b ] 2f3b ] 3b 1b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
861       "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
862   EXPECT_EQ(expected_2, bookmarks::test::ModelStringFromNode(root));
865 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToSubfolder) {
866   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
867   const BookmarkNode* root = model->bookmark_bar_node();
868   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
869       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
870       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
871   bookmarks::test::AddNodesFromModelString(model, root, model_string);
873   // Validate initial model.
874   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
875   EXPECT_EQ(model_string, actualModelString);
877   // Pop up a folder menu and a subfolder menu.
878   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
879   ASSERT_TRUE(toFolder);
880   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
881                           withObject:toFolder];
882   BookmarkBarFolderController* folderController = [bar_ folderController];
883   EXPECT_TRUE(folderController);
884   NSWindow* toWindow = [folderController window];
885   EXPECT_TRUE(toWindow);
886   NSRect oldToWindowFrame = [toWindow frame];
887   BookmarkButton* toSubfolder =
888       [folderController buttonWithTitleEqualTo:@"4f2f"];
889   ASSERT_TRUE(toSubfolder);
890   [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:)
891                              withObject:toSubfolder];
892   BookmarkBarFolderController* subfolderController =
893       [folderController folderController];
894   EXPECT_TRUE(subfolderController);
895   NSWindow* toSubwindow = [subfolderController window];
896   EXPECT_TRUE(toSubwindow);
897   NSRect oldToSubwindowFrame = [toSubwindow frame];
898   // Drag a bar button onto a bookmark (i.e. not a folder) in a folder
899   // so it should end up below the target bookmark.
900   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"5b"];
901   ASSERT_TRUE(draggedButton);
902   BookmarkButton* targetButton =
903       [subfolderController buttonWithTitleEqualTo:@"4f2f3b"];
904   ASSERT_TRUE(targetButton);
905   [subfolderController dragButton:draggedButton
906                                to:[targetButton center]
907                              copy:NO];
908   // The button should have landed just after "2f".
909   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b "
910       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ "
911       "4f2f1b 4f2f2b 4f2f3b 5b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] ");
912   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
914   // Check button spacing.
915   [folderController validateMenuSpacing];
916   [subfolderController validateMenuSpacing];
918   // Check the window layouts. The folder window should not have changed,
919   // but the subfolder window should have shifted vertically and grown.
920   NSRect newToWindowFrame = [toWindow frame];
921   EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame);
922   NSRect newToSubwindowFrame = [toSubwindow frame];
923   NSRect expectedToSubwindowFrame = oldToSubwindowFrame;
924   expectedToSubwindowFrame.origin.y -= bookmarks::kBookmarkFolderButtonHeight;
925   expectedToSubwindowFrame.size.height +=
926       bookmarks::kBookmarkFolderButtonHeight;
927   EXPECT_NSRECT_EQ(expectedToSubwindowFrame, newToSubwindowFrame);
930 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveWithinFolder) {
931   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
932   const BookmarkNode* root = model->bookmark_bar_node();
933   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
934       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
935       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
936   bookmarks::test::AddNodesFromModelString(model, root, model_string);
938   // Validate initial model.
939   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
940   EXPECT_EQ(model_string, actualModelString);
942   // Pop up a folder menu.
943   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
944   ASSERT_TRUE(toFolder);
945   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
946                           withObject:toFolder];
947   BookmarkBarFolderController* folderController = [bar_ folderController];
948   EXPECT_TRUE(folderController);
949   NSWindow* toWindow = [folderController window];
950   EXPECT_TRUE(toWindow);
951   NSRect oldToWindowFrame = [toWindow frame];
952   // Drag a folder button to the top within the same parent.
953   BookmarkButton* draggedButton =
954       [folderController buttonWithTitleEqualTo:@"4f2f"];
955   ASSERT_TRUE(draggedButton);
956   BookmarkButton* targetButton =
957       [folderController buttonWithTitleEqualTo:@"4f1f"];
958   ASSERT_TRUE(targetButton);
959   [folderController dragButton:draggedButton
960                             to:[targetButton top]
961                           copy:NO];
962   // The button should have landed above "4f1f".
963   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b "
964       "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f2f:[ 4f2f1b 4f2f2b 4f2f3b ] "
965       "4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
966   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
968   // The window should not have gone away.
969   EXPECT_TRUE([bar_ folderController]);
971   // The folder window should not have changed.
972   NSRect newToWindowFrame = [toWindow frame];
973   EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame);
975   // Check button spacing.
976   [folderController validateMenuSpacing];
979 TEST_F(BookmarkBarFolderControllerMenuTest, DragParentOntoChild) {
980   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
981   const BookmarkNode* root = model->bookmark_bar_node();
982   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
983       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
984       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
985   bookmarks::test::AddNodesFromModelString(model, root, model_string);
987   // Validate initial model.
988   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
989   EXPECT_EQ(model_string, actualModelString);
991   // Pop up a folder menu.
992   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
993   ASSERT_TRUE(toFolder);
994   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
995                           withObject:toFolder];
996   BookmarkBarFolderController* folderController = [bar_ folderController];
997   EXPECT_TRUE(folderController);
998   NSWindow* toWindow = [folderController window];
999   EXPECT_TRUE(toWindow);
1000   // Drag a folder button to one of its children.
1001   BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"4f"];
1002   ASSERT_TRUE(draggedButton);
1003   BookmarkButton* targetButton =
1004       [folderController buttonWithTitleEqualTo:@"4f3f"];
1005   ASSERT_TRUE(targetButton);
1006   [folderController dragButton:draggedButton
1007                             to:[targetButton top]
1008                           copy:NO];
1009   // The model should not have changed.
1010   EXPECT_EQ(model_string, bookmarks::test::ModelStringFromNode(root));
1012   // Check button spacing.
1013   [folderController validateMenuSpacing];
1016 TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveChildToParent) {
1017   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1018   const BookmarkNode* root = model->bookmark_bar_node();
1019   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1020       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b "
1021       "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1022   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1024   // Validate initial model.
1025   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1026   EXPECT_EQ(model_string, actualModelString);
1028   // Pop up a folder menu and a subfolder menu.
1029   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"];
1030   ASSERT_TRUE(toFolder);
1031   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1032                           withObject:toFolder];
1033   BookmarkBarFolderController* folderController = [bar_ folderController];
1034   EXPECT_TRUE(folderController);
1035   BookmarkButton* toSubfolder =
1036       [folderController buttonWithTitleEqualTo:@"4f2f"];
1037   ASSERT_TRUE(toSubfolder);
1038   [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1039                              withObject:toSubfolder];
1040   BookmarkBarFolderController* subfolderController =
1041       [folderController folderController];
1042   EXPECT_TRUE(subfolderController);
1044   // Drag a subfolder bookmark to the parent folder.
1045   BookmarkButton* draggedButton =
1046       [subfolderController buttonWithTitleEqualTo:@"4f2f3b"];
1047   ASSERT_TRUE(draggedButton);
1048   BookmarkButton* targetButton =
1049       [folderController buttonWithTitleEqualTo:@"4f2f"];
1050   ASSERT_TRUE(targetButton);
1051   [folderController dragButton:draggedButton
1052                             to:[targetButton top]
1053                           copy:NO];
1054   // The button should have landed above "4f2f".
1055   const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1056       "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f3b 4f2f:[ "
1057       "4f2f1b 4f2f2b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b ");
1058   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
1060   // Check button spacing.
1061   [folderController validateMenuSpacing];
1062   // The window should not have gone away.
1063   EXPECT_TRUE([bar_ folderController]);
1064   // The subfolder should have gone away.
1065   EXPECT_FALSE([folderController folderController]);
1068 TEST_F(BookmarkBarFolderControllerMenuTest, DragWindowResizing) {
1069   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1070   const BookmarkNode* root = model->bookmark_bar_node();
1071   const std::string model_string(
1072       "a b:[ b1 b2 b3 ] reallyReallyLongBookmarkName c ");
1073   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1075   // Validate initial model.
1076   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1077   EXPECT_EQ(model_string, actualModelString);
1079   // Pop up a folder menu.
1080   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"b"];
1081   ASSERT_TRUE(toFolder);
1082   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1083                           withObject:toFolder];
1084   BookmarkBarFolderController* folderController = [bar_ folderController];
1085   EXPECT_TRUE(folderController);
1086   NSWindow* toWindow = [folderController window];
1087   EXPECT_TRUE(toWindow);
1088   CGFloat oldWidth = NSWidth([toWindow frame]);
1089   // Drag the bookmark with a long name to the folder.
1090   BookmarkButton* draggedButton =
1091       [bar_ buttonWithTitleEqualTo:@"reallyReallyLongBookmarkName"];
1092   ASSERT_TRUE(draggedButton);
1093   BookmarkButton* targetButton =
1094       [folderController buttonWithTitleEqualTo:@"b1"];
1095   ASSERT_TRUE(targetButton);
1096   [folderController dragButton:draggedButton
1097                             to:[targetButton center]
1098                           copy:NO];
1099   // Verify the model change.
1100   const std::string expected_string(
1101       "a b:[ b1 reallyReallyLongBookmarkName b2 b3 ] c ");
1102   EXPECT_EQ(expected_string, bookmarks::test::ModelStringFromNode(root));
1103   // Verify the window grew. Just test a reasonable width gain.
1104   CGFloat newWidth = NSWidth([toWindow frame]);
1105   EXPECT_LT(oldWidth + 30.0, newWidth);
1108 TEST_F(BookmarkBarFolderControllerMenuTest, MoveRemoveAddButtons) {
1109   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1110   const BookmarkNode* root = model->bookmark_bar_node();
1111   const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
1112   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1114   // Validate initial model.
1115   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1116   EXPECT_EQ(model_string, actualModelString);
1118   // Pop up a folder menu.
1119   BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
1120   ASSERT_TRUE(toFolder);
1121   [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:)
1122                           withObject:toFolder];
1123   BookmarkBarFolderController* folder = [bar_ folderController];
1124   EXPECT_TRUE(folder);
1126   // Remember how many buttons are showing.
1127   NSArray* buttons = [folder buttons];
1128   NSUInteger oldDisplayedButtons = [buttons count];
1130   // Move a button around a bit.
1131   [folder moveButtonFromIndex:0 toIndex:2];
1132   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:0] title]);
1133   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:1] title]);
1134   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:2] title]);
1135   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1136   [folder moveButtonFromIndex:2 toIndex:0];
1137   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1138   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]);
1139   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]);
1140   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1142   // Add a couple of buttons.
1143   const BookmarkNode* node = root->GetChild(2); // Purloin an existing node.
1144   [folder addButtonForNode:node atIndex:0];
1145   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1146   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]);
1147   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]);
1148   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]);
1149   EXPECT_EQ(oldDisplayedButtons + 1, [buttons count]);
1150   node = root->GetChild(3);
1151   [folder addButtonForNode:node atIndex:-1];
1152   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1153   EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]);
1154   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]);
1155   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]);
1156   EXPECT_NSEQ(@"4b", [[buttons objectAtIndex:4] title]);
1157   EXPECT_EQ(oldDisplayedButtons + 2, [buttons count]);
1159   // Remove a couple of buttons.
1160   [folder removeButton:4 animate:NO];
1161   [folder removeButton:1 animate:NO];
1162   EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]);
1163   EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]);
1164   EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]);
1165   EXPECT_EQ(oldDisplayedButtons, [buttons count]);
1167   // Check button spacing.
1168   [folder validateMenuSpacing];
1171 TEST_F(BookmarkBarFolderControllerMenuTest, RemoveLastButtonOtherBookmarks) {
1172   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1173   const BookmarkNode* otherBookmarks = model->other_node();
1175   BookmarkButton* otherButton = [bar_ otherBookmarksButton];
1176   ASSERT_TRUE(otherButton);
1178   // Open the folder to get the folderController_.
1179   [[otherButton target] openBookmarkFolderFromButton:otherButton];
1180   BookmarkBarFolderController* folder = [bar_ folderController];
1181   EXPECT_TRUE(folder);
1183   // Initially there is only (empty) placeholder button, hence buttonCount
1184   // should be 1.
1185   NSArray* buttons = [folder buttons];
1186   EXPECT_TRUE(buttons);
1187   EXPECT_EQ(1U, [buttons count]);
1189   // Add a new bookmark into 'Other bookmarks' folder.
1190   model->AddURL(otherBookmarks, otherBookmarks->child_count(),
1191                 ASCIIToUTF16("TheOther"),
1192                 GURL("http://www.other.com"));
1194   // buttonCount still should be 1, as we remove the (empty) placeholder button
1195   // when adding a new button to an empty folder.
1196   EXPECT_EQ(1U, [buttons count]);
1198   // Now we have only 1 button; remove it so that 'Other bookmarks' folder
1199   // is hidden.
1200   [folder removeButton:0 animate:NO];
1201   EXPECT_EQ(0U, [buttons count]);
1203   // 'Other bookmarks' folder gets closed once we remove the last button. Hence
1204   // folderController_ should be NULL.
1205   EXPECT_FALSE([bar_ folderController]);
1208 TEST_F(BookmarkBarFolderControllerMenuTest, ControllerForNode) {
1209   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1210   const BookmarkNode* root = model->bookmark_bar_node();
1211   const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1212   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1214   // Validate initial model.
1215   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1216   EXPECT_EQ(model_string, actualModelString);
1218   // Find the main bar controller.
1219   const void* expectedController = bar_;
1220   const void* actualController = [bar_ controllerForNode:root];
1221   EXPECT_EQ(expectedController, actualController);
1223   // Pop up the folder menu.
1224   BookmarkButton* targetFolder = [bar_ buttonWithTitleEqualTo:@"2f"];
1225   ASSERT_TRUE(targetFolder);
1226   [[targetFolder target]
1227    performSelector:@selector(openBookmarkFolderFromButton:)
1228    withObject:targetFolder];
1229   BookmarkBarFolderController* folder = [bar_ folderController];
1230   EXPECT_TRUE(folder);
1232   // Find the folder controller using the folder controller.
1233   const BookmarkNode* targetNode = root->GetChild(1);
1234   expectedController = folder;
1235   actualController = [bar_ controllerForNode:targetNode];
1236   EXPECT_EQ(expectedController, actualController);
1238   // Find the folder controller from the bar.
1239   actualController = [folder controllerForNode:targetNode];
1240   EXPECT_EQ(expectedController, actualController);
1243 TEST_F(BookmarkBarFolderControllerMenuTest, MenuSizingAndScrollArrows) {
1244   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1245   const BookmarkNode* root = model->bookmark_bar_node();
1246   const std::string model_string("1b 2b 3b ");
1247   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1249   // Validate initial model.
1250   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1251   EXPECT_EQ(model_string, actualModelString);
1253   const BookmarkNode* parent = model->bookmark_bar_node();
1254   const BookmarkNode* folder = model->AddFolder(parent,
1255                                                 parent->child_count(),
1256                                                 ASCIIToUTF16("BIG"));
1258   // Pop open the new folder window and verify it has one (empty) item.
1259   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"];
1260   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1261                         withObject:button];
1262   BookmarkBarFolderController* folderController = [bar_ folderController];
1263   EXPECT_TRUE(folderController);
1264   NSWindow* folderWindow = [folderController window];
1265   EXPECT_TRUE(folderWindow);
1266   CGFloat expectedHeight = (CGFloat)bookmarks::kBookmarkFolderButtonHeight +
1267       (2*bookmarks::kBookmarkVerticalPadding);
1268   NSRect windowFrame = [folderWindow frame];
1269   CGFloat windowHeight = NSHeight(windowFrame);
1270   EXPECT_CGFLOAT_EQ(expectedHeight, windowHeight);
1271   EXPECT_FALSE([folderController canScrollUp]);
1272   EXPECT_FALSE([folderController canScrollDown]);
1274   // Now add a real bookmark and reopen.
1275   model->AddURL(folder, folder->child_count(), ASCIIToUTF16("a"),
1276                 GURL("http://a.com/"));
1277   folderController = [bar_ folderController];
1278   EXPECT_TRUE(folderController);
1279   NSView* folderView = [folderController folderView];
1280   EXPECT_TRUE(folderView);
1281   NSRect menuFrame = [folderView frame];
1282   NSView* visibleView = [folderController visibleView];
1283   NSRect visibleFrame = [visibleView frame];
1284   NSScrollView* scrollView = [folderController scrollView];
1285   NSRect scrollFrame = [scrollView frame];
1287   // Determine the margins between the scroll frame and the visible frame.
1288   CGFloat widthDelta = NSWidth(visibleFrame) - NSWidth(scrollFrame);
1290   CGFloat menuHeight = NSHeight(menuFrame);
1291   EXPECT_CGFLOAT_EQ(expectedHeight, menuHeight);
1292   CGFloat scrollerWidth = NSWidth(scrollFrame);
1293   button = [folderController buttonWithTitleEqualTo:@"a"];
1294   CGFloat buttonWidth = NSWidth([button frame]);
1295   EXPECT_CGFLOAT_EQ(scrollerWidth, buttonWidth);
1296   CGFloat visibleWidth = NSWidth(visibleFrame);
1297   EXPECT_CGFLOAT_EQ(visibleWidth - widthDelta, buttonWidth);
1298   EXPECT_LT(scrollerWidth, NSWidth([folderView frame]));
1300   // Add a wider bookmark and make sure the button widths match.
1301   int reallyWideButtonNumber = folder->child_count();
1302   model->AddURL(folder, reallyWideButtonNumber,
1303                 ASCIIToUTF16("A really, really, really, really, really, "
1304                             "really long name"),
1305                 GURL("http://www.google.com/a"));
1306   BookmarkButton* bigButton =
1307       [folderController buttonWithTitleEqualTo:
1308        @"A really, really, really, really, really, really long name"];
1309   EXPECT_TRUE(bigButton);
1310   CGFloat buttonWidthB = NSWidth([bigButton frame]);
1311   EXPECT_LT(buttonWidth, buttonWidthB);
1312   // Add a bunch of bookmarks until the window becomes scrollable, then check
1313   // for a scroll up arrow.
1314   NSUInteger tripWire = 0;  // Prevent a runaway.
1315   while (![folderController canScrollUp] && ++tripWire < 1000) {
1316     model->AddURL(folder, folder->child_count(), ASCIIToUTF16("B"),
1317                   GURL("http://b.com/"));
1318   }
1319   EXPECT_TRUE([folderController canScrollUp]);
1321   // Remove one bookmark and make sure the scroll down arrow has been removed.
1322   // We'll remove the really long node so we can see if the buttons get resized.
1323   scrollerWidth = NSWidth([folderView frame]);
1324   buttonWidth = NSWidth([button frame]);
1325   model->Remove(folder, reallyWideButtonNumber);
1326   EXPECT_FALSE([folderController canScrollUp]);
1327   EXPECT_FALSE([folderController canScrollDown]);
1329   // Check the size. It should have reduced.
1330   EXPECT_GT(scrollerWidth, NSWidth([folderView frame]));
1331   EXPECT_GT(buttonWidth, NSWidth([button frame]));
1333   // Check button spacing.
1334   [folderController validateMenuSpacing];
1337 // See http://crbug.com/46101
1338 TEST_F(BookmarkBarFolderControllerMenuTest, HoverThenDeleteBookmark) {
1339   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1340   const BookmarkNode* root = model->bookmark_bar_node();
1341   const BookmarkNode* folder = model->AddFolder(root,
1342                                                 root->child_count(),
1343                                                 ASCIIToUTF16("BIG"));
1344   for (int i = 0; i < kLotsOfNodesCount; i++)
1345     model->AddURL(folder, folder->child_count(), ASCIIToUTF16("kid"),
1346                   GURL("http://kid.com/smile"));
1348   // Pop open the new folder window and hover one of its kids.
1349   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"];
1350   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1351                         withObject:button];
1352   BookmarkBarFolderController* bbfc = [bar_ folderController];
1353   NSArray* buttons = [bbfc buttons];
1355   // Hover over a button and verify that it is now known.
1356   button = [buttons objectAtIndex:3];
1357   BookmarkButton* buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1358   EXPECT_FALSE(buttonThatMouseIsIn);
1359   [bbfc mouseEnteredButton:button event:nil];
1360   buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1361   EXPECT_EQ(button, buttonThatMouseIsIn);
1363   // Delete the bookmark and verify that it is now not known.
1364   model->Remove(folder, 3);
1365   buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn];
1366   EXPECT_FALSE(buttonThatMouseIsIn);
1369 // Just like a BookmarkBarFolderController but intercedes when providing
1370 // pasteboard drag data.
1371 @interface BookmarkBarFolderControllerDragData : BookmarkBarFolderController {
1372   const BookmarkNode* dragDataNode_;  // Weak
1374 - (void)setDragDataNode:(const BookmarkNode*)node;
1375 @end
1377 @implementation BookmarkBarFolderControllerDragData
1379 - (id)initWithParentButton:(BookmarkButton*)button
1380           parentController:(BookmarkBarFolderController*)parentController
1381              barController:(BookmarkBarController*)barController
1382                    profile:(Profile*)profile {
1383   if ((self = [super initWithParentButton:button
1384                          parentController:parentController
1385                             barController:barController
1386                                   profile:profile])) {
1387     dragDataNode_ = NULL;
1388   }
1389   return self;
1392 - (void)setDragDataNode:(const BookmarkNode*)node {
1393   dragDataNode_ = node;
1396 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
1397   std::vector<const BookmarkNode*> dragDataNodes;
1398   if(dragDataNode_) {
1399     dragDataNodes.push_back(dragDataNode_);
1400   }
1401   return dragDataNodes;
1404 @end
1406 TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkData) {
1407   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1408   const BookmarkNode* root = model->bookmark_bar_node();
1409   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1410                                  "2f3b ] 3b 4b ");
1411   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1412   const BookmarkNode* other = model->other_node();
1413   const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1414                                  "O4f:[ O4f1b O4f2f ] 05b ");
1415   bookmarks::test::AddNodesFromModelString(model, other, other_string);
1417   // Validate initial model.
1418   std::string actual = bookmarks::test::ModelStringFromNode(root);
1419   EXPECT_EQ(model_string, actual);
1420   actual = bookmarks::test::ModelStringFromNode(other);
1421   EXPECT_EQ(other_string, actual);
1423   // Pop open a folder.
1424   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1425   base::scoped_nsobject<BookmarkBarFolderControllerDragData> folderController;
1426   folderController.reset([[BookmarkBarFolderControllerDragData alloc]
1427                           initWithParentButton:button
1428                               parentController:nil
1429                                  barController:bar_
1430                                        profile:profile()]);
1431   BookmarkButton* targetButton =
1432       [folderController buttonWithTitleEqualTo:@"2f1b"];
1433   ASSERT_TRUE(targetButton);
1435   // Gen up some dragging data.
1436   const BookmarkNode* newNode = other->GetChild(2);
1437   [folderController setDragDataNode:newNode];
1438   base::scoped_nsobject<FakedDragInfo> dragInfo([[FakedDragInfo alloc] init]);
1439   [dragInfo setDropLocation:[targetButton top]];
1440   [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1442   // Verify the model.
1443   const std::string expected("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ 2f2f1b "
1444                              "2f2f2b 2f2f3b ] 2f3b ] 3b 4b ");
1445   actual = bookmarks::test::ModelStringFromNode(root);
1446   EXPECT_EQ(expected, actual);
1448   // Now drag over a folder button.
1449   targetButton = [folderController buttonWithTitleEqualTo:@"2f2f"];
1450   ASSERT_TRUE(targetButton);
1451   newNode = other->GetChild(2);  // Should be O4f.
1452   EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1453   [folderController setDragDataNode:newNode];
1454   [dragInfo setDropLocation:[targetButton center]];
1455   [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1457   // Verify the model.
1458   const std::string expectedA("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ "
1459                               "2f2f1b 2f2f2b 2f2f3b O4f:[ O4f1b O4f2f ] ] "
1460                               "2f3b ] 3b 4b ");
1461   actual = bookmarks::test::ModelStringFromNode(root);
1462   EXPECT_EQ(expectedA, actual);
1464   // Check button spacing.
1465   [folderController validateMenuSpacing];
1468 TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkDataToTrash) {
1469   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1470   const BookmarkNode* root = model->bookmark_bar_node();
1471   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1472                                  "2f3b ] 3b 4b ");
1473   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1475   // Validate initial model.
1476   std::string actual = bookmarks::test::ModelStringFromNode(root);
1477   EXPECT_EQ(model_string, actual);
1479   const BookmarkNode* folderNode = root->GetChild(1);
1480   int oldFolderChildCount = folderNode->child_count();
1482   // Pop open a folder.
1483   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1484   base::scoped_nsobject<BookmarkBarFolderControllerDragData> folderController;
1485   folderController.reset([[BookmarkBarFolderControllerDragData alloc]
1486                           initWithParentButton:button
1487                               parentController:nil
1488                                  barController:bar_
1489                                        profile:profile()]);
1491   // Drag a button to the trash.
1492   BookmarkButton* buttonToDelete =
1493       [folderController buttonWithTitleEqualTo:@"2f1b"];
1494   ASSERT_TRUE(buttonToDelete);
1495   EXPECT_TRUE([folderController canDragBookmarkButtonToTrash:buttonToDelete]);
1496   [folderController didDragBookmarkToTrash:buttonToDelete];
1498   // There should be one less button in the folder.
1499   int newFolderChildCount = folderNode->child_count();
1500   EXPECT_EQ(oldFolderChildCount - 1, newFolderChildCount);
1501   // Verify the model.
1502   const std::string expected("1b 2f:[ 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1503                              "2f3b ] 3b 4b ");
1504   actual = bookmarks::test::ModelStringFromNode(root);
1505   EXPECT_EQ(expected, actual);
1507   // Check button spacing.
1508   [folderController validateMenuSpacing];
1511 TEST_F(BookmarkBarFolderControllerMenuTest, AddURLs) {
1512   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1513   const BookmarkNode* root = model->bookmark_bar_node();
1514   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1515                                  "2f3b ] 3b 4b ");
1516   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1518   // Validate initial model.
1519   std::string actual = bookmarks::test::ModelStringFromNode(root);
1520   EXPECT_EQ(model_string, actual);
1522   // Pop open a folder.
1523   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1524   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1525                         withObject:button];
1526   BookmarkBarFolderController* folderController = [bar_ folderController];
1527   EXPECT_TRUE(folderController);
1528   NSArray* buttons = [folderController buttons];
1529   EXPECT_TRUE(buttons);
1531   // Remember how many buttons are showing.
1532   int oldDisplayedButtons = [buttons count];
1534   BookmarkButton* targetButton =
1535       [folderController buttonWithTitleEqualTo:@"2f1b"];
1536   ASSERT_TRUE(targetButton);
1538   NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
1539                    @"http://www.b.com/", nil];
1540   NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
1541   [folderController addURLs:urls withTitles:titles at:[targetButton top]];
1543   // There should two more buttons in the folder.
1544   int newDisplayedButtons = [buttons count];
1545   EXPECT_EQ(oldDisplayedButtons + 2, newDisplayedButtons);
1546   // Verify the model.
1547   const std::string expected("1b 2f:[ SiteA SiteB 2f1b 2f2f:[ 2f2f1b 2f2f2b "
1548                              "2f2f3b ] 2f3b ] 3b 4b ");
1549   actual = bookmarks::test::ModelStringFromNode(root);
1550   EXPECT_EQ(expected, actual);
1552   // Check button spacing.
1553   [folderController validateMenuSpacing];
1556 TEST_F(BookmarkBarFolderControllerMenuTest, DropPositionIndicator) {
1557   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1558   const BookmarkNode* root = model->bookmark_bar_node();
1559   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1560                                  "2f3b ] 3b 4b ");
1561   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1563   // Validate initial model.
1564   std::string actual = bookmarks::test::ModelStringFromNode(root);
1565   EXPECT_EQ(model_string, actual);
1567   // Pop open the folder.
1568   BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"];
1569   [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
1570                         withObject:button];
1571   BookmarkBarFolderController* folder = [bar_ folderController];
1572   EXPECT_TRUE(folder);
1574   // Test a series of points starting at the top of the folder.
1575   const CGFloat yOffset = 0.5 * bookmarks::kBookmarkVerticalPadding;
1576   BookmarkButton* targetButton = [folder buttonWithTitleEqualTo:@"2f1b"];
1577   ASSERT_TRUE(targetButton);
1578   NSPoint targetPoint = [targetButton top];
1579   CGFloat pos = [folder indicatorPosForDragToPoint:targetPoint];
1580   EXPECT_CGFLOAT_EQ(targetPoint.y + yOffset, pos);
1581   pos = [folder indicatorPosForDragToPoint:[targetButton bottom]];
1582   targetButton = [folder buttonWithTitleEqualTo:@"2f2f"];
1583   EXPECT_CGFLOAT_EQ([targetButton top].y + yOffset, pos);
1584   pos = [folder indicatorPosForDragToPoint:NSMakePoint(10,0)];
1585   targetButton = [folder buttonWithTitleEqualTo:@"2f3b"];
1586   EXPECT_CGFLOAT_EQ([targetButton bottom].y - yOffset, pos);
1589 @interface BookmarkBarControllerNoDelete : BookmarkBarController
1590 - (IBAction)deleteBookmark:(id)sender;
1591 @end
1593 @implementation BookmarkBarControllerNoDelete
1594 - (IBAction)deleteBookmark:(id)sender {
1595   // NOP
1597 @end
1599 class BookmarkBarFolderControllerClosingTest : public
1600     BookmarkBarFolderControllerMenuTest {
1601  public:
1602   void SetUp() override {
1603     BookmarkBarFolderControllerMenuTest::SetUp();
1604     ASSERT_TRUE(browser());
1606     bar_.reset([[BookmarkBarControllerNoDelete alloc]
1607                 initWithBrowser:browser()
1608                    initialWidth:NSWidth([parent_view_ frame])
1609                        delegate:nil
1610                  resizeDelegate:resizeDelegate_.get()]);
1611     InstallAndToggleBar(bar_.get());
1612   }
1615 TEST_F(BookmarkBarFolderControllerClosingTest, DeleteClosesFolder) {
1616   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1617   const BookmarkNode* root = model->bookmark_bar_node();
1618   const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b ] "
1619                                  "2f3b ] 3b ");
1620   bookmarks::test::AddNodesFromModelString(model, root, model_string);
1622   // Validate initial model.
1623   std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1624   EXPECT_EQ(model_string, actualModelString);
1626   // Open the folder menu and submenu.
1627   BookmarkButton* target = [bar_ buttonWithTitleEqualTo:@"2f"];
1628   ASSERT_TRUE(target);
1629   [[target target] performSelector:@selector(openBookmarkFolderFromButton:)
1630                               withObject:target];
1631   BookmarkBarFolderController* folder = [bar_ folderController];
1632   EXPECT_TRUE(folder);
1633   BookmarkButton* subTarget = [folder buttonWithTitleEqualTo:@"2f2f"];
1634   ASSERT_TRUE(subTarget);
1635   [[subTarget target] performSelector:@selector(openBookmarkFolderFromButton:)
1636                            withObject:subTarget];
1637   BookmarkBarFolderController* subFolder = [folder folderController];
1638   EXPECT_TRUE(subFolder);
1640   // Delete the folder node and verify the window closed down by looking
1641   // for its controller again.
1642   DeleteBookmark([folder parentButton], profile());
1643   EXPECT_FALSE([folder folderController]);
1646 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1647 // they are hard to test.  Factor out "fire timers" into routines
1648 // which can be overridden to fire immediately to make behavior
1649 // confirmable.
1650 // There is a similar problem with mouseEnteredButton: and
1651 // mouseExitedButton:.