1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import <Cocoa/Cocoa.h>
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/bookmarks/bookmark_model.h"
15 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
16 #include "chrome/browser/bookmarks/bookmark_test_helpers.h"
17 #include "chrome/browser/bookmarks/bookmark_utils.h"
18 #include "chrome/browser/extensions/test_extension_system.h"
19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
20 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
21 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h"
22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h"
23 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h"
24 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
25 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"
26 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
27 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/test/base/testing_profile.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #import "testing/gtest_mac.h"
33 #include "testing/platform_test.h"
34 #import "third_party/ocmock/OCMock/OCMock.h"
35 #include "third_party/ocmock/gtest_support.h"
36 #include "ui/base/cocoa/animation_utils.h"
37 #include "ui/base/test/cocoa_test_event_utils.h"
38 #include "ui/base/theme_provider.h"
39 #include "ui/gfx/image/image_skia.h"
41 // Unit tests don't need time-consuming asynchronous animations.
42 @interface BookmarkBarControllerTestable : BookmarkBarController {
47 @implementation BookmarkBarControllerTestable
49 - (id)initWithBrowser:(Browser*)browser
50 initialWidth:(CGFloat)initialWidth
51 delegate:(id<BookmarkBarControllerDelegate>)delegate
52 resizeDelegate:(id<ViewResizer>)resizeDelegate {
53 if ((self = [super initWithBrowser:browser
54 initialWidth:initialWidth
56 resizeDelegate:resizeDelegate])) {
57 [self setStateAnimationsEnabled:NO];
58 [self setInnerContentAnimationsEnabled:NO];
65 // Just like a BookmarkBarController but openURL: is stubbed out.
66 @interface BookmarkBarControllerNoOpen : BookmarkBarControllerTestable {
68 std::vector<GURL> urls_;
69 std::vector<WindowOpenDisposition> dispositions_;
73 @implementation BookmarkBarControllerNoOpen
74 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition {
76 dispositions_.push_back(disposition);
80 dispositions_.clear();
85 // NSCell that is pre-provided with a desired size that becomes the
86 // return value for -(NSSize)cellSize:.
87 @interface CellWithDesiredSize : NSCell {
91 @property (nonatomic, readonly) NSSize cellSize;
94 @implementation CellWithDesiredSize
96 @synthesize cellSize = cellSize_;
98 - (id)initTextCell:(NSString*)string desiredSize:(NSSize)size {
99 if ((self = [super initTextCell:string])) {
107 // Remember the number of times we've gotten a frameDidChange notification.
108 @interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen {
112 @property (nonatomic, readonly) int toggles;
115 @implementation BookmarkBarControllerTogglePong
117 @synthesize toggles = toggles_;
119 - (void)frameDidChange {
125 // Remembers if a notification callback was called.
126 @interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen {
127 BOOL windowWillCloseReceived_;
128 BOOL windowDidResignKeyReceived_;
130 @property (nonatomic, readonly) BOOL windowWillCloseReceived;
131 @property (nonatomic, readonly) BOOL windowDidResignKeyReceived;
134 @implementation BookmarkBarControllerNotificationPong
135 @synthesize windowWillCloseReceived = windowWillCloseReceived_;
136 @synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_;
138 // Override NSNotificationCenter callback.
139 - (void)parentWindowWillClose:(NSNotification*)notification {
140 windowWillCloseReceived_ = YES;
143 // NSNotificationCenter callback.
144 - (void)parentWindowDidResignKey:(NSNotification*)notification {
145 windowDidResignKeyReceived_ = YES;
149 // Remembers if and what kind of openAll was performed.
150 @interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen {
151 WindowOpenDisposition dispositionDetected_;
153 @property (nonatomic) WindowOpenDisposition dispositionDetected;
156 @implementation BookmarkBarControllerOpenAllPong
157 @synthesize dispositionDetected = dispositionDetected_;
159 // Intercede for the openAll:disposition: method.
160 - (void)openAll:(const BookmarkNode*)node
161 disposition:(WindowOpenDisposition)disposition {
162 [self setDispositionDetected:disposition];
167 // Just like a BookmarkBarController but intercedes when providing
168 // pasteboard drag data.
169 @interface BookmarkBarControllerDragData : BookmarkBarControllerTestable {
170 const BookmarkNode* dragDataNode_; // Weak
172 - (void)setDragDataNode:(const BookmarkNode*)node;
175 @implementation BookmarkBarControllerDragData
177 - (id)initWithBrowser:(Browser*)browser
178 initialWidth:(CGFloat)initialWidth
179 delegate:(id<BookmarkBarControllerDelegate>)delegate
180 resizeDelegate:(id<ViewResizer>)resizeDelegate {
181 if ((self = [super initWithBrowser:browser
182 initialWidth:initialWidth
184 resizeDelegate:resizeDelegate])) {
185 dragDataNode_ = NULL;
190 - (void)setDragDataNode:(const BookmarkNode*)node {
191 dragDataNode_ = node;
194 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
195 std::vector<const BookmarkNode*> dragDataNodes;
197 dragDataNodes.push_back(dragDataNode_);
199 return dragDataNodes;
205 class FakeTheme : public ui::ThemeProvider {
207 FakeTheme(NSColor* color) : color_(color) {}
208 base::scoped_nsobject<NSColor> color_;
210 virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE {
213 virtual SkColor GetColor(int id) const OVERRIDE { return SkColor(); }
214 virtual int GetDisplayProperty(int id) const OVERRIDE {
217 virtual bool ShouldUseNativeFrame() const OVERRIDE { return false; }
218 virtual bool HasCustomImage(int id) const OVERRIDE { return false; }
219 virtual base::RefCountedMemory* GetRawData(
221 ui::ScaleFactor scale_factor) const OVERRIDE {
224 virtual NSImage* GetNSImageNamed(int id) const OVERRIDE {
227 virtual NSColor* GetNSImageColorNamed(int id) const OVERRIDE {
230 virtual NSColor* GetNSColor(int id) const OVERRIDE {
233 virtual NSColor* GetNSColorTint(int id) const OVERRIDE {
236 virtual NSGradient* GetNSGradient(int id) const OVERRIDE {
242 @interface FakeDragInfo : NSObject {
244 NSPoint dropLocation_;
245 NSDragOperation sourceMask_;
247 @property (nonatomic, assign) NSPoint dropLocation;
248 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
251 @implementation FakeDragInfo
253 @synthesize dropLocation = dropLocation_;
256 if ((self = [super init])) {
257 dropLocation_ = NSZeroPoint;
258 sourceMask_ = NSDragOperationMove;
263 // NSDraggingInfo protocol functions.
265 - (id)draggingPasteboard {
269 - (id)draggingSource {
273 - (NSDragOperation)draggingSourceOperationMask {
277 - (NSPoint)draggingLocation {
278 return dropLocation_;
283 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
292 class BookmarkBarControllerTestBase : public CocoaProfileTest {
294 base::scoped_nsobject<NSView> parent_view_;
295 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
297 virtual void SetUp() {
298 CocoaProfileTest::SetUp();
299 ASSERT_TRUE(profile());
301 base::FilePath extension_dir;
302 static_cast<extensions::TestExtensionSystem*>(
303 extensions::ExtensionSystem::Get(profile()))->
304 CreateExtensionService(
305 CommandLine::ForCurrentProcess(),
306 extension_dir, false);
307 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
308 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
309 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
310 [parent_view_ setHidden:YES];
313 void InstallAndToggleBar(BookmarkBarController* bar) {
314 // Force loading of the nib.
316 // Awkwardness to look like we've been installed.
317 for (NSView* subView in [parent_view_ subviews])
318 [subView removeFromSuperview];
319 [parent_view_ addSubview:[bar view]];
320 NSRect frame = [[[bar view] superview] frame];
321 frame.origin.y = 100;
322 [[[bar view] superview] setFrame:frame];
324 // Make sure it's on in a window so viewDidMoveToWindow is called
325 NSView* contentView = [test_window() contentView];
326 if (![parent_view_ isDescendantOf:contentView])
327 [contentView addSubview:parent_view_];
329 // Make sure it's open so certain things aren't no-ops.
330 [bar updateState:BookmarkBar::SHOW
331 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
335 class BookmarkBarControllerTest : public BookmarkBarControllerTestBase {
337 base::scoped_nsobject<NSButtonCell> cell_;
338 base::scoped_nsobject<BookmarkBarControllerNoOpen> bar_;
340 virtual void SetUp() {
341 BookmarkBarControllerTestBase::SetUp();
342 ASSERT_TRUE(browser());
343 AddCommandLineSwitches();
346 [[BookmarkBarControllerNoOpen alloc]
347 initWithBrowser:browser()
348 initialWidth:NSWidth([parent_view_ frame])
350 resizeDelegate:resizeDelegate_.get()]);
352 InstallAndToggleBar(bar_.get());
355 virtual void AddCommandLineSwitches() {}
357 BookmarkBarControllerNoOpen* noOpenBar() {
358 return (BookmarkBarControllerNoOpen*)bar_.get();
362 TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) {
363 [bar_ updateState:BookmarkBar::SHOW
364 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
365 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
366 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
367 EXPECT_TRUE([bar_ isVisible]);
368 EXPECT_FALSE([bar_ isAnimationRunning]);
369 EXPECT_FALSE([[bar_ view] isHidden]);
370 EXPECT_GT([resizeDelegate_ height], 0);
371 EXPECT_GT([[bar_ view] frame].size.height, 0);
374 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) {
375 [bar_ updateState:BookmarkBar::HIDDEN
376 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
377 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
378 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
379 EXPECT_FALSE([bar_ isVisible]);
380 EXPECT_FALSE([bar_ isAnimationRunning]);
381 EXPECT_TRUE([[bar_ view] isHidden]);
382 EXPECT_EQ(0, [resizeDelegate_ height]);
383 EXPECT_EQ(0, [[bar_ view] frame].size.height);
386 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) {
387 [bar_ setBookmarkBarEnabled:NO];
388 [bar_ updateState:BookmarkBar::SHOW
389 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
390 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
391 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
392 EXPECT_FALSE([bar_ isVisible]);
393 EXPECT_FALSE([bar_ isAnimationRunning]);
394 EXPECT_TRUE([[bar_ view] isHidden]);
395 EXPECT_EQ(0, [resizeDelegate_ height]);
396 EXPECT_EQ(0, [[bar_ view] frame].size.height);
399 TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) {
400 [bar_ updateState:BookmarkBar::DETACHED
401 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
402 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
403 EXPECT_TRUE([bar_ isInState:BookmarkBar::DETACHED]);
404 EXPECT_TRUE([bar_ isVisible]);
405 EXPECT_FALSE([bar_ isAnimationRunning]);
406 EXPECT_FALSE([[bar_ view] isHidden]);
407 EXPECT_GT([resizeDelegate_ height], 0);
408 EXPECT_GT([[bar_ view] frame].size.height, 0);
410 // Make sure no buttons fall off the bar, either now or when resized
411 // bigger or smaller.
412 CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 };
413 CGFloat previousX = 0.0;
414 for (unsigned x = 0; x < arraysize(sizes); x++) {
415 // Confirm the buttons moved from the last check (which may be
416 // init but that's fine).
417 CGFloat newX = [[bar_ offTheSideButton] frame].origin.x;
418 EXPECT_NE(previousX, newX);
421 // Confirm the buttons have a reasonable bounds. Recall that |-frame|
422 // returns rectangles in the superview's coordinates.
423 NSRect buttonViewFrame =
424 [[bar_ buttonView] convertRect:[[bar_ buttonView] frame]
425 fromView:[[bar_ buttonView] superview]];
426 EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]);
427 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
428 [[bar_ offTheSideButton] frame]));
429 EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]);
430 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
431 [[bar_ otherBookmarksButton] frame]));
433 // Now move them implicitly.
434 // We confirm FrameChangeNotification works in the next unit test;
435 // we simply assume it works here to resize or reposition the
437 NSRect frame = [[bar_ view] frame];
438 frame.size.width += sizes[x];
439 [[bar_ view] setFrame:frame];
443 // Test whether |-updateState:...| sets currentState as expected. Make
444 // sure things don't crash.
445 TEST_F(BookmarkBarControllerTest, StateChanges) {
446 // First, go in one-at-a-time cycle.
447 [bar_ updateState:BookmarkBar::HIDDEN
448 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
449 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
450 EXPECT_FALSE([bar_ isVisible]);
451 EXPECT_FALSE([bar_ isAnimationRunning]);
453 [bar_ updateState:BookmarkBar::SHOW
454 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
455 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
456 EXPECT_TRUE([bar_ isVisible]);
457 EXPECT_FALSE([bar_ isAnimationRunning]);
459 [bar_ updateState:BookmarkBar::DETACHED
460 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
461 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
462 EXPECT_TRUE([bar_ isVisible]);
463 EXPECT_FALSE([bar_ isAnimationRunning]);
465 // Now try some "jumps".
466 for (int i = 0; i < 2; i++) {
467 [bar_ updateState:BookmarkBar::HIDDEN
468 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
469 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
470 EXPECT_FALSE([bar_ isVisible]);
471 EXPECT_FALSE([bar_ isAnimationRunning]);
473 [bar_ updateState:BookmarkBar::SHOW
474 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
475 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
476 EXPECT_TRUE([bar_ isVisible]);
477 EXPECT_FALSE([bar_ isAnimationRunning]);
480 // Now try some "jumps".
481 for (int i = 0; i < 2; i++) {
482 [bar_ updateState:BookmarkBar::SHOW
483 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
484 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
485 EXPECT_TRUE([bar_ isVisible]);
486 EXPECT_FALSE([bar_ isAnimationRunning]);
488 [bar_ updateState:BookmarkBar::DETACHED
489 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
490 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
491 EXPECT_TRUE([bar_ isVisible]);
492 EXPECT_FALSE([bar_ isAnimationRunning]);
496 // Make sure we're watching for frame change notifications.
497 TEST_F(BookmarkBarControllerTest, FrameChangeNotification) {
498 base::scoped_nsobject<BookmarkBarControllerTogglePong> bar;
500 [[BookmarkBarControllerTogglePong alloc]
501 initWithBrowser:browser()
502 initialWidth:100 // arbitrary
504 resizeDelegate:resizeDelegate_.get()]);
505 InstallAndToggleBar(bar.get());
507 // Send a frame did change notification for the pong's view.
508 [[NSNotificationCenter defaultCenter]
509 postNotificationName:NSViewFrameDidChangeNotification
512 EXPECT_GT([bar toggles], 0);
515 // Confirm our "no items" container goes away when we add the 1st
516 // bookmark, and comes back when we delete the bookmark.
517 TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) {
518 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
519 const BookmarkNode* bar = model->bookmark_bar_node();
522 BookmarkBarView* view = [bar_ buttonView];
524 NSView* noItemContainer = [view noItemContainer];
525 DCHECK(noItemContainer);
527 EXPECT_FALSE([noItemContainer isHidden]);
528 const BookmarkNode* node = model->AddURL(bar, bar->child_count(),
529 ASCIIToUTF16("title"),
530 GURL("http://www.google.com"));
531 EXPECT_TRUE([noItemContainer isHidden]);
532 model->Remove(bar, bar->GetIndexOf(node));
533 EXPECT_FALSE([noItemContainer isHidden]);
535 // Now try it using a bookmark from the Other Bookmarks.
536 const BookmarkNode* otherBookmarks = model->other_node();
537 node = model->AddURL(otherBookmarks, otherBookmarks->child_count(),
538 ASCIIToUTF16("TheOther"),
539 GURL("http://www.other.com"));
540 EXPECT_FALSE([noItemContainer isHidden]);
541 // Move it from Other Bookmarks to the bar.
542 model->Move(node, bar, 0);
543 EXPECT_TRUE([noItemContainer isHidden]);
544 // Move it back to Other Bookmarks from the bar.
545 model->Move(node, otherBookmarks, 0);
546 EXPECT_FALSE([noItemContainer isHidden]);
549 // Confirm off the side button only enabled when reasonable.
550 TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
551 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
554 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
556 for (int i = 0; i < 2; i++) {
557 bookmark_utils::AddIfNotBookmarked(
558 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
559 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
562 const BookmarkNode* parent = model->bookmark_bar_node();
563 for (int i = 0; i < 20; i++) {
564 model->AddURL(parent, parent->child_count(),
565 ASCIIToUTF16("super duper wide title"),
566 GURL("http://superfriends.hall-of-justice.edu"));
568 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
570 // Open the "off the side" and start deleting nodes. Make sure
571 // deletion of the last node in "off the side" causes the folder to
573 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
574 NSButton* offTheSideButton = [bar_ offTheSideButton];
575 // Open "off the side" menu.
576 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
577 BookmarkBarFolderController* bbfc = [bar_ folderController];
579 [bbfc setIgnoreAnimations:YES];
580 while (!parent->empty()) {
581 // We've completed the job so we're done.
582 if ([bar_ offTheSideButtonIsHidden])
584 // Delete the last button.
585 model->Remove(parent, parent->child_count() - 1);
586 // If last one make sure the menu is closed and the button is hidden.
587 // Else make sure menu stays open.
588 if ([bar_ offTheSideButtonIsHidden]) {
589 EXPECT_FALSE([bar_ folderController]);
591 EXPECT_TRUE([bar_ folderController]);
596 // http://crbug.com/46175 is a crash when deleting bookmarks from the
597 // off-the-side menu while it is open. This test tries to bang hard
598 // in this area to reproduce the crash.
599 TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) {
600 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
603 // Add a lot of bookmarks (per the bug).
604 const BookmarkNode* parent = model->bookmark_bar_node();
605 for (int i = 0; i < 100; i++) {
606 std::ostringstream title;
607 title << "super duper wide title " << i;
608 model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()),
609 GURL("http://superfriends.hall-of-justice.edu"));
611 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
613 // Open "off the side" menu.
614 NSButton* offTheSideButton = [bar_ offTheSideButton];
615 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
616 BookmarkBarFolderController* bbfc = [bar_ folderController];
618 [bbfc setIgnoreAnimations:YES];
620 // Start deleting items; try and delete randomish ones in case it
621 // makes a difference.
622 int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 };
623 while (!parent->empty()) {
624 for (unsigned int i = 0; i < arraysize(indices); i++) {
625 if (indices[i] < parent->child_count()) {
626 // First we mouse-enter the button to make things harder.
627 NSArray* buttons = [bbfc buttons];
628 for (BookmarkButton* button in buttons) {
629 if ([button bookmarkNode] == parent->GetChild(indices[i])) {
630 [bbfc mouseEnteredButton:button event:nil];
634 // Then we remove the node. This triggers the button to get
636 model->Remove(parent, indices[i]);
637 // Force visual update which is otherwise delayed.
638 [[bbfc window] displayIfNeeded];
644 // Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is
646 TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) {
647 [bar_ updateState:BookmarkBar::HIDDEN
648 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
649 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
651 [bar_ updateState:BookmarkBar::SHOW
652 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
653 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
655 [bar_ updateState:BookmarkBar::DETACHED
656 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
657 EXPECT_FALSE([bar_ dragShouldLockBarVisibility]);
660 TEST_F(BookmarkBarControllerTest, TagMap) {
661 int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 };
662 std::vector<int32> tags;
664 // Generate some tags
665 for (unsigned int i = 0; i < arraysize(ids); i++) {
666 tags.push_back([bar_ menuTagFromNodeId:ids[i]]);
669 // Confirm reverse mapping.
670 for (unsigned int i = 0; i < arraysize(ids); i++) {
671 EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]);
674 // Confirm uniqueness.
675 std::sort(tags.begin(), tags.end());
676 for (unsigned int i=0; i<(tags.size()-1); i++) {
677 EXPECT_NE(tags[i], tags[i+1]);
681 TEST_F(BookmarkBarControllerTest, MenuForFolderNode) {
682 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
684 // First make sure something (e.g. "(empty)" string) is always present.
685 NSMenu* menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
686 EXPECT_GT([menu numberOfItems], 0);
688 // Test two bookmarks.
689 GURL gurl("http://www.foo.com");
690 bookmark_utils::AddIfNotBookmarked(model, gurl, ASCIIToUTF16("small"));
691 bookmark_utils::AddIfNotBookmarked(
692 model, GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title"));
693 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
694 EXPECT_EQ([menu numberOfItems], 2);
695 NSMenuItem *item = [menu itemWithTitle:@"bigger title"];
697 item = [menu itemWithTitle:@"small"];
700 int64 tag = [bar_ nodeIdFromMenuTag:[item tag]];
701 const BookmarkNode* node = model->GetNodeByID(tag);
703 EXPECT_EQ(gurl, node->url());
706 // Test with an actual folder as well
707 const BookmarkNode* parent = model->bookmark_bar_node();
708 const BookmarkNode* folder = model->AddFolder(parent,
709 parent->child_count(),
710 ASCIIToUTF16("folder"));
711 model->AddURL(folder, folder->child_count(),
712 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
713 model->AddURL(folder, folder->child_count(),
714 ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com"));
715 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
716 EXPECT_EQ([menu numberOfItems], 3);
718 item = [menu itemWithTitle:@"folder"];
720 EXPECT_TRUE([item hasSubmenu]);
721 NSMenu *submenu = [item submenu];
722 EXPECT_TRUE(submenu);
723 EXPECT_EQ(2, [submenu numberOfItems]);
724 EXPECT_TRUE([submenu itemWithTitle:@"f1"]);
725 EXPECT_TRUE([submenu itemWithTitle:@"f2"]);
728 // Confirm openBookmark: forwards the request to the controller's delegate
729 TEST_F(BookmarkBarControllerTest, OpenBookmark) {
730 GURL gurl("http://walla.walla.ding.dong.com");
731 scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl));
733 base::scoped_nsobject<BookmarkButtonCell> cell(
734 [[BookmarkButtonCell alloc] init]);
735 [cell setBookmarkNode:node.get()];
736 base::scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]);
737 [button setCell:cell.get()];
738 [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]];
740 [bar_ openBookmark:button];
741 EXPECT_EQ(noOpenBar()->urls_[0], node->url());
742 EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB);
745 TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
746 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
747 NSView* buttonView = [bar_ buttonView];
748 EXPECT_EQ(0U, [[bar_ buttons] count]);
749 unsigned int initial_subview_count = [[buttonView subviews] count];
751 // Make sure a redundant call doesn't choke
752 [bar_ clearBookmarkBar];
753 EXPECT_EQ(0U, [[bar_ buttons] count]);
754 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
756 GURL gurl1("http://superfriends.hall-of-justice.edu");
757 // Short titles increase the chances of this test succeeding if the view is
759 // TODO(viettrungluu): make the test independent of window/view size, font
760 // metrics, button size and spacing, and everything else.
761 string16 title1(ASCIIToUTF16("x"));
762 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
763 EXPECT_EQ(1U, [[bar_ buttons] count]);
764 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
766 GURL gurl2("http://legion-of-doom.gov");
767 string16 title2(ASCIIToUTF16("y"));
768 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
769 EXPECT_EQ(2U, [[bar_ buttons] count]);
770 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
772 for (int i = 0; i < 3; i++) {
773 bookmark_utils::RemoveAllBookmarks(model, gurl2);
774 EXPECT_EQ(1U, [[bar_ buttons] count]);
775 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
778 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
779 EXPECT_EQ(2U, [[bar_ buttons] count]);
780 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
783 [bar_ clearBookmarkBar];
784 EXPECT_EQ(0U, [[bar_ buttons] count]);
785 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
787 // Explicit test of loaded: since this is a convenient spot
789 EXPECT_EQ(2U, [[bar_ buttons] count]);
790 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
793 // Make sure we don't create too many buttons; we only really need
794 // ones that will be visible.
795 TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
796 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
797 EXPECT_EQ(0U, [[bar_ buttons] count]);
798 // Add one; make sure we see it.
799 const BookmarkNode* parent = model->bookmark_bar_node();
800 model->AddURL(parent, parent->child_count(),
801 ASCIIToUTF16("title"), GURL("http://www.google.com"));
802 EXPECT_EQ(1U, [[bar_ buttons] count]);
804 // Add 30 which we expect to be 'too many'. Make sure we don't see
806 model->Remove(parent, 0);
807 EXPECT_EQ(0U, [[bar_ buttons] count]);
808 for (int i=0; i<30; i++) {
809 model->AddURL(parent, parent->child_count(),
810 ASCIIToUTF16("title"), GURL("http://www.google.com"));
812 int count = [[bar_ buttons] count];
813 EXPECT_LT(count, 30L);
815 // Add 10 more (to the front of the list so the on-screen buttons
816 // would change) and make sure the count stays the same.
817 for (int i=0; i<10; i++) {
818 model->AddURL(parent, 0, /* index is 0, so front, not end */
819 ASCIIToUTF16("title"), GURL("http://www.google.com"));
822 // Finally, grow the view and make sure the button count goes up.
823 NSRect frame = [[bar_ view] frame];
824 frame.size.width += 600;
825 [[bar_ view] setFrame:frame];
826 int finalcount = [[bar_ buttons] count];
827 EXPECT_GT(finalcount, count);
830 // Make sure that each button we add marches to the right and does not
831 // overlap with the previous one.
832 TEST_F(BookmarkBarControllerTest, TestButtonMarch) {
833 base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]);
835 CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 };
836 for (unsigned int i = 0; i < arraysize(widths); i++) {
837 NSCell* cell = [[CellWithDesiredSize alloc]
839 desiredSize:NSMakeSize(widths[i], 30)];
840 [cells addObject:cell];
845 CGFloat x_end = x_offset; // end of the previous button
846 for (unsigned int i = 0; i < arraysize(widths); i++) {
847 NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i]
849 EXPECT_GE(r.origin.x, x_end);
854 TEST_F(BookmarkBarControllerTest, CheckForGrowth) {
855 WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope.
856 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
857 GURL gurl1("http://www.google.com");
858 string16 title1(ASCIIToUTF16("x"));
859 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
861 GURL gurl2("http://www.google.com/blah");
862 string16 title2(ASCIIToUTF16("y"));
863 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
865 EXPECT_EQ(2U, [[bar_ buttons] count]);
866 CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width;
867 CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x;
869 NSButton* first = [[bar_ buttons] objectAtIndex:0];
870 [[first cell] setTitle:@"This is a really big title; watch out mom!"];
871 [bar_ checkForBookmarkButtonGrowth:first];
873 // Make sure the 1st button is now wider, the 2nd one is moved over,
874 // and they don't overlap.
875 NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame];
876 NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame];
877 EXPECT_GT(frame_1.size.width, width_1);
878 EXPECT_GT(frame_2.origin.x, x_2);
879 EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width);
882 TEST_F(BookmarkBarControllerTest, DeleteBookmark) {
883 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
885 const char* urls[] = { "https://secret.url.com",
886 "http://super.duper.web.site.for.doodz.gov",
887 "http://www.foo-bar-baz.com/" };
888 const BookmarkNode* parent = model->bookmark_bar_node();
889 for (unsigned int i = 0; i < arraysize(urls); i++) {
890 model->AddURL(parent, parent->child_count(),
891 ASCIIToUTF16("title"), GURL(urls[i]));
893 EXPECT_EQ(3, parent->child_count());
894 const BookmarkNode* middle_node = parent->GetChild(1);
895 model->Remove(middle_node->parent(),
896 middle_node->parent()->GetIndexOf(middle_node));
898 EXPECT_EQ(2, parent->child_count());
899 EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0]));
900 // node 2 moved into spot 1
901 EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2]));
904 // TODO(jrg): write a test to confirm that nodeFaviconLoaded calls
905 // checkForBookmarkButtonGrowth:.
907 TEST_F(BookmarkBarControllerTest, Cell) {
908 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
911 const BookmarkNode* parent = model->bookmark_bar_node();
912 model->AddURL(parent, parent->child_count(),
913 ASCIIToUTF16("supertitle"),
914 GURL("http://superfriends.hall-of-justice.edu"));
915 const BookmarkNode* node = parent->GetChild(0);
917 NSCell* cell = [bar_ cellForBookmarkNode:node];
919 EXPECT_NSEQ(@"supertitle", [cell title]);
920 EXPECT_EQ(node, [[cell representedObject] pointerValue]);
921 EXPECT_TRUE([cell menu]);
923 // Empty cells still have a menu.
924 cell = [bar_ cellForBookmarkNode:nil];
925 EXPECT_TRUE([cell menu]);
926 // Even empty cells have a title (of "(empty)")
927 EXPECT_TRUE([cell title]);
929 // cell is autoreleased; no need to release here
932 // Test drawing, mostly to ensure nothing leaks or crashes.
933 TEST_F(BookmarkBarControllerTest, Display) {
934 [[bar_ view] display];
937 // Test that middle clicking on a bookmark button results in an open action.
938 TEST_F(BookmarkBarControllerTest, MiddleClick) {
939 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
940 GURL gurl1("http://www.google.com/");
941 string16 title1(ASCIIToUTF16("x"));
942 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
944 EXPECT_EQ(1U, [[bar_ buttons] count]);
945 NSButton* first = [[bar_ buttons] objectAtIndex:0];
949 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
950 EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
953 TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) {
954 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
956 EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]);
959 TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) {
960 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
962 const BookmarkNode* parent = model->bookmark_bar_node();
963 model->AddURL(parent, parent->child_count(),
964 ASCIIToUTF16("title"), GURL("http://one.com"));
967 EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]);
970 TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) {
971 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
973 const BookmarkNode* parent = model->bookmark_bar_node();
974 model->AddURL(parent, parent->child_count(),
975 ASCIIToUTF16("title"), GURL("http://one.com"));
979 // Make sure the internal bookmark button also is the correct height.
980 NSArray* buttons = [bar_ buttons];
981 EXPECT_GT([buttons count], 0u);
982 for (NSButton* button in buttons) {
984 (chrome::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) -
985 2 * bookmarks::kBookmarkVerticalPadding,
986 [button frame].size.height);
990 TEST_F(BookmarkBarControllerTest, DropBookmarks) {
991 const char* urls[] = {
994 "javascript:alert('lolwut')",
995 "file://localhost/tmp/local-file.txt" // As if dragged from the desktop.
997 const char* titles[] = {
1003 EXPECT_EQ(arraysize(urls), arraysize(titles));
1005 NSMutableArray* nsurls = [NSMutableArray array];
1006 NSMutableArray* nstitles = [NSMutableArray array];
1007 for (size_t i = 0; i < arraysize(urls); ++i) {
1008 [nsurls addObject:base::SysUTF8ToNSString(urls[i])];
1009 [nstitles addObject:base::SysUTF8ToNSString(titles[i])];
1012 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1013 const BookmarkNode* parent = model->bookmark_bar_node();
1014 [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint];
1015 EXPECT_EQ(4, parent->child_count());
1016 for (int i = 0; i < parent->child_count(); ++i) {
1017 GURL gurl = parent->GetChild(i)->url();
1018 if (gurl.scheme() == "http" ||
1019 gurl.scheme() == "javascript") {
1020 EXPECT_EQ(parent->GetChild(i)->url(), GURL(urls[i]));
1022 // Be flexible if the scheme needed to be added.
1023 std::string gurl_string = gurl.spec();
1024 std::string my_string = parent->GetChild(i)->url().spec();
1025 EXPECT_NE(gurl_string.find(my_string), std::string::npos);
1027 EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i]));
1031 TEST_F(BookmarkBarControllerTest, TestDragButton) {
1032 WithNoAnimation at_all;
1033 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1035 GURL gurls[] = { GURL("http://www.google.com/a"),
1036 GURL("http://www.google.com/b"),
1037 GURL("http://www.google.com/c") };
1038 string16 titles[] = { ASCIIToUTF16("a"),
1040 ASCIIToUTF16("c") };
1041 for (unsigned i = 0; i < arraysize(titles); i++)
1042 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
1044 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1045 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1047 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1050 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1051 // Make sure a 'copy' did not happen.
1052 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1054 [bar_ dragButton:[[bar_ buttons] objectAtIndex:1]
1055 to:NSMakePoint(1000, 0)
1057 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1058 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1059 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1060 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1062 // A drop of the 1st between the next 2.
1063 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1064 x += [[bar_ view] frame].origin.x;
1065 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1066 to:NSMakePoint(x, 0)
1068 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]);
1069 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]);
1070 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1071 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1073 // A drop on a non-folder button. (Shouldn't try and go in it.)
1074 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1075 x += [[bar_ view] frame].origin.x;
1076 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1077 to:NSMakePoint(x, 0)
1079 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1081 // A drop on a folder button.
1082 const BookmarkNode* folder = model->AddFolder(
1083 model->bookmark_bar_node(), 0, ASCIIToUTF16("awesome folder"));
1085 model->AddURL(folder, 0, ASCIIToUTF16("already"),
1086 GURL("http://www.google.com"));
1087 EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]);
1088 EXPECT_EQ(1, folder->child_count());
1089 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1090 x += [[bar_ view] frame].origin.x;
1091 string16 title = [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle();
1092 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1093 to:NSMakePoint(x, 0)
1095 // Gone from the bar
1096 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1098 EXPECT_EQ(2, folder->child_count());
1100 EXPECT_EQ(title, folder->GetChild(1)->GetTitle());
1103 TEST_F(BookmarkBarControllerTest, TestCopyButton) {
1104 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1106 GURL gurls[] = { GURL("http://www.google.com/a"),
1107 GURL("http://www.google.com/b"),
1108 GURL("http://www.google.com/c") };
1109 string16 titles[] = { ASCIIToUTF16("a"),
1111 ASCIIToUTF16("c") };
1112 for (unsigned i = 0; i < arraysize(titles); i++)
1113 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
1115 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1116 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1118 // Drag 'a' between 'b' and 'c'.
1119 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1120 x += [[bar_ view] frame].origin.x;
1121 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1122 to:NSMakePoint(x, 0)
1124 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1125 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1126 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1127 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]);
1128 EXPECT_EQ([[bar_ buttons] count], 4U);
1131 // Fake a theme with colored text. Apply it and make sure bookmark
1132 // buttons have the same colored text. Repeat more than once.
1133 TEST_F(BookmarkBarControllerTest, TestThemedButton) {
1134 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1135 bookmark_utils::AddIfNotBookmarked(
1136 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
1137 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1138 EXPECT_TRUE(button);
1140 NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor],
1141 [NSColor blueColor],
1143 for (NSColor* color in colors) {
1144 FakeTheme theme(color);
1145 [bar_ updateTheme:&theme];
1146 NSAttributedString* astr = [button attributedTitle];
1148 EXPECT_NSEQ(@"small", [astr string]);
1149 // Pick a char in the middle to test (index 3)
1150 NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL];
1152 [attributes objectForKey:NSForegroundColorAttributeName];
1153 EXPECT_NSEQ(newColor, color);
1157 // Test that delegates and targets of buttons are cleared on dealloc.
1158 TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) {
1159 // Make some bookmark buttons.
1160 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1161 GURL gurls[] = { GURL("http://www.foo.com/"),
1162 GURL("http://www.bar.com/"),
1163 GURL("http://www.baz.com/") };
1164 string16 titles[] = { ASCIIToUTF16("a"),
1166 ASCIIToUTF16("c") };
1167 for (size_t i = 0; i < arraysize(titles); i++)
1168 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
1170 // Get and retain the buttons so we can examine them after dealloc.
1171 base::scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]);
1172 EXPECT_EQ([buttons count], arraysize(titles));
1174 // Make sure that everything is set.
1175 for (BookmarkButton* button in buttons.get()) {
1176 ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]);
1177 EXPECT_TRUE([button delegate]);
1178 EXPECT_TRUE([button target]);
1179 EXPECT_TRUE([button action]);
1182 // This will dealloc....
1185 // Make sure that everything is cleared.
1186 for (BookmarkButton* button in buttons.get()) {
1187 EXPECT_FALSE([button delegate]);
1188 EXPECT_FALSE([button target]);
1189 EXPECT_FALSE([button action]);
1193 TEST_F(BookmarkBarControllerTest, TestFolders) {
1194 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1196 // Create some folder buttons.
1197 const BookmarkNode* parent = model->bookmark_bar_node();
1198 const BookmarkNode* folder = model->AddFolder(parent,
1199 parent->child_count(),
1200 ASCIIToUTF16("folder"));
1201 model->AddURL(folder, folder->child_count(),
1202 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
1203 folder = model->AddFolder(parent, parent->child_count(),
1204 ASCIIToUTF16("empty"));
1206 EXPECT_EQ([[bar_ buttons] count], 2U);
1208 // First confirm mouseEntered does nothing if "menus" aren't active.
1210 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0);
1211 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1212 EXPECT_FALSE([bar_ folderController]);
1214 // Make one active. Entering it is now a no-op.
1215 [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]];
1216 BookmarkBarFolderController* bbfc = [bar_ folderController];
1218 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1219 EXPECT_EQ(bbfc, [bar_ folderController]);
1221 // Enter a different one; a new folderController is active.
1222 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event];
1223 EXPECT_NE(bbfc, [bar_ folderController]);
1225 // Confirm exited is a no-op.
1226 [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event];
1227 EXPECT_NE(bbfc, [bar_ folderController]);
1230 [bar_ closeBookmarkFolder:nil];
1233 // Verify that the folder menu presentation properly tracks mouse movements
1234 // over the bar. Until there is a click no folder menus should show. After a
1235 // click on a folder folder menus should show until another click on a folder
1236 // button, and a click outside the bar and its folder menus.
1237 TEST_F(BookmarkBarControllerTest, TestFolderButtons) {
1238 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1239 const BookmarkNode* root = model->bookmark_bar_node();
1240 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] ");
1241 test::AddNodesFromModelString(model, root, model_string);
1243 // Validate initial model and that we do not have a folder controller.
1244 std::string actualModelString = test::ModelStringFromNode(root);
1245 EXPECT_EQ(model_string, actualModelString);
1246 EXPECT_FALSE([bar_ folderController]);
1248 // Add a real bookmark so we can click on it.
1249 const BookmarkNode* folder = root->GetChild(3);
1250 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("CLICK ME"),
1251 GURL("http://www.google.com/"));
1253 // Click on a folder button.
1254 BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"];
1255 EXPECT_TRUE(button);
1256 [bar_ openBookmarkFolderFromButton:button];
1257 BookmarkBarFolderController* bbfc = [bar_ folderController];
1260 // Make sure a 2nd click on the same button closes things.
1261 [bar_ openBookmarkFolderFromButton:button];
1262 EXPECT_FALSE([bar_ folderController]);
1264 // Next open is a different button.
1265 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1266 EXPECT_TRUE(button);
1267 [bar_ openBookmarkFolderFromButton:button];
1268 EXPECT_TRUE([bar_ folderController]);
1270 // Mouse over a non-folder button and confirm controller has gone away.
1271 button = [bar_ buttonWithTitleEqualTo:@"1b"];
1272 EXPECT_TRUE(button);
1273 NSEvent* event = cocoa_test_event_utils::MouseEventAtPoint([button center],
1275 [bar_ mouseEnteredButton:button event:event];
1276 EXPECT_FALSE([bar_ folderController]);
1278 // Mouse over the original folder and confirm a new controller.
1279 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1280 EXPECT_TRUE(button);
1281 [bar_ mouseEnteredButton:button event:event];
1282 BookmarkBarFolderController* oldBBFC = [bar_ folderController];
1283 EXPECT_TRUE(oldBBFC);
1285 // 'Jump' over to a different folder and confirm a new controller.
1286 button = [bar_ buttonWithTitleEqualTo:@"4f"];
1287 EXPECT_TRUE(button);
1288 [bar_ mouseEnteredButton:button event:event];
1289 BookmarkBarFolderController* newBBFC = [bar_ folderController];
1290 EXPECT_TRUE(newBBFC);
1291 EXPECT_NE(oldBBFC, newBBFC);
1294 // Make sure the "off the side" folder looks like a bookmark folder
1295 // but only contains "off the side" items.
1296 TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
1298 // It starts hidden.
1299 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
1301 // Create some buttons.
1302 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1303 const BookmarkNode* parent = model->bookmark_bar_node();
1304 for (int x = 0; x < 30; x++) {
1305 model->AddURL(parent, parent->child_count(),
1306 ASCIIToUTF16("medium-size-title"),
1307 GURL("http://framma-lamma.com"));
1309 // Add a couple more so we can delete one and make sure its button goes away.
1310 model->AddURL(parent, parent->child_count(),
1311 ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com"));
1312 model->AddURL(parent, parent->child_count(),
1313 ASCIIToUTF16("medium-size-title"),
1314 GURL("http://framma-lamma.com"));
1316 // Should no longer be hidden.
1317 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
1319 // Open it; make sure we have a folder controller.
1320 EXPECT_FALSE([bar_ folderController]);
1321 [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]];
1322 BookmarkBarFolderController* bbfc = [bar_ folderController];
1325 // Confirm the contents are only buttons which fell off the side by
1326 // making sure that none of the nodes in the off-the-side folder are
1327 // found in bar buttons. Be careful since not all the bar buttons
1328 // may be currently displayed.
1329 NSArray* folderButtons = [bbfc buttons];
1330 NSArray* barButtons = [bar_ buttons];
1331 for (BookmarkButton* folderButton in folderButtons) {
1332 for (BookmarkButton* barButton in barButtons) {
1333 if ([barButton superview]) {
1334 EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]);
1339 // Delete a bookmark in the off-the-side and verify it's gone.
1340 BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1341 EXPECT_TRUE(button);
1342 model->Remove(parent, parent->child_count() - 2);
1343 button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1344 EXPECT_FALSE(button);
1347 TEST_F(BookmarkBarControllerTest, EventToExitCheck) {
1348 NSEvent* event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0);
1349 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1351 BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc]
1353 [[[bar_ view] window] addChildWindow:folderWindow
1354 ordered:NSWindowAbove];
1355 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1),
1357 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1359 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
1360 NSMakePoint(100,100), test_window());
1361 EXPECT_TRUE([bar_ isEventAnExitEvent:event]);
1363 // Many components are arbitrary (e.g. location, keycode).
1364 event = [NSEvent keyEventWithType:NSKeyDown
1365 location:NSMakePoint(1,1)
1371 charactersIgnoringModifiers:@"x"
1374 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1376 [[[bar_ view] window] removeChildWindow:folderWindow];
1379 TEST_F(BookmarkBarControllerTest, DropDestination) {
1380 // Make some buttons.
1381 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1382 const BookmarkNode* parent = model->bookmark_bar_node();
1383 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 1"));
1384 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 2"));
1385 EXPECT_EQ([[bar_ buttons] count], 2U);
1387 // Confirm "off to left" and "off to right" match nothing.
1388 NSPoint p = NSMakePoint(-1, 2);
1389 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1390 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1391 p = NSMakePoint(50000, 10);
1392 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1393 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1395 // Confirm "right in the center" (give or take a pixel) is a match,
1396 // and confirm "just barely in the button" is not. Anything more
1397 // specific seems likely to be tweaked.
1398 CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x;
1399 for (BookmarkButton* button in [bar_ buttons]) {
1400 CGFloat x = NSMidX([button frame]) + viewFrameXOffset;
1401 // Somewhere near the center: a match
1403 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]);
1405 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]);
1406 EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);;
1408 // On the very edges: NOT a match
1409 x = NSMinX([button frame]) + viewFrameXOffset;
1411 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]);
1412 x = NSMaxX([button frame]) + viewFrameXOffset;
1414 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]);
1418 TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) {
1419 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1420 [bar_ setStateAnimationsEnabled:YES];
1421 const BookmarkNode* parent = model->bookmark_bar_node();
1422 const BookmarkNode* folder = model->AddFolder(parent,
1423 parent->child_count(),
1424 ASCIIToUTF16("folder"));
1425 model->AddFolder(parent, parent->child_count(),
1426 ASCIIToUTF16("sibbling folder"));
1427 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("title a"),
1428 GURL("http://www.google.com/a"));
1429 model->AddURL(folder, folder->child_count(),
1430 ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
1431 GURL("http://www.google.com/b"));
1432 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1433 EXPECT_FALSE([bar_ folderController]);
1434 [bar_ openBookmarkFolderFromButton:button];
1435 BookmarkBarFolderController* bbfc = [bar_ folderController];
1436 // The following tells us that the folder menu is showing. We want to make
1437 // sure the folder menu goes away if the bookmark bar is hidden.
1439 EXPECT_TRUE([bar_ isVisible]);
1441 // Hide the bookmark bar.
1442 [bar_ updateState:BookmarkBar::DETACHED
1443 changeType:BookmarkBar::ANIMATE_STATE_CHANGE];
1444 EXPECT_TRUE([bar_ isAnimationRunning]);
1446 // Now that we've closed the bookmark bar (with animation) the folder menu
1447 // should have been closed thus releasing the folderController.
1448 EXPECT_FALSE([bar_ folderController]);
1451 TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) {
1452 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1453 const BookmarkNode* root = model->bookmark_bar_node();
1454 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1455 test::AddNodesFromModelString(model, root, model_string);
1457 // Validate initial model.
1458 std::string actualModelString = test::ModelStringFromNode(root);
1459 EXPECT_EQ(model_string, actualModelString);
1461 // Remember how many buttons are showing.
1462 int oldDisplayedButtons = [bar_ displayedButtonCount];
1463 NSArray* buttons = [bar_ buttons];
1465 // Move a button around a bit.
1466 [bar_ moveButtonFromIndex:0 toIndex:2];
1467 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]);
1468 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]);
1469 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]);
1470 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1471 [bar_ moveButtonFromIndex:2 toIndex:0];
1472 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]);
1473 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1474 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1475 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1477 // Add a couple of buttons.
1478 const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node.
1479 const BookmarkNode* node = parent->GetChild(0);
1480 [bar_ addButtonForNode:node atIndex:0];
1481 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1482 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1483 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1484 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1485 EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]);
1486 node = parent->GetChild(1);
1487 [bar_ addButtonForNode:node atIndex:-1];
1488 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1489 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1490 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1491 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1492 EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]);
1493 EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]);
1495 // Remove a couple of buttons.
1496 [bar_ removeButton:4 animate:NO];
1497 [bar_ removeButton:1 animate:NO];
1498 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1499 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1500 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1501 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1504 TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) {
1505 NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0);
1506 NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease];
1507 EXPECT_FALSE([view isHidden]);
1508 [bar_ shrinkOrHideView:view forMaxX:500.0];
1509 EXPECT_EQ(500.0, NSWidth([view frame]));
1510 EXPECT_FALSE([view isHidden]);
1511 [bar_ shrinkOrHideView:view forMaxX:450.0];
1512 EXPECT_EQ(450.0, NSWidth([view frame]));
1513 EXPECT_FALSE([view isHidden]);
1514 [bar_ shrinkOrHideView:view forMaxX:40.0];
1515 EXPECT_EQ(40.0, NSWidth([view frame]));
1516 EXPECT_FALSE([view isHidden]);
1517 [bar_ shrinkOrHideView:view forMaxX:31.0];
1518 EXPECT_EQ(31.0, NSWidth([view frame]));
1519 EXPECT_FALSE([view isHidden]);
1520 [bar_ shrinkOrHideView:view forMaxX:29.0];
1521 EXPECT_TRUE([view isHidden]);
1524 TEST_F(BookmarkBarControllerTest, LastBookmarkResizeBehavior) {
1525 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1526 const BookmarkNode* root = model->bookmark_bar_node();
1527 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1528 test::AddNodesFromModelString(model, root, model_string);
1529 [bar_ frameDidChange];
1531 CGFloat viewWidths[] = { 123.0, 124.0, 151.0, 152.0, 153.0, 154.0, 155.0,
1532 200.0, 155.0, 154.0, 153.0, 152.0, 151.0, 124.0,
1534 BOOL offTheSideButtonIsHiddenResults[] = { NO, NO, NO, NO, YES, YES, YES, YES,
1535 YES, YES, YES, NO, NO, NO, NO};
1536 int displayedButtonCountResults[] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2,
1539 for (unsigned int i = 0; i < sizeof(viewWidths) / sizeof(viewWidths[0]);
1541 NSRect frame = [[bar_ view] frame];
1542 frame.size.width = viewWidths[i] + bookmarks::kBookmarkRightMargin;
1543 [[bar_ view] setFrame:frame];
1544 EXPECT_EQ(offTheSideButtonIsHiddenResults[i],
1545 [bar_ offTheSideButtonIsHidden]);
1546 EXPECT_EQ(displayedButtonCountResults[i], [bar_ displayedButtonCount]);
1550 class BookmarkBarControllerWithInstantExtendedTest :
1551 public BookmarkBarControllerTest {
1553 virtual void AddCommandLineSwitches() OVERRIDE {
1554 CommandLine::ForCurrentProcess()->AppendSwitch(
1555 switches::kEnableInstantExtendedAPI);
1559 TEST_F(BookmarkBarControllerWithInstantExtendedTest,
1560 BookmarksWithAppsPageShortcut) {
1561 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1562 const BookmarkNode* root = model->bookmark_bar_node();
1563 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1564 test::AddNodesFromModelString(model, root, model_string);
1565 [bar_ frameDidChange];
1567 // Apps page shortcut button should be visible.
1568 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1570 // Bookmarks should be to the right of the Apps page shortcut button.
1571 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1572 CGFloat right = apps_button_right;
1573 NSArray* buttons = [bar_ buttons];
1574 for (size_t i = 0; i < [buttons count]; ++i) {
1575 EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame]));
1576 right = NSMaxX([[buttons objectAtIndex:i] frame]);
1579 // Removing the Apps button should move every bookmark to the left.
1580 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
1582 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1583 EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame]));
1584 for (size_t i = 1; i < [buttons count]; ++i) {
1585 EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]),
1586 NSMinX([[buttons objectAtIndex:i] frame]));
1590 TEST_F(BookmarkBarControllerWithInstantExtendedTest,
1591 BookmarksWithoutAppsPageShortcut) {
1592 // The no item containers should be to the right of the Apps button.
1593 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1594 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1595 EXPECT_LE(apps_button_right,
1596 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1597 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1598 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1600 // Removing the Apps button should move the no item containers to the left.
1601 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
1603 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1604 EXPECT_GT(apps_button_right,
1605 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1606 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1607 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1610 class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest {
1612 virtual void SetUp() {
1613 BookmarkBarControllerTest::SetUp();
1614 ASSERT_TRUE(profile());
1616 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1617 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1619 [[BookmarkBarControllerOpenAllPong alloc]
1620 initWithBrowser:browser()
1621 initialWidth:NSWidth(parent_frame)
1623 resizeDelegate:resizeDelegate_.get()]);
1625 // Awkwardness to look like we've been installed.
1626 [parent_view_ addSubview:[bar_ view]];
1627 NSRect frame = [[[bar_ view] superview] frame];
1628 frame.origin.y = 100;
1629 [[[bar_ view] superview] setFrame:frame];
1631 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1632 parent_ = model->bookmark_bar_node();
1633 // { one, { two-one, two-two }, three }
1634 model->AddURL(parent_, parent_->child_count(), ASCIIToUTF16("title"),
1635 GURL("http://one.com"));
1636 folder_ = model->AddFolder(parent_, parent_->child_count(),
1637 ASCIIToUTF16("folder"));
1638 model->AddURL(folder_, folder_->child_count(),
1639 ASCIIToUTF16("title"), GURL("http://two-one.com"));
1640 model->AddURL(folder_, folder_->child_count(),
1641 ASCIIToUTF16("title"), GURL("http://two-two.com"));
1642 model->AddURL(parent_, parent_->child_count(),
1643 ASCIIToUTF16("title"), GURL("https://three.com"));
1645 const BookmarkNode* parent_; // Weak
1646 const BookmarkNode* folder_; // Weak
1649 // Command-click on a folder should open all the bookmarks in it.
1650 TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) {
1651 NSButton* first = [[bar_ buttons] objectAtIndex:0];
1654 // Create the right kind of event; mock NSApp so [NSApp
1655 // currentEvent] finds it.
1656 NSEvent* commandClick =
1657 cocoa_test_event_utils::MouseEventAtPoint(NSZeroPoint,
1660 id fakeApp = [OCMockObject partialMockForObject:NSApp];
1661 [[[fakeApp stub] andReturn:commandClick] currentEvent];
1664 size_t originalDispositionCount = noOpenBar()->dispositions_.size();
1667 [first performClick:first];
1669 size_t dispositionCount = noOpenBar()->dispositions_.size();
1670 EXPECT_EQ(originalDispositionCount+1, dispositionCount);
1671 EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB);
1677 class BookmarkBarControllerNotificationTest : public CocoaProfileTest {
1679 virtual void SetUp() {
1680 CocoaProfileTest::SetUp();
1681 ASSERT_TRUE(browser());
1683 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1684 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1685 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
1686 [parent_view_ setHidden:YES];
1688 [[BookmarkBarControllerNotificationPong alloc]
1689 initWithBrowser:browser()
1690 initialWidth:NSWidth(parent_frame)
1692 resizeDelegate:resizeDelegate_.get()]);
1694 // Force loading of the nib.
1696 // Awkwardness to look like we've been installed.
1697 [parent_view_ addSubview:[bar_ view]];
1698 NSRect frame = [[[bar_ view] superview] frame];
1699 frame.origin.y = 100;
1700 [[[bar_ view] superview] setFrame:frame];
1702 // Do not add the bar to a window, yet.
1705 base::scoped_nsobject<NSView> parent_view_;
1706 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
1707 base::scoped_nsobject<BookmarkBarControllerNotificationPong> bar_;
1710 TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) {
1711 NSWindow* window = [[CocoaTestHelperWindow alloc] init];
1712 [window setReleasedWhenClosed:YES];
1714 // First add the bookmark bar to the temp window, then to another window.
1715 [[window contentView] addSubview:parent_view_];
1716 [[test_window() contentView] addSubview:parent_view_];
1718 // Post a fake windowDidResignKey notification for the temp window and make
1719 // sure the bookmark bar controller wasn't listening.
1720 [[NSNotificationCenter defaultCenter]
1721 postNotificationName:NSWindowDidResignKeyNotification
1723 EXPECT_FALSE([bar_ windowDidResignKeyReceived]);
1725 // Close the temp window and make sure no notification was received.
1727 EXPECT_FALSE([bar_ windowWillCloseReceived]);
1731 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1732 // they are hard to test. Factor out "fire timers" into routines
1733 // which can be overridden to fire immediately to make behavior
1736 // TODO(jrg): add unit test to make sure "Other Bookmarks" responds
1737 // properly to a hover open.
1739 // TODO(viettrungluu): figure out how to test animations.
1741 class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase {
1743 base::scoped_nsobject<BookmarkBarControllerDragData> bar_;
1745 virtual void SetUp() {
1746 BookmarkBarControllerTestBase::SetUp();
1747 ASSERT_TRUE(browser());
1750 [[BookmarkBarControllerDragData alloc]
1751 initWithBrowser:browser()
1752 initialWidth:NSWidth([parent_view_ frame])
1754 resizeDelegate:resizeDelegate_.get()]);
1755 InstallAndToggleBar(bar_.get());
1759 TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
1760 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1761 const BookmarkNode* root = model->bookmark_bar_node();
1762 const std::string model_string("1bWithLongName 2fWithLongName:[ "
1763 "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName "
1764 "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] "
1765 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1766 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1767 "11bWithLongName 12bWithLongName 13b ");
1768 test::AddNodesFromModelString(model, root, model_string);
1770 // Validate initial model.
1771 std::string actualModelString = test::ModelStringFromNode(root);
1772 EXPECT_EQ(model_string, actualModelString);
1774 // Insure that the off-the-side is not showing.
1775 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1777 // Remember how many buttons are showing and are available.
1778 int oldDisplayedButtons = [bar_ displayedButtonCount];
1779 int oldChildCount = root->child_count();
1781 // Pop up the off-the-side menu.
1782 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1783 ASSERT_TRUE(otsButton);
1784 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1785 withObject:otsButton];
1786 BookmarkBarFolderController* otsController = [bar_ folderController];
1787 EXPECT_TRUE(otsController);
1788 NSWindow* toWindow = [otsController window];
1789 EXPECT_TRUE(toWindow);
1790 BookmarkButton* draggedButton =
1791 [bar_ buttonWithTitleEqualTo:@"3bWithLongName"];
1792 ASSERT_TRUE(draggedButton);
1793 int oldOTSCount = (int)[[otsController buttons] count];
1794 EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons);
1795 BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0];
1796 ASSERT_TRUE(targetButton);
1797 [otsController dragButton:draggedButton
1798 to:[targetButton center]
1800 // There should still be the same number of buttons in the bar
1801 // and off-the-side should have one more.
1802 int newDisplayedButtons = [bar_ displayedButtonCount];
1803 int newChildCount = root->child_count();
1804 int newOTSCount = (int)[[otsController buttons] count];
1805 EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons);
1806 EXPECT_EQ(oldChildCount + 1, newChildCount);
1807 EXPECT_EQ(oldOTSCount + 1, newOTSCount);
1808 EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons);
1811 TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) {
1812 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1813 const BookmarkNode* root = model->bookmark_bar_node();
1814 const std::string model_string("1bWithLongName 2bWithLongName "
1815 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1816 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1817 "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName "
1818 "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName "
1819 "19bWithLongName 20bWithLongName ");
1820 test::AddNodesFromModelString(model, root, model_string);
1822 const BookmarkNode* other = model->other_node();
1823 const std::string other_string("1other 2other 3other ");
1824 test::AddNodesFromModelString(model, other, other_string);
1826 // Validate initial model.
1827 std::string actualModelString = test::ModelStringFromNode(root);
1828 EXPECT_EQ(model_string, actualModelString);
1829 std::string actualOtherString = test::ModelStringFromNode(other);
1830 EXPECT_EQ(other_string, actualOtherString);
1832 // Insure that the off-the-side is showing.
1833 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1835 // Remember how many buttons are showing and are available.
1836 int oldDisplayedButtons = [bar_ displayedButtonCount];
1837 int oldRootCount = root->child_count();
1838 int oldOtherCount = other->child_count();
1840 // Pop up the off-the-side menu.
1841 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1842 ASSERT_TRUE(otsButton);
1843 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1844 withObject:otsButton];
1845 BookmarkBarFolderController* otsController = [bar_ folderController];
1846 EXPECT_TRUE(otsController);
1847 int oldOTSCount = (int)[[otsController buttons] count];
1848 EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons);
1850 // Pick an off-the-side button and drag it to the other bookmarks.
1851 BookmarkButton* draggedButton =
1852 [otsController buttonWithTitleEqualTo:@"20bWithLongName"];
1853 ASSERT_TRUE(draggedButton);
1854 BookmarkButton* targetButton = [bar_ otherBookmarksButton];
1855 ASSERT_TRUE(targetButton);
1856 [bar_ dragButton:draggedButton to:[targetButton center] copy:NO];
1858 // There should one less button in the bar, one less in off-the-side,
1859 // and one more in other bookmarks.
1860 int newRootCount = root->child_count();
1861 int newOTSCount = (int)[[otsController buttons] count];
1862 int newOtherCount = other->child_count();
1863 EXPECT_EQ(oldRootCount - 1, newRootCount);
1864 EXPECT_EQ(oldOTSCount - 1, newOTSCount);
1865 EXPECT_EQ(oldOtherCount + 1, newOtherCount);
1868 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) {
1869 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1870 const BookmarkNode* root = model->bookmark_bar_node();
1871 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1873 test::AddNodesFromModelString(model, root, model_string);
1874 const BookmarkNode* other = model->other_node();
1875 const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1876 "O4f:[ O4f1b O4f2f ] 05b ");
1877 test::AddNodesFromModelString(model, other, other_string);
1879 // Validate initial model.
1880 std::string actual = test::ModelStringFromNode(root);
1881 EXPECT_EQ(model_string, actual);
1882 actual = test::ModelStringFromNode(other);
1883 EXPECT_EQ(other_string, actual);
1885 // Remember the little ones.
1886 int oldChildCount = root->child_count();
1888 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1889 ASSERT_TRUE(targetButton);
1891 // Gen up some dragging data.
1892 const BookmarkNode* newNode = other->GetChild(2);
1893 [bar_ setDragDataNode:newNode];
1894 base::scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]);
1895 [dragInfo setDropLocation:[targetButton center]];
1896 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1898 // There should one more button in the bar.
1899 int newChildCount = root->child_count();
1900 EXPECT_EQ(oldChildCount + 1, newChildCount);
1901 // Verify the model.
1902 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1903 "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b ");
1904 actual = test::ModelStringFromNode(root);
1905 EXPECT_EQ(expected, actual);
1906 oldChildCount = newChildCount;
1908 // Now do it over a folder button.
1909 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
1910 ASSERT_TRUE(targetButton);
1911 NSPoint targetPoint = [targetButton center];
1912 newNode = other->GetChild(2); // Should be O4f.
1913 EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1914 [bar_ setDragDataNode:newNode];
1915 [dragInfo setDropLocation:targetPoint];
1916 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1918 newChildCount = root->child_count();
1919 EXPECT_EQ(oldChildCount, newChildCount);
1920 // Verify the model.
1921 const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1922 "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] "
1924 actual = test::ModelStringFromNode(root);
1925 EXPECT_EQ(expected1, actual);
1928 TEST_F(BookmarkBarControllerDragDropTest, AddURLs) {
1929 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1930 const BookmarkNode* root = model->bookmark_bar_node();
1931 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1933 test::AddNodesFromModelString(model, root, model_string);
1935 // Validate initial model.
1936 std::string actual = test::ModelStringFromNode(root);
1937 EXPECT_EQ(model_string, actual);
1939 // Remember the children.
1940 int oldChildCount = root->child_count();
1942 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1943 ASSERT_TRUE(targetButton);
1945 NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
1946 @"http://www.b.com/", nil];
1947 NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
1948 [bar_ addURLs:urls withTitles:titles at:[targetButton center]];
1950 // There should two more nodes in the bar.
1951 int newChildCount = root->child_count();
1952 EXPECT_EQ(oldChildCount + 2, newChildCount);
1953 // Verify the model.
1954 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1955 "2f3b ] SiteA SiteB 3b 4b ");
1956 actual = test::ModelStringFromNode(root);
1957 EXPECT_EQ(expected, actual);
1960 TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) {
1961 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1962 const BookmarkNode* root = model->bookmark_bar_node();
1963 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1964 test::AddNodesFromModelString(model, root, model_string);
1966 // Validate initial model.
1967 std::string actualModelString = test::ModelStringFromNode(root);
1968 EXPECT_EQ(model_string, actualModelString);
1970 // Find the main bar controller.
1971 const void* expectedController = bar_;
1972 const void* actualController = [bar_ controllerForNode:root];
1973 EXPECT_EQ(expectedController, actualController);
1976 TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
1977 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1978 const BookmarkNode* root = model->bookmark_bar_node();
1979 const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
1980 test::AddNodesFromModelString(model, root, model_string);
1982 // Validate initial model.
1983 std::string actualModel = test::ModelStringFromNode(root);
1984 EXPECT_EQ(model_string, actualModel);
1986 // Test a series of points starting at the right edge of the bar.
1987 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"];
1988 ASSERT_TRUE(targetButton);
1989 NSPoint targetPoint = [targetButton left];
1990 CGFloat leftMarginIndicatorPosition = bookmarks::kBookmarkLeftMargin - 0.5 *
1991 bookmarks::kBookmarkHorizontalPadding;
1992 const CGFloat baseOffset = targetPoint.x;
1993 CGFloat expected = leftMarginIndicatorPosition;
1994 CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint];
1995 EXPECT_CGFLOAT_EQ(expected, actual);
1996 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
1997 actual = [bar_ indicatorPosForDragToPoint:[targetButton right]];
1998 targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1999 expected = [targetButton left].x - baseOffset + leftMarginIndicatorPosition;
2000 EXPECT_CGFLOAT_EQ(expected, actual);
2001 targetButton = [bar_ buttonWithTitleEqualTo:@"4b"];
2002 targetPoint = [targetButton right];
2003 targetPoint.x += 100; // Somewhere off to the right.
2004 CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding;
2005 expected = NSMaxX([targetButton frame]) + xDelta;
2006 actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2007 EXPECT_CGFLOAT_EQ(expected, actual);
2010 TEST_F(BookmarkBarControllerDragDropTest, PulseButton) {
2011 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2012 const BookmarkNode* root = model->bookmark_bar_node();
2013 GURL gurl("http://www.google.com");
2014 const BookmarkNode* node = model->AddURL(root, root->child_count(),
2015 ASCIIToUTF16("title"), gurl);
2017 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
2018 EXPECT_FALSE([button isContinuousPulsing]);
2020 NSValue *value = [NSValue valueWithPointer:node];
2021 NSDictionary *dict = [NSDictionary
2022 dictionaryWithObjectsAndKeys:value,
2023 bookmark_button::kBookmarkKey,
2024 [NSNumber numberWithBool:YES],
2025 bookmark_button::kBookmarkPulseFlagKey,
2027 [[NSNotificationCenter defaultCenter]
2028 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2031 EXPECT_TRUE([button isContinuousPulsing]);
2033 dict = [NSDictionary dictionaryWithObjectsAndKeys:value,
2034 bookmark_button::kBookmarkKey,
2035 [NSNumber numberWithBool:NO],
2036 bookmark_button::kBookmarkPulseFlagKey,
2038 [[NSNotificationCenter defaultCenter]
2039 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2042 EXPECT_FALSE([button isContinuousPulsing]);
2045 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) {
2046 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2047 const BookmarkNode* root = model->bookmark_bar_node();
2048 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2050 test::AddNodesFromModelString(model, root, model_string);
2052 // Validate initial model.
2053 std::string actual = test::ModelStringFromNode(root);
2054 EXPECT_EQ(model_string, actual);
2056 int oldChildCount = root->child_count();
2058 // Drag a button to the trash.
2059 BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"];
2060 ASSERT_TRUE(buttonToDelete);
2061 EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]);
2062 [bar_ didDragBookmarkToTrash:buttonToDelete];
2064 // There should be one less button in the bar.
2065 int newChildCount = root->child_count();
2066 EXPECT_EQ(oldChildCount - 1, newChildCount);
2067 // Verify the model.
2068 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2070 actual = test::ModelStringFromNode(root);
2071 EXPECT_EQ(expected, actual);
2073 // Verify that the other bookmark folder can't be deleted.
2074 BookmarkButton *otherButton = [bar_ otherBookmarksButton];
2075 EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]);