Home | History | Annotate | Download | only in bookmarks
      1 // Copyright (c) 2010 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/sys_string_conversions.h"
     10 #include "chrome/browser/bookmarks/bookmark_model.h"
     11 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_drag_source.h"
     12 #include "chrome/browser/tab_contents/tab_contents_view_mac.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 kWebURLsWithTitlesPboardType =
     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(0, 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->set_title(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->set_title(base::SysNSStringToUTF16(title));
     71       new_node->SetURL(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(NSPasteboard* pb,
     82       std::vector<BookmarkNodeData::Element>& elements) {
     83   NSArray* bookmarks =
     84       [pb propertyListForType:kBookmarkDictionaryListPboardType];
     85   if (!bookmarks) return false;
     86   ConvertPlistToElements(bookmarks, elements);
     87   return true;
     88 }
     89 
     90 bool ReadWebURLsWithTitlesPboardType(NSPasteboard* pb,
     91       std::vector<BookmarkNodeData::Element>& elements) {
     92   NSArray* bookmarkPairs =
     93       [pb propertyListForType:kWebURLsWithTitlesPboardType];
     94   if (![bookmarkPairs isKindOfClass:[NSArray class]]) {
     95     return false;
     96   }
     97   NSArray* urlsArr = [bookmarkPairs objectAtIndex:0];
     98   NSArray* titlesArr = [bookmarkPairs objectAtIndex:1];
     99   if ([urlsArr count] < 1) {
    100     return false;
    101   }
    102   if ([urlsArr count] != [titlesArr count]) {
    103     return false;
    104   }
    105 
    106   NSUInteger len = [titlesArr count];
    107   for (NSUInteger i = 0; i < len; ++i) {
    108     string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]);
    109     std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]);
    110     if (!url.empty()) {
    111       BookmarkNodeData::Element element;
    112       element.is_url = true;
    113       element.url = GURL(url);
    114       element.title = title;
    115       elements.push_back(element);
    116     }
    117   }
    118   return true;
    119 }
    120 
    121 bool ReadNSURLPboardType(NSPasteboard* pb,
    122                          std::vector<BookmarkNodeData::Element>& elements) {
    123   NSURL* url = [NSURL URLFromPasteboard:pb];
    124   if (url == nil) {
    125     return false;
    126   }
    127   std::string urlString = base::SysNSStringToUTF8([url absoluteString]);
    128   NSString* title = [pb stringForType:kNSURLTitlePboardType];
    129   if (!title)
    130     title = [pb stringForType:NSStringPboardType];
    131 
    132   BookmarkNodeData::Element element;
    133   element.is_url = true;
    134   element.url = GURL(urlString);
    135   element.title = base::SysNSStringToUTF16(title);
    136   elements.push_back(element);
    137   return true;
    138 }
    139 
    140 NSArray* GetPlistForBookmarkList(
    141     const std::vector<BookmarkNodeData::Element>& elements) {
    142   NSMutableArray* plist = [NSMutableArray array];
    143   for (size_t i = 0; i < elements.size(); ++i) {
    144     BookmarkNodeData::Element element = elements[i];
    145     if (element.is_url) {
    146       NSString* title = base::SysUTF16ToNSString(element.title);
    147       NSString* url = base::SysUTF8ToNSString(element.url.spec());
    148       int64 elementId = element.get_id();
    149       NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
    150       NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
    151               title, @"title", nil];
    152       NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
    153           uriDictionary, @"URIDictionary",
    154           url, @"URLString",
    155           kWebBookmarkTypeLeaf, kWebBookmarkType,
    156           idNum, kChromiumBookmarkId,
    157           nil];
    158       [plist addObject:object];
    159     } else {
    160       NSString* title = base::SysUTF16ToNSString(element.title);
    161       NSArray* children = GetPlistForBookmarkList(element.children);
    162       int64 elementId = element.get_id();
    163       NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
    164       NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
    165           title, @"Title",
    166           children, @"Children",
    167           kWebBookmarkTypeList, kWebBookmarkType,
    168           idNum, kChromiumBookmarkId,
    169           nil];
    170       [plist addObject:object];
    171     }
    172   }
    173   return plist;
    174 }
    175 
    176 void WriteBookmarkDictionaryListPboardType(NSPasteboard* pb,
    177     const std::vector<BookmarkNodeData::Element>& elements) {
    178   NSArray* plist = GetPlistForBookmarkList(elements);
    179   [pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
    180 }
    181 
    182 void FillFlattenedArraysForBookmarks(
    183     const std::vector<BookmarkNodeData::Element>& elements,
    184     NSMutableArray* titles, NSMutableArray* urls) {
    185   for (size_t i = 0; i < elements.size(); ++i) {
    186     BookmarkNodeData::Element element = elements[i];
    187     if (element.is_url) {
    188       NSString* title = base::SysUTF16ToNSString(element.title);
    189       NSString* url = base::SysUTF8ToNSString(element.url.spec());
    190       [titles addObject:title];
    191       [urls addObject:url];
    192     } else {
    193       FillFlattenedArraysForBookmarks(element.children, titles, urls);
    194     }
    195   }
    196 }
    197 
    198 void WriteSimplifiedBookmarkTypes(NSPasteboard* pb,
    199     const std::vector<BookmarkNodeData::Element>& elements) {
    200   NSMutableArray* titles = [NSMutableArray array];
    201   NSMutableArray* urls = [NSMutableArray array];
    202   FillFlattenedArraysForBookmarks(elements, titles, urls);
    203 
    204   // These bookmark types only act on urls, not folders.
    205   if ([urls count] < 1)
    206     return;
    207 
    208   // Write WebURLsWithTitlesPboardType.
    209   [pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil]
    210               forType:kWebURLsWithTitlesPboardType];
    211 
    212   // Write NSStringPboardType.
    213   [pb setString:[urls componentsJoinedByString:@"\n"]
    214       forType:NSStringPboardType];
    215 
    216   // Write NSURLPboardType (with title).
    217   NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]];
    218   [url writeToPasteboard:pb];
    219   NSString* titleString = [titles objectAtIndex:0];
    220   [pb setString:titleString forType:kNSURLTitlePboardType];
    221 }
    222 
    223 void WriteToClipboardPrivate(
    224     const std::vector<BookmarkNodeData::Element>& elements,
    225     NSPasteboard* pb,
    226     FilePath::StringType profile_path) {
    227   if (elements.empty())
    228     return;
    229 
    230   NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
    231                                              kWebURLsWithTitlesPboardType,
    232                                              NSStringPboardType,
    233                                              NSURLPboardType,
    234                                              kNSURLTitlePboardType,
    235                                              kChromiumProfilePathPboardType,
    236                                              nil];
    237   [pb declareTypes:types owner:nil];
    238   [pb setString:base::SysUTF8ToNSString(profile_path)
    239         forType:kChromiumProfilePathPboardType];
    240   WriteBookmarkDictionaryListPboardType(pb, elements);
    241   WriteSimplifiedBookmarkTypes(pb, elements);
    242 }
    243 
    244 bool ReadFromClipboardPrivate(
    245     std::vector<BookmarkNodeData::Element>& elements,
    246     NSPasteboard* pb,
    247     FilePath::StringType* profile_path) {
    248   elements.clear();
    249   NSString* profile = [pb stringForType:kChromiumProfilePathPboardType];
    250   profile_path->assign(base::SysNSStringToUTF8(profile));
    251   return (ReadBookmarkDictionaryListPboardType(pb, elements) ||
    252          ReadWebURLsWithTitlesPboardType(pb, elements) ||
    253          ReadNSURLPboardType(pb, elements));
    254 }
    255 
    256 bool ClipboardContainsBookmarksPrivate(NSPasteboard* pb) {
    257   NSArray* availableTypes =
    258       [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
    259                                 kWebURLsWithTitlesPboardType,
    260                                 NSURLPboardType,
    261                                 nil];
    262   return [pb availableTypeFromArray:availableTypes] != nil;
    263 }
    264 
    265 }  // anonymous namespace
    266 
    267 namespace bookmark_pasteboard_helper_mac {
    268 
    269 void WriteToClipboard(const std::vector<BookmarkNodeData::Element>& elements,
    270                       FilePath::StringType profile_path) {
    271   NSPasteboard* pb = [NSPasteboard generalPasteboard];
    272   WriteToClipboardPrivate(elements, pb, profile_path);
    273 }
    274 
    275 void WriteToDragClipboard(
    276     const std::vector<BookmarkNodeData::Element>& elements,
    277     FilePath::StringType profile_path) {
    278   NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
    279   WriteToClipboardPrivate(elements, pb, profile_path);
    280 }
    281 
    282 bool ReadFromClipboard(std::vector<BookmarkNodeData::Element>& elements,
    283                        FilePath::StringType* profile_path) {
    284   NSPasteboard* pb = [NSPasteboard generalPasteboard];
    285   return ReadFromClipboardPrivate(elements, pb, profile_path);
    286 }
    287 
    288 bool ReadFromDragClipboard(std::vector<BookmarkNodeData::Element>& elements,
    289                            FilePath::StringType* profile_path) {
    290   NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
    291   return ReadFromClipboardPrivate(elements, pb, profile_path);
    292 }
    293 
    294 
    295 bool ClipboardContainsBookmarks() {
    296   NSPasteboard* pb = [NSPasteboard generalPasteboard];
    297   return ClipboardContainsBookmarksPrivate(pb);
    298 }
    299 
    300 bool DragClipboardContainsBookmarks() {
    301   NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
    302   return ClipboardContainsBookmarksPrivate(pb);
    303 }
    304 
    305 void StartDrag(Profile* profile, const std::vector<const BookmarkNode*>& nodes,
    306     gfx::NativeView view) {
    307   DCHECK([view isKindOfClass:[TabContentsViewCocoa class]]);
    308   TabContentsViewCocoa* tabView = static_cast<TabContentsViewCocoa*>(view);
    309   std::vector<BookmarkNodeData::Element> elements;
    310   for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin();
    311        it != nodes.end(); ++it) {
    312     elements.push_back(BookmarkNodeData::Element(*it));
    313   }
    314   NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
    315   scoped_nsobject<BookmarkDragSource> source([[BookmarkDragSource alloc]
    316       initWithContentsView:tabView
    317                   dropData:elements
    318                    profile:profile
    319                 pasteboard:pb
    320          dragOperationMask:NSDragOperationEvery]);
    321   [source startDrag];
    322 }
    323 
    324 }  // namespace bookmark_pasteboard_helper_mac
    325