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 #include "base/mac/scoped_nsobject.h" 6 #include "base/strings/string16.h" 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/browser/bookmarks/bookmark_model.h" 9 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 10 #include "chrome/browser/profiles/profile.h" 11 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" 12 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h" 13 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" 14 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h" 15 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" 16 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h" 17 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" 18 #import "chrome/browser/ui/cocoa/url_drop_target.h" 19 #include "chrome/test/base/ui_test_utils.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "testing/platform_test.h" 22 #import "third_party/mozilla/NSPasteboard+Utils.h" 23 24 namespace { 25 // Some values used for mocks and fakes. 26 const CGFloat kFakeIndicatorPos = 7.0; 27 const NSPoint kPoint = {10, 10}; 28 }; 29 30 // Fake DraggingInfo, fake BookmarkBarController, fake NSPasteboard... 31 @interface FakeBookmarkDraggingInfo : NSObject { 32 @public 33 BOOL dragButtonToPong_; 34 BOOL dragButtonToShouldCopy_; 35 BOOL dragURLsPong_; 36 BOOL dragBookmarkDataPong_; 37 BOOL dropIndicatorShown_; 38 BOOL draggingEnteredCalled_; 39 // Only mock one type of drag data at a time. 40 NSString* dragDataType_; 41 BookmarkButton* button_; // weak 42 BookmarkModel* bookmarkModel_; // weak 43 id draggingSource_; 44 } 45 @property (nonatomic) BOOL dropIndicatorShown; 46 @property (nonatomic) BOOL draggingEnteredCalled; 47 @property (nonatomic, copy) NSString* dragDataType; 48 @property (nonatomic, assign) BookmarkButton* button; 49 @property (nonatomic, assign) BookmarkModel* bookmarkModel; 50 @end 51 52 @implementation FakeBookmarkDraggingInfo 53 54 @synthesize dropIndicatorShown = dropIndicatorShown_; 55 @synthesize draggingEnteredCalled = draggingEnteredCalled_; 56 @synthesize dragDataType = dragDataType_; 57 @synthesize button = button_; 58 @synthesize bookmarkModel = bookmarkModel_; 59 60 - (id)init { 61 if ((self = [super init])) { 62 dropIndicatorShown_ = YES; 63 } 64 return self; 65 } 66 67 - (void)dealloc { 68 [dragDataType_ release]; 69 [super dealloc]; 70 } 71 72 - (void)reset { 73 [dragDataType_ release]; 74 dragDataType_ = nil; 75 dragButtonToPong_ = NO; 76 dragURLsPong_ = NO; 77 dragBookmarkDataPong_ = NO; 78 dropIndicatorShown_ = YES; 79 draggingEnteredCalled_ = NO; 80 draggingSource_ = self; 81 } 82 83 - (void)setDraggingSource:(id)draggingSource { 84 draggingSource_ = draggingSource; 85 } 86 87 // NSDragInfo mocking functions. 88 89 - (id)draggingPasteboard { 90 return self; 91 } 92 93 // So we can look local. 94 - (id)draggingSource { 95 return draggingSource_; 96 } 97 98 - (NSDragOperation)draggingSourceOperationMask { 99 return NSDragOperationCopy | NSDragOperationMove; 100 } 101 102 - (NSPoint)draggingLocation { 103 return kPoint; 104 } 105 106 // NSPasteboard mocking functions. 107 108 - (BOOL)containsURLData { 109 NSArray* urlTypes = [URLDropTargetHandler handledDragTypes]; 110 if (dragDataType_) 111 return [urlTypes containsObject:dragDataType_]; 112 return NO; 113 } 114 115 - (NSData*)dataForType:(NSString*)type { 116 if (dragDataType_ && [dragDataType_ isEqualToString:type]) { 117 if (button_) 118 return [NSData dataWithBytes:&button_ length:sizeof(button_)]; 119 else 120 return [NSData data]; // Return something, anything. 121 } 122 return nil; 123 } 124 125 // Fake a controller for callback ponging 126 127 - (BOOL)dragButton:(BookmarkButton*)button to:(NSPoint)point copy:(BOOL)copy { 128 dragButtonToPong_ = YES; 129 dragButtonToShouldCopy_ = copy; 130 return YES; 131 } 132 133 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { 134 dragURLsPong_ = YES; 135 return YES; 136 } 137 138 - (void)getURLs:(NSArray**)outUrls 139 andTitles:(NSArray**)outTitles 140 convertingFilenames:(BOOL)convertFilenames { 141 } 142 143 - (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info { 144 dragBookmarkDataPong_ = YES; 145 return NO; 146 } 147 148 - (BOOL)canEditBookmarks { 149 return YES; 150 } 151 152 // Confirm the pongs. 153 154 - (BOOL)dragButtonToPong { 155 return dragButtonToPong_; 156 } 157 158 - (BOOL)dragButtonToShouldCopy { 159 return dragButtonToShouldCopy_; 160 } 161 162 - (BOOL)dragURLsPong { 163 return dragURLsPong_; 164 } 165 166 - (BOOL)dragBookmarkDataPong { 167 return dragBookmarkDataPong_; 168 } 169 170 - (CGFloat)indicatorPosForDragToPoint:(NSPoint)point { 171 return kFakeIndicatorPos; 172 } 173 174 - (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point { 175 return dropIndicatorShown_; 176 } 177 178 - (BOOL)draggingAllowed:(id<NSDraggingInfo>)info { 179 return YES; 180 } 181 182 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { 183 draggingEnteredCalled_ = YES; 184 return NSDragOperationNone; 185 } 186 187 - (void)setDropInsertionPos:(CGFloat)where { 188 } 189 190 - (void)clearDropInsertionPos { 191 } 192 193 @end 194 195 namespace { 196 197 class BookmarkBarViewTest : public CocoaProfileTest { 198 public: 199 virtual void SetUp() { 200 CocoaProfileTest::SetUp(); 201 view_.reset([[BookmarkBarView alloc] init]); 202 } 203 204 base::scoped_nsobject<BookmarkBarView> view_; 205 }; 206 207 TEST_F(BookmarkBarViewTest, CanDragWindow) { 208 EXPECT_FALSE([view_ mouseDownCanMoveWindow]); 209 } 210 211 TEST_F(BookmarkBarViewTest, BookmarkButtonDragAndDrop) { 212 base::scoped_nsobject<FakeBookmarkDraggingInfo> info( 213 [[FakeBookmarkDraggingInfo alloc] init]); 214 [view_ setController:info.get()]; 215 [info reset]; 216 217 BookmarkModel* bookmark_model = 218 BookmarkModelFactory::GetForProfile(profile()); 219 const BookmarkNode* node = 220 bookmark_model->AddURL(bookmark_model->bookmark_bar_node(), 221 0, 222 ASCIIToUTF16("Test Bookmark"), 223 GURL("http://www.exmaple.com")); 224 225 base::scoped_nsobject<BookmarkButtonCell> button_cell( 226 [[BookmarkButtonCell buttonCellForNode:node 227 text:nil 228 image:nil 229 menuController:nil] retain]); 230 base::scoped_nsobject<BookmarkButton> dragged_button( 231 [[BookmarkButton alloc] init]); 232 [dragged_button setCell:button_cell]; 233 [info setDraggingSource:dragged_button.get()]; 234 [info setDragDataType:kBookmarkButtonDragType]; 235 [info setButton:dragged_button.get()]; 236 [info setBookmarkModel:bookmark_model]; 237 EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); 238 EXPECT_TRUE([view_ performDragOperation:(id)info.get()]); 239 EXPECT_TRUE([info dragButtonToPong]); 240 EXPECT_FALSE([info dragButtonToShouldCopy]); 241 EXPECT_FALSE([info dragURLsPong]); 242 EXPECT_TRUE([info dragBookmarkDataPong]); 243 } 244 245 // When dragging bookmarks across profiles, we should always copy, never move. 246 TEST_F(BookmarkBarViewTest, BookmarkButtonDragAndDropAcrossProfiles) { 247 base::scoped_nsobject<FakeBookmarkDraggingInfo> info( 248 [[FakeBookmarkDraggingInfo alloc] init]); 249 [view_ setController:info.get()]; 250 [info reset]; 251 252 // |other_profile| is owned by the |testing_profile_manager|. 253 TestingProfile* other_profile = 254 testing_profile_manager()->CreateTestingProfile("other"); 255 other_profile->CreateBookmarkModel(true); 256 257 BookmarkModel* bookmark_model = 258 BookmarkModelFactory::GetForProfile(profile()); 259 ui_test_utils::WaitForBookmarkModelToLoad(bookmark_model); 260 261 const BookmarkNode* node = 262 bookmark_model->AddURL(bookmark_model->bookmark_bar_node(), 263 0, 264 ASCIIToUTF16("Test Bookmark"), 265 GURL("http://www.exmaple.com")); 266 267 base::scoped_nsobject<BookmarkButtonCell> button_cell( 268 [[BookmarkButtonCell buttonCellForNode:node 269 text:nil 270 image:nil 271 menuController:nil] retain]); 272 base::scoped_nsobject<BookmarkButton> dragged_button( 273 [[BookmarkButton alloc] init]); 274 [dragged_button setCell:button_cell]; 275 [info setDraggingSource:dragged_button.get()]; 276 [info setDragDataType:kBookmarkButtonDragType]; 277 [info setButton:dragged_button.get()]; 278 [info setBookmarkModel:BookmarkModelFactory::GetForProfile(other_profile)]; 279 EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); 280 EXPECT_TRUE([view_ performDragOperation:(id)info.get()]); 281 EXPECT_TRUE([info dragButtonToPong]); 282 EXPECT_TRUE([info dragButtonToShouldCopy]); 283 EXPECT_FALSE([info dragURLsPong]); 284 EXPECT_TRUE([info dragBookmarkDataPong]); 285 } 286 287 TEST_F(BookmarkBarViewTest, URLDragAndDrop) { 288 base::scoped_nsobject<FakeBookmarkDraggingInfo> info( 289 [[FakeBookmarkDraggingInfo alloc] init]); 290 [view_ setController:info.get()]; 291 [info reset]; 292 293 NSArray* dragTypes = [URLDropTargetHandler handledDragTypes]; 294 for (NSString* type in dragTypes) { 295 [info setDragDataType:type]; 296 EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationCopy); 297 EXPECT_TRUE([view_ performDragOperation:(id)info.get()]); 298 EXPECT_FALSE([info dragButtonToPong]); 299 EXPECT_TRUE([info dragURLsPong]); 300 EXPECT_TRUE([info dragBookmarkDataPong]); 301 [info reset]; 302 } 303 } 304 305 TEST_F(BookmarkBarViewTest, BookmarkButtonDropIndicator) { 306 base::scoped_nsobject<FakeBookmarkDraggingInfo> info( 307 [[FakeBookmarkDraggingInfo alloc] init]); 308 [view_ setController:info.get()]; 309 [info reset]; 310 311 base::scoped_nsobject<BookmarkButton> dragged_button( 312 [[BookmarkButton alloc] init]); 313 [info setDraggingSource:dragged_button.get()]; 314 [info setDragDataType:kBookmarkButtonDragType]; 315 EXPECT_FALSE([info draggingEnteredCalled]); 316 EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); 317 EXPECT_TRUE([info draggingEnteredCalled]); // Ensure controller pinged. 318 EXPECT_TRUE([view_ dropIndicatorShown]); 319 EXPECT_EQ([view_ dropIndicatorPosition], kFakeIndicatorPos); 320 321 [info setDropIndicatorShown:NO]; 322 EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); 323 EXPECT_FALSE([view_ dropIndicatorShown]); 324 } 325 326 } // namespace 327