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