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 "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" 6 7 #import <Cocoa/Cocoa.h> 8 9 #include "base/files/file_path.h" 10 #include "base/strings/sys_string_conversions.h" 11 #include "chrome/browser/bookmarks/bookmark_model.h" 12 #include "ui/base/clipboard/clipboard.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 kCrWebURLsWithTitlesPboardType = 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(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->SetTitle(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->SetTitle(base::SysNSStringToUTF16(title)); 71 new_node->set_url(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( 82 NSPasteboard* pb, 83 std::vector<BookmarkNodeData::Element>& elements) { 84 NSArray* bookmarks = 85 [pb propertyListForType:kBookmarkDictionaryListPboardType]; 86 if (!bookmarks) 87 return false; 88 ConvertPlistToElements(bookmarks, elements); 89 return true; 90 } 91 92 bool ReadWebURLsWithTitlesPboardType( 93 NSPasteboard* pb, 94 std::vector<BookmarkNodeData::Element>& elements) { 95 NSArray* bookmarkPairs = 96 [pb propertyListForType:kCrWebURLsWithTitlesPboardType]; 97 if (![bookmarkPairs isKindOfClass:[NSArray class]]) 98 return false; 99 100 NSArray* urlsArr = [bookmarkPairs objectAtIndex:0]; 101 NSArray* titlesArr = [bookmarkPairs objectAtIndex:1]; 102 if ([urlsArr count] < 1) 103 return false; 104 if ([urlsArr count] != [titlesArr count]) 105 return false; 106 107 NSUInteger len = [titlesArr count]; 108 for (NSUInteger i = 0; i < len; ++i) { 109 string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]); 110 std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]); 111 if (!url.empty()) { 112 BookmarkNodeData::Element element; 113 element.is_url = true; 114 element.url = GURL(url); 115 element.title = title; 116 elements.push_back(element); 117 } 118 } 119 return true; 120 } 121 122 bool ReadNSURLPboardType(NSPasteboard* pb, 123 std::vector<BookmarkNodeData::Element>& elements) { 124 NSURL* url = [NSURL URLFromPasteboard:pb]; 125 if (url == nil) 126 return false; 127 128 std::string urlString = base::SysNSStringToUTF8([url absoluteString]); 129 NSString* title = [pb stringForType:kNSURLTitlePboardType]; 130 if (!title) 131 title = [pb stringForType:NSStringPboardType]; 132 133 BookmarkNodeData::Element element; 134 element.is_url = true; 135 element.url = GURL(urlString); 136 element.title = base::SysNSStringToUTF16(title); 137 elements.push_back(element); 138 return true; 139 } 140 141 NSArray* GetPlistForBookmarkList( 142 const std::vector<BookmarkNodeData::Element>& elements) { 143 NSMutableArray* plist = [NSMutableArray array]; 144 for (size_t i = 0; i < elements.size(); ++i) { 145 BookmarkNodeData::Element element = elements[i]; 146 if (element.is_url) { 147 NSString* title = base::SysUTF16ToNSString(element.title); 148 NSString* url = base::SysUTF8ToNSString(element.url.spec()); 149 int64 elementId = element.id(); 150 NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; 151 NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 152 title, @"title", nil]; 153 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: 154 uriDictionary, @"URIDictionary", 155 url, @"URLString", 156 kWebBookmarkTypeLeaf, kWebBookmarkType, 157 idNum, kChromiumBookmarkId, 158 nil]; 159 [plist addObject:object]; 160 } else { 161 NSString* title = base::SysUTF16ToNSString(element.title); 162 NSArray* children = GetPlistForBookmarkList(element.children); 163 int64 elementId = element.id(); 164 NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; 165 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: 166 title, @"Title", 167 children, @"Children", 168 kWebBookmarkTypeList, kWebBookmarkType, 169 idNum, kChromiumBookmarkId, 170 nil]; 171 [plist addObject:object]; 172 } 173 } 174 return plist; 175 } 176 177 void WriteBookmarkDictionaryListPboardType( 178 NSPasteboard* pb, 179 const std::vector<BookmarkNodeData::Element>& elements) { 180 NSArray* plist = GetPlistForBookmarkList(elements); 181 [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType]; 182 } 183 184 void FillFlattenedArraysForBookmarks( 185 const std::vector<BookmarkNodeData::Element>& elements, 186 NSMutableArray* titles, NSMutableArray* urls) { 187 for (size_t i = 0; i < elements.size(); ++i) { 188 BookmarkNodeData::Element element = elements[i]; 189 if (element.is_url) { 190 NSString* title = base::SysUTF16ToNSString(element.title); 191 NSString* url = base::SysUTF8ToNSString(element.url.spec()); 192 [titles addObject:title]; 193 [urls addObject:url]; 194 } else { 195 FillFlattenedArraysForBookmarks(element.children, titles, urls); 196 } 197 } 198 } 199 200 void WriteSimplifiedBookmarkTypes(NSPasteboard* pb, 201 const std::vector<BookmarkNodeData::Element>& elements) { 202 NSMutableArray* titles = [NSMutableArray array]; 203 NSMutableArray* urls = [NSMutableArray array]; 204 FillFlattenedArraysForBookmarks(elements, titles, urls); 205 206 // These bookmark types only act on urls, not folders. 207 if ([urls count] < 1) 208 return; 209 210 // Write WebURLsWithTitlesPboardType. 211 [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil] 212 forType:kCrWebURLsWithTitlesPboardType]; 213 214 // Write NSStringPboardType. 215 [pb setString:[urls componentsJoinedByString:@"\n"] 216 forType:NSStringPboardType]; 217 218 // Write NSURLPboardType (with title). 219 NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]]; 220 [url writeToPasteboard:pb]; 221 NSString* titleString = [titles objectAtIndex:0]; 222 [pb setString:titleString forType:kNSURLTitlePboardType]; 223 } 224 225 NSPasteboard* PasteboardFromType( 226 bookmark_pasteboard_helper_mac::PasteboardType type) { 227 NSString* type_string = nil; 228 switch (type) { 229 case bookmark_pasteboard_helper_mac::kCopyPastePasteboard: 230 type_string = NSGeneralPboard; 231 break; 232 case bookmark_pasteboard_helper_mac::kDragPasteboard: 233 type_string = NSDragPboard; 234 break; 235 } 236 237 return [NSPasteboard pasteboardWithName:type_string]; 238 } 239 240 } // namespace 241 242 namespace bookmark_pasteboard_helper_mac { 243 244 void WriteToPasteboard(PasteboardType type, 245 const std::vector<BookmarkNodeData::Element>& elements, 246 const base::FilePath& profile_path) { 247 if (elements.empty()) 248 return; 249 250 NSPasteboard* pb = PasteboardFromType(type); 251 252 NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, 253 kCrWebURLsWithTitlesPboardType, 254 NSStringPboardType, 255 NSURLPboardType, 256 kNSURLTitlePboardType, 257 kChromiumProfilePathPboardType, 258 nil]; 259 [pb declareTypes:types owner:nil]; 260 [pb setString:base::SysUTF8ToNSString(profile_path.value()) 261 forType:kChromiumProfilePathPboardType]; 262 WriteBookmarkDictionaryListPboardType(pb, elements); 263 WriteSimplifiedBookmarkTypes(pb, elements); 264 } 265 266 bool ReadFromPasteboard(PasteboardType type, 267 std::vector<BookmarkNodeData::Element>& elements, 268 base::FilePath* profile_path) { 269 NSPasteboard* pb = PasteboardFromType(type); 270 271 elements.clear(); 272 NSString* profile = [pb stringForType:kChromiumProfilePathPboardType]; 273 *profile_path = base::FilePath(base::SysNSStringToUTF8(profile)); 274 return ReadBookmarkDictionaryListPboardType(pb, elements) || 275 ReadWebURLsWithTitlesPboardType(pb, elements) || 276 ReadNSURLPboardType(pb, elements); 277 } 278 279 bool PasteboardContainsBookmarks(PasteboardType type) { 280 NSPasteboard* pb = PasteboardFromType(type); 281 282 NSArray* availableTypes = 283 [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, 284 kCrWebURLsWithTitlesPboardType, 285 NSURLPboardType, 286 nil]; 287 return [pb availableTypeFromArray:availableTypes] != nil; 288 } 289 290 } // namespace bookmark_pasteboard_helper_mac 291