1 // Copyright (c) 2010 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 "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" 6 7 #import <Cocoa/Cocoa.h> 8 9 #include "base/sys_string_conversions.h" 10 #include "chrome/browser/bookmarks/bookmark_model.h" 11 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_drag_source.h" 12 #include "chrome/browser/tab_contents/tab_contents_view_mac.h" 13 14 NSString* const kBookmarkDictionaryListPboardType = 15 @"BookmarkDictionaryListPboardType"; 16 17 namespace { 18 19 // An unofficial standard pasteboard title type to be provided alongside the 20 // |NSURLPboardType|. 21 NSString* const kNSURLTitlePboardType = 22 @"public.url-name"; 23 24 // Pasteboard type used to store profile path to determine which profile 25 // a set of bookmarks came from. 26 NSString* const kChromiumProfilePathPboardType = 27 @"ChromiumProfilePathPboardType"; 28 29 // Internal bookmark ID for a bookmark node. Used only when moving inside 30 // of one profile. 31 NSString* const kChromiumBookmarkId = 32 @"ChromiumBookmarkId"; 33 34 // Mac WebKit uses this type, declared in 35 // WebKit/mac/History/WebURLsWithTitles.h. 36 NSString* const kWebURLsWithTitlesPboardType = 37 @"WebURLsWithTitlesPboardType"; 38 39 // Keys for the type of node in BookmarkDictionaryListPboardType. 40 NSString* const kWebBookmarkType = 41 @"WebBookmarkType"; 42 43 NSString* const kWebBookmarkTypeList = 44 @"WebBookmarkTypeList"; 45 46 NSString* const kWebBookmarkTypeLeaf = 47 @"WebBookmarkTypeLeaf"; 48 49 void ConvertPlistToElements(NSArray* input, 50 std::vector<BookmarkNodeData::Element>& elements) { 51 NSUInteger len = [input count]; 52 for (NSUInteger i = 0; i < len; ++i) { 53 NSDictionary* pboardBookmark = [input objectAtIndex:i]; 54 scoped_ptr<BookmarkNode> new_node(new BookmarkNode(0, GURL())); 55 int64 node_id = 56 [[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue]; 57 new_node->set_id(node_id); 58 BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType] 59 isEqualToString:kWebBookmarkTypeList]; 60 if (is_folder) { 61 new_node->set_type(BookmarkNode::FOLDER); 62 NSString* title = [pboardBookmark objectForKey:@"Title"]; 63 new_node->set_title(base::SysNSStringToUTF16(title)); 64 } else { 65 new_node->set_type(BookmarkNode::URL); 66 NSDictionary* uriDictionary = 67 [pboardBookmark objectForKey:@"URIDictionary"]; 68 NSString* title = [uriDictionary objectForKey:@"title"]; 69 NSString* urlString = [pboardBookmark objectForKey:@"URLString"]; 70 new_node->set_title(base::SysNSStringToUTF16(title)); 71 new_node->SetURL(GURL(base::SysNSStringToUTF8(urlString))); 72 } 73 BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get()); 74 if(is_folder) 75 ConvertPlistToElements([pboardBookmark objectForKey:@"Children"], 76 e.children); 77 elements.push_back(e); 78 } 79 } 80 81 bool ReadBookmarkDictionaryListPboardType(NSPasteboard* pb, 82 std::vector<BookmarkNodeData::Element>& elements) { 83 NSArray* bookmarks = 84 [pb propertyListForType:kBookmarkDictionaryListPboardType]; 85 if (!bookmarks) return false; 86 ConvertPlistToElements(bookmarks, elements); 87 return true; 88 } 89 90 bool ReadWebURLsWithTitlesPboardType(NSPasteboard* pb, 91 std::vector<BookmarkNodeData::Element>& elements) { 92 NSArray* bookmarkPairs = 93 [pb propertyListForType:kWebURLsWithTitlesPboardType]; 94 if (![bookmarkPairs isKindOfClass:[NSArray class]]) { 95 return false; 96 } 97 NSArray* urlsArr = [bookmarkPairs objectAtIndex:0]; 98 NSArray* titlesArr = [bookmarkPairs objectAtIndex:1]; 99 if ([urlsArr count] < 1) { 100 return false; 101 } 102 if ([urlsArr count] != [titlesArr count]) { 103 return false; 104 } 105 106 NSUInteger len = [titlesArr count]; 107 for (NSUInteger i = 0; i < len; ++i) { 108 string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]); 109 std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]); 110 if (!url.empty()) { 111 BookmarkNodeData::Element element; 112 element.is_url = true; 113 element.url = GURL(url); 114 element.title = title; 115 elements.push_back(element); 116 } 117 } 118 return true; 119 } 120 121 bool ReadNSURLPboardType(NSPasteboard* pb, 122 std::vector<BookmarkNodeData::Element>& elements) { 123 NSURL* url = [NSURL URLFromPasteboard:pb]; 124 if (url == nil) { 125 return false; 126 } 127 std::string urlString = base::SysNSStringToUTF8([url absoluteString]); 128 NSString* title = [pb stringForType:kNSURLTitlePboardType]; 129 if (!title) 130 title = [pb stringForType:NSStringPboardType]; 131 132 BookmarkNodeData::Element element; 133 element.is_url = true; 134 element.url = GURL(urlString); 135 element.title = base::SysNSStringToUTF16(title); 136 elements.push_back(element); 137 return true; 138 } 139 140 NSArray* GetPlistForBookmarkList( 141 const std::vector<BookmarkNodeData::Element>& elements) { 142 NSMutableArray* plist = [NSMutableArray array]; 143 for (size_t i = 0; i < elements.size(); ++i) { 144 BookmarkNodeData::Element element = elements[i]; 145 if (element.is_url) { 146 NSString* title = base::SysUTF16ToNSString(element.title); 147 NSString* url = base::SysUTF8ToNSString(element.url.spec()); 148 int64 elementId = element.get_id(); 149 NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; 150 NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 151 title, @"title", nil]; 152 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: 153 uriDictionary, @"URIDictionary", 154 url, @"URLString", 155 kWebBookmarkTypeLeaf, kWebBookmarkType, 156 idNum, kChromiumBookmarkId, 157 nil]; 158 [plist addObject:object]; 159 } else { 160 NSString* title = base::SysUTF16ToNSString(element.title); 161 NSArray* children = GetPlistForBookmarkList(element.children); 162 int64 elementId = element.get_id(); 163 NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; 164 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: 165 title, @"Title", 166 children, @"Children", 167 kWebBookmarkTypeList, kWebBookmarkType, 168 idNum, kChromiumBookmarkId, 169 nil]; 170 [plist addObject:object]; 171 } 172 } 173 return plist; 174 } 175 176 void WriteBookmarkDictionaryListPboardType(NSPasteboard* pb, 177 const std::vector<BookmarkNodeData::Element>& elements) { 178 NSArray* plist = GetPlistForBookmarkList(elements); 179 [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType]; 180 } 181 182 void FillFlattenedArraysForBookmarks( 183 const std::vector<BookmarkNodeData::Element>& elements, 184 NSMutableArray* titles, NSMutableArray* urls) { 185 for (size_t i = 0; i < elements.size(); ++i) { 186 BookmarkNodeData::Element element = elements[i]; 187 if (element.is_url) { 188 NSString* title = base::SysUTF16ToNSString(element.title); 189 NSString* url = base::SysUTF8ToNSString(element.url.spec()); 190 [titles addObject:title]; 191 [urls addObject:url]; 192 } else { 193 FillFlattenedArraysForBookmarks(element.children, titles, urls); 194 } 195 } 196 } 197 198 void WriteSimplifiedBookmarkTypes(NSPasteboard* pb, 199 const std::vector<BookmarkNodeData::Element>& elements) { 200 NSMutableArray* titles = [NSMutableArray array]; 201 NSMutableArray* urls = [NSMutableArray array]; 202 FillFlattenedArraysForBookmarks(elements, titles, urls); 203 204 // These bookmark types only act on urls, not folders. 205 if ([urls count] < 1) 206 return; 207 208 // Write WebURLsWithTitlesPboardType. 209 [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil] 210 forType:kWebURLsWithTitlesPboardType]; 211 212 // Write NSStringPboardType. 213 [pb setString:[urls componentsJoinedByString:@"\n"] 214 forType:NSStringPboardType]; 215 216 // Write NSURLPboardType (with title). 217 NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]]; 218 [url writeToPasteboard:pb]; 219 NSString* titleString = [titles objectAtIndex:0]; 220 [pb setString:titleString forType:kNSURLTitlePboardType]; 221 } 222 223 void WriteToClipboardPrivate( 224 const std::vector<BookmarkNodeData::Element>& elements, 225 NSPasteboard* pb, 226 FilePath::StringType profile_path) { 227 if (elements.empty()) 228 return; 229 230 NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, 231 kWebURLsWithTitlesPboardType, 232 NSStringPboardType, 233 NSURLPboardType, 234 kNSURLTitlePboardType, 235 kChromiumProfilePathPboardType, 236 nil]; 237 [pb declareTypes:types owner:nil]; 238 [pb setString:base::SysUTF8ToNSString(profile_path) 239 forType:kChromiumProfilePathPboardType]; 240 WriteBookmarkDictionaryListPboardType(pb, elements); 241 WriteSimplifiedBookmarkTypes(pb, elements); 242 } 243 244 bool ReadFromClipboardPrivate( 245 std::vector<BookmarkNodeData::Element>& elements, 246 NSPasteboard* pb, 247 FilePath::StringType* profile_path) { 248 elements.clear(); 249 NSString* profile = [pb stringForType:kChromiumProfilePathPboardType]; 250 profile_path->assign(base::SysNSStringToUTF8(profile)); 251 return (ReadBookmarkDictionaryListPboardType(pb, elements) || 252 ReadWebURLsWithTitlesPboardType(pb, elements) || 253 ReadNSURLPboardType(pb, elements)); 254 } 255 256 bool ClipboardContainsBookmarksPrivate(NSPasteboard* pb) { 257 NSArray* availableTypes = 258 [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, 259 kWebURLsWithTitlesPboardType, 260 NSURLPboardType, 261 nil]; 262 return [pb availableTypeFromArray:availableTypes] != nil; 263 } 264 265 } // anonymous namespace 266 267 namespace bookmark_pasteboard_helper_mac { 268 269 void WriteToClipboard(const std::vector<BookmarkNodeData::Element>& elements, 270 FilePath::StringType profile_path) { 271 NSPasteboard* pb = [NSPasteboard generalPasteboard]; 272 WriteToClipboardPrivate(elements, pb, profile_path); 273 } 274 275 void WriteToDragClipboard( 276 const std::vector<BookmarkNodeData::Element>& elements, 277 FilePath::StringType profile_path) { 278 NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; 279 WriteToClipboardPrivate(elements, pb, profile_path); 280 } 281 282 bool ReadFromClipboard(std::vector<BookmarkNodeData::Element>& elements, 283 FilePath::StringType* profile_path) { 284 NSPasteboard* pb = [NSPasteboard generalPasteboard]; 285 return ReadFromClipboardPrivate(elements, pb, profile_path); 286 } 287 288 bool ReadFromDragClipboard(std::vector<BookmarkNodeData::Element>& elements, 289 FilePath::StringType* profile_path) { 290 NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; 291 return ReadFromClipboardPrivate(elements, pb, profile_path); 292 } 293 294 295 bool ClipboardContainsBookmarks() { 296 NSPasteboard* pb = [NSPasteboard generalPasteboard]; 297 return ClipboardContainsBookmarksPrivate(pb); 298 } 299 300 bool DragClipboardContainsBookmarks() { 301 NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; 302 return ClipboardContainsBookmarksPrivate(pb); 303 } 304 305 void StartDrag(Profile* profile, const std::vector<const BookmarkNode*>& nodes, 306 gfx::NativeView view) { 307 DCHECK([view isKindOfClass:[TabContentsViewCocoa class]]); 308 TabContentsViewCocoa* tabView = static_cast<TabContentsViewCocoa*>(view); 309 std::vector<BookmarkNodeData::Element> elements; 310 for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin(); 311 it != nodes.end(); ++it) { 312 elements.push_back(BookmarkNodeData::Element(*it)); 313 } 314 NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard]; 315 scoped_nsobject<BookmarkDragSource> source([[BookmarkDragSource alloc] 316 initWithContentsView:tabView 317 dropData:elements 318 profile:profile 319 pasteboard:pb 320 dragOperationMask:NSDragOperationEvery]); 321 [source startDrag]; 322 } 323 324 } // namespace bookmark_pasteboard_helper_mac 325