| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 "ui/base/clipboard/clipboard_mac.h" | |
| 6 | |
| 7 #import <Cocoa/Cocoa.h> | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "base/files/file_path.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/mac/mac_util.h" | |
| 13 #include "base/mac/scoped_cftyperef.h" | |
| 14 #import "base/mac/scoped_nsexception_enabler.h" | |
| 15 #include "base/mac/scoped_nsobject.h" | |
| 16 #include "base/stl_util.h" | |
| 17 #include "base/strings/sys_string_conversions.h" | |
| 18 #include "base/strings/utf_string_conversions.h" | |
| 19 #include "skia/ext/skia_utils_mac.h" | |
| 20 #import "third_party/mozilla/NSPasteboard+Utils.h" | |
| 21 #include "third_party/skia/include/core/SkBitmap.h" | |
| 22 #include "ui/base/clipboard/custom_data_helper.h" | |
| 23 #include "ui/gfx/canvas.h" | |
| 24 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | |
| 25 #include "ui/gfx/size.h" | |
| 26 | |
| 27 namespace ui { | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 // Would be nice if this were in UTCoreTypes.h, but it isn't | |
| 32 NSString* const kUTTypeURLName = @"public.url-name"; | |
| 33 | |
| 34 // Tells us if WebKit was the last to write to the pasteboard. There's no | |
| 35 // actual data associated with this type. | |
| 36 NSString* const kWebSmartPastePboardType = @"NeXT smart paste pasteboard type"; | |
| 37 | |
| 38 // Pepper custom data format type. | |
| 39 NSString* const kPepperCustomDataPboardType = | |
| 40 @"org.chromium.pepper-custom-data"; | |
| 41 | |
| 42 NSPasteboard* GetPasteboard() { | |
| 43 // The pasteboard should not be nil in a UI session, but this handy DCHECK | |
| 44 // can help track down problems if someone tries using clipboard code outside | |
| 45 // of a UI session. | |
| 46 NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; | |
| 47 DCHECK(pasteboard); | |
| 48 return pasteboard; | |
| 49 } | |
| 50 | |
| 51 } // namespace | |
| 52 | |
| 53 // Clipboard::FormatType implementation. | |
| 54 Clipboard::FormatType::FormatType() : data_(nil) { | |
| 55 } | |
| 56 | |
| 57 Clipboard::FormatType::FormatType(NSString* native_format) | |
| 58 : data_([native_format retain]) { | |
| 59 } | |
| 60 | |
| 61 Clipboard::FormatType::FormatType(const FormatType& other) | |
| 62 : data_([other.data_ retain]) { | |
| 63 } | |
| 64 | |
| 65 Clipboard::FormatType& Clipboard::FormatType::operator=( | |
| 66 const FormatType& other) { | |
| 67 if (this != &other) { | |
| 68 [data_ release]; | |
| 69 data_ = [other.data_ retain]; | |
| 70 } | |
| 71 return *this; | |
| 72 } | |
| 73 | |
| 74 bool Clipboard::FormatType::Equals(const FormatType& other) const { | |
| 75 return [data_ isEqualToString:other.data_]; | |
| 76 } | |
| 77 | |
| 78 Clipboard::FormatType::~FormatType() { | |
| 79 [data_ release]; | |
| 80 } | |
| 81 | |
| 82 std::string Clipboard::FormatType::Serialize() const { | |
| 83 return base::SysNSStringToUTF8(data_); | |
| 84 } | |
| 85 | |
| 86 // static | |
| 87 Clipboard::FormatType Clipboard::FormatType::Deserialize( | |
| 88 const std::string& serialization) { | |
| 89 return FormatType(base::SysUTF8ToNSString(serialization)); | |
| 90 } | |
| 91 | |
| 92 // Various predefined FormatTypes. | |
| 93 // static | |
| 94 Clipboard::FormatType Clipboard::GetFormatType( | |
| 95 const std::string& format_string) { | |
| 96 return FormatType::Deserialize(format_string); | |
| 97 } | |
| 98 | |
| 99 // static | |
| 100 const Clipboard::FormatType& Clipboard::GetUrlFormatType() { | |
| 101 CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSURLPboardType)); | |
| 102 return type; | |
| 103 } | |
| 104 | |
| 105 // static | |
| 106 const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { | |
| 107 return GetUrlFormatType(); | |
| 108 } | |
| 109 | |
| 110 // static | |
| 111 const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { | |
| 112 CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSStringPboardType)); | |
| 113 return type; | |
| 114 } | |
| 115 | |
| 116 // static | |
| 117 const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { | |
| 118 return GetPlainTextFormatType(); | |
| 119 } | |
| 120 | |
| 121 // static | |
| 122 const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { | |
| 123 CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSFilenamesPboardType)); | |
| 124 return type; | |
| 125 } | |
| 126 | |
| 127 // static | |
| 128 const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { | |
| 129 return GetFilenameFormatType(); | |
| 130 } | |
| 131 | |
| 132 // static | |
| 133 const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { | |
| 134 CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSHTMLPboardType)); | |
| 135 return type; | |
| 136 } | |
| 137 | |
| 138 // static | |
| 139 const Clipboard::FormatType& Clipboard::GetRtfFormatType() { | |
| 140 CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSRTFPboardType)); | |
| 141 return type; | |
| 142 } | |
| 143 | |
| 144 // static | |
| 145 const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { | |
| 146 CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSTIFFPboardType)); | |
| 147 return type; | |
| 148 } | |
| 149 | |
| 150 // static | |
| 151 const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { | |
| 152 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebSmartPastePboardType)); | |
| 153 return type; | |
| 154 } | |
| 155 | |
| 156 // static | |
| 157 const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { | |
| 158 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebCustomDataPboardType)); | |
| 159 return type; | |
| 160 } | |
| 161 | |
| 162 // static | |
| 163 const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { | |
| 164 CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPepperCustomDataPboardType)); | |
| 165 return type; | |
| 166 } | |
| 167 | |
| 168 // Clipboard factory method. | |
| 169 // static | |
| 170 Clipboard* Clipboard::Create() { | |
| 171 return new ClipboardMac; | |
| 172 } | |
| 173 | |
| 174 // ClipboardMac implementation. | |
| 175 ClipboardMac::ClipboardMac() { | |
| 176 DCHECK(CalledOnValidThread()); | |
| 177 } | |
| 178 | |
| 179 ClipboardMac::~ClipboardMac() { | |
| 180 DCHECK(CalledOnValidThread()); | |
| 181 } | |
| 182 | |
| 183 uint64 ClipboardMac::GetSequenceNumber(ClipboardType type) { | |
| 184 DCHECK(CalledOnValidThread()); | |
| 185 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 186 | |
| 187 NSPasteboard* pb = GetPasteboard(); | |
| 188 return [pb changeCount]; | |
| 189 } | |
| 190 | |
| 191 bool ClipboardMac::IsFormatAvailable(const FormatType& format, | |
| 192 ClipboardType type) const { | |
| 193 DCHECK(CalledOnValidThread()); | |
| 194 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 195 | |
| 196 NSPasteboard* pb = GetPasteboard(); | |
| 197 NSArray* types = [pb types]; | |
| 198 | |
| 199 // Safari only places RTF on the pasteboard, never HTML. We can convert RTF | |
| 200 // to HTML, so the presence of either indicates success when looking for HTML. | |
| 201 if ([format.ToNSString() isEqualToString:NSHTMLPboardType]) { | |
| 202 return [types containsObject:NSHTMLPboardType] || | |
| 203 [types containsObject:NSRTFPboardType]; | |
| 204 } | |
| 205 return [types containsObject:format.ToNSString()]; | |
| 206 } | |
| 207 | |
| 208 void ClipboardMac::Clear(ClipboardType type) { | |
| 209 DCHECK(CalledOnValidThread()); | |
| 210 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 211 | |
| 212 NSPasteboard* pb = GetPasteboard(); | |
| 213 [pb declareTypes:[NSArray array] owner:nil]; | |
| 214 } | |
| 215 | |
| 216 void ClipboardMac::ReadAvailableTypes(ClipboardType type, | |
| 217 std::vector<base::string16>* types, | |
| 218 bool* contains_filenames) const { | |
| 219 DCHECK(CalledOnValidThread()); | |
| 220 types->clear(); | |
| 221 if (IsFormatAvailable(Clipboard::GetPlainTextFormatType(), type)) | |
| 222 types->push_back(base::UTF8ToUTF16(kMimeTypeText)); | |
| 223 if (IsFormatAvailable(Clipboard::GetHtmlFormatType(), type)) | |
| 224 types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); | |
| 225 if (IsFormatAvailable(Clipboard::GetRtfFormatType(), type)) | |
| 226 types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); | |
| 227 if ([NSImage canInitWithPasteboard:GetPasteboard()]) | |
| 228 types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); | |
| 229 *contains_filenames = false; | |
| 230 | |
| 231 NSPasteboard* pb = GetPasteboard(); | |
| 232 if ([[pb types] containsObject:kWebCustomDataPboardType]) { | |
| 233 NSData* data = [pb dataForType:kWebCustomDataPboardType]; | |
| 234 if ([data length]) | |
| 235 ReadCustomDataTypes([data bytes], [data length], types); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 void ClipboardMac::ReadText(ClipboardType type, base::string16* result) const { | |
| 240 DCHECK(CalledOnValidThread()); | |
| 241 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 242 NSPasteboard* pb = GetPasteboard(); | |
| 243 NSString* contents = [pb stringForType:NSStringPboardType]; | |
| 244 | |
| 245 *result = base::SysNSStringToUTF16(contents); | |
| 246 } | |
| 247 | |
| 248 void ClipboardMac::ReadAsciiText(ClipboardType type, | |
| 249 std::string* result) const { | |
| 250 DCHECK(CalledOnValidThread()); | |
| 251 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 252 NSPasteboard* pb = GetPasteboard(); | |
| 253 NSString* contents = [pb stringForType:NSStringPboardType]; | |
| 254 | |
| 255 if (!contents) | |
| 256 result->clear(); | |
| 257 else | |
| 258 result->assign([contents UTF8String]); | |
| 259 } | |
| 260 | |
| 261 void ClipboardMac::ReadHTML(ClipboardType type, | |
| 262 base::string16* markup, | |
| 263 std::string* src_url, | |
| 264 uint32* fragment_start, | |
| 265 uint32* fragment_end) const { | |
| 266 DCHECK(CalledOnValidThread()); | |
| 267 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 268 | |
| 269 // TODO(avi): src_url? | |
| 270 markup->clear(); | |
| 271 if (src_url) | |
| 272 src_url->clear(); | |
| 273 | |
| 274 NSPasteboard* pb = GetPasteboard(); | |
| 275 NSArray* supportedTypes = [NSArray arrayWithObjects:NSHTMLPboardType, | |
| 276 NSRTFPboardType, | |
| 277 NSStringPboardType, | |
| 278 nil]; | |
| 279 NSString* bestType = [pb availableTypeFromArray:supportedTypes]; | |
| 280 if (bestType) { | |
| 281 NSString* contents = [pb stringForType:bestType]; | |
| 282 if ([bestType isEqualToString:NSRTFPboardType]) | |
| 283 contents = [pb htmlFromRtf]; | |
| 284 *markup = base::SysNSStringToUTF16(contents); | |
| 285 } | |
| 286 | |
| 287 *fragment_start = 0; | |
| 288 DCHECK(markup->length() <= kuint32max); | |
| 289 *fragment_end = static_cast<uint32>(markup->length()); | |
| 290 } | |
| 291 | |
| 292 void ClipboardMac::ReadRTF(ClipboardType type, std::string* result) const { | |
| 293 DCHECK(CalledOnValidThread()); | |
| 294 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 295 | |
| 296 return ReadData(GetRtfFormatType(), result); | |
| 297 } | |
| 298 | |
| 299 SkBitmap ClipboardMac::ReadImage(ClipboardType type) const { | |
| 300 DCHECK(CalledOnValidThread()); | |
| 301 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 302 | |
| 303 // If the pasteboard's image data is not to its liking, the guts of NSImage | |
| 304 // may throw, and that exception will leak. Prevent a crash in that case; | |
| 305 // a blank image is better. | |
| 306 base::scoped_nsobject<NSImage> image(base::mac::RunBlockIgnoringExceptions(^{ | |
| 307 return [[NSImage alloc] initWithPasteboard:GetPasteboard()]; | |
| 308 })); | |
| 309 SkBitmap bitmap; | |
| 310 if (image.get()) { | |
| 311 bitmap = gfx::NSImageToSkBitmapWithColorSpace( | |
| 312 image.get(), /*is_opaque=*/ false, base::mac::GetSystemColorSpace()); | |
| 313 } | |
| 314 return bitmap; | |
| 315 } | |
| 316 | |
| 317 void ClipboardMac::ReadCustomData(ClipboardType clipboard_type, | |
| 318 const base::string16& type, | |
| 319 base::string16* result) const { | |
| 320 DCHECK(CalledOnValidThread()); | |
| 321 DCHECK_EQ(clipboard_type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 322 | |
| 323 NSPasteboard* pb = GetPasteboard(); | |
| 324 if ([[pb types] containsObject:kWebCustomDataPboardType]) { | |
| 325 NSData* data = [pb dataForType:kWebCustomDataPboardType]; | |
| 326 if ([data length]) | |
| 327 ReadCustomDataForType([data bytes], [data length], type, result); | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 void ClipboardMac::ReadBookmark(base::string16* title, std::string* url) const { | |
| 332 DCHECK(CalledOnValidThread()); | |
| 333 NSPasteboard* pb = GetPasteboard(); | |
| 334 | |
| 335 if (title) { | |
| 336 NSString* contents = [pb stringForType:kUTTypeURLName]; | |
| 337 *title = base::SysNSStringToUTF16(contents); | |
| 338 } | |
| 339 | |
| 340 if (url) { | |
| 341 NSString* url_string = [[NSURL URLFromPasteboard:pb] absoluteString]; | |
| 342 if (!url_string) | |
| 343 url->clear(); | |
| 344 else | |
| 345 url->assign([url_string UTF8String]); | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 void ClipboardMac::ReadData(const FormatType& format, | |
| 350 std::string* result) const { | |
| 351 DCHECK(CalledOnValidThread()); | |
| 352 NSPasteboard* pb = GetPasteboard(); | |
| 353 NSData* data = [pb dataForType:format.ToNSString()]; | |
| 354 if ([data length]) | |
| 355 result->assign(static_cast<const char*>([data bytes]), [data length]); | |
| 356 } | |
| 357 | |
| 358 void ClipboardMac::WriteObjects(ClipboardType type, const ObjectMap& objects) { | |
| 359 DCHECK(CalledOnValidThread()); | |
| 360 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | |
| 361 | |
| 362 NSPasteboard* pb = GetPasteboard(); | |
| 363 [pb declareTypes:[NSArray array] owner:nil]; | |
| 364 | |
| 365 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); | |
| 366 ++iter) { | |
| 367 DispatchObject(static_cast<ObjectType>(iter->first), iter->second); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 void ClipboardMac::WriteText(const char* text_data, size_t text_len) { | |
| 372 std::string text_str(text_data, text_len); | |
| 373 NSString* text = base::SysUTF8ToNSString(text_str); | |
| 374 NSPasteboard* pb = GetPasteboard(); | |
| 375 [pb addTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; | |
| 376 [pb setString:text forType:NSStringPboardType]; | |
| 377 } | |
| 378 | |
| 379 void ClipboardMac::WriteHTML(const char* markup_data, | |
| 380 size_t markup_len, | |
| 381 const char* url_data, | |
| 382 size_t url_len) { | |
| 383 // We need to mark it as utf-8. (see crbug.com/11957) | |
| 384 std::string html_fragment_str("<meta charset='utf-8'>"); | |
| 385 html_fragment_str.append(markup_data, markup_len); | |
| 386 NSString* html_fragment = base::SysUTF8ToNSString(html_fragment_str); | |
| 387 | |
| 388 // TODO(avi): url_data? | |
| 389 NSPasteboard* pb = GetPasteboard(); | |
| 390 [pb addTypes:[NSArray arrayWithObject:NSHTMLPboardType] owner:nil]; | |
| 391 [pb setString:html_fragment forType:NSHTMLPboardType]; | |
| 392 } | |
| 393 | |
| 394 void ClipboardMac::WriteRTF(const char* rtf_data, size_t data_len) { | |
| 395 WriteData(GetRtfFormatType(), rtf_data, data_len); | |
| 396 } | |
| 397 | |
| 398 void ClipboardMac::WriteBookmark(const char* title_data, | |
| 399 size_t title_len, | |
| 400 const char* url_data, | |
| 401 size_t url_len) { | |
| 402 std::string title_str(title_data, title_len); | |
| 403 NSString* title = base::SysUTF8ToNSString(title_str); | |
| 404 std::string url_str(url_data, url_len); | |
| 405 NSString* url = base::SysUTF8ToNSString(url_str); | |
| 406 | |
| 407 // TODO(playmobil): In the Windows version of this function, an HTML | |
| 408 // representation of the bookmark is also added to the clipboard, to support | |
| 409 // drag and drop of web shortcuts. I don't think we need to do this on the | |
| 410 // Mac, but we should double check later on. | |
| 411 NSURL* nsurl = [NSURL URLWithString:url]; | |
| 412 | |
| 413 NSPasteboard* pb = GetPasteboard(); | |
| 414 // passing UTIs into the pasteboard methods is valid >= 10.5 | |
| 415 [pb addTypes:[NSArray arrayWithObjects:NSURLPboardType, kUTTypeURLName, nil] | |
| 416 owner:nil]; | |
| 417 [nsurl writeToPasteboard:pb]; | |
| 418 [pb setString:title forType:kUTTypeURLName]; | |
| 419 } | |
| 420 | |
| 421 void ClipboardMac::WriteBitmap(const SkBitmap& bitmap) { | |
| 422 NSImage* image = gfx::SkBitmapToNSImageWithColorSpace( | |
| 423 bitmap, base::mac::GetSystemColorSpace()); | |
| 424 // An API to ask the NSImage to write itself to the clipboard comes in 10.6 :( | |
| 425 // For now, spit out the image as a TIFF. | |
| 426 NSPasteboard* pb = GetPasteboard(); | |
| 427 [pb addTypes:[NSArray arrayWithObject:NSTIFFPboardType] owner:nil]; | |
| 428 NSData* tiff_data = [image TIFFRepresentation]; | |
| 429 LOG_IF(ERROR, tiff_data == NULL) << "Failed to allocate image for clipboard"; | |
| 430 if (tiff_data) { | |
| 431 [pb setData:tiff_data forType:NSTIFFPboardType]; | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 void ClipboardMac::WriteData(const FormatType& format, | |
| 436 const char* data_data, | |
| 437 size_t data_len) { | |
| 438 NSPasteboard* pb = GetPasteboard(); | |
| 439 [pb addTypes:[NSArray arrayWithObject:format.ToNSString()] owner:nil]; | |
| 440 [pb setData:[NSData dataWithBytes:data_data length:data_len] | |
| 441 forType:format.ToNSString()]; | |
| 442 } | |
| 443 | |
| 444 // Write an extra flavor that signifies WebKit was the last to modify the | |
| 445 // pasteboard. This flavor has no data. | |
| 446 void ClipboardMac::WriteWebSmartPaste() { | |
| 447 NSPasteboard* pb = GetPasteboard(); | |
| 448 NSString* format = GetWebKitSmartPasteFormatType().ToNSString(); | |
| 449 [pb addTypes:[NSArray arrayWithObject:format] owner:nil]; | |
| 450 [pb setData:nil forType:format]; | |
| 451 } | |
| 452 | |
| 453 } // namespace ui | |
| OLD | NEW |