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 =
     22     @"public.url-name";
     23 
     24 // Pasteboard type used to store profile path to determine which profile
     25 // a set of bookmarks came from.
     26 NSString* const kChromiumProfilePathPboardType =
     27     @"ChromiumProfilePathPboardType";
     28 
     29 // Internal bookmark ID for a bookmark node.  Used only when moving inside
     30 // of one profile.
     31 NSString* const kChromiumBookmarkId =
     32     @"ChromiumBookmarkId";
     33 
     34 // Mac WebKit uses this type, declared in
     35 // WebKit/mac/History/WebURLsWithTitles.h.
     36 NSString* const kCrWebURLsWithTitlesPboardType =
     37     @"WebURLsWithTitlesPboardType";
     38 
     39 // Keys for the type of node in BookmarkDictionaryListPboardType.
     40 NSString* const kWebBookmarkType =
     41     @"WebBookmarkType";
     42 
     43 NSString* const kWebBookmarkTypeList =
     44     @"WebBookmarkTypeList";
     45 
     46 NSString* const kWebBookmarkTypeLeaf =
     47     @"WebBookmarkTypeLeaf";
     48 
     49 void ConvertPlistToElements(NSArray* input,
     50                             std::vector<BookmarkNodeData::Element>& elements) {
     51   NSUInteger len = [input count];
     52   for (NSUInteger i = 0; i < len; ++i) {
     53     NSDictionary* pboardBookmark = [input objectAtIndex:i];
     54     scoped_ptr<BookmarkNode> new_node(new BookmarkNode(GURL()));
     55     int64 node_id =
     56         [[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue];
     57     new_node->set_id(node_id);
     58     BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType]
     59         isEqualToString:kWebBookmarkTypeList];
     60     if (is_folder) {
     61       new_node->set_type(BookmarkNode::FOLDER);
     62       NSString* title = [pboardBookmark objectForKey:@"Title"];
     63       new_node->SetTitle(base::SysNSStringToUTF16(title));
     64     } else {
     65       new_node->set_type(BookmarkNode::URL);
     66       NSDictionary* uriDictionary =
     67           [pboardBookmark objectForKey:@"URIDictionary"];
     68       NSString* title = [uriDictionary objectForKey:@"title"];
     69       NSString* urlString = [pboardBookmark objectForKey:@"URLString"];
     70       new_node->SetTitle(base::SysNSStringToUTF16(title));
     71       new_node->set_url(GURL(base::SysNSStringToUTF8(urlString)));
     72     }
     73     BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get());
     74     if(is_folder)
     75       ConvertPlistToElements([pboardBookmark objectForKey:@"Children"],
     76                              e.children);
     77     elements.push_back(e);
     78   }
     79 }
     80 
     81 bool ReadBookmarkDictionaryListPboardType(
     82     NSPasteboard* pb,
     83     std::vector<BookmarkNodeData::Element>& elements) {
     84   NSArray* bookmarks =
     85       [pb propertyListForType:kBookmarkDictionaryListPboardType];
     86   if (!bookmarks)
     87     return false;
     88   ConvertPlistToElements(bookmarks, elements);
     89   return true;
     90 }
     91 
     92 bool ReadWebURLsWithTitlesPboardType(
     93     NSPasteboard* pb,
     94     std::vector<BookmarkNodeData::Element>& elements) {
     95   NSArray* bookmarkPairs =
     96       [pb propertyListForType:kCrWebURLsWithTitlesPboardType];
     97   if (![bookmarkPairs isKindOfClass:[NSArray class]])
     98     return false;
     99 
    100   NSArray* urlsArr = [bookmarkPairs objectAtIndex:0];
    101   NSArray* titlesArr = [bookmarkPairs objectAtIndex:1];
    102   if ([urlsArr count] < 1)
    103     return false;
    104   if ([urlsArr count] != [titlesArr count])
    105     return false;
    106 
    107   NSUInteger len = [titlesArr count];
    108   for (NSUInteger i = 0; i < len; ++i) {
    109     string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]);
    110     std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]);
    111     if (!url.empty()) {
    112       BookmarkNodeData::Element element;
    113       element.is_url = true;
    114       element.url = GURL(url);
    115       element.title = title;
    116       elements.push_back(element);
    117     }
    118   }
    119   return true;
    120 }
    121 
    122 bool ReadNSURLPboardType(NSPasteboard* pb,
    123                          std::vector<BookmarkNodeData::Element>& elements) {
    124   NSURL* url = [NSURL URLFromPasteboard:pb];
    125   if (url == nil)
    126     return false;
    127 
    128   std::string urlString = base::SysNSStringToUTF8([url absoluteString]);
    129   NSString* title = [pb stringForType:kNSURLTitlePboardType];
    130   if (!title)
    131     title = [pb stringForType:NSStringPboardType];
    132 
    133   BookmarkNodeData::Element element;
    134   element.is_url = true;
    135   element.url = GURL(urlString);
    136   element.title = base::SysNSStringToUTF16(title);
    137   elements.push_back(element);
    138   return true;
    139 }
    140 
    141 NSArray* GetPlistForBookmarkList(
    142     const std::vector<BookmarkNodeData::Element>& elements) {
    143   NSMutableArray* plist = [NSMutableArray array];
    144   for (size_t i = 0; i < elements.size(); ++i) {
    145     BookmarkNodeData::Element element = elements[i];
    146     if (element.is_url) {
    147       NSString* title = base::SysUTF16ToNSString(element.title);
    148       NSString* url = base::SysUTF8ToNSString(element.url.spec());
    149       int64 elementId = element.id();
    150       NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
    151       NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
    152               title, @"title", nil];
    153       NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
    154           uriDictionary, @"URIDictionary",
    155           url, @"URLString",
    156           kWebBookmarkTypeLeaf, kWebBookmarkType,
    157           idNum, kChromiumBookmarkId,
    158           nil];
    159       [plist addObject:object];
    160     } else {
    161       NSString* title = base::SysUTF16ToNSString(element.title);
    162       NSArray* children = GetPlistForBookmarkList(element.children);
    163       int64 elementId = element.id();
    164       NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
    165       NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
    166           title, @"Title",
    167           children, @"Children",
    168           kWebBookmarkTypeList, kWebBookmarkType,
    169           idNum, kChromiumBookmarkId,
    170           nil];
    171       [plist addObject:object];
    172     }
    173   }
    174   return plist;
    175 }
    176 
    177 void WriteBookmarkDictionaryListPboardType(
    178     NSPasteboard* pb,
    179     const std::vector<BookmarkNodeData::Element>& elements) {
    180   NSArray* plist = GetPlistForBookmarkList(elements);
    181   [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
    182 }
    183 
    184 void FillFlattenedArraysForBookmarks(
    185     const std::vector<BookmarkNodeData::Element>& elements,
    186     NSMutableArray* titles, NSMutableArray* urls) {
    187   for (size_t i = 0; i < elements.size(); ++i) {
    188     BookmarkNodeData::Element element = elements[i];
    189     if (element.is_url) {
    190       NSString* title = base::SysUTF16ToNSString(element.title);
    191       NSString* url = base::SysUTF8ToNSString(element.url.spec());
    192       [titles addObject:title];
    193       [urls addObject:url];
    194     } else {
    195       FillFlattenedArraysForBookmarks(element.children, titles, urls);
    196     }
    197   }
    198 }
    199 
    200 void WriteSimplifiedBookmarkTypes(NSPasteboard* pb,
    201     const std::vector<BookmarkNodeData::Element>& elements) {
    202   NSMutableArray* titles = [NSMutableArray array];
    203   NSMutableArray* urls = [NSMutableArray array];
    204   FillFlattenedArraysForBookmarks(elements, titles, urls);
    205 
    206   // These bookmark types only act on urls, not folders.
    207   if ([urls count] < 1)
    208     return;
    209 
    210   // Write WebURLsWithTitlesPboardType.
    211   [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil]
    212               forType:kCrWebURLsWithTitlesPboardType];
    213 
    214   // Write NSStringPboardType.
    215   [pb setString:[urls componentsJoinedByString:@"\n"]
    216         forType:NSStringPboardType];
    217 
    218   // Write NSURLPboardType (with title).
    219   NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]];
    220   [url writeToPasteboard:pb];
    221   NSString* titleString = [titles objectAtIndex:0];
    222   [pb setString:titleString forType:kNSURLTitlePboardType];
    223 }
    224 
    225 NSPasteboard* PasteboardFromType(
    226     bookmark_pasteboard_helper_mac::PasteboardType type) {
    227   NSString* type_string = nil;
    228   switch (type) {
    229     case bookmark_pasteboard_helper_mac::kCopyPastePasteboard:
    230       type_string = NSGeneralPboard;
    231       break;
    232     case bookmark_pasteboard_helper_mac::kDragPasteboard:
    233       type_string = NSDragPboard;
    234       break;
    235   }
    236 
    237   return [NSPasteboard pasteboardWithName:type_string];
    238 }
    239 
    240 }  // namespace
    241 
    242 namespace bookmark_pasteboard_helper_mac {
    243 
    244 void WriteToPasteboard(PasteboardType type,
    245                        const std::vector<BookmarkNodeData::Element>& elements,
    246                        const base::FilePath& profile_path) {
    247   if (elements.empty())
    248     return;
    249 
    250   NSPasteboard* pb = PasteboardFromType(type);
    251 
    252   NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
    253                                              kCrWebURLsWithTitlesPboardType,
    254                                              NSStringPboardType,
    255                                              NSURLPboardType,
    256                                              kNSURLTitlePboardType,
    257                                              kChromiumProfilePathPboardType,
    258                                              nil];
    259   [pb declareTypes:types owner:nil];
    260   [pb setString:base::SysUTF8ToNSString(profile_path.value())
    261         forType:kChromiumProfilePathPboardType];
    262   WriteBookmarkDictionaryListPboardType(pb, elements);
    263   WriteSimplifiedBookmarkTypes(pb, elements);
    264 }
    265 
    266 bool ReadFromPasteboard(PasteboardType type,
    267                         std::vector<BookmarkNodeData::Element>& elements,
    268                         base::FilePath* profile_path) {
    269   NSPasteboard* pb = PasteboardFromType(type);
    270 
    271   elements.clear();
    272   NSString* profile = [pb stringForType:kChromiumProfilePathPboardType];
    273   *profile_path = base::FilePath(base::SysNSStringToUTF8(profile));
    274   return ReadBookmarkDictionaryListPboardType(pb, elements) ||
    275          ReadWebURLsWithTitlesPboardType(pb, elements) ||
    276          ReadNSURLPboardType(pb, elements);
    277 }
    278 
    279 bool PasteboardContainsBookmarks(PasteboardType type) {
    280   NSPasteboard* pb = PasteboardFromType(type);
    281 
    282   NSArray* availableTypes =
    283       [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
    284                                 kCrWebURLsWithTitlesPboardType,
    285                                 NSURLPboardType,
    286                                 nil];
    287   return [pb availableTypeFromArray:availableTypes] != nil;
    288 }
    289 
    290 }  // namespace bookmark_pasteboard_helper_mac
    291