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