blob: ee907cfe6a67e3726ee165778b6ed6ef9ffd23a4 [file] [log] [blame]
// Copyright (c) 2010 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/sys_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/ui/cocoa/bookmarks/bookmark_drag_source.h"
#include "chrome/browser/tab_contents/tab_contents_view_mac.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 kWebURLsWithTitlesPboardType =
@"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(0, 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->set_title(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->set_title(base::SysNSStringToUTF16(title));
new_node->SetURL(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:kWebURLsWithTitlesPboardType];
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.get_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.get_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:kWebURLsWithTitlesPboardType];
// 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];
}
void WriteToClipboardPrivate(
const std::vector<BookmarkNodeData::Element>& elements,
NSPasteboard* pb,
FilePath::StringType profile_path) {
if (elements.empty())
return;
NSArray* types = [NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
kWebURLsWithTitlesPboardType,
NSStringPboardType,
NSURLPboardType,
kNSURLTitlePboardType,
kChromiumProfilePathPboardType,
nil];
[pb declareTypes:types owner:nil];
[pb setString:base::SysUTF8ToNSString(profile_path)
forType:kChromiumProfilePathPboardType];
WriteBookmarkDictionaryListPboardType(pb, elements);
WriteSimplifiedBookmarkTypes(pb, elements);
}
bool ReadFromClipboardPrivate(
std::vector<BookmarkNodeData::Element>& elements,
NSPasteboard* pb,
FilePath::StringType* profile_path) {
elements.clear();
NSString* profile = [pb stringForType:kChromiumProfilePathPboardType];
profile_path->assign(base::SysNSStringToUTF8(profile));
return (ReadBookmarkDictionaryListPboardType(pb, elements) ||
ReadWebURLsWithTitlesPboardType(pb, elements) ||
ReadNSURLPboardType(pb, elements));
}
bool ClipboardContainsBookmarksPrivate(NSPasteboard* pb) {
NSArray* availableTypes =
[NSArray arrayWithObjects:kBookmarkDictionaryListPboardType,
kWebURLsWithTitlesPboardType,
NSURLPboardType,
nil];
return [pb availableTypeFromArray:availableTypes] != nil;
}
} // anonymous namespace
namespace bookmark_pasteboard_helper_mac {
void WriteToClipboard(const std::vector<BookmarkNodeData::Element>& elements,
FilePath::StringType profile_path) {
NSPasteboard* pb = [NSPasteboard generalPasteboard];
WriteToClipboardPrivate(elements, pb, profile_path);
}
void WriteToDragClipboard(
const std::vector<BookmarkNodeData::Element>& elements,
FilePath::StringType profile_path) {
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
WriteToClipboardPrivate(elements, pb, profile_path);
}
bool ReadFromClipboard(std::vector<BookmarkNodeData::Element>& elements,
FilePath::StringType* profile_path) {
NSPasteboard* pb = [NSPasteboard generalPasteboard];
return ReadFromClipboardPrivate(elements, pb, profile_path);
}
bool ReadFromDragClipboard(std::vector<BookmarkNodeData::Element>& elements,
FilePath::StringType* profile_path) {
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
return ReadFromClipboardPrivate(elements, pb, profile_path);
}
bool ClipboardContainsBookmarks() {
NSPasteboard* pb = [NSPasteboard generalPasteboard];
return ClipboardContainsBookmarksPrivate(pb);
}
bool DragClipboardContainsBookmarks() {
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
return ClipboardContainsBookmarksPrivate(pb);
}
void StartDrag(Profile* profile, const std::vector<const BookmarkNode*>& nodes,
gfx::NativeView view) {
DCHECK([view isKindOfClass:[TabContentsViewCocoa class]]);
TabContentsViewCocoa* tabView = static_cast<TabContentsViewCocoa*>(view);
std::vector<BookmarkNodeData::Element> elements;
for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin();
it != nodes.end(); ++it) {
elements.push_back(BookmarkNodeData::Element(*it));
}
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
scoped_nsobject<BookmarkDragSource> source([[BookmarkDragSource alloc]
initWithContentsView:tabView
dropData:elements
profile:profile
pasteboard:pb
dragOperationMask:NSDragOperationEvery]);
[source startDrag];
}
} // namespace bookmark_pasteboard_helper_mac