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";
21 // An unofficial standard pasteboard title type to be provided alongside the
23 NSString* const kNSURLTitlePboardType = @"public.url-name";
25 // Pasteboard type used to store profile path to determine which profile
26 // a set of bookmarks came from.
27 NSString* const kChromiumProfilePathPboardType =
28 @"ChromiumProfilePathPboardType";
30 // Internal bookmark ID for a bookmark node. Used only when moving inside
32 NSString* const kChromiumBookmarkId = @"ChromiumBookmarkId";
34 // Internal bookmark meta info dictionary for a bookmark node.
35 NSString* const kChromiumBookmarkMetaInfo = @"ChromiumBookmarkMetaInfo";
37 // Mac WebKit uses this type, declared in
38 // WebKit/mac/History/WebURLsWithTitles.h.
39 NSString* const kCrWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType";
41 // Keys for the type of node in BookmarkDictionaryListPboardType.
42 NSString* const kWebBookmarkType = @"WebBookmarkType";
44 NSString* const kWebBookmarkTypeList = @"WebBookmarkTypeList";
46 NSString* const kWebBookmarkTypeLeaf = @"WebBookmarkTypeLeaf";
48 BookmarkNode::MetaInfoMap MetaInfoMapFromDictionary(NSDictionary* dictionary) {
49 BookmarkNode::MetaInfoMap meta_info_map;
51 for (NSString* key in dictionary) {
52 meta_info_map[base::SysNSStringToUTF8(key)] =
53 base::SysNSStringToUTF8([dictionary objectForKey:key]);
59 void ConvertPlistToElements(NSArray* input,
60 std::vector<BookmarkNodeData::Element>& elements) {
61 NSUInteger len = [input count];
62 for (NSUInteger i = 0; i < len; ++i) {
63 NSDictionary* pboardBookmark = [input objectAtIndex:i];
64 scoped_ptr<BookmarkNode> new_node(new BookmarkNode(GURL()));
66 [[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue];
67 new_node->set_id(node_id);
69 NSDictionary* metaInfoDictionary =
70 [pboardBookmark objectForKey:kChromiumBookmarkMetaInfo];
71 if (metaInfoDictionary)
72 new_node->SetMetaInfoMap(MetaInfoMapFromDictionary(metaInfoDictionary));
74 BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType]
75 isEqualToString:kWebBookmarkTypeList];
77 new_node->set_type(BookmarkNode::FOLDER);
78 NSString* title = [pboardBookmark objectForKey:@"Title"];
79 new_node->SetTitle(base::SysNSStringToUTF16(title));
81 new_node->set_type(BookmarkNode::URL);
82 NSDictionary* uriDictionary =
83 [pboardBookmark objectForKey:@"URIDictionary"];
84 NSString* title = [uriDictionary objectForKey:@"title"];
85 NSString* urlString = [pboardBookmark objectForKey:@"URLString"];
86 new_node->SetTitle(base::SysNSStringToUTF16(title));
87 new_node->set_url(GURL(base::SysNSStringToUTF8(urlString)));
89 BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get());
91 ConvertPlistToElements([pboardBookmark objectForKey:@"Children"],
94 elements.push_back(e);
98 bool ReadBookmarkDictionaryListPboardType(
100 std::vector<BookmarkNodeData::Element>& elements) {
102 [pb propertyListForType:kBookmarkDictionaryListPboardType];
105 ConvertPlistToElements(bookmarks, elements);
109 bool ReadWebURLsWithTitlesPboardType(
111 std::vector<BookmarkNodeData::Element>& elements) {
112 NSArray* bookmarkPairs =
113 [pb propertyListForType:kCrWebURLsWithTitlesPboardType];
114 if (![bookmarkPairs isKindOfClass:[NSArray class]])
117 NSArray* urlsArr = [bookmarkPairs objectAtIndex:0];
118 NSArray* titlesArr = [bookmarkPairs objectAtIndex:1];
119 if ([urlsArr count] < 1)
121 if ([urlsArr count] != [titlesArr count])
124 NSUInteger len = [titlesArr count];
125 for (NSUInteger i = 0; i < len; ++i) {
126 base::string16 title =
127 base::SysNSStringToUTF16([titlesArr objectAtIndex:i]);
128 std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]);
130 BookmarkNodeData::Element element;
131 element.is_url = true;
132 element.url = GURL(url);
133 element.title = title;
134 elements.push_back(element);
140 bool ReadNSURLPboardType(NSPasteboard* pb,
141 std::vector<BookmarkNodeData::Element>& elements) {
142 NSURL* url = [NSURL URLFromPasteboard:pb];
146 std::string urlString = base::SysNSStringToUTF8([url absoluteString]);
147 NSString* title = [pb stringForType:kNSURLTitlePboardType];
149 title = [pb stringForType:NSStringPboardType];
151 BookmarkNodeData::Element element;
152 element.is_url = true;
153 element.url = GURL(urlString);
154 element.title = base::SysNSStringToUTF16(title);
155 elements.push_back(element);
159 NSDictionary* DictionaryFromBookmarkMetaInfo(
160 const BookmarkNode::MetaInfoMap& meta_info_map) {
161 NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
163 for (BookmarkNode::MetaInfoMap::const_iterator it = meta_info_map.begin();
164 it != meta_info_map.end(); ++it) {
165 [dictionary setObject:base::SysUTF8ToNSString(it->second)
166 forKey:base::SysUTF8ToNSString(it->first)];
172 NSArray* GetPlistForBookmarkList(
173 const std::vector<BookmarkNodeData::Element>& elements) {
174 NSMutableArray* plist = [NSMutableArray array];
175 for (size_t i = 0; i < elements.size(); ++i) {
176 BookmarkNodeData::Element element = elements[i];
177 NSDictionary* metaInfoDictionary =
178 DictionaryFromBookmarkMetaInfo(element.meta_info_map);
179 if (element.is_url) {
180 NSString* title = base::SysUTF16ToNSString(element.title);
181 NSString* url = base::SysUTF8ToNSString(element.url.spec());
182 int64 elementId = element.id();
183 NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
184 NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
185 title, @"title", nil];
186 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
187 uriDictionary, @"URIDictionary",
189 kWebBookmarkTypeLeaf, kWebBookmarkType,
190 idNum, kChromiumBookmarkId,
191 metaInfoDictionary, kChromiumBookmarkMetaInfo,
193 [plist addObject:object];
195 NSString* title = base::SysUTF16ToNSString(element.title);
196 NSArray* children = GetPlistForBookmarkList(element.children);
197 int64 elementId = element.id();
198 NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
199 NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
201 children, @"Children",
202 kWebBookmarkTypeList, kWebBookmarkType,
203 idNum, kChromiumBookmarkId,
204 metaInfoDictionary, kChromiumBookmarkMetaInfo,
206 [plist addObject:object];
212 void WriteBookmarkDictionaryListPboardType(
214 const std::vector<BookmarkNodeData::Element>& elements) {
215 NSArray* plist = GetPlistForBookmarkList(elements);
216 [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
219 void FillFlattenedArraysForBookmarks(
220 const std::vector<BookmarkNodeData::Element>& elements,
221 NSMutableArray* url_titles,
222 NSMutableArray* urls,
223 NSMutableArray* toplevel_string_data) {
224 for (const BookmarkNodeData::Element& element : elements) {
225 NSString* title = base::SysUTF16ToNSString(element.title);
226 if (element.is_url) {
227 NSString* url = base::SysUTF8ToNSString(element.url.spec());
228 [url_titles addObject:title];
229 [urls addObject:url];
230 if (toplevel_string_data)
231 [toplevel_string_data addObject:url];
233 if (toplevel_string_data)
234 [toplevel_string_data addObject:title];
235 FillFlattenedArraysForBookmarks(element.children, url_titles, urls, nil);
240 void WriteSimplifiedBookmarkTypes(
242 const std::vector<BookmarkNodeData::Element>& elements) {
243 NSMutableArray* url_titles = [NSMutableArray array];
244 NSMutableArray* urls = [NSMutableArray array];
245 NSMutableArray* toplevel_string_data = [NSMutableArray array];
246 FillFlattenedArraysForBookmarks(
247 elements, url_titles, urls, toplevel_string_data);
249 // Write NSStringPboardType.
250 [pb setString:[toplevel_string_data componentsJoinedByString:@"\n"]
251 forType:NSStringPboardType];
253 // The following pasteboard types only act on urls, not folders.
254 if ([urls count] < 1)
257 // Write WebURLsWithTitlesPboardType.
258 [pb setPropertyList:[NSArray arrayWithObjects:urls, url_titles, nil]
259 forType:kCrWebURLsWithTitlesPboardType];
261 // Write NSURLPboardType (with title).
262 NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]];
263 [url writeToPasteboard:pb];
264 NSString* titleString = [url_titles objectAtIndex:0];
265 [pb setString:titleString forType:kNSURLTitlePboardType];
268 NSPasteboard* PasteboardFromType(ui::ClipboardType type) {
269 NSString* type_string = nil;
271 case ui::CLIPBOARD_TYPE_COPY_PASTE:
272 type_string = NSGeneralPboard;
274 case ui::CLIPBOARD_TYPE_DRAG:
275 type_string = NSDragPboard;
277 case ui::CLIPBOARD_TYPE_SELECTION:
282 return [NSPasteboard pasteboardWithName:type_string];
287 void WriteBookmarksToPasteboard(
288 ui::ClipboardType type,
289 const std::vector<BookmarkNodeData::Element>& elements,
290 const base::FilePath& profile_path) {
291 if (elements.empty())
294 NSPasteboard* pb = PasteboardFromType(type);
296 NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
297 kCrWebURLsWithTitlesPboardType,
300 kNSURLTitlePboardType,
301 kChromiumProfilePathPboardType,
303 [pb declareTypes:types owner:nil];
304 [pb setString:base::SysUTF8ToNSString(profile_path.value())
305 forType:kChromiumProfilePathPboardType];
306 WriteBookmarkDictionaryListPboardType(pb, elements);
307 WriteSimplifiedBookmarkTypes(pb, elements);
310 bool ReadBookmarksFromPasteboard(
311 ui::ClipboardType type,
312 std::vector<BookmarkNodeData::Element>& elements,
313 base::FilePath* profile_path) {
314 NSPasteboard* pb = PasteboardFromType(type);
317 NSString* profile = [pb stringForType:kChromiumProfilePathPboardType];
318 *profile_path = base::FilePath(base::SysNSStringToUTF8(profile));
319 return ReadBookmarkDictionaryListPboardType(pb, elements) ||
320 ReadWebURLsWithTitlesPboardType(pb, elements) ||
321 ReadNSURLPboardType(pb, elements);
324 bool PasteboardContainsBookmarks(ui::ClipboardType type) {
325 NSPasteboard* pb = PasteboardFromType(type);
327 NSArray* availableTypes =
328 [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
329 kCrWebURLsWithTitlesPboardType,
332 return [pb availableTypeFromArray:availableTypes] != nil;
335 } // namespace bookmarks