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.
5 #include "chrome/browser/bookmarks/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 "chrome/browser/bookmarks/bookmark_model.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 = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]);
104 std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]);
106 BookmarkNodeData::Element element;
107 element.is_url = true;
108 element.url = GURL(url);
109 element.title = title;
110 elements.push_back(element);
116 bool ReadNSURLPboardType(NSPasteboard* pb,
117 std::vector<BookmarkNodeData::Element>& elements) {
118 NSURL* url = [NSURL URLFromPasteboard:pb];
122 std::string urlString = base::SysNSStringToUTF8([url absoluteString]);
123 NSString* title = [pb stringForType:kNSURLTitlePboardType];
125 title = [pb stringForType:NSStringPboardType];
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);
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",
150 kWebBookmarkTypeLeaf, kWebBookmarkType,
151 idNum, kChromiumBookmarkId,
153 [plist addObject:object];
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:
161 children, @"Children",
162 kWebBookmarkTypeList, kWebBookmarkType,
163 idNum, kChromiumBookmarkId,
165 [plist addObject:object];
171 void WriteBookmarkDictionaryListPboardType(
173 const std::vector<BookmarkNodeData::Element>& elements) {
174 NSArray* plist = GetPlistForBookmarkList(elements);
175 [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
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];
190 FillFlattenedArraysForBookmarks(element.children, titles, urls);
195 void WriteSimplifiedBookmarkTypes(
197 const std::vector<BookmarkNodeData::Element>& elements) {
198 NSMutableArray* titles = [NSMutableArray array];
199 NSMutableArray* urls = [NSMutableArray array];
200 FillFlattenedArraysForBookmarks(elements, titles, urls);
202 // These bookmark types only act on urls, not folders.
203 if ([urls count] < 1)
206 // Write WebURLsWithTitlesPboardType.
207 [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil]
208 forType:kCrWebURLsWithTitlesPboardType];
210 // Write NSStringPboardType.
211 [pb setString:[urls componentsJoinedByString:@"\n"]
212 forType:NSStringPboardType];
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];
221 NSPasteboard* PasteboardFromType(ui::ClipboardType type) {
222 NSString* type_string = nil;
224 case ui::CLIPBOARD_TYPE_COPY_PASTE:
225 type_string = NSGeneralPboard;
227 case ui::CLIPBOARD_TYPE_DRAG:
228 type_string = NSDragPboard;
230 case ui::CLIPBOARD_TYPE_SELECTION:
235 return [NSPasteboard pasteboardWithName:type_string];
240 void WriteBookmarksToPasteboard(
241 ui::ClipboardType type,
242 const std::vector<BookmarkNodeData::Element>& elements,
243 const base::FilePath& profile_path) {
244 if (elements.empty())
247 NSPasteboard* pb = PasteboardFromType(type);
249 NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
250 kCrWebURLsWithTitlesPboardType,
253 kNSURLTitlePboardType,
254 kChromiumProfilePathPboardType,
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);
263 bool ReadBookmarksFromPasteboard(
264 ui::ClipboardType type,
265 std::vector<BookmarkNodeData::Element>& elements,
266 base::FilePath* profile_path) {
267 NSPasteboard* pb = PasteboardFromType(type);
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);
277 bool PasteboardContainsBookmarks(ui::ClipboardType type) {
278 NSPasteboard* pb = PasteboardFromType(type);
280 NSArray* availableTypes =
281 [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
282 kCrWebURLsWithTitlesPboardType,
285 return [pb availableTypeFromArray:availableTypes] != nil;