// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" #import #include "base/files/file_path.h" #include "base/strings/sys_string_conversions.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "ui/base/clipboard/clipboard.h" NSString* const kBookmarkDictionaryListPboardType = @"BookmarkDictionaryListPboardType"; namespace { // An unofficial standard pasteboard title type to be provided alongside the // |NSURLPboardType|. NSString* const kNSURLTitlePboardType = @"public.url-name"; // Pasteboard type used to store profile path to determine which profile // a set of bookmarks came from. NSString* const kChromiumProfilePathPboardType = @"ChromiumProfilePathPboardType"; // Internal bookmark ID for a bookmark node. Used only when moving inside // of one profile. NSString* const kChromiumBookmarkId = @"ChromiumBookmarkId"; // Mac WebKit uses this type, declared in // WebKit/mac/History/WebURLsWithTitles.h. NSString* const kCrWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType"; // Keys for the type of node in BookmarkDictionaryListPboardType. NSString* const kWebBookmarkType = @"WebBookmarkType"; NSString* const kWebBookmarkTypeList = @"WebBookmarkTypeList"; NSString* const kWebBookmarkTypeLeaf = @"WebBookmarkTypeLeaf"; void ConvertPlistToElements(NSArray* input, std::vector& elements) { NSUInteger len = [input count]; for (NSUInteger i = 0; i < len; ++i) { NSDictionary* pboardBookmark = [input objectAtIndex:i]; scoped_ptr new_node(new BookmarkNode(GURL())); int64 node_id = [[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue]; new_node->set_id(node_id); BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType] isEqualToString:kWebBookmarkTypeList]; if (is_folder) { new_node->set_type(BookmarkNode::FOLDER); NSString* title = [pboardBookmark objectForKey:@"Title"]; new_node->SetTitle(base::SysNSStringToUTF16(title)); } else { new_node->set_type(BookmarkNode::URL); NSDictionary* uriDictionary = [pboardBookmark objectForKey:@"URIDictionary"]; NSString* title = [uriDictionary objectForKey:@"title"]; NSString* urlString = [pboardBookmark objectForKey:@"URLString"]; new_node->SetTitle(base::SysNSStringToUTF16(title)); new_node->set_url(GURL(base::SysNSStringToUTF8(urlString))); } BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get()); if(is_folder) ConvertPlistToElements([pboardBookmark objectForKey:@"Children"], e.children); elements.push_back(e); } } bool ReadBookmarkDictionaryListPboardType( NSPasteboard* pb, std::vector& elements) { NSArray* bookmarks = [pb propertyListForType:kBookmarkDictionaryListPboardType]; if (!bookmarks) return false; ConvertPlistToElements(bookmarks, elements); return true; } bool ReadWebURLsWithTitlesPboardType( NSPasteboard* pb, std::vector& elements) { NSArray* bookmarkPairs = [pb propertyListForType:kCrWebURLsWithTitlesPboardType]; if (![bookmarkPairs isKindOfClass:[NSArray class]]) return false; NSArray* urlsArr = [bookmarkPairs objectAtIndex:0]; NSArray* titlesArr = [bookmarkPairs objectAtIndex:1]; if ([urlsArr count] < 1) return false; if ([urlsArr count] != [titlesArr count]) return false; NSUInteger len = [titlesArr count]; for (NSUInteger i = 0; i < len; ++i) { string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]); std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]); if (!url.empty()) { BookmarkNodeData::Element element; element.is_url = true; element.url = GURL(url); element.title = title; elements.push_back(element); } } return true; } bool ReadNSURLPboardType(NSPasteboard* pb, std::vector& elements) { NSURL* url = [NSURL URLFromPasteboard:pb]; if (url == nil) return false; std::string urlString = base::SysNSStringToUTF8([url absoluteString]); NSString* title = [pb stringForType:kNSURLTitlePboardType]; if (!title) title = [pb stringForType:NSStringPboardType]; BookmarkNodeData::Element element; element.is_url = true; element.url = GURL(urlString); element.title = base::SysNSStringToUTF16(title); elements.push_back(element); return true; } NSArray* GetPlistForBookmarkList( const std::vector& elements) { NSMutableArray* plist = [NSMutableArray array]; for (size_t i = 0; i < elements.size(); ++i) { BookmarkNodeData::Element element = elements[i]; if (element.is_url) { NSString* title = base::SysUTF16ToNSString(element.title); NSString* url = base::SysUTF8ToNSString(element.url.spec()); int64 elementId = element.id(); NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys: title, @"title", nil]; NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: uriDictionary, @"URIDictionary", url, @"URLString", kWebBookmarkTypeLeaf, kWebBookmarkType, idNum, kChromiumBookmarkId, nil]; [plist addObject:object]; } else { NSString* title = base::SysUTF16ToNSString(element.title); NSArray* children = GetPlistForBookmarkList(element.children); int64 elementId = element.id(); NSNumber* idNum = [NSNumber numberWithLongLong:elementId]; NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys: title, @"Title", children, @"Children", kWebBookmarkTypeList, kWebBookmarkType, idNum, kChromiumBookmarkId, nil]; [plist addObject:object]; } } return plist; } void WriteBookmarkDictionaryListPboardType( NSPasteboard* pb, const std::vector& elements) { NSArray* plist = GetPlistForBookmarkList(elements); [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType]; } void FillFlattenedArraysForBookmarks( const std::vector& elements, NSMutableArray* titles, NSMutableArray* urls) { for (size_t i = 0; i < elements.size(); ++i) { BookmarkNodeData::Element element = elements[i]; if (element.is_url) { NSString* title = base::SysUTF16ToNSString(element.title); NSString* url = base::SysUTF8ToNSString(element.url.spec()); [titles addObject:title]; [urls addObject:url]; } else { FillFlattenedArraysForBookmarks(element.children, titles, urls); } } } void WriteSimplifiedBookmarkTypes(NSPasteboard* pb, const std::vector& elements) { NSMutableArray* titles = [NSMutableArray array]; NSMutableArray* urls = [NSMutableArray array]; FillFlattenedArraysForBookmarks(elements, titles, urls); // These bookmark types only act on urls, not folders. if ([urls count] < 1) return; // Write WebURLsWithTitlesPboardType. [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil] forType:kCrWebURLsWithTitlesPboardType]; // Write NSStringPboardType. [pb setString:[urls componentsJoinedByString:@"\n"] forType:NSStringPboardType]; // Write NSURLPboardType (with title). NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]]; [url writeToPasteboard:pb]; NSString* titleString = [titles objectAtIndex:0]; [pb setString:titleString forType:kNSURLTitlePboardType]; } NSPasteboard* PasteboardFromType( bookmark_pasteboard_helper_mac::PasteboardType type) { NSString* type_string = nil; switch (type) { case bookmark_pasteboard_helper_mac::kCopyPastePasteboard: type_string = NSGeneralPboard; break; case bookmark_pasteboard_helper_mac::kDragPasteboard: type_string = NSDragPboard; break; } return [NSPasteboard pasteboardWithName:type_string]; } } // namespace namespace bookmark_pasteboard_helper_mac { void WriteToPasteboard(PasteboardType type, const std::vector& elements, const base::FilePath& profile_path) { if (elements.empty()) return; NSPasteboard* pb = PasteboardFromType(type); NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, kCrWebURLsWithTitlesPboardType, NSStringPboardType, NSURLPboardType, kNSURLTitlePboardType, kChromiumProfilePathPboardType, nil]; [pb declareTypes:types owner:nil]; [pb setString:base::SysUTF8ToNSString(profile_path.value()) forType:kChromiumProfilePathPboardType]; WriteBookmarkDictionaryListPboardType(pb, elements); WriteSimplifiedBookmarkTypes(pb, elements); } bool ReadFromPasteboard(PasteboardType type, std::vector& elements, base::FilePath* profile_path) { NSPasteboard* pb = PasteboardFromType(type); elements.clear(); NSString* profile = [pb stringForType:kChromiumProfilePathPboardType]; *profile_path = base::FilePath(base::SysNSStringToUTF8(profile)); return ReadBookmarkDictionaryListPboardType(pb, elements) || ReadWebURLsWithTitlesPboardType(pb, elements) || ReadNSURLPboardType(pb, elements); } bool PasteboardContainsBookmarks(PasteboardType type) { NSPasteboard* pb = PasteboardFromType(type); NSArray* availableTypes = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType, kCrWebURLsWithTitlesPboardType, NSURLPboardType, nil]; return [pb availableTypeFromArray:availableTypes] != nil; } } // namespace bookmark_pasteboard_helper_mac