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