[go: nahoru, domu]

blob: a081aace77b01a9d6262da0d28eaee9701d5f8de [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/dragdrop/os_exchange_data_provider_mac.h"
#import <Cocoa/Cocoa.h>
#include "base/apple/foundation_util.h"
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/pickle.h"
#include "base/ranges/algorithm.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/filename_util.h"
#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#import "ui/base/clipboard/clipboard_util_mac.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/clipboard/file_info.h"
#include "ui/base/data_transfer_policy/data_transfer_policy_controller.h"
#include "url/gurl.h"
@interface CrPasteboardItemWrapper : NSObject <NSPasteboardWriting>
- (instancetype)initWithPasteboardItem:(NSPasteboardItem*)pasteboardItem;
@end
@implementation CrPasteboardItemWrapper {
NSPasteboardItem* __strong _pasteboardItem;
}
- (instancetype)initWithPasteboardItem:(NSPasteboardItem*)pasteboardItem {
if ((self = [super init])) {
_pasteboardItem = pasteboardItem;
}
return self;
}
- (NSArray<NSString*>*)writableTypesForPasteboard:(NSPasteboard*)pasteboard {
// If the NSPasteboardItem hasn't been added to an NSPasteboard, then the
// -[NSPasteboardItem writableTypesForPasteboard:] will return -types. But if
// it has been added to a pasteboard, it will return nil. This pasteboard item
// was added implicitly by adding flavors to the owned pasteboard of
// OwningProvider, so call -types to actually get data.
//
// Merge in the ui::kUTTypeChromiumInitiatedDrag type, so that all of Chromium
// is marked to receive the drags. TODO(avi): Wire up MacViews so that
// BridgedContentView properly registers the result of View::GetDropFormats()
// rather than OSExchangeDataProviderMac::SupportedPasteboardTypes().
return [_pasteboardItem.types
arrayByAddingObject:ui::kUTTypeChromiumInitiatedDrag];
}
- (NSPasteboardWritingOptions)writingOptionsForType:(NSString*)type
pasteboard:(NSPasteboard*)pasteboard {
// It is critical to return 0 here. If any flavors are promised, then when the
// app quits, AppKit will call in the promises, and the backing pasteboard
// will likely be long-deallocated. Yes, AppKit will call in promises for
// *all* promised flavors on *all* pasteboards, not just those pasteboards
// used for copy/paste.
return 0;
}
- (id)pasteboardPropertyListForType:(NSString*)type {
if ([type isEqual:ui::kUTTypeChromiumInitiatedDrag])
return [NSData data];
// Like above, an NSPasteboardItem added to a pasteboard will return nil from
// -pasteboardPropertyListForType:, so call -dataForType: instead.
return [_pasteboardItem dataForType:type];
}
@end
namespace ui {
namespace {
class OwningProvider : public OSExchangeDataProviderMac {
public:
OwningProvider() : owned_pasteboard_(new UniquePasteboard) {}
OwningProvider(const OwningProvider& provider) = default;
std::unique_ptr<OSExchangeDataProvider> Clone() const override {
return std::make_unique<OwningProvider>(*this);
}
NSPasteboard* GetPasteboard() const override {
return owned_pasteboard_->get();
}
private:
scoped_refptr<UniquePasteboard> owned_pasteboard_;
};
class WrappingProvider : public OSExchangeDataProviderMac {
public:
explicit WrappingProvider(NSPasteboard* pasteboard)
: wrapped_pasteboard_(pasteboard) {}
WrappingProvider(const WrappingProvider& provider) = default;
std::unique_ptr<OSExchangeDataProvider> Clone() const override {
return std::make_unique<WrappingProvider>(*this);
}
NSPasteboard* GetPasteboard() const override { return wrapped_pasteboard_; }
private:
__strong NSPasteboard* wrapped_pasteboard_;
};
} // namespace
OSExchangeDataProviderMac::OSExchangeDataProviderMac() = default;
OSExchangeDataProviderMac::OSExchangeDataProviderMac(
const OSExchangeDataProviderMac&) = default;
OSExchangeDataProviderMac& OSExchangeDataProviderMac::operator=(
const OSExchangeDataProviderMac&) = default;
OSExchangeDataProviderMac::~OSExchangeDataProviderMac() = default;
// static
std::unique_ptr<OSExchangeDataProviderMac>
OSExchangeDataProviderMac::CreateProvider() {
return std::make_unique<OwningProvider>();
}
// static
std::unique_ptr<OSExchangeDataProviderMac>
OSExchangeDataProviderMac::CreateProviderWrappingPasteboard(
NSPasteboard* pasteboard) {
return std::make_unique<WrappingProvider>(pasteboard);
}
void OSExchangeDataProviderMac::MarkOriginatedFromRenderer() {
[GetPasteboard() setData:[NSData data]
forType:kUTTypeChromiumRendererInitiatedDrag];
}
bool OSExchangeDataProviderMac::DidOriginateFromRenderer() const {
return [GetPasteboard().types
containsObject:kUTTypeChromiumRendererInitiatedDrag];
}
void OSExchangeDataProviderMac::MarkAsFromPrivileged() {
[GetPasteboard() setData:[NSData data]
forType:kUTTypeChromiumPrivilegedInitiatedDrag];
}
bool OSExchangeDataProviderMac::IsFromPrivileged() const {
return [GetPasteboard().types
containsObject:kUTTypeChromiumPrivilegedInitiatedDrag];
}
void OSExchangeDataProviderMac::SetString(const std::u16string& string) {
[GetPasteboard() setString:base::SysUTF16ToNSString(string)
forType:NSPasteboardTypeString];
}
void OSExchangeDataProviderMac::SetURL(const GURL& url,
const std::u16string& title) {
NSArray<NSPasteboardItem*>* items = clipboard_util::PasteboardItemsFromUrls(
@[ base::SysUTF8ToNSString(url.spec()) ],
@[ base::SysUTF16ToNSString(title) ]);
clipboard_util::AddDataToPasteboard(GetPasteboard(), items.firstObject);
}
void OSExchangeDataProviderMac::SetFilename(const base::FilePath& path) {
std::vector<FileInfo> filenames(1, FileInfo(path, base::FilePath()));
clipboard_util::WriteFilesToPasteboard(GetPasteboard(), filenames);
}
void OSExchangeDataProviderMac::SetFilenames(
const std::vector<FileInfo>& filenames) {
clipboard_util::WriteFilesToPasteboard(GetPasteboard(), filenames);
}
void OSExchangeDataProviderMac::SetPickledData(
const ClipboardFormatType& format,
const base::Pickle& data) {
NSData* ns_data = [NSData dataWithBytes:data.data() length:data.size()];
[GetPasteboard() setData:ns_data forType:format.ToNSString()];
}
bool OSExchangeDataProviderMac::GetString(std::u16string* data) const {
DCHECK(data);
NSString* item = [GetPasteboard() stringForType:NSPasteboardTypeString];
if (item) {
*data = base::SysNSStringToUTF16(item);
return true;
}
// There was no NSString, check for an NSURL.
GURL url;
std::u16string title;
bool result = GetURLAndTitle(FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES,
&url, &title);
if (result)
*data = base::UTF8ToUTF16(url.spec());
return result;
}
bool OSExchangeDataProviderMac::GetURLAndTitle(FilenameToURLPolicy policy,
GURL* url,
std::u16string* title) const {
DCHECK(url);
DCHECK(title);
NSArray<URLAndTitle*>* urls_and_titles =
clipboard_util::URLsAndTitlesFromPasteboard(GetPasteboard(),
/*include_files=*/false);
if (urls_and_titles.count) {
*url = GURL(base::SysNSStringToUTF8(urls_and_titles.firstObject.URL));
*title = base::SysNSStringToUTF16(urls_and_titles.firstObject.title);
return true;
}
// If there are no URLs, try to convert a filename to a URL if the policy
// allows it. The title remains blank.
//
// This could be done in the call to `URLsAndTitlesFromPasteboard` above if
// `true` were passed in for the `include_files` parameter, but that function
// strips the trailing slashes off of paths and always returns the last path
// element as the title whereas no path conversion nor title is wanted.
//
// TODO(avi): What is going on here? This comment and code was written for the
// old pasteboard code; is this still true with the new pasteboard code? What
// uses this, and why does it care about titles or path conversion?
base::FilePath path;
if (policy != FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES &&
GetFilename(&path)) {
*url = net::FilePathToFileURL(path);
return true;
}
return false;
}
bool OSExchangeDataProviderMac::GetFilename(base::FilePath* path) const {
std::vector<FileInfo> files =
clipboard_util::FilesFromPasteboard(GetPasteboard());
if (files.empty()) {
return false;
}
*path = files[0].path;
return true;
}
bool OSExchangeDataProviderMac::GetFilenames(
std::vector<FileInfo>* filenames) const {
std::vector<FileInfo> files =
clipboard_util::FilesFromPasteboard(GetPasteboard());
bool result = !files.empty();
base::ranges::move(files, std::back_inserter(*filenames));
return result;
}
bool OSExchangeDataProviderMac::GetPickledData(
const ClipboardFormatType& format,
base::Pickle* data) const {
DCHECK(data);
NSData* ns_data = [GetPasteboard() dataForType:format.ToNSString()];
if (!ns_data)
return false;
*data =
base::Pickle(static_cast<const char*>([ns_data bytes]), [ns_data length]);
return true;
}
bool OSExchangeDataProviderMac::HasString() const {
std::u16string string;
return GetString(&string);
}
bool OSExchangeDataProviderMac::HasURL(FilenameToURLPolicy policy) const {
GURL url;
std::u16string title;
return GetURLAndTitle(policy, &url, &title);
}
bool OSExchangeDataProviderMac::HasFile() const {
return [GetPasteboard().types containsObject:NSPasteboardTypeFileURL];
}
bool OSExchangeDataProviderMac::HasCustomFormat(
const ClipboardFormatType& format) const {
return [GetPasteboard().types containsObject:format.ToNSString()];
}
void OSExchangeDataProviderMac::SetFileContents(
const base::FilePath& filename,
const std::string& file_contents) {
NOTIMPLEMENTED();
}
bool OSExchangeDataProviderMac::GetFileContents(
base::FilePath* filename,
std::string* file_contents) const {
NOTIMPLEMENTED();
return false;
}
bool OSExchangeDataProviderMac::HasFileContents() const {
NOTIMPLEMENTED();
return false;
}
void OSExchangeDataProviderMac::SetDragImage(
const gfx::ImageSkia& image,
const gfx::Vector2d& cursor_offset) {
drag_image_ = image;
cursor_offset_ = cursor_offset;
}
gfx::ImageSkia OSExchangeDataProviderMac::GetDragImage() const {
return drag_image_;
}
gfx::Vector2d OSExchangeDataProviderMac::GetDragImageOffset() const {
return cursor_offset_;
}
NSArray<NSDraggingItem*>* OSExchangeDataProviderMac::GetDraggingItems() const {
// What's going on here is that initiating a drag (-[NSView
// beginDraggingSessionWithItems...]) requires a dragging item. Even though
// pasteboard items are NSPasteboardWriters, they are locked to their
// pasteboard and cannot be used to initiate a drag with another pasteboard
// (hello https://crbug.com/928684). Therefore, wrap them.
NSArray<NSPasteboardItem*>* pasteboard_items =
GetPasteboard().pasteboardItems;
if (!pasteboard_items) {
return nil;
}
NSMutableArray<NSDraggingItem*>* drag_items = [NSMutableArray array];
for (NSPasteboardItem* item in pasteboard_items) {
CrPasteboardItemWrapper* wrapper =
[[CrPasteboardItemWrapper alloc] initWithPasteboardItem:item];
NSDraggingItem* drag_item =
[[NSDraggingItem alloc] initWithPasteboardWriter:wrapper];
[drag_items addObject:drag_item];
}
return drag_items;
}
// static
NSArray* OSExchangeDataProviderMac::SupportedPasteboardTypes() {
return @[
kUTTypeChromiumInitiatedDrag, kUTTypeChromiumPrivilegedInitiatedDrag,
kUTTypeChromiumRendererInitiatedDrag, kUTTypeChromiumWebCustomData,
kUTTypeWebKitWebURLsWithTitles, NSPasteboardTypeFileURL,
NSPasteboardTypeHTML, NSPasteboardTypeRTF, NSPasteboardTypeString,
NSPasteboardTypeURL
];
}
void OSExchangeDataProviderMac::SetSource(
std::unique_ptr<DataTransferEndpoint> data_source) {}
DataTransferEndpoint* OSExchangeDataProviderMac::GetSource() const {
return nullptr;
}
} // namespace ui