1 // Copyright 2014 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.
5 #include "components/bookmarks/browser/bookmark_pasteboard_helper_mac.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/files/file_path.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "components/bookmarks/browser/bookmark_node.h"
12 #include "ui/base/clipboard/clipboard.h"
14 NSString* const kBookmarkDictionaryListPboardType =
15 @"BookmarkDictionaryListPboardType";
19 // An unofficial standard pasteboard title type to be provided alongside the
21 NSString* const kNSURLTitlePboardType = @"public.url-name";
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";
28 // Internal bookmark ID for a bookmark node. Used only when moving inside
30 NSString* const kChromiumBookmarkId = @"ChromiumBookmarkId";
32 // Mac WebKit uses this type, declared in
33 // WebKit/mac/History/WebURLsWithTitles.h.
34 NSString* const kCrWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType";
36 // Keys for the type of node in BookmarkDictionaryListPboardType.
37 NSString* const kWebBookmarkType = @"WebBookmarkType";
39 NSString* const kWebBookmarkTypeList = @"WebBookmarkTypeList";
41 NSString* const kWebBookmarkTypeLeaf = @"WebBookmarkTypeLeaf";
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()));
50 [[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue];
51 new_node->set_id(node_id);
52 BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType]
53 isEqualToString:kWebBookmarkTypeList];
55 new_node->set_type(BookmarkNode::FOLDER);
56 NSString* title = [pboardBookmark objectForKey:@"Title"];
57 new_node->SetTitle(base::SysNSStringToUTF16(title));
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)));
67 BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get());
69 ConvertPlistToElements([pboardBookmark objectForKey:@"Children"],
71 elements.push_back(e);
75 bool ReadBookmarkDictionaryListPboardType(
77 std::vector<BookmarkNodeData::Element>& elements) {
79 [pb propertyListForType:kBookmarkDictionaryListPboardType];
82 ConvertPlistToElements(bookmarks, elements);
86 bool ReadWebURLsWithTitlesPboardType(
88 std::vector<BookmarkNodeData::Element>& elements) {
89 NSArray* bookmarkPairs =
90 [pb propertyListForType:kCrWebURLsWithTitlesPboardType];
91 if (![bookmarkPairs isKindOfClass:[NSArray class]])
94 NSArray* urlsArr = [bookmarkPairs objectAtIndex:0];
95 NSArray* titlesArr = [bookmarkPairs objectAtIndex:1];
96 if ([urlsArr count] < 1)
98 if ([urlsArr count] != [titlesArr count])
101 NSUInteger len = [titlesArr count];
102 for (NSUInteger i = 0; i < len; ++i) {
103 base::string16 title =
104 base::SysNSStringToUTF16([titlesArr objectAtIndex:i]);
105 std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]);
107 BookmarkNodeData::Element element;
108 element.is_url = true;
109 element.url = GURL(url);
110 element.title = title;
111 elements.push_back(element);
117 bool ReadNSURLPboardType(NSPasteboard* pb,
118 std::vector<BookmarkNodeData::Element>& elements) {
119 NSURL* url = [NSURL URLFromPasteboard:pb];
123 std::string urlString = base::SysNSStringToUTF8([url absoluteString]);
124 NSString* title = [pb stringForType:kNSURLTitlePboardType];
126 title = [pb stringForType:NSStringPboardType];
128 BookmarkNodeData::Element element;
129 element.is_url = true;
130 element.url = GURL(urlString);
131 element.title = base::SysNSStringToUTF16(title);
132 elements.push_back(element);
136 NSArray* GetPlistForBookmarkList(
137 const std::vector<BookmarkNodeData::Element>& elements) {
138 NSMutableArray* plist = [NSMutableArray array];
139 for (size_t i = 0; i < elements.size(); ++i) {
140 BookmarkNodeData::Element element = elements[i];
141 if (element.is_url) {
142 NSString* title = base::SysUTF16ToNSString(element.title);
143 NSString* url = base::SysUTF8ToNSString(element.url.spec());
144 int64 elementId = element.id();
145 NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
146 NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
147 title, @"title", nil];
148 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
149 uriDictionary, @"URIDictionary",
151 kWebBookmarkTypeLeaf, kWebBookmarkType,
152 idNum, kChromiumBookmarkId,
154 [plist addObject:object];
156 NSString* title = base::SysUTF16ToNSString(element.title);
157 NSArray* children = GetPlistForBookmarkList(element.children);
158 int64 elementId = element.id();
159 NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
160 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
162 children, @"Children",
163 kWebBookmarkTypeList, kWebBookmarkType,
164 idNum, kChromiumBookmarkId,
166 [plist addObject:object];
172 void WriteBookmarkDictionaryListPboardType(
174 const std::vector<BookmarkNodeData::Element>& elements) {
175 NSArray* plist = GetPlistForBookmarkList(elements);
176 [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
179 void FillFlattenedArraysForBookmarks(
180 const std::vector<BookmarkNodeData::Element>& elements,
181 NSMutableArray* titles,
182 NSMutableArray* urls) {
183 for (size_t i = 0; i < elements.size(); ++i) {
184 BookmarkNodeData::Element element = elements[i];
185 if (element.is_url) {
186 NSString* title = base::SysUTF16ToNSString(element.title);
187 NSString* url = base::SysUTF8ToNSString(element.url.spec());
188 [titles addObject:title];
189 [urls addObject:url];
191 FillFlattenedArraysForBookmarks(element.children, titles, urls);
196 void WriteSimplifiedBookmarkTypes(
198 const std::vector<BookmarkNodeData::Element>& elements) {
199 NSMutableArray* titles = [NSMutableArray array];
200 NSMutableArray* urls = [NSMutableArray array];
201 FillFlattenedArraysForBookmarks(elements, titles, urls);
203 // These bookmark types only act on urls, not folders.
204 if ([urls count] < 1)
207 // Write WebURLsWithTitlesPboardType.
208 [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil]
209 forType:kCrWebURLsWithTitlesPboardType];
211 // Write NSStringPboardType.
212 [pb setString:[urls componentsJoinedByString:@"\n"]
213 forType:NSStringPboardType];
215 // Write NSURLPboardType (with title).
216 NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]];
217 [url writeToPasteboard:pb];
218 NSString* titleString = [titles objectAtIndex:0];
219 [pb setString:titleString forType:kNSURLTitlePboardType];
222 NSPasteboard* PasteboardFromType(ui::ClipboardType type) {
223 NSString* type_string = nil;
225 case ui::CLIPBOARD_TYPE_COPY_PASTE:
226 type_string = NSGeneralPboard;
228 case ui::CLIPBOARD_TYPE_DRAG:
229 type_string = NSDragPboard;
231 case ui::CLIPBOARD_TYPE_SELECTION:
236 return [NSPasteboard pasteboardWithName:type_string];
241 void WriteBookmarksToPasteboard(
242 ui::ClipboardType type,
243 const std::vector<BookmarkNodeData::Element>& elements,
244 const base::FilePath& profile_path) {
245 if (elements.empty())
248 NSPasteboard* pb = PasteboardFromType(type);
250 NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
251 kCrWebURLsWithTitlesPboardType,
254 kNSURLTitlePboardType,
255 kChromiumProfilePathPboardType,
257 [pb declareTypes:types owner:nil];
258 [pb setString:base::SysUTF8ToNSString(profile_path.value())
259 forType:kChromiumProfilePathPboardType];
260 WriteBookmarkDictionaryListPboardType(pb, elements);
261 WriteSimplifiedBookmarkTypes(pb, elements);
264 bool ReadBookmarksFromPasteboard(
265 ui::ClipboardType type,
266 std::vector<BookmarkNodeData::Element>& elements,
267 base::FilePath* profile_path) {
268 NSPasteboard* pb = PasteboardFromType(type);
271 NSString* profile = [pb stringForType:kChromiumProfilePathPboardType];
272 *profile_path = base::FilePath(base::SysNSStringToUTF8(profile));
273 return ReadBookmarkDictionaryListPboardType(pb, elements) ||
274 ReadWebURLsWithTitlesPboardType(pb, elements) ||
275 ReadNSURLPboardType(pb, elements);
278 bool PasteboardContainsBookmarks(ui::ClipboardType type) {
279 NSPasteboard* pb = PasteboardFromType(type);
281 NSArray* availableTypes =
282 [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
283 kCrWebURLsWithTitlesPboardType,
286 return [pb availableTypeFromArray:availableTypes] != nil;