• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
14NSString* const kBookmarkDictionaryListPboardType =
15    @"BookmarkDictionaryListPboardType";
16
17namespace {
18
19// An unofficial standard pasteboard title type to be provided alongside the
20// |NSURLPboardType|.
21NSString* 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.
25NSString* const kChromiumProfilePathPboardType =
26    @"ChromiumProfilePathPboardType";
27
28// Internal bookmark ID for a bookmark node.  Used only when moving inside
29// of one profile.
30NSString* const kChromiumBookmarkId = @"ChromiumBookmarkId";
31
32// Mac WebKit uses this type, declared in
33// WebKit/mac/History/WebURLsWithTitles.h.
34NSString* const kCrWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType";
35
36// Keys for the type of node in BookmarkDictionaryListPboardType.
37NSString* const kWebBookmarkType = @"WebBookmarkType";
38
39NSString* const kWebBookmarkTypeList = @"WebBookmarkTypeList";
40
41NSString* const kWebBookmarkTypeLeaf = @"WebBookmarkTypeLeaf";
42
43void 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
75bool 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
86bool 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
116bool 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
135NSArray* 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
171void WriteBookmarkDictionaryListPboardType(
172    NSPasteboard* pb,
173    const std::vector<BookmarkNodeData::Element>& elements) {
174  NSArray* plist = GetPlistForBookmarkList(elements);
175  [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
176}
177
178void 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
195void 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
221NSPasteboard* 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
240void 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
263bool 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
277bool 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