blob: e7a17dd7742df263de3c6f6e8bbe14339855b9d2 [file] [log] [blame]
// Copyright 2016 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 "ui/base/clipboard/clipboard_util_mac.h"
#include "base/mac/foundation_util.h"
#import "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h"
namespace ui {
NSString* const kUTTypeURLName = @"public.url-name";
namespace {
NSString* const kWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType";
// It's much more convenient to return an NSString than a
// base::ScopedCFTypeRef<CFStringRef>, since the methods on NSPasteboardItem
// require an NSString*.
NSString* UTIFromPboardType(NSString* type) {
return [base::mac::CFToNSCast(UTTypeCreatePreferredIdentifierForTag(
kUTTagClassNSPboardType, base::mac::NSToCFCast(type), kUTTypeData))
autorelease];
}
} // namespace
UniquePasteboard::UniquePasteboard()
: pasteboard_([[NSPasteboard pasteboardWithUniqueName] retain]) {}
UniquePasteboard::~UniquePasteboard() {
[pasteboard_ releaseGlobally];
if (base::mac::IsOS10_12()) {
// On 10.12, move ownership to the autorelease pool rather than possibly
// triggering -[NSPasteboard dealloc] here. This is a speculative workaround
// for https://crbug.com/877979 where a call to __CFPasteboardDeallocate
// from here is triggering "Semaphore object deallocated while in use".
pasteboard_.autorelease();
}
}
// static
base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromUrl(
NSString* urlString,
NSString* title) {
DCHECK(urlString);
if (!title)
title = urlString;
base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
NSURL* url = [NSURL URLWithString:urlString];
if ([url isFileURL] &&
[[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
[item setPropertyList:@[ [url path] ]
forType:UTIFromPboardType(NSFilenamesPboardType)];
}
// Set Safari's URL + title arrays Pboard type.
NSArray* urlsAndTitles = @[ @[ urlString ], @[ title ] ];
[item setPropertyList:urlsAndTitles
forType:UTIFromPboardType(kWebURLsWithTitlesPboardType)];
// Set NSURLPboardType. The format of the property list is divined from
// Webkit's function PlatformPasteboard::setStringForType.
// https://github.com/WebKit/webkit/blob/master/Source/WebCore/platform/mac/PlatformPasteboardMac.mm
NSURL* base = [url baseURL];
if (base) {
[item setPropertyList:@[ [url relativeString], [base absoluteString] ]
forType:UTIFromPboardType(NSURLPboardType)];
} else if (url) {
[item setPropertyList:@[ [url absoluteString], @"" ]
forType:UTIFromPboardType(NSURLPboardType)];
}
[item setString:urlString forType:NSPasteboardTypeString];
[item setString:urlString forType:base::mac::CFToNSCast(kUTTypeURL)];
[item setString:title forType:kUTTypeURLName];
return item;
}
// static
base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromUrls(
NSArray* urls,
NSArray* titles) {
base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
// Set Safari's URL + title arrays Pboard type.
NSArray* urlsAndTitles = @[ urls, titles ];
[item setPropertyList:urlsAndTitles
forType:UTIFromPboardType(kWebURLsWithTitlesPboardType)];
return item;
}
// static
base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromString(
NSString* string) {
base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
[item setString:string forType:NSPasteboardTypeString];
return item;
}
//static
NSString* ClipboardUtil::GetTitleFromPasteboardURL(NSPasteboard* pboard) {
return [pboard stringForType:kUTTypeURLName];
}
//static
NSString* ClipboardUtil::GetURLFromPasteboardURL(NSPasteboard* pboard) {
return [pboard stringForType:base::mac::CFToNSCast(kUTTypeURL)];
}
// static
NSString* ClipboardUtil::UTIForPasteboardType(NSString* type) {
return UTIFromPboardType(type);
}
// static
NSString* ClipboardUtil::UTIForWebURLsAndTitles() {
return UTIFromPboardType(kWebURLsWithTitlesPboardType);
}
// static
void ClipboardUtil::AddDataToPasteboard(NSPasteboard* pboard,
NSPasteboardItem* item) {
NSSet* oldTypes = [NSSet setWithArray:[pboard types]];
NSMutableSet* newTypes = [NSMutableSet setWithArray:[item types]];
[newTypes minusSet:oldTypes];
[pboard addTypes:[newTypes allObjects] owner:nil];
for (NSString* type in newTypes) {
// Technically, the object associated with |type| might be an NSString or a
// property list. It doesn't matter though, since the type gets pulled from
// and shoved into an NSDictionary.
[pboard setData:[item dataForType:type] forType:type];
}
}
// static
bool ClipboardUtil::URLsAndTitlesFromPasteboard(NSPasteboard* pboard,
NSArray** urls,
NSArray** titles) {
NSArray* bookmarkPairs = base::mac::ObjCCast<NSArray>([pboard
propertyListForType:UTIFromPboardType(kWebURLsWithTitlesPboardType)]);
if (!bookmarkPairs)
return false;
if ([bookmarkPairs count] != 2)
return false;
NSArray* urlsArr = base::mac::ObjCCast<NSArray>(bookmarkPairs[0]);
NSArray* titlesArr = base::mac::ObjCCast<NSArray>(bookmarkPairs[1]);
if (!urlsArr || !titlesArr)
return false;
if ([urlsArr count] < 1)
return false;
if ([urlsArr count] != [titlesArr count])
return false;
for (id obj in urlsArr) {
if (![obj isKindOfClass:[NSString class]])
return false;
}
for (id obj in titlesArr) {
if (![obj isKindOfClass:[NSString class]])
return false;
}
*urls = urlsArr;
*titles = titlesArr;
return true;
}
// static
NSPasteboard* ClipboardUtil::PasteboardFromType(ui::ClipboardType type) {
NSString* type_string = nil;
switch (type) {
case ui::CLIPBOARD_TYPE_COPY_PASTE:
type_string = NSGeneralPboard;
break;
case ui::CLIPBOARD_TYPE_DRAG:
type_string = NSDragPboard;
break;
case ui::CLIPBOARD_TYPE_SELECTION:
NOTREACHED();
break;
}
return [NSPasteboard pasteboardWithName:type_string];
}
} // namespace ui