1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2/* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6#include "mozilla/Logging.h" 7 8#include "mozilla/Unused.h" 9 10#include "gfxPlatform.h" 11#include "nsArrayUtils.h" 12#include "nsCOMPtr.h" 13#include "nsClipboard.h" 14#include "nsString.h" 15#include "nsISupportsPrimitives.h" 16#include "nsPrimitiveHelpers.h" 17#include "nsMemory.h" 18#include "nsIFile.h" 19#include "nsStringStream.h" 20#include "nsDragService.h" 21#include "nsEscape.h" 22#include "nsPrintfCString.h" 23#include "nsObjCExceptions.h" 24#include "imgIContainer.h" 25#include "nsCocoaUtils.h" 26 27using mozilla::gfx::DataSourceSurface; 28using mozilla::gfx::SourceSurface; 29using mozilla::LogLevel; 30 31extern mozilla::LazyLogModule sCocoaLog; 32 33extern void EnsureLogInitialized(); 34 35mozilla::StaticRefPtr<nsITransferable> nsClipboard::sSelectionCache; 36 37@implementation UTIHelper 38 39+ (NSString*)stringFromPboardType:(NSString*)aType 40{ 41 if ([aType isEqualToString:kMozWildcardPboardType] || 42 [aType isEqualToString:kMozCustomTypesPboardType] || 43 [aType isEqualToString:kPublicUrlPboardType] || 44 [aType isEqualToString:kPublicUrlNamePboardType] || 45 [aType isEqualToString:kMozFileUrlsPboardType] || 46 [aType isEqualToString:(NSString*)kPasteboardTypeFileURLPromise] || 47 [aType isEqualToString:(NSString*)kPasteboardTypeFilePromiseContent] || 48 [aType isEqualToString:(NSString*)kUTTypeFileURL] || 49 [aType isEqualToString:NSStringPboardType] || 50 [aType isEqualToString:NSPasteboardTypeString] || 51 [aType isEqualToString:NSPasteboardTypeHTML] || 52 [aType isEqualToString:NSPasteboardTypeRTF] || 53 [aType isEqualToString:NSPasteboardTypeTIFF] || 54 [aType isEqualToString:NSPasteboardTypePNG]) { 55 return [NSString stringWithString:aType]; 56 } 57 NSString* dynamicType = 58 (NSString*)UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, 59 (CFStringRef)aType, 60 kUTTypeData); 61 NSString* result = [NSString stringWithString:dynamicType]; 62 [dynamicType release]; 63 return result; 64} 65 66@end // UTIHelper 67 68nsClipboard::nsClipboard() 69 : mCachedClipboard(-1) 70 , mChangeCount(0) 71 , mIgnoreEmptyNotification(false) 72{ 73 EnsureLogInitialized(); 74} 75 76nsClipboard::~nsClipboard() 77{ 78 EmptyClipboard(kGlobalClipboard); 79 EmptyClipboard(kFindClipboard); 80 ClearSelectionCache(); 81} 82 83NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard) 84 85// We separate this into its own function because after an @try, all local 86// variables within that function get marked as volatile, and our C++ type 87// system doesn't like volatile things. 88static NSData* 89GetDataFromPasteboard(NSPasteboard* aPasteboard, NSString* aType) 90{ 91 NSData *data = nil; 92 @try { 93 data = [aPasteboard dataForType:aType]; 94 } @catch (NSException* e) { 95 NS_WARNING(nsPrintfCString("Exception raised while getting data from the pasteboard: \"%s - %s\"", 96 [[e name] UTF8String], [[e reason] UTF8String]).get()); 97 mozilla::Unused << e; 98 } 99 return data; 100} 101 102void 103nsClipboard::SetSelectionCache(nsITransferable *aTransferable) 104{ 105 sSelectionCache = aTransferable; 106} 107 108void 109nsClipboard::ClearSelectionCache() 110{ 111 sSelectionCache = nullptr; 112} 113 114NS_IMETHODIMP 115nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard) 116{ 117 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 118 119 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !mTransferable) 120 return NS_ERROR_FAILURE; 121 122 mIgnoreEmptyNotification = true; 123 124 NSDictionary* pasteboardOutputDict = PasteboardDictFromTransferable(mTransferable); 125 if (!pasteboardOutputDict) 126 return NS_ERROR_FAILURE; 127 128 unsigned int outputCount = [pasteboardOutputDict count]; 129 NSArray* outputKeys = [pasteboardOutputDict allKeys]; 130 NSPasteboard* cocoaPasteboard; 131 if (aWhichClipboard == kFindClipboard) { 132 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; 133 NSString* stringType = 134 [UTIHelper stringFromPboardType:NSPasteboardTypeString]; 135 [cocoaPasteboard declareTypes: 136 [NSArray arrayWithObject:stringType] owner:nil]; 137 } else { 138 // Write everything else out to the general pasteboard. 139 cocoaPasteboard = [NSPasteboard generalPasteboard]; 140 [cocoaPasteboard declareTypes:outputKeys owner:nil]; 141 } 142 143 for (unsigned int i = 0; i < outputCount; i++) { 144 NSString* currentKey = [outputKeys objectAtIndex:i]; 145 id currentValue = [pasteboardOutputDict valueForKey:currentKey]; 146 if (aWhichClipboard == kFindClipboard) { 147 if ([currentKey isEqualToString: 148 [UTIHelper stringFromPboardType:NSPasteboardTypeString]]) { 149 [cocoaPasteboard setString:currentValue forType:currentKey]; 150 } 151 } else { 152 if ([currentKey isEqualToString: 153 [UTIHelper stringFromPboardType:NSPasteboardTypeString]] || 154 [currentKey isEqualToString: 155 [UTIHelper stringFromPboardType:kPublicUrlPboardType]] || 156 [currentKey isEqualToString: 157 [UTIHelper stringFromPboardType:kPublicUrlNamePboardType]]) { 158 [cocoaPasteboard setString:currentValue forType:currentKey]; 159 } else if ([currentKey isEqualToString: 160 [UTIHelper stringFromPboardType: 161 kUrlsWithTitlesPboardType]]) { 162 [cocoaPasteboard setPropertyList: 163 [pasteboardOutputDict valueForKey:currentKey] 164 forType:currentKey]; 165 } else if ([currentKey isEqualToString: 166 [UTIHelper stringFromPboardType:NSPasteboardTypeHTML]]) { 167 [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) 168 forType:currentKey]; 169 } else if ([currentKey isEqualToString: 170 [UTIHelper stringFromPboardType:kMozFileUrlsPboardType]]) { 171 [cocoaPasteboard writeObjects:currentValue]; 172 } else { 173 [cocoaPasteboard setData:currentValue forType:currentKey]; 174 } 175 } 176 } 177 178 mCachedClipboard = aWhichClipboard; 179 mChangeCount = [cocoaPasteboard changeCount]; 180 181 mIgnoreEmptyNotification = false; 182 183 return NS_OK; 184 185 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 186} 187 188nsresult 189nsClipboard::TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteboard *cocoaPasteboard) 190{ 191 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 192 193 // get flavor list that includes all acceptable flavors (including ones obtained through conversion) 194 nsCOMPtr<nsIArray> flavorList; 195 nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); 196 if (NS_FAILED(rv)) 197 return NS_ERROR_FAILURE; 198 199 uint32_t flavorCount; 200 flavorList->GetLength(&flavorCount); 201 202 for (uint32_t i = 0; i < flavorCount; i++) { 203 nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); 204 if (!currentFlavor) 205 continue; 206 207 nsCString flavorStr; 208 currentFlavor->ToString(getter_Copies(flavorStr)); // i has a flavr 209 210 // printf("looking for clipboard data of type %s\n", flavorStr.get()); 211 212 NSString *pboardType = nil; 213 if (nsClipboard::IsStringType(flavorStr, &pboardType)) { 214 NSString* pString = [cocoaPasteboard stringForType:pboardType]; 215 if (!pString) 216 continue; 217 218 NSData* stringData; 219 if ([pboardType isEqualToString: 220 [UTIHelper stringFromPboardType:NSPasteboardTypeRTF]]) { 221 stringData = [pString dataUsingEncoding:NSASCIIStringEncoding]; 222 } else { 223 stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; 224 } 225 unsigned int dataLength = [stringData length]; 226 void* clipboardDataPtr = malloc(dataLength); 227 if (!clipboardDataPtr) { 228 [pboardType release]; 229 return NS_ERROR_OUT_OF_MEMORY; 230 } 231 [stringData getBytes:clipboardDataPtr]; 232 233 // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. 234 int32_t signedDataLength = dataLength; 235 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength); 236 dataLength = signedDataLength; 237 238 // skip BOM (Byte Order Mark to distinguish little or big endian) 239 char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr; 240 if ((dataLength > 2) && 241 ((clipboardDataPtrNoBOM[0] == 0xFEFF) || 242 (clipboardDataPtrNoBOM[0] == 0xFFFE))) { 243 dataLength -= sizeof(char16_t); 244 clipboardDataPtrNoBOM += 1; 245 } 246 247 nsCOMPtr<nsISupports> genericDataWrapper; 248 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, 249 getter_AddRefs(genericDataWrapper)); 250 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper, dataLength); 251 free(clipboardDataPtr); 252 break; 253 } 254 else if (flavorStr.EqualsLiteral(kCustomTypesMime)) { 255 NSString* type = 256 [cocoaPasteboard availableTypeFromArray: 257 [NSArray arrayWithObject: 258 [UTIHelper stringFromPboardType:kMozCustomTypesPboardType]]]; 259 if (!type) { 260 continue; 261 } 262 263 NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type); 264 if (!pasteboardData) { 265 continue; 266 } 267 268 unsigned int dataLength = [pasteboardData length]; 269 void* clipboardDataPtr = malloc(dataLength); 270 if (!clipboardDataPtr) { 271 [pboardType release]; 272 return NS_ERROR_OUT_OF_MEMORY; 273 } 274 [pasteboardData getBytes:clipboardDataPtr]; 275 276 nsCOMPtr<nsISupports> genericDataWrapper; 277 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength, 278 getter_AddRefs(genericDataWrapper)); 279 280 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper, dataLength); 281 free(clipboardDataPtr); 282 } 283 else if (flavorStr.EqualsLiteral(kJPEGImageMime) || 284 flavorStr.EqualsLiteral(kJPGImageMime) || 285 flavorStr.EqualsLiteral(kPNGImageMime) || 286 flavorStr.EqualsLiteral(kGIFImageMime)) { 287 // Figure out if there's data on the pasteboard we can grab (sanity check) 288 NSString* type = 289 [cocoaPasteboard availableTypeFromArray: 290 [NSArray arrayWithObjects: 291 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF], 292 [UTIHelper stringFromPboardType:NSPasteboardTypePNG], 293 nil]]; 294 if (!type) 295 continue; 296 297 // Read data off the clipboard 298 NSData *pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type); 299 if (!pasteboardData) 300 continue; 301 302 // Figure out what type we're converting to 303 CFStringRef outputType = NULL; 304 if (flavorStr.EqualsLiteral(kJPEGImageMime) || 305 flavorStr.EqualsLiteral(kJPGImageMime)) 306 outputType = CFSTR("public.jpeg"); 307 else if (flavorStr.EqualsLiteral(kPNGImageMime)) 308 outputType = CFSTR("public.png"); 309 else if (flavorStr.EqualsLiteral(kGIFImageMime)) 310 outputType = CFSTR("com.compuserve.gif"); 311 else 312 continue; 313 314 // Use ImageIO to interpret the data on the clipboard and transcode. 315 // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely 316 // and safely in most cases (like ObjC). A notable exception is CFRelease. 317 NSDictionary *options = 318 [NSDictionary dictionaryWithObjectsAndKeys: 319 (NSNumber*)kCFBooleanTrue, 320 kCGImageSourceShouldAllowFloat, 321 (type == [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF] ? 322 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF] : 323 [UTIHelper stringFromPboardType:NSPasteboardTypePNG]), 324 kCGImageSourceTypeIdentifierHint, nil]; 325 326 CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, 327 (CFDictionaryRef)options); 328 NSMutableData *encodedData = [NSMutableData data]; 329 CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData, 330 outputType, 331 1, NULL); 332 CGImageDestinationAddImageFromSource(dest, source, 0, NULL); 333 bool successfullyConverted = CGImageDestinationFinalize(dest); 334 335 if (successfullyConverted) { 336 // Put the converted data in a form Gecko can understand 337 nsCOMPtr<nsIInputStream> byteStream; 338 NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)[encodedData bytes], 339 [encodedData length], NS_ASSIGNMENT_COPY); 340 341 aTransferable->SetTransferData(flavorStr.get(), byteStream, sizeof(nsIInputStream*)); 342 } 343 344 if (dest) 345 CFRelease(dest); 346 if (source) 347 CFRelease(source); 348 349 if (successfullyConverted) 350 break; 351 else 352 continue; 353 } 354 [pboardType release]; 355 } 356 357 return NS_OK; 358 359 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 360} 361 362NS_IMETHODIMP 363nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, int32_t aWhichClipboard) 364{ 365 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 366 367 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !aTransferable) 368 return NS_ERROR_FAILURE; 369 370 NSPasteboard* cocoaPasteboard; 371 if (aWhichClipboard == kFindClipboard) { 372 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; 373 } else { 374 cocoaPasteboard = [NSPasteboard generalPasteboard]; 375 } 376 if (!cocoaPasteboard) 377 return NS_ERROR_FAILURE; 378 379 // get flavor list that includes all acceptable flavors (including ones obtained through conversion) 380 nsCOMPtr<nsIArray> flavorList; 381 nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); 382 if (NS_FAILED(rv)) 383 return NS_ERROR_FAILURE; 384 385 uint32_t flavorCount; 386 flavorList->GetLength(&flavorCount); 387 388 // If we were the last ones to put something on the pasteboard, then just use the cached 389 // transferable. Otherwise clear it because it isn't relevant any more. 390 if (mCachedClipboard == aWhichClipboard && 391 mChangeCount == [cocoaPasteboard changeCount]) { 392 if (mTransferable) { 393 for (uint32_t i = 0; i < flavorCount; i++) { 394 nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); 395 if (!currentFlavor) 396 continue; 397 398 nsCString flavorStr; 399 currentFlavor->ToString(getter_Copies(flavorStr)); 400 401 nsCOMPtr<nsISupports> dataSupports; 402 uint32_t dataSize = 0; 403 rv = mTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(dataSupports), &dataSize); 404 if (NS_SUCCEEDED(rv)) { 405 aTransferable->SetTransferData(flavorStr.get(), dataSupports, dataSize); 406 return NS_OK; // maybe try to fill in more types? Is there a point? 407 } 408 } 409 } 410 } else { 411 EmptyClipboard(aWhichClipboard); 412 } 413 414 // at this point we can't satisfy the request from cache data so let's look 415 // for things other people put on the system clipboard 416 417 return nsClipboard::TransferableFromPasteboard(aTransferable, cocoaPasteboard); 418 419 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 420} 421 422// returns true if we have *any* of the passed in flavors available for pasting 423NS_IMETHODIMP 424nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength, 425 int32_t aWhichClipboard, bool* outResult) 426{ 427 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 428 429 *outResult = false; 430 431 if ((aWhichClipboard != kGlobalClipboard) || !aFlavorList) 432 return NS_OK; 433 434 // first see if we have data for this in our cached transferable 435 if (mTransferable) { 436 nsCOMPtr<nsIArray> transferableFlavorList; 437 nsresult rv = mTransferable->FlavorsTransferableCanImport(getter_AddRefs(transferableFlavorList)); 438 if (NS_SUCCEEDED(rv)) { 439 uint32_t transferableFlavorCount; 440 transferableFlavorList->GetLength(&transferableFlavorCount); 441 for (uint32_t j = 0; j < transferableFlavorCount; j++) { 442 nsCOMPtr<nsISupportsCString> currentTransferableFlavor = 443 do_QueryElementAt(transferableFlavorList, j); 444 if (!currentTransferableFlavor) 445 continue; 446 nsCString transferableFlavorStr; 447 currentTransferableFlavor->ToString(getter_Copies(transferableFlavorStr)); 448 449 for (uint32_t k = 0; k < aLength; k++) { 450 if (transferableFlavorStr.Equals(aFlavorList[k])) { 451 *outResult = true; 452 return NS_OK; 453 } 454 } 455 } 456 } 457 } 458 459 NSPasteboard* generalPBoard = [NSPasteboard generalPasteboard]; 460 461 for (uint32_t i = 0; i < aLength; i++) { 462 nsDependentCString mimeType(aFlavorList[i]); 463 NSString *pboardType = nil; 464 465 if (nsClipboard::IsStringType(mimeType, &pboardType)) { 466 NSString* availableType = [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:pboardType]]; 467 if (availableType && [availableType isEqualToString:pboardType]) { 468 *outResult = true; 469 break; 470 } 471 } else if (!strcmp(aFlavorList[i], kCustomTypesMime)) { 472 NSString* availableType = 473 [generalPBoard availableTypeFromArray: 474 [NSArray arrayWithObject: 475 [UTIHelper stringFromPboardType:kMozCustomTypesPboardType]]]; 476 if (availableType) { 477 *outResult = true; 478 break; 479 } 480 } else if (!strcmp(aFlavorList[i], kJPEGImageMime) || 481 !strcmp(aFlavorList[i], kJPGImageMime) || 482 !strcmp(aFlavorList[i], kPNGImageMime) || 483 !strcmp(aFlavorList[i], kGIFImageMime)) { 484 NSString* availableType = 485 [generalPBoard availableTypeFromArray: 486 [NSArray arrayWithObjects: 487 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF], 488 [UTIHelper stringFromPboardType:NSPasteboardTypePNG], 489 nil]]; 490 if (availableType) { 491 *outResult = true; 492 break; 493 } 494 } 495 [pboardType release]; 496 } 497 498 return NS_OK; 499 500 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 501} 502 503NS_IMETHODIMP 504nsClipboard::SupportsFindClipboard(bool *_retval) 505{ 506 NS_ENSURE_ARG_POINTER(_retval); 507 *_retval = true; 508 return NS_OK; 509} 510 511// This function converts anything that other applications might understand into the system format 512// and puts it into a dictionary which it returns. 513// static 514NSDictionary* 515nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) 516{ 517 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 518 519 if (!aTransferable) 520 return nil; 521 522 NSMutableDictionary* pasteboardOutputDict = [NSMutableDictionary dictionary]; 523 524 nsCOMPtr<nsIArray> flavorList; 525 nsresult rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); 526 if (NS_FAILED(rv)) 527 return nil; 528 529 uint32_t flavorCount; 530 flavorList->GetLength(&flavorCount); 531 for (uint32_t i = 0; i < flavorCount; i++) { 532 nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); 533 if (!currentFlavor) 534 continue; 535 536 nsCString flavorStr; 537 currentFlavor->ToString(getter_Copies(flavorStr)); 538 539 MOZ_LOG(sCocoaLog, LogLevel::Info, ("writing out clipboard data of type %s (%d)\n", flavorStr.get(), i)); 540 541 NSString *pboardType = nil; 542 543 if (nsClipboard::IsStringType(flavorStr, &pboardType)) { 544 void* data = nullptr; 545 uint32_t dataSize = 0; 546 nsCOMPtr<nsISupports> genericDataWrapper; 547 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericDataWrapper), &dataSize); 548 nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); 549 550 NSString* nativeString; 551 if (data) 552 nativeString = [NSString stringWithCharacters:(const unichar*)data length:(dataSize / sizeof(char16_t))]; 553 else 554 nativeString = [NSString string]; 555 556 // be nice to Carbon apps, normalize the receiver's contents using Form C. 557 nativeString = [nativeString precomposedStringWithCanonicalMapping]; 558 559 [pasteboardOutputDict setObject:nativeString forKey:pboardType]; 560 561 free(data); 562 } 563 else if (flavorStr.EqualsLiteral(kCustomTypesMime)) { 564 void* data = nullptr; 565 uint32_t dataSize = 0; 566 nsCOMPtr<nsISupports> genericDataWrapper; 567 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericDataWrapper), &dataSize); 568 nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); 569 570 if (data) { 571 NSData* nativeData = [NSData dataWithBytes:data length:dataSize]; 572 NSString* customType = 573 [UTIHelper stringFromPboardType:kMozCustomTypesPboardType]; 574 [pasteboardOutputDict setObject:nativeData forKey:customType]; 575 free(data); 576 } 577 } 578 else if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || 579 flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime) || 580 flavorStr.EqualsLiteral(kNativeImageMime)) { 581 uint32_t dataSize = 0; 582 nsCOMPtr<nsISupports> transferSupports; 583 aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(transferSupports), &dataSize); 584 nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(transferSupports)); 585 if (!ptrPrimitive) 586 continue; 587 588 nsCOMPtr<nsISupports> primitiveData; 589 ptrPrimitive->GetData(getter_AddRefs(primitiveData)); 590 591 nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData)); 592 if (!image) { 593 NS_WARNING("Image isn't an imgIContainer in transferable"); 594 continue; 595 } 596 597 RefPtr<SourceSurface> surface = 598 image->GetFrame(imgIContainer::FRAME_CURRENT, 599 imgIContainer::FLAG_SYNC_DECODE); 600 if (!surface) { 601 continue; 602 } 603 CGImageRef imageRef = NULL; 604 rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef); 605 if (NS_FAILED(rv) || !imageRef) { 606 continue; 607 } 608 609 // Convert the CGImageRef to TIFF data. 610 CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0); 611 CGImageDestinationRef destRef = CGImageDestinationCreateWithData(tiffData, 612 CFSTR("public.tiff"), 613 1, 614 NULL); 615 CGImageDestinationAddImage(destRef, imageRef, NULL); 616 bool successfullyConverted = CGImageDestinationFinalize(destRef); 617 618 CGImageRelease(imageRef); 619 if (destRef) 620 CFRelease(destRef); 621 622 if (!successfullyConverted) { 623 if (tiffData) 624 CFRelease(tiffData); 625 continue; 626 } 627 628 NSString* tiffType = 629 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF]; 630 [pasteboardOutputDict setObject:(NSMutableData*)tiffData 631 forKey:tiffType]; 632 if (tiffData) 633 CFRelease(tiffData); 634 } 635 else if (flavorStr.EqualsLiteral(kFileMime)) { 636 uint32_t len = 0; 637 nsCOMPtr<nsISupports> genericFile; 638 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericFile), &len); 639 if (NS_FAILED(rv)) { 640 continue; 641 } 642 643 nsCOMPtr<nsIFile> file(do_QueryInterface(genericFile)); 644 if (!file) { 645 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericFile)); 646 647 if (ptr) { 648 ptr->GetData(getter_AddRefs(genericFile)); 649 file = do_QueryInterface(genericFile); 650 } 651 } 652 653 if (!file) { 654 continue; 655 } 656 657 nsAutoString fileURI; 658 rv = file->GetPath(fileURI); 659 if (NS_FAILED(rv)) { 660 continue; 661 } 662 663 NSString* str = nsCocoaUtils::ToNSString(fileURI); 664 NSURL* url = [NSURL fileURLWithPath:str isDirectory:NO]; 665 NSString* fileUTType = 666 [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL]; 667 [pasteboardOutputDict setObject:[url absoluteString] 668 forKey:fileUTType]; 669 } 670 else if (flavorStr.EqualsLiteral(kFilePromiseMime)) { 671 NSString* urlPromise = 672 [UTIHelper stringFromPboardType: 673 (NSString*)kPasteboardTypeFileURLPromise]; 674 NSString* urlPromiseContent = 675 [UTIHelper stringFromPboardType: 676 (NSString*)kPasteboardTypeFilePromiseContent]; 677 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] 678 forKey:urlPromise]; 679 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] 680 forKey:urlPromiseContent]; 681 } 682 else if (flavorStr.EqualsLiteral(kURLMime)) { 683 uint32_t len = 0; 684 nsCOMPtr<nsISupports> genericURL; 685 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericURL), &len); 686 nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL)); 687 688 nsAutoString url; 689 urlObject->GetData(url); 690 691 NSString* nativeTitle = nil; 692 693 // A newline embedded in the URL means that the form is actually URL + 694 // title. This embedding occurs in nsDragService::GetData. 695 int32_t newlinePos = url.FindChar(char16_t('\n')); 696 if (newlinePos >= 0) { 697 url.Truncate(newlinePos); 698 699 nsAutoString urlTitle; 700 urlObject->GetData(urlTitle); 701 urlTitle.Mid(urlTitle, newlinePos + 1, len - (newlinePos + 1)); 702 703 nativeTitle = 704 [NSString stringWithCharacters: 705 reinterpret_cast<const unichar*>(urlTitle.get()) 706 length:urlTitle.Length()]; 707 } 708 // The Finder doesn't like getting random binary data aka 709 // Unicode, so change it into an escaped URL containing only 710 // ASCII. 711 nsAutoCString utf8Data = NS_ConvertUTF16toUTF8(url.get(), url.Length()); 712 nsAutoCString escData; 713 NS_EscapeURL(utf8Data.get(), 714 utf8Data.Length(), 715 esc_OnlyNonASCII|esc_AlwaysCopy, 716 escData); 717 718 NSString* nativeURL = [NSString stringWithUTF8String:escData.get()]; 719 NSString* publicUrl = 720 [UTIHelper stringFromPboardType:kPublicUrlPboardType]; 721 [pasteboardOutputDict setObject:nativeURL forKey:publicUrl]; 722 if (nativeTitle) { 723 NSArray* urlsAndTitles = @[@[nativeURL], @[nativeTitle]]; 724 NSString* urlName = 725 [UTIHelper stringFromPboardType:kPublicUrlNamePboardType]; 726 NSString* urlsWithTitles = 727 [UTIHelper stringFromPboardType:kUrlsWithTitlesPboardType]; 728 [pasteboardOutputDict setObject:nativeTitle 729 forKey:urlName]; 730 [pasteboardOutputDict setObject:urlsAndTitles 731 forKey:urlsWithTitles]; 732 } 733 } 734 [pboardType release]; 735 // If it wasn't a type that we recognize as exportable we don't put it on the system 736 // clipboard. We'll just access it from our cached transferable when we need it. 737 } 738 739 return pasteboardOutputDict; 740 741 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 742} 743 744// aPasteboardType is being retained and needs to be released by the caller. 745bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType) 746{ 747 if (aMIMEType.EqualsLiteral(kUnicodeMime)) { 748 *aPasteboardType = 749 [[UTIHelper stringFromPboardType:NSPasteboardTypeString] retain]; 750 return true; 751 } else if (aMIMEType.EqualsLiteral(kRTFMime)) { 752 *aPasteboardType = 753 [[UTIHelper stringFromPboardType:NSPasteboardTypeRTF] retain]; 754 return true; 755 } else if (aMIMEType.EqualsLiteral(kHTMLMime)) { 756 *aPasteboardType = 757 [[UTIHelper stringFromPboardType:NSPasteboardTypeHTML] retain]; 758 return true; 759 } else { 760 return false; 761 } 762} 763 764NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString) 765{ 766 NSString* wrapped = 767 [NSString stringWithFormat: 768 @"<html>" 769 "<head>" 770 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">" 771 "</head>" 772 "<body>" 773 "%@" 774 "</body>" 775 "</html>", aString]; 776 return wrapped; 777} 778 779/** 780 * Sets the transferable object 781 * 782 */ 783NS_IMETHODIMP 784nsClipboard::SetData(nsITransferable* aTransferable, nsIClipboardOwner* anOwner, 785 int32_t aWhichClipboard) 786{ 787 NS_ASSERTION (aTransferable, "clipboard given a null transferable"); 788 789 if (aWhichClipboard == kSelectionCache) { 790 if (aTransferable) { 791 SetSelectionCache(aTransferable); 792 return NS_OK; 793 } 794 return NS_ERROR_FAILURE; 795 } 796 797 if (aTransferable == mTransferable && anOwner == mClipboardOwner) { 798 return NS_OK; 799 } 800 bool selectClipPresent; 801 SupportsSelectionClipboard(&selectClipPresent); 802 bool findClipPresent; 803 SupportsFindClipboard(&findClipPresent); 804 if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) { 805 return NS_ERROR_FAILURE; 806 } 807 808 EmptyClipboard(aWhichClipboard); 809 810 mClipboardOwner = anOwner; 811 mTransferable = aTransferable; 812 813 nsresult rv = NS_ERROR_FAILURE; 814 if (mTransferable) { 815 rv = SetNativeClipboardData(aWhichClipboard); 816 } 817 818 return rv; 819} 820 821/** 822 * Gets the transferable object 823 * 824 */ 825NS_IMETHODIMP 826nsClipboard::GetData(nsITransferable* aTransferable, int32_t aWhichClipboard) 827{ 828 NS_ASSERTION (aTransferable, "clipboard given a null transferable"); 829 830 bool selectClipPresent; 831 SupportsSelectionClipboard(&selectClipPresent); 832 bool findClipPresent; 833 SupportsFindClipboard(&findClipPresent); 834 if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) 835 return NS_ERROR_FAILURE; 836 837 if (aTransferable) { 838 return GetNativeClipboardData(aTransferable, aWhichClipboard); 839 } 840 841 return NS_ERROR_FAILURE; 842} 843 844NS_IMETHODIMP 845nsClipboard::EmptyClipboard(int32_t aWhichClipboard) 846{ 847 if (aWhichClipboard == kSelectionCache) { 848 ClearSelectionCache(); 849 return NS_OK; 850 } 851 852 bool selectClipPresent; 853 SupportsSelectionClipboard(&selectClipPresent); 854 bool findClipPresent; 855 SupportsFindClipboard(&findClipPresent); 856 if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) { 857 return NS_ERROR_FAILURE; 858 } 859 860 if (mIgnoreEmptyNotification) { 861 return NS_OK; 862 } 863 864 if (mClipboardOwner) { 865 mClipboardOwner->LosingOwnership(mTransferable); 866 mClipboardOwner = nullptr; 867 } 868 869 mTransferable = nullptr; 870 return NS_OK; 871} 872 873NS_IMETHODIMP 874nsClipboard::SupportsSelectionClipboard(bool* _retval) 875{ 876 *_retval = false; // we don't support the selection clipboard by default. 877 return NS_OK; 878} 879