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 = @"public.url-name"; 22 23 // Pasteboard type used to store profile path to determine which profile 24 // a set of bookmarks came from. 25 NSString* const kChromiumProfilePathPboardType = 26 @"ChromiumProfilePathPboardType"; 27 28 // Internal bookmark ID for a bookmark node. Used only when moving inside 29 // of one profile. 30 NSString* const kChromiumBookmarkId = @"ChromiumBookmarkId"; 31 32 // Mac WebKit uses this type, declared in 33 // WebKit/mac/History/WebURLsWithTitles.h. 34 NSString* const kCrWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType"; 35 36 // Keys for the type of node in BookmarkDictionaryListPboardType. 37 NSString* const kWebBookmarkType = @"WebBookmarkType"; 38 39 NSString* const kWebBookmarkTypeList = @"WebBookmarkTypeList"; 40 41 NSString* const kWebBookmarkTypeLeaf = @"WebBookmarkTypeLeaf"; 42 43 void ConvertPlistToElements(NSArray* input, 44 std::vector<BookmarkNodeData::Element>& elements) { 45 NSUInteger len = [input count]; 46 for (NSUInteger i = 0; i < len; ++i) { 47 NSDictionary* pboardBookmark = [input objectAtIndex:i]; 48 scoped_ptr<BookmarkNode> new_node(new BookmarkNode(GURL())); 49 int64 node_id = 50 [[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue]; 51 new_node->set_id(node_id); 52 BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType] 53 isEqualToString:kWebBookmarkTypeList]; 54 if (is_folder) { 55 new_node->set_type(BookmarkNode::FOLDER); 56 NSString* title = [pboardBookmark objectForKey:@"Title"]; 57 new_node->SetTitle(base::SysNSStringToUTF16(title)); 58 } else { 59 new_node->set_type(BookmarkNode::URL); 60 NSDictionary* uriDictionary = 61 [pboardBookmark objectForKey:@"URIDictionary"]; 62 NSString* title = [uriDictionary objectForKey:@"title"]; 63 NSString* urlString = [pboardBookmark objectForKey:@"URLString"]; 64 new_node->SetTitle(base::SysNSStringToUTF16(title)); 65 new_node->set_url(GURL(base::SysNSStringToUTF8(urlString))); 66 } 67 BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get()); 68 if(is_folder) 69 ConvertPlistToElements([pboardBookmark objectForKey:@"Children"], 70 e.children); 71 elements.push_back(e); 72 } 73 } 74 75 bool ReadBookmarkDictionaryListPboardType( 76 NSPasteboard* pb, 77 std::vector<BookmarkNodeData::Element>& elements) { 78 NSArray* bookmarks = 79 [pb propertyListForType:kBookmarkDictionaryListPboardType]; 80 if (!bookmarks) 81 return false; 82 ConvertPlistToElements(bookmarks, elements); 83 return true; 84 } 85 86 bool ReadWebURLsWithTitlesPboardType( 87 NSPasteboard* pb, 88 std::vector<BookmarkNodeData::Element>& elements) { 89 NSArray* bookmarkPairs = 90 [pb propertyListForType:kCrWebURLsWithTitlesPboardType]; 91 if (![bookmarkPairs isKindOfClass:[NSArray class]]) 92 return false; 93 94 NSArray* urlsArr = [bookmarkPairs objectAtIndex:0]; 95 NSArray* titlesArr = [bookmarkPairs objectAtIndex:1]; 96 if ([urlsArr count] < 1) 97 return false; 98 if ([urlsArr count] != [titlesArr count]) 99 return false; 100 101 NSUInteger len = [titlesArr count]; 102 for (NSUInteger i = 0; i < len; ++i) { 103 base::string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]); 104 std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]); 105 if (!url.empty()) { 106 BookmarkNodeData::Element element; 107 element.is_url = true; 108 element.url = GURL(url); 109 element.title = title; 110 elements.push_back(element); 111 } 112 } 113 return true; 114 } 115 116 bool ReadNSURLPboardType(NSPasteboard* pb, 117 std::vector<BookmarkNodeData::Element>& elements) { 118 NSURL* url = [NSURL URLFromPasteboard:pb]; 119 if (url == nil) 120 return false; 121 122 std::string urlString = base::SysNSStringToUTF8([url absoluteString]); 123 NSString* title = [pb stringForType:kNSURLTitlePboardType]; 124 if (!title) 125 title = [pb stringForType:NSStringPboardType]; 126 127 BookmarkNodeData::Element element; 128 element.is_url = true; 129 element.url = GURL(urlString); 130 element.title = base::SysNSStringToUTF16(title); 131 elements.push_back(element); 132 return true; 133 } 134 135 NSArray* GetPlistForBookmarkList( 136 const std::vector<BookmarkNodeData::Element>& elements) { 137 NSMutableArray* plist = [NSMutableArray array]; 138 for (size_t i = 0; i < elements.size(); ++i) { 139 BookmarkNodeData::Element element = elements[i]; 140 if (element.is_url) { 141 NSString* title = base::SysUTF16ToNSString(element.title); 142 NSString* url = base::SysUTF8ToNSString(element.url.spec()); 143 int64 elementId = element.id(); 144 NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; 145 NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 146 title, @"title", nil]; 147 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: 148 uriDictionary, @"URIDictionary", 149 url, @"URLString", 150 kWebBookmarkTypeLeaf, kWebBookmarkType, 151 idNum, kChromiumBookmarkId, 152 nil]; 153 [plist addObject:object]; 154 } else { 155 NSString* title = base::SysUTF16ToNSString(element.title); 156 NSArray* children = GetPlistForBookmarkList(element.children); 157 int64 elementId = element.id(); 158 NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; 159 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: 160 title, @"Title", 161 children, @"Children", 162 kWebBookmarkTypeList, kWebBookmarkType, 163 idNum, kChromiumBookmarkId, 164 nil]; 165 [plist addObject:object]; 166 } 167 } 168 return plist; 169 } 170 171 void WriteBookmarkDictionaryListPboardType( 172 NSPasteboard* pb, 173 const std::vector<BookmarkNodeData::Element>& elements) { 174 NSArray* plist = GetPlistForBookmarkList(elements); 175 [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType]; 176 } 177 178 void FillFlattenedArraysForBookmarks( 179 const std::vector<BookmarkNodeData::Element>& elements, 180 NSMutableArray* titles, 181 NSMutableArray* urls) { 182 for (size_t i = 0; i < elements.size(); ++i) { 183 BookmarkNodeData::Element element = elements[i]; 184 if (element.is_url) { 185 NSString* title = base::SysUTF16ToNSString(element.title); 186 NSString* url = base::SysUTF8ToNSString(element.url.spec()); 187 [titles addObject:title]; 188 [urls addObject:url]; 189 } else { 190 FillFlattenedArraysForBookmarks(element.children, titles, urls); 191 } 192 } 193 } 194 195 void WriteSimplifiedBookmarkTypes( 196 NSPasteboard* pb, 197 const std::vector<BookmarkNodeData::Element>& elements) { 198 NSMutableArray* titles = [NSMutableArray array]; 199 NSMutableArray* urls = [NSMutableArray array]; 200 FillFlattenedArraysForBookmarks(elements, titles, urls); 201 202 // These bookmark types only act on urls, not folders. 203 if ([urls count] < 1) 204 return; 205 206 // Write WebURLsWithTitlesPboardType. 207 [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil] 208 forType:kCrWebURLsWithTitlesPboardType]; 209 210 // Write NSStringPboardType. 211 [pb setString:[urls componentsJoinedByString:@"\n"] 212 forType:NSStringPboardType]; 213 214 // Write NSURLPboardType (with title). 215 NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]]; 216 [url writeToPasteboard:pb]; 217 NSString* titleString = [titles objectAtIndex:0]; 218 [pb setString:titleString forType:kNSURLTitlePboardType]; 219 } 220 221 NSPasteboard* PasteboardFromType(ui::ClipboardType type) { 222 NSString* type_string = nil; 223 switch (type) { 224 case ui::CLIPBOARD_TYPE_COPY_PASTE: 225 type_string = NSGeneralPboard; 226 break; 227 case ui::CLIPBOARD_TYPE_DRAG: 228 type_string = NSDragPboard; 229 break; 230 case ui::CLIPBOARD_TYPE_SELECTION: 231 NOTREACHED(); 232 break; 233 } 234 235 return [NSPasteboard pasteboardWithName:type_string]; 236 } 237 238 } // namespace 239 240 void WriteBookmarksToPasteboard( 241 ui::ClipboardType type, 242 const std::vector<BookmarkNodeData::Element>& elements, 243 const base::FilePath& profile_path) { 244 if (elements.empty()) 245 return; 246 247 NSPasteboard* pb = PasteboardFromType(type); 248 249 NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, 250 kCrWebURLsWithTitlesPboardType, 251 NSStringPboardType, 252 NSURLPboardType, 253 kNSURLTitlePboardType, 254 kChromiumProfilePathPboardType, 255 nil]; 256 [pb declareTypes:types owner:nil]; 257 [pb setString:base::SysUTF8ToNSString(profile_path.value()) 258 forType:kChromiumProfilePathPboardType]; 259 WriteBookmarkDictionaryListPboardType(pb, elements); 260 WriteSimplifiedBookmarkTypes(pb, elements); 261 } 262 263 bool ReadBookmarksFromPasteboard( 264 ui::ClipboardType type, 265 std::vector<BookmarkNodeData::Element>& elements, 266 base::FilePath* profile_path) { 267 NSPasteboard* pb = PasteboardFromType(type); 268 269 elements.clear(); 270 NSString* profile = [pb stringForType:kChromiumProfilePathPboardType]; 271 *profile_path = base::FilePath(base::SysNSStringToUTF8(profile)); 272 return ReadBookmarkDictionaryListPboardType(pb, elements) || 273 ReadWebURLsWithTitlesPboardType(pb, elements) || 274 ReadNSURLPboardType(pb, elements); 275 } 276 277 bool PasteboardContainsBookmarks(ui::ClipboardType type) { 278 NSPasteboard* pb = PasteboardFromType(type); 279 280 NSArray* availableTypes = 281 [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, 282 kCrWebURLsWithTitlesPboardType, 283 NSURLPboardType, 284 nil]; 285 return [pb availableTypeFromArray:availableTypes] != nil; 286 } 287