blob: bea892585210bbe1ea7b4b9230ab56ba7302a0e9 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
#import <Cocoa/Cocoa.h>
#include "base/files/file_path.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "ui/base/clipboard/clipboard.h"
NSString* const kBookmarkDictionaryListPboardType =
@"BookmarkDictionaryListPboardType";
namespace {
// An unofficial standard pasteboard title type to be provided alongside the
// |NSURLPboardType|.
NSString* const kNSURLTitlePboardType =
@"public.url-name";
// Pasteboard type used to store profile path to determine which profile
// a set of bookmarks came from.
NSString* const kChromiumProfilePathPboardType =
@"ChromiumProfilePathPboardType";
// Internal bookmark ID for a bookmark node. Used only when moving inside
// of one profile.
NSString* const kChromiumBookmarkId =
@"ChromiumBookmarkId";
// Mac WebKit uses this type, declared in
// WebKit/mac/History/WebURLsWithTitles.h.
NSString* const kCrWebURLsWithTitlesPboardType =
@"WebURLsWithTitlesPboardType";
// Keys for the type of node in BookmarkDictionaryListPboardType.
NSString* const kWebBookmarkType =
@"WebBookmarkType";
NSString* const kWebBookmarkTypeList =
@"WebBookmarkTypeList";
NSString* const kWebBookmarkTypeLeaf =
@"WebBookmarkTypeLeaf";
void ConvertPlistToElements(NSArray* input,
std::vector<BookmarkNodeData::Element>& elements) {
NSUInteger len = [input count];
for (NSUInteger i = 0; i < len; ++i) {
NSDictionary* pboardBookmark = [input objectAtIndex:i];
scoped_ptr<BookmarkNode> new_node(new BookmarkNode(GURL()));
int64 node_id =
[[pboardBookmark objectForKey:kChromiumBookmarkId] longLongValue];
new_node->set_id(node_id);
BOOL is_folder = [[pboardBookmark objectForKey:kWebBookmarkType]
isEqualToString:kWebBookmarkTypeList];
if (is_folder) {
new_node->set_type(BookmarkNode::FOLDER);
NSString* title = [pboardBookmark objectForKey:@"Title"];
new_node->SetTitle(base::SysNSStringToUTF16(title));
} else {
new_node->set_type(BookmarkNode::URL);
NSDictionary* uriDictionary =
[pboardBookmark objectForKey:@"URIDictionary"];
NSString* title = [uriDictionary objectForKey:@"title"];
NSString* urlString = [pboardBookmark objectForKey:@"URLString"];
new_node->SetTitle(base::SysNSStringToUTF16(title));
new_node->set_url(GURL(base::SysNSStringToUTF8(urlString)));
}
BookmarkNodeData::Element e = BookmarkNodeData::Element(new_node.get());
if(is_folder)
ConvertPlistToElements([pboardBookmark objectForKey:@"Children"],
e.children);
elements.push_back(e);
}
}
bool ReadBookmarkDictionaryListPboardType(
NSPasteboard* pb,
std::vector<BookmarkNodeData::Element>& elements) {
NSArray* bookmarks =
[pb propertyListForType:kBookmarkDictionaryListPboardType];
if (!bookmarks)
return false;
ConvertPlistToElements(bookmarks, elements);
return true;
}
bool ReadWebURLsWithTitlesPboardType(
NSPasteboard* pb,
std::vector<BookmarkNodeData::Element>& elements) {
NSArray* bookmarkPairs =
[pb propertyListForType:kCrWebURLsWithTitlesPboardType];
if (![bookmarkPairs isKindOfClass:[NSArray class]])
return false;
NSArray* urlsArr = [bookmarkPairs objectAtIndex:0];
NSArray* titlesArr = [bookmarkPairs objectAtIndex:1];
if ([urlsArr count] < 1)
return false;
if ([urlsArr count] != [titlesArr count])
return false;
NSUInteger len = [titlesArr count];
for (NSUInteger i = 0; i < len; ++i) {
string16 title = base::SysNSStringToUTF16([titlesArr objectAtIndex:i]);
std::string url = base::SysNSStringToUTF8([urlsArr objectAtIndex:i]);
if (!url.empty()) {
BookmarkNodeData::Element element;
element.is_url = true;
element.url = GURL(url);
element.title = title;
elements.push_back(element);
}
}
return true;
}
bool ReadNSURLPboardType(NSPasteboard* pb,
std::vector<BookmarkNodeData::Element>& elements) {
NSURL* url = [NSURL URLFromPasteboard:pb];
if (url == nil)
return false;
std::string urlString = base::SysNSStringToUTF8([url absoluteString]);
NSString* title = [pb stringForType:kNSURLTitlePboardType];
if (!title)
title = [pb stringForType:NSStringPboardType];
BookmarkNodeData::Element element;
element.is_url = true;
element.url = GURL(urlString);
element.title = base::SysNSStringToUTF16(title);
elements.push_back(element);
return true;
}
NSArray* GetPlistForBookmarkList(
const std::vector<BookmarkNodeData::Element>& elements) {
NSMutableArray* plist = [NSMutableArray array];
for (size_t i = 0; i < elements.size(); ++i) {
BookmarkNodeData::Element element = elements[i];
if (element.is_url) {
NSString* title = base::SysUTF16ToNSString(element.title);
NSString* url = base::SysUTF8ToNSString(element.url.spec());
int64 elementId = element.id();
NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
NSDictionary* uriDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
title, @"title", nil];
NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
uriDictionary, @"URIDictionary",
url, @"URLString",
kWebBookmarkTypeLeaf, kWebBookmarkType,
idNum, kChromiumBookmarkId,
nil];
[plist addObject:object];
} else {
NSString* title = base::SysUTF16ToNSString(element.title);
NSArray* children = GetPlistForBookmarkList(element.children);
int64 elementId = element.id();
NSNumber* idNum = [NSNumber numberWithLongLong:elementId];
NSDictionary* object = [NSDictionary dictionaryWithObjectsAndKeys:
title, @"Title",
children, @"Children",
kWebBookmarkTypeList, kWebBookmarkType,
idNum, kChromiumBookmarkId,
nil];
[plist addObject:object];
}
}
return plist;
}
void WriteBookmarkDictionaryListPboardType(
NSPasteboard* pb,
const std::vector<BookmarkNodeData::Element>& elements) {
NSArray* plist = GetPlistForBookmarkList(elements);
[pb setPropertyList:plist forType:kBookmarkDictionaryListPboardType];
}
void FillFlattenedArraysForBookmarks(
const std::vector<BookmarkNodeData::Element>& elements,
NSMutableArray* titles, NSMutableArray* urls) {
for (size_t i = 0; i < elements.size(); ++i) {
BookmarkNodeData::Element element = elements[i];
if (element.is_url) {
NSString* title = base::SysUTF16ToNSString(element.title);
NSString* url = base::SysUTF8ToNSString(element.url.spec());
[titles addObject:title];
[urls addObject:url];
} else {
FillFlattenedArraysForBookmarks(element.children, titles, urls);
}
}
}
void WriteSimplifiedBookmarkTypes(NSPasteboard* pb,
const std::vector<BookmarkNodeData::Element>& elements) {
NSMutableArray* titles = [NSMutableArray array];
NSMutableArray* urls = [NSMutableArray array];
FillFlattenedArraysForBookmarks(elements, titles, urls);
// These bookmark types only act on urls, not folders.
if ([urls count] < 1)
return;
// Write WebURLsWithTitlesPboardType.
[pb setPropertyList:[NSArray arrayWithObjects:urls, titles, nil]
forType:kCrWebURLsWithTitlesPboardType];
// Write NSStringPboardType.
[pb setString:[urls componentsJoinedByString:@"\n"]
forType:NSStringPboardType];
// Write NSURLPboardType (with title).
NSURL* url = [NSURL URLWithString:[urls objectAtIndex:0]];
[url writeToPasteboard:pb];
NSString* titleString = [titles objectAtIndex:0];
[pb setString:titleString forType:kNSURLTitlePboardType];
}
NSPasteboard* PasteboardFromType(
bookmark_pasteboard_helper_mac::PasteboardType type) {
NSString* type_string = nil;
switch (type) {
case bookmark_pasteboard_helper_mac::kCopyPastePasteboard:
type_string = NSGeneralPboard;
break;
case bookmark_pasteboard_helper_mac::kDragPasteboard:
type_string = NSDragPboard;
break;
}
return [NSPasteboard pasteboardWithName:type_string];
}
} // namespace
namespace bookmark_pasteboard_helper_mac {
void WriteToPasteboard(PasteboardType type,
const std::vector<BookmarkNodeData::Element>& elements,
const base::FilePath& profile_path) {
if (elements.empty())
return;
NSPasteboard* pb = PasteboardFromType(type);
NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
kCrWebURLsWithTitlesPboardType,
NSStringPboardType,
NSURLPboardType,
kNSURLTitlePboardType,
kChromiumProfilePathPboardType,
nil];
[pb declareTypes:types owner:nil];
[pb setString:base::SysUTF8ToNSString(profile_path.value())
forType:kChromiumProfilePathPboardType];
WriteBookmarkDictionaryListPboardType(pb, elements);
WriteSimplifiedBookmarkTypes(pb, elements);
}
bool ReadFromPasteboard(PasteboardType type,
std::vector<BookmarkNodeData::Element>& elements,
base::FilePath* profile_path) {
NSPasteboard* pb = PasteboardFromType(type);
elements.clear();
NSString* profile = [pb stringForType:kChromiumProfilePathPboardType];
*profile_path = base::FilePath(base::SysNSStringToUTF8(profile));
return ReadBookmarkDictionaryListPboardType(pb, elements) ||
ReadWebURLsWithTitlesPboardType(pb, elements) ||
ReadNSURLPboardType(pb, elements);
}
bool PasteboardContainsBookmarks(PasteboardType type) {
NSPasteboard* pb = PasteboardFromType(type);
NSArray* availableTypes =
[NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
kCrWebURLsWithTitlesPboardType,
NSURLPboardType,
nil];
return [pb availableTypeFromArray:availableTypes] != nil;
}
} // namespace bookmark_pasteboard_helper_mac