widget/cocoa/nsClipboard.mm

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #ifdef MOZ_LOGGING
michael@0 7 #define FORCE_PR_LOG
michael@0 8 #endif
michael@0 9 #include "prlog.h"
michael@0 10
michael@0 11 #include "gfxPlatform.h"
michael@0 12 #include "nsCOMPtr.h"
michael@0 13 #include "nsClipboard.h"
michael@0 14 #include "nsString.h"
michael@0 15 #include "nsISupportsPrimitives.h"
michael@0 16 #include "nsXPIDLString.h"
michael@0 17 #include "nsPrimitiveHelpers.h"
michael@0 18 #include "nsMemory.h"
michael@0 19 #include "nsIFile.h"
michael@0 20 #include "nsStringStream.h"
michael@0 21 #include "nsDragService.h"
michael@0 22 #include "nsEscape.h"
michael@0 23 #include "nsPrintfCString.h"
michael@0 24 #include "nsObjCExceptions.h"
michael@0 25 #include "imgIContainer.h"
michael@0 26 #include "nsCocoaUtils.h"
michael@0 27
michael@0 28 using mozilla::gfx::DataSourceSurface;
michael@0 29 using mozilla::gfx::SourceSurface;
michael@0 30 using mozilla::RefPtr;
michael@0 31
michael@0 32 // Screenshots use the (undocumented) png pasteboard type.
michael@0 33 #define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil
michael@0 34
michael@0 35 #ifdef PR_LOGGING
michael@0 36 extern PRLogModuleInfo* sCocoaLog;
michael@0 37 #endif
michael@0 38
michael@0 39 extern void EnsureLogInitialized();
michael@0 40
michael@0 41 nsClipboard::nsClipboard() : nsBaseClipboard()
michael@0 42 {
michael@0 43 mCachedClipboard = -1;
michael@0 44 mChangeCount = 0;
michael@0 45
michael@0 46 EnsureLogInitialized();
michael@0 47 }
michael@0 48
michael@0 49 nsClipboard::~nsClipboard()
michael@0 50 {
michael@0 51 }
michael@0 52
michael@0 53 // We separate this into its own function because after an @try, all local
michael@0 54 // variables within that function get marked as volatile, and our C++ type
michael@0 55 // system doesn't like volatile things.
michael@0 56 static NSData*
michael@0 57 GetDataFromPasteboard(NSPasteboard* aPasteboard, NSString* aType)
michael@0 58 {
michael@0 59 NSData *data = nil;
michael@0 60 @try {
michael@0 61 data = [aPasteboard dataForType:aType];
michael@0 62 } @catch (NSException* e) {
michael@0 63 NS_WARNING(nsPrintfCString("Exception raised while getting data from the pasteboard: \"%s - %s\"",
michael@0 64 [[e name] UTF8String], [[e reason] UTF8String]).get());
michael@0 65 }
michael@0 66 return data;
michael@0 67 }
michael@0 68
michael@0 69 NS_IMETHODIMP
michael@0 70 nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard)
michael@0 71 {
michael@0 72 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
michael@0 73
michael@0 74 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !mTransferable)
michael@0 75 return NS_ERROR_FAILURE;
michael@0 76
michael@0 77 mIgnoreEmptyNotification = true;
michael@0 78
michael@0 79 NSDictionary* pasteboardOutputDict = PasteboardDictFromTransferable(mTransferable);
michael@0 80 if (!pasteboardOutputDict)
michael@0 81 return NS_ERROR_FAILURE;
michael@0 82
michael@0 83 unsigned int outputCount = [pasteboardOutputDict count];
michael@0 84 NSArray* outputKeys = [pasteboardOutputDict allKeys];
michael@0 85 NSPasteboard* cocoaPasteboard;
michael@0 86 if (aWhichClipboard == kFindClipboard) {
michael@0 87 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
michael@0 88 [cocoaPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
michael@0 89 } else {
michael@0 90 // Write everything else out to the general pasteboard.
michael@0 91 cocoaPasteboard = [NSPasteboard generalPasteboard];
michael@0 92 [cocoaPasteboard declareTypes:outputKeys owner:nil];
michael@0 93 }
michael@0 94
michael@0 95 for (unsigned int i = 0; i < outputCount; i++) {
michael@0 96 NSString* currentKey = [outputKeys objectAtIndex:i];
michael@0 97 id currentValue = [pasteboardOutputDict valueForKey:currentKey];
michael@0 98 if (aWhichClipboard == kFindClipboard) {
michael@0 99 if (currentKey == NSStringPboardType)
michael@0 100 [cocoaPasteboard setString:currentValue forType:currentKey];
michael@0 101 } else {
michael@0 102 if (currentKey == NSStringPboardType ||
michael@0 103 currentKey == kCorePboardType_url ||
michael@0 104 currentKey == kCorePboardType_urld ||
michael@0 105 currentKey == kCorePboardType_urln) {
michael@0 106 [cocoaPasteboard setString:currentValue forType:currentKey];
michael@0 107 } else if (currentKey == NSHTMLPboardType) {
michael@0 108 [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
michael@0 109 forType:currentKey];
michael@0 110 } else {
michael@0 111 [cocoaPasteboard setData:currentValue forType:currentKey];
michael@0 112 }
michael@0 113 }
michael@0 114 }
michael@0 115
michael@0 116 mCachedClipboard = aWhichClipboard;
michael@0 117 mChangeCount = [cocoaPasteboard changeCount];
michael@0 118
michael@0 119 mIgnoreEmptyNotification = false;
michael@0 120
michael@0 121 return NS_OK;
michael@0 122
michael@0 123 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
michael@0 124 }
michael@0 125
michael@0 126 nsresult
michael@0 127 nsClipboard::TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteboard *cocoaPasteboard)
michael@0 128 {
michael@0 129 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
michael@0 130
michael@0 131 // get flavor list that includes all acceptable flavors (including ones obtained through conversion)
michael@0 132 nsCOMPtr<nsISupportsArray> flavorList;
michael@0 133 nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
michael@0 134 if (NS_FAILED(rv))
michael@0 135 return NS_ERROR_FAILURE;
michael@0 136
michael@0 137 uint32_t flavorCount;
michael@0 138 flavorList->Count(&flavorCount);
michael@0 139
michael@0 140 for (uint32_t i = 0; i < flavorCount; i++) {
michael@0 141 nsCOMPtr<nsISupports> genericFlavor;
michael@0 142 flavorList->GetElementAt(i, getter_AddRefs(genericFlavor));
michael@0 143 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
michael@0 144 if (!currentFlavor)
michael@0 145 continue;
michael@0 146
michael@0 147 nsXPIDLCString flavorStr;
michael@0 148 currentFlavor->ToString(getter_Copies(flavorStr)); // i has a flavr
michael@0 149
michael@0 150 // printf("looking for clipboard data of type %s\n", flavorStr.get());
michael@0 151
michael@0 152 NSString *pboardType = nil;
michael@0 153 if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
michael@0 154 NSString* pString = [cocoaPasteboard stringForType:pboardType];
michael@0 155 if (!pString)
michael@0 156 continue;
michael@0 157
michael@0 158 NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
michael@0 159 unsigned int dataLength = [stringData length];
michael@0 160 void* clipboardDataPtr = malloc(dataLength);
michael@0 161 if (!clipboardDataPtr)
michael@0 162 return NS_ERROR_OUT_OF_MEMORY;
michael@0 163 [stringData getBytes:clipboardDataPtr];
michael@0 164
michael@0 165 // The DOM only wants LF, so convert from MacOS line endings to DOM line endings.
michael@0 166 int32_t signedDataLength = dataLength;
michael@0 167 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength);
michael@0 168 dataLength = signedDataLength;
michael@0 169
michael@0 170 // skip BOM (Byte Order Mark to distinguish little or big endian)
michael@0 171 char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr;
michael@0 172 if ((dataLength > 2) &&
michael@0 173 ((clipboardDataPtrNoBOM[0] == 0xFEFF) ||
michael@0 174 (clipboardDataPtrNoBOM[0] == 0xFFFE))) {
michael@0 175 dataLength -= sizeof(char16_t);
michael@0 176 clipboardDataPtrNoBOM += 1;
michael@0 177 }
michael@0 178
michael@0 179 nsCOMPtr<nsISupports> genericDataWrapper;
michael@0 180 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength,
michael@0 181 getter_AddRefs(genericDataWrapper));
michael@0 182 aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength);
michael@0 183 free(clipboardDataPtr);
michael@0 184 break;
michael@0 185 }
michael@0 186 else if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
michael@0 187 flavorStr.EqualsLiteral(kJPGImageMime) ||
michael@0 188 flavorStr.EqualsLiteral(kPNGImageMime) ||
michael@0 189 flavorStr.EqualsLiteral(kGIFImageMime)) {
michael@0 190 // Figure out if there's data on the pasteboard we can grab (sanity check)
michael@0 191 NSString *type = [cocoaPasteboard availableTypeFromArray:[NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]];
michael@0 192 if (!type)
michael@0 193 continue;
michael@0 194
michael@0 195 // Read data off the clipboard
michael@0 196 NSData *pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type);
michael@0 197 if (!pasteboardData)
michael@0 198 continue;
michael@0 199
michael@0 200 // Figure out what type we're converting to
michael@0 201 CFStringRef outputType = NULL;
michael@0 202 if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
michael@0 203 flavorStr.EqualsLiteral(kJPGImageMime))
michael@0 204 outputType = CFSTR("public.jpeg");
michael@0 205 else if (flavorStr.EqualsLiteral(kPNGImageMime))
michael@0 206 outputType = CFSTR("public.png");
michael@0 207 else if (flavorStr.EqualsLiteral(kGIFImageMime))
michael@0 208 outputType = CFSTR("com.compuserve.gif");
michael@0 209 else
michael@0 210 continue;
michael@0 211
michael@0 212 // Use ImageIO to interpret the data on the clipboard and transcode.
michael@0 213 // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely
michael@0 214 // and safely in most cases (like ObjC). A notable exception is CFRelease.
michael@0 215 NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
michael@0 216 (NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat,
michael@0 217 (type == NSTIFFPboardType ? @"public.tiff" : @"public.png"),
michael@0 218 kCGImageSourceTypeIdentifierHint, nil];
michael@0 219
michael@0 220 CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData,
michael@0 221 (CFDictionaryRef)options);
michael@0 222 NSMutableData *encodedData = [NSMutableData data];
michael@0 223 CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData,
michael@0 224 outputType,
michael@0 225 1, NULL);
michael@0 226 CGImageDestinationAddImageFromSource(dest, source, 0, NULL);
michael@0 227 bool successfullyConverted = CGImageDestinationFinalize(dest);
michael@0 228
michael@0 229 if (successfullyConverted) {
michael@0 230 // Put the converted data in a form Gecko can understand
michael@0 231 nsCOMPtr<nsIInputStream> byteStream;
michael@0 232 NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)[encodedData bytes],
michael@0 233 [encodedData length], NS_ASSIGNMENT_COPY);
michael@0 234
michael@0 235 aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*));
michael@0 236 }
michael@0 237
michael@0 238 if (dest)
michael@0 239 CFRelease(dest);
michael@0 240 if (source)
michael@0 241 CFRelease(source);
michael@0 242
michael@0 243 if (successfullyConverted)
michael@0 244 break;
michael@0 245 else
michael@0 246 continue;
michael@0 247 }
michael@0 248 }
michael@0 249
michael@0 250 return NS_OK;
michael@0 251
michael@0 252 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
michael@0 253 }
michael@0 254
michael@0 255 NS_IMETHODIMP
michael@0 256 nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, int32_t aWhichClipboard)
michael@0 257 {
michael@0 258 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
michael@0 259
michael@0 260 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !aTransferable)
michael@0 261 return NS_ERROR_FAILURE;
michael@0 262
michael@0 263 NSPasteboard* cocoaPasteboard;
michael@0 264 if (aWhichClipboard == kFindClipboard) {
michael@0 265 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
michael@0 266 } else {
michael@0 267 cocoaPasteboard = [NSPasteboard generalPasteboard];
michael@0 268 }
michael@0 269 if (!cocoaPasteboard)
michael@0 270 return NS_ERROR_FAILURE;
michael@0 271
michael@0 272 // get flavor list that includes all acceptable flavors (including ones obtained through conversion)
michael@0 273 nsCOMPtr<nsISupportsArray> flavorList;
michael@0 274 nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
michael@0 275 if (NS_FAILED(rv))
michael@0 276 return NS_ERROR_FAILURE;
michael@0 277
michael@0 278 uint32_t flavorCount;
michael@0 279 flavorList->Count(&flavorCount);
michael@0 280
michael@0 281 // If we were the last ones to put something on the pasteboard, then just use the cached
michael@0 282 // transferable. Otherwise clear it because it isn't relevant any more.
michael@0 283 if (mCachedClipboard == aWhichClipboard &&
michael@0 284 mChangeCount == [cocoaPasteboard changeCount]) {
michael@0 285 if (mTransferable) {
michael@0 286 for (uint32_t i = 0; i < flavorCount; i++) {
michael@0 287 nsCOMPtr<nsISupports> genericFlavor;
michael@0 288 flavorList->GetElementAt(i, getter_AddRefs(genericFlavor));
michael@0 289 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
michael@0 290 if (!currentFlavor)
michael@0 291 continue;
michael@0 292
michael@0 293 nsXPIDLCString flavorStr;
michael@0 294 currentFlavor->ToString(getter_Copies(flavorStr));
michael@0 295
michael@0 296 nsCOMPtr<nsISupports> dataSupports;
michael@0 297 uint32_t dataSize = 0;
michael@0 298 rv = mTransferable->GetTransferData(flavorStr, getter_AddRefs(dataSupports), &dataSize);
michael@0 299 if (NS_SUCCEEDED(rv)) {
michael@0 300 aTransferable->SetTransferData(flavorStr, dataSupports, dataSize);
michael@0 301 return NS_OK; // maybe try to fill in more types? Is there a point?
michael@0 302 }
michael@0 303 }
michael@0 304 }
michael@0 305 } else {
michael@0 306 nsBaseClipboard::EmptyClipboard(aWhichClipboard);
michael@0 307 }
michael@0 308
michael@0 309 // at this point we can't satisfy the request from cache data so let's look
michael@0 310 // for things other people put on the system clipboard
michael@0 311
michael@0 312 return nsClipboard::TransferableFromPasteboard(aTransferable, cocoaPasteboard);
michael@0 313
michael@0 314 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
michael@0 315 }
michael@0 316
michael@0 317 // returns true if we have *any* of the passed in flavors available for pasting
michael@0 318 NS_IMETHODIMP
michael@0 319 nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength,
michael@0 320 int32_t aWhichClipboard, bool* outResult)
michael@0 321 {
michael@0 322 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
michael@0 323
michael@0 324 *outResult = false;
michael@0 325
michael@0 326 if ((aWhichClipboard != kGlobalClipboard) || !aFlavorList)
michael@0 327 return NS_OK;
michael@0 328
michael@0 329 // first see if we have data for this in our cached transferable
michael@0 330 if (mTransferable) {
michael@0 331 nsCOMPtr<nsISupportsArray> transferableFlavorList;
michael@0 332 nsresult rv = mTransferable->FlavorsTransferableCanImport(getter_AddRefs(transferableFlavorList));
michael@0 333 if (NS_SUCCEEDED(rv)) {
michael@0 334 uint32_t transferableFlavorCount;
michael@0 335 transferableFlavorList->Count(&transferableFlavorCount);
michael@0 336 for (uint32_t j = 0; j < transferableFlavorCount; j++) {
michael@0 337 nsCOMPtr<nsISupports> transferableFlavorSupports;
michael@0 338 transferableFlavorList->GetElementAt(j, getter_AddRefs(transferableFlavorSupports));
michael@0 339 nsCOMPtr<nsISupportsCString> currentTransferableFlavor(do_QueryInterface(transferableFlavorSupports));
michael@0 340 if (!currentTransferableFlavor)
michael@0 341 continue;
michael@0 342 nsXPIDLCString transferableFlavorStr;
michael@0 343 currentTransferableFlavor->ToString(getter_Copies(transferableFlavorStr));
michael@0 344
michael@0 345 for (uint32_t k = 0; k < aLength; k++) {
michael@0 346 if (transferableFlavorStr.Equals(aFlavorList[k])) {
michael@0 347 *outResult = true;
michael@0 348 return NS_OK;
michael@0 349 }
michael@0 350 }
michael@0 351 }
michael@0 352 }
michael@0 353 }
michael@0 354
michael@0 355 NSPasteboard* generalPBoard = [NSPasteboard generalPasteboard];
michael@0 356
michael@0 357 for (uint32_t i = 0; i < aLength; i++) {
michael@0 358 nsDependentCString mimeType(aFlavorList[i]);
michael@0 359 NSString *pboardType = nil;
michael@0 360
michael@0 361 if (nsClipboard::IsStringType(mimeType, &pboardType)) {
michael@0 362 NSString* availableType = [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:pboardType]];
michael@0 363 if (availableType && [availableType isEqualToString:pboardType]) {
michael@0 364 *outResult = true;
michael@0 365 break;
michael@0 366 }
michael@0 367 } else if (!strcmp(aFlavorList[i], kJPEGImageMime) ||
michael@0 368 !strcmp(aFlavorList[i], kJPGImageMime) ||
michael@0 369 !strcmp(aFlavorList[i], kPNGImageMime) ||
michael@0 370 !strcmp(aFlavorList[i], kGIFImageMime)) {
michael@0 371 NSString* availableType = [generalPBoard availableTypeFromArray:
michael@0 372 [NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]];
michael@0 373 if (availableType) {
michael@0 374 *outResult = true;
michael@0 375 break;
michael@0 376 }
michael@0 377 }
michael@0 378 }
michael@0 379
michael@0 380 return NS_OK;
michael@0 381
michael@0 382 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
michael@0 383 }
michael@0 384
michael@0 385 NS_IMETHODIMP
michael@0 386 nsClipboard::SupportsFindClipboard(bool *_retval)
michael@0 387 {
michael@0 388 NS_ENSURE_ARG_POINTER(_retval);
michael@0 389 *_retval = true;
michael@0 390 return NS_OK;
michael@0 391 }
michael@0 392
michael@0 393 // This function converts anything that other applications might understand into the system format
michael@0 394 // and puts it into a dictionary which it returns.
michael@0 395 // static
michael@0 396 NSDictionary*
michael@0 397 nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable)
michael@0 398 {
michael@0 399 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 400
michael@0 401 if (!aTransferable)
michael@0 402 return nil;
michael@0 403
michael@0 404 NSMutableDictionary* pasteboardOutputDict = [NSMutableDictionary dictionary];
michael@0 405
michael@0 406 nsCOMPtr<nsISupportsArray> flavorList;
michael@0 407 nsresult rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
michael@0 408 if (NS_FAILED(rv))
michael@0 409 return nil;
michael@0 410
michael@0 411 uint32_t flavorCount;
michael@0 412 flavorList->Count(&flavorCount);
michael@0 413 for (uint32_t i = 0; i < flavorCount; i++) {
michael@0 414 nsCOMPtr<nsISupports> genericFlavor;
michael@0 415 flavorList->GetElementAt(i, getter_AddRefs(genericFlavor));
michael@0 416 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
michael@0 417 if (!currentFlavor)
michael@0 418 continue;
michael@0 419
michael@0 420 nsXPIDLCString flavorStr;
michael@0 421 currentFlavor->ToString(getter_Copies(flavorStr));
michael@0 422
michael@0 423 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("writing out clipboard data of type %s (%d)\n", flavorStr.get(), i));
michael@0 424
michael@0 425 NSString *pboardType = nil;
michael@0 426
michael@0 427 if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
michael@0 428 void* data = nullptr;
michael@0 429 uint32_t dataSize = 0;
michael@0 430 nsCOMPtr<nsISupports> genericDataWrapper;
michael@0 431 rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize);
michael@0 432 nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize);
michael@0 433
michael@0 434 NSString* nativeString;
michael@0 435 if (data)
michael@0 436 nativeString = [NSString stringWithCharacters:(const unichar*)data length:(dataSize / sizeof(char16_t))];
michael@0 437 else
michael@0 438 nativeString = [NSString string];
michael@0 439
michael@0 440 // be nice to Carbon apps, normalize the receiver's contents using Form C.
michael@0 441 nativeString = [nativeString precomposedStringWithCanonicalMapping];
michael@0 442
michael@0 443 [pasteboardOutputDict setObject:nativeString forKey:pboardType];
michael@0 444
michael@0 445 nsMemory::Free(data);
michael@0 446 }
michael@0 447 else if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) ||
michael@0 448 flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime) ||
michael@0 449 flavorStr.EqualsLiteral(kNativeImageMime)) {
michael@0 450 uint32_t dataSize = 0;
michael@0 451 nsCOMPtr<nsISupports> transferSupports;
michael@0 452 aTransferable->GetTransferData(flavorStr, getter_AddRefs(transferSupports), &dataSize);
michael@0 453 nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(transferSupports));
michael@0 454 if (!ptrPrimitive)
michael@0 455 continue;
michael@0 456
michael@0 457 nsCOMPtr<nsISupports> primitiveData;
michael@0 458 ptrPrimitive->GetData(getter_AddRefs(primitiveData));
michael@0 459
michael@0 460 nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData));
michael@0 461 if (!image) {
michael@0 462 NS_WARNING("Image isn't an imgIContainer in transferable");
michael@0 463 continue;
michael@0 464 }
michael@0 465
michael@0 466 RefPtr<SourceSurface> surface =
michael@0 467 image->GetFrame(imgIContainer::FRAME_CURRENT,
michael@0 468 imgIContainer::FLAG_SYNC_DECODE);
michael@0 469 if (!surface) {
michael@0 470 continue;
michael@0 471 }
michael@0 472 CGImageRef imageRef = NULL;
michael@0 473 nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef);
michael@0 474 if (NS_FAILED(rv) || !imageRef) {
michael@0 475 continue;
michael@0 476 }
michael@0 477
michael@0 478 // Convert the CGImageRef to TIFF data.
michael@0 479 CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0);
michael@0 480 CGImageDestinationRef destRef = CGImageDestinationCreateWithData(tiffData,
michael@0 481 CFSTR("public.tiff"),
michael@0 482 1,
michael@0 483 NULL);
michael@0 484 CGImageDestinationAddImage(destRef, imageRef, NULL);
michael@0 485 bool successfullyConverted = CGImageDestinationFinalize(destRef);
michael@0 486
michael@0 487 CGImageRelease(imageRef);
michael@0 488 if (destRef)
michael@0 489 CFRelease(destRef);
michael@0 490
michael@0 491 if (!successfullyConverted) {
michael@0 492 if (tiffData)
michael@0 493 CFRelease(tiffData);
michael@0 494 continue;
michael@0 495 }
michael@0 496
michael@0 497 [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType];
michael@0 498 if (tiffData)
michael@0 499 CFRelease(tiffData);
michael@0 500 }
michael@0 501 else if (flavorStr.EqualsLiteral(kFileMime)) {
michael@0 502 uint32_t len = 0;
michael@0 503 nsCOMPtr<nsISupports> genericFile;
michael@0 504 rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericFile), &len);
michael@0 505 if (NS_FAILED(rv)) {
michael@0 506 continue;
michael@0 507 }
michael@0 508
michael@0 509 nsCOMPtr<nsIFile> file(do_QueryInterface(genericFile));
michael@0 510 if (!file) {
michael@0 511 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericFile));
michael@0 512
michael@0 513 if (ptr) {
michael@0 514 ptr->GetData(getter_AddRefs(genericFile));
michael@0 515 file = do_QueryInterface(genericFile);
michael@0 516 }
michael@0 517 }
michael@0 518
michael@0 519 if (!file) {
michael@0 520 continue;
michael@0 521 }
michael@0 522
michael@0 523 nsAutoString fileURI;
michael@0 524 rv = file->GetPath(fileURI);
michael@0 525 if (NS_FAILED(rv)) {
michael@0 526 continue;
michael@0 527 }
michael@0 528
michael@0 529 NSString* str = nsCocoaUtils::ToNSString(fileURI);
michael@0 530 NSArray* fileList = [NSArray arrayWithObjects:str, nil];
michael@0 531 [pasteboardOutputDict setObject:fileList forKey:NSFilenamesPboardType];
michael@0 532 }
michael@0 533 else if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
michael@0 534 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:NSFilesPromisePboardType];
michael@0 535 }
michael@0 536 else if (flavorStr.EqualsLiteral(kURLMime)) {
michael@0 537 uint32_t len = 0;
michael@0 538 nsCOMPtr<nsISupports> genericURL;
michael@0 539 rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericURL), &len);
michael@0 540 nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL));
michael@0 541
michael@0 542 nsAutoString url;
michael@0 543 urlObject->GetData(url);
michael@0 544
michael@0 545 // A newline embedded in the URL means that the form is actually URL + title.
michael@0 546 int32_t newlinePos = url.FindChar(char16_t('\n'));
michael@0 547 if (newlinePos >= 0) {
michael@0 548 url.Truncate(newlinePos);
michael@0 549
michael@0 550 nsAutoString urlTitle;
michael@0 551 urlObject->GetData(urlTitle);
michael@0 552 urlTitle.Mid(urlTitle, newlinePos + 1, len - (newlinePos + 1));
michael@0 553
michael@0 554 NSString *nativeTitle = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(urlTitle.get())
michael@0 555 length:urlTitle.Length()];
michael@0 556 // be nice to Carbon apps, normalize the receiver's contents using Form C.
michael@0 557 [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urln];
michael@0 558 // Also put the title out as 'urld', since some recipients will look for that.
michael@0 559 [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urld];
michael@0 560 [nativeTitle release];
michael@0 561 }
michael@0 562
michael@0 563 // The Finder doesn't like getting random binary data aka
michael@0 564 // Unicode, so change it into an escaped URL containing only
michael@0 565 // ASCII.
michael@0 566 nsAutoCString utf8Data = NS_ConvertUTF16toUTF8(url.get(), url.Length());
michael@0 567 nsAutoCString escData;
michael@0 568 NS_EscapeURL(utf8Data.get(), utf8Data.Length(), esc_OnlyNonASCII|esc_AlwaysCopy, escData);
michael@0 569
michael@0 570 // printf("Escaped url is %s, length %d\n", escData.get(), escData.Length());
michael@0 571
michael@0 572 NSString *nativeURL = [NSString stringWithUTF8String:escData.get()];
michael@0 573 [pasteboardOutputDict setObject:nativeURL forKey:kCorePboardType_url];
michael@0 574 }
michael@0 575 // If it wasn't a type that we recognize as exportable we don't put it on the system
michael@0 576 // clipboard. We'll just access it from our cached transferable when we need it.
michael@0 577 }
michael@0 578
michael@0 579 return pasteboardOutputDict;
michael@0 580
michael@0 581 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 582 }
michael@0 583
michael@0 584 bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType)
michael@0 585 {
michael@0 586 if (aMIMEType.EqualsLiteral(kUnicodeMime) ||
michael@0 587 aMIMEType.EqualsLiteral(kHTMLMime)) {
michael@0 588 if (aMIMEType.EqualsLiteral(kUnicodeMime))
michael@0 589 *aPasteboardType = NSStringPboardType;
michael@0 590 else
michael@0 591 *aPasteboardType = NSHTMLPboardType;
michael@0 592 return true;
michael@0 593 } else {
michael@0 594 return false;
michael@0 595 }
michael@0 596 }
michael@0 597
michael@0 598 NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString)
michael@0 599 {
michael@0 600 NSString* wrapped =
michael@0 601 [NSString stringWithFormat:
michael@0 602 @"<html>"
michael@0 603 "<head>"
michael@0 604 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"
michael@0 605 "</head>"
michael@0 606 "<body>"
michael@0 607 "%@"
michael@0 608 "</body>"
michael@0 609 "</html>", aString];
michael@0 610 return wrapped;
michael@0 611 }

mercurial