michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG michael@0: #endif michael@0: #include "prlog.h" michael@0: michael@0: #include "gfxPlatform.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsClipboard.h" michael@0: #include "nsString.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsPrimitiveHelpers.h" michael@0: #include "nsMemory.h" michael@0: #include "nsIFile.h" michael@0: #include "nsStringStream.h" michael@0: #include "nsDragService.h" michael@0: #include "nsEscape.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsObjCExceptions.h" michael@0: #include "imgIContainer.h" michael@0: #include "nsCocoaUtils.h" michael@0: michael@0: using mozilla::gfx::DataSourceSurface; michael@0: using mozilla::gfx::SourceSurface; michael@0: using mozilla::RefPtr; michael@0: michael@0: // Screenshots use the (undocumented) png pasteboard type. michael@0: #define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil michael@0: michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* sCocoaLog; michael@0: #endif michael@0: michael@0: extern void EnsureLogInitialized(); michael@0: michael@0: nsClipboard::nsClipboard() : nsBaseClipboard() michael@0: { michael@0: mCachedClipboard = -1; michael@0: mChangeCount = 0; michael@0: michael@0: EnsureLogInitialized(); michael@0: } michael@0: michael@0: nsClipboard::~nsClipboard() michael@0: { michael@0: } michael@0: michael@0: // We separate this into its own function because after an @try, all local michael@0: // variables within that function get marked as volatile, and our C++ type michael@0: // system doesn't like volatile things. michael@0: static NSData* michael@0: GetDataFromPasteboard(NSPasteboard* aPasteboard, NSString* aType) michael@0: { michael@0: NSData *data = nil; michael@0: @try { michael@0: data = [aPasteboard dataForType:aType]; michael@0: } @catch (NSException* e) { michael@0: NS_WARNING(nsPrintfCString("Exception raised while getting data from the pasteboard: \"%s - %s\"", michael@0: [[e name] UTF8String], [[e reason] UTF8String]).get()); michael@0: } michael@0: return data; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !mTransferable) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mIgnoreEmptyNotification = true; michael@0: michael@0: NSDictionary* pasteboardOutputDict = PasteboardDictFromTransferable(mTransferable); michael@0: if (!pasteboardOutputDict) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: unsigned int outputCount = [pasteboardOutputDict count]; michael@0: NSArray* outputKeys = [pasteboardOutputDict allKeys]; michael@0: NSPasteboard* cocoaPasteboard; michael@0: if (aWhichClipboard == kFindClipboard) { michael@0: cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; michael@0: [cocoaPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; michael@0: } else { michael@0: // Write everything else out to the general pasteboard. michael@0: cocoaPasteboard = [NSPasteboard generalPasteboard]; michael@0: [cocoaPasteboard declareTypes:outputKeys owner:nil]; michael@0: } michael@0: michael@0: for (unsigned int i = 0; i < outputCount; i++) { michael@0: NSString* currentKey = [outputKeys objectAtIndex:i]; michael@0: id currentValue = [pasteboardOutputDict valueForKey:currentKey]; michael@0: if (aWhichClipboard == kFindClipboard) { michael@0: if (currentKey == NSStringPboardType) michael@0: [cocoaPasteboard setString:currentValue forType:currentKey]; michael@0: } else { michael@0: if (currentKey == NSStringPboardType || michael@0: currentKey == kCorePboardType_url || michael@0: currentKey == kCorePboardType_urld || michael@0: currentKey == kCorePboardType_urln) { michael@0: [cocoaPasteboard setString:currentValue forType:currentKey]; michael@0: } else if (currentKey == NSHTMLPboardType) { michael@0: [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) michael@0: forType:currentKey]; michael@0: } else { michael@0: [cocoaPasteboard setData:currentValue forType:currentKey]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: mCachedClipboard = aWhichClipboard; michael@0: mChangeCount = [cocoaPasteboard changeCount]; michael@0: michael@0: mIgnoreEmptyNotification = false; michael@0: michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: nsresult michael@0: nsClipboard::TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteboard *cocoaPasteboard) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: // get flavor list that includes all acceptable flavors (including ones obtained through conversion) michael@0: nsCOMPtr flavorList; michael@0: nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: uint32_t flavorCount; michael@0: flavorList->Count(&flavorCount); michael@0: michael@0: for (uint32_t i = 0; i < flavorCount; i++) { michael@0: nsCOMPtr genericFlavor; michael@0: flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); michael@0: nsCOMPtr currentFlavor(do_QueryInterface(genericFlavor)); michael@0: if (!currentFlavor) michael@0: continue; michael@0: michael@0: nsXPIDLCString flavorStr; michael@0: currentFlavor->ToString(getter_Copies(flavorStr)); // i has a flavr michael@0: michael@0: // printf("looking for clipboard data of type %s\n", flavorStr.get()); michael@0: michael@0: NSString *pboardType = nil; michael@0: if (nsClipboard::IsStringType(flavorStr, &pboardType)) { michael@0: NSString* pString = [cocoaPasteboard stringForType:pboardType]; michael@0: if (!pString) michael@0: continue; michael@0: michael@0: NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; michael@0: unsigned int dataLength = [stringData length]; michael@0: void* clipboardDataPtr = malloc(dataLength); michael@0: if (!clipboardDataPtr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: [stringData getBytes:clipboardDataPtr]; michael@0: michael@0: // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. michael@0: int32_t signedDataLength = dataLength; michael@0: nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength); michael@0: dataLength = signedDataLength; michael@0: michael@0: // skip BOM (Byte Order Mark to distinguish little or big endian) michael@0: char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr; michael@0: if ((dataLength > 2) && michael@0: ((clipboardDataPtrNoBOM[0] == 0xFEFF) || michael@0: (clipboardDataPtrNoBOM[0] == 0xFFFE))) { michael@0: dataLength -= sizeof(char16_t); michael@0: clipboardDataPtrNoBOM += 1; michael@0: } michael@0: michael@0: nsCOMPtr genericDataWrapper; michael@0: nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, michael@0: getter_AddRefs(genericDataWrapper)); michael@0: aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); michael@0: free(clipboardDataPtr); michael@0: break; michael@0: } michael@0: else if (flavorStr.EqualsLiteral(kJPEGImageMime) || michael@0: flavorStr.EqualsLiteral(kJPGImageMime) || michael@0: flavorStr.EqualsLiteral(kPNGImageMime) || michael@0: flavorStr.EqualsLiteral(kGIFImageMime)) { michael@0: // Figure out if there's data on the pasteboard we can grab (sanity check) michael@0: NSString *type = [cocoaPasteboard availableTypeFromArray:[NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; michael@0: if (!type) michael@0: continue; michael@0: michael@0: // Read data off the clipboard michael@0: NSData *pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type); michael@0: if (!pasteboardData) michael@0: continue; michael@0: michael@0: // Figure out what type we're converting to michael@0: CFStringRef outputType = NULL; michael@0: if (flavorStr.EqualsLiteral(kJPEGImageMime) || michael@0: flavorStr.EqualsLiteral(kJPGImageMime)) michael@0: outputType = CFSTR("public.jpeg"); michael@0: else if (flavorStr.EqualsLiteral(kPNGImageMime)) michael@0: outputType = CFSTR("public.png"); michael@0: else if (flavorStr.EqualsLiteral(kGIFImageMime)) michael@0: outputType = CFSTR("com.compuserve.gif"); michael@0: else michael@0: continue; michael@0: michael@0: // Use ImageIO to interpret the data on the clipboard and transcode. michael@0: // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely michael@0: // and safely in most cases (like ObjC). A notable exception is CFRelease. michael@0: NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: michael@0: (NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat, michael@0: (type == NSTIFFPboardType ? @"public.tiff" : @"public.png"), michael@0: kCGImageSourceTypeIdentifierHint, nil]; michael@0: michael@0: CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, michael@0: (CFDictionaryRef)options); michael@0: NSMutableData *encodedData = [NSMutableData data]; michael@0: CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData, michael@0: outputType, michael@0: 1, NULL); michael@0: CGImageDestinationAddImageFromSource(dest, source, 0, NULL); michael@0: bool successfullyConverted = CGImageDestinationFinalize(dest); michael@0: michael@0: if (successfullyConverted) { michael@0: // Put the converted data in a form Gecko can understand michael@0: nsCOMPtr byteStream; michael@0: NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)[encodedData bytes], michael@0: [encodedData length], NS_ASSIGNMENT_COPY); michael@0: michael@0: aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); michael@0: } michael@0: michael@0: if (dest) michael@0: CFRelease(dest); michael@0: if (source) michael@0: CFRelease(source); michael@0: michael@0: if (successfullyConverted) michael@0: break; michael@0: else michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, int32_t aWhichClipboard) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !aTransferable) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NSPasteboard* cocoaPasteboard; michael@0: if (aWhichClipboard == kFindClipboard) { michael@0: cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; michael@0: } else { michael@0: cocoaPasteboard = [NSPasteboard generalPasteboard]; michael@0: } michael@0: if (!cocoaPasteboard) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // get flavor list that includes all acceptable flavors (including ones obtained through conversion) michael@0: nsCOMPtr flavorList; michael@0: nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: uint32_t flavorCount; michael@0: flavorList->Count(&flavorCount); michael@0: michael@0: // If we were the last ones to put something on the pasteboard, then just use the cached michael@0: // transferable. Otherwise clear it because it isn't relevant any more. michael@0: if (mCachedClipboard == aWhichClipboard && michael@0: mChangeCount == [cocoaPasteboard changeCount]) { michael@0: if (mTransferable) { michael@0: for (uint32_t i = 0; i < flavorCount; i++) { michael@0: nsCOMPtr genericFlavor; michael@0: flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); michael@0: nsCOMPtr currentFlavor(do_QueryInterface(genericFlavor)); michael@0: if (!currentFlavor) michael@0: continue; michael@0: michael@0: nsXPIDLCString flavorStr; michael@0: currentFlavor->ToString(getter_Copies(flavorStr)); michael@0: michael@0: nsCOMPtr dataSupports; michael@0: uint32_t dataSize = 0; michael@0: rv = mTransferable->GetTransferData(flavorStr, getter_AddRefs(dataSupports), &dataSize); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: aTransferable->SetTransferData(flavorStr, dataSupports, dataSize); michael@0: return NS_OK; // maybe try to fill in more types? Is there a point? michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: nsBaseClipboard::EmptyClipboard(aWhichClipboard); michael@0: } michael@0: michael@0: // at this point we can't satisfy the request from cache data so let's look michael@0: // for things other people put on the system clipboard michael@0: michael@0: return nsClipboard::TransferableFromPasteboard(aTransferable, cocoaPasteboard); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: // returns true if we have *any* of the passed in flavors available for pasting michael@0: NS_IMETHODIMP michael@0: nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength, michael@0: int32_t aWhichClipboard, bool* outResult) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: *outResult = false; michael@0: michael@0: if ((aWhichClipboard != kGlobalClipboard) || !aFlavorList) michael@0: return NS_OK; michael@0: michael@0: // first see if we have data for this in our cached transferable michael@0: if (mTransferable) { michael@0: nsCOMPtr transferableFlavorList; michael@0: nsresult rv = mTransferable->FlavorsTransferableCanImport(getter_AddRefs(transferableFlavorList)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t transferableFlavorCount; michael@0: transferableFlavorList->Count(&transferableFlavorCount); michael@0: for (uint32_t j = 0; j < transferableFlavorCount; j++) { michael@0: nsCOMPtr transferableFlavorSupports; michael@0: transferableFlavorList->GetElementAt(j, getter_AddRefs(transferableFlavorSupports)); michael@0: nsCOMPtr currentTransferableFlavor(do_QueryInterface(transferableFlavorSupports)); michael@0: if (!currentTransferableFlavor) michael@0: continue; michael@0: nsXPIDLCString transferableFlavorStr; michael@0: currentTransferableFlavor->ToString(getter_Copies(transferableFlavorStr)); michael@0: michael@0: for (uint32_t k = 0; k < aLength; k++) { michael@0: if (transferableFlavorStr.Equals(aFlavorList[k])) { michael@0: *outResult = true; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: NSPasteboard* generalPBoard = [NSPasteboard generalPasteboard]; michael@0: michael@0: for (uint32_t i = 0; i < aLength; i++) { michael@0: nsDependentCString mimeType(aFlavorList[i]); michael@0: NSString *pboardType = nil; michael@0: michael@0: if (nsClipboard::IsStringType(mimeType, &pboardType)) { michael@0: NSString* availableType = [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:pboardType]]; michael@0: if (availableType && [availableType isEqualToString:pboardType]) { michael@0: *outResult = true; michael@0: break; michael@0: } michael@0: } else if (!strcmp(aFlavorList[i], kJPEGImageMime) || michael@0: !strcmp(aFlavorList[i], kJPGImageMime) || michael@0: !strcmp(aFlavorList[i], kPNGImageMime) || michael@0: !strcmp(aFlavorList[i], kGIFImageMime)) { michael@0: NSString* availableType = [generalPBoard availableTypeFromArray: michael@0: [NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; michael@0: if (availableType) { michael@0: *outResult = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::SupportsFindClipboard(bool *_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // This function converts anything that other applications might understand into the system format michael@0: // and puts it into a dictionary which it returns. michael@0: // static michael@0: NSDictionary* michael@0: nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (!aTransferable) michael@0: return nil; michael@0: michael@0: NSMutableDictionary* pasteboardOutputDict = [NSMutableDictionary dictionary]; michael@0: michael@0: nsCOMPtr flavorList; michael@0: nsresult rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); michael@0: if (NS_FAILED(rv)) michael@0: return nil; michael@0: michael@0: uint32_t flavorCount; michael@0: flavorList->Count(&flavorCount); michael@0: for (uint32_t i = 0; i < flavorCount; i++) { michael@0: nsCOMPtr genericFlavor; michael@0: flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); michael@0: nsCOMPtr currentFlavor(do_QueryInterface(genericFlavor)); michael@0: if (!currentFlavor) michael@0: continue; michael@0: michael@0: nsXPIDLCString flavorStr; michael@0: currentFlavor->ToString(getter_Copies(flavorStr)); michael@0: michael@0: PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("writing out clipboard data of type %s (%d)\n", flavorStr.get(), i)); michael@0: michael@0: NSString *pboardType = nil; michael@0: michael@0: if (nsClipboard::IsStringType(flavorStr, &pboardType)) { michael@0: void* data = nullptr; michael@0: uint32_t dataSize = 0; michael@0: nsCOMPtr genericDataWrapper; michael@0: rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize); michael@0: nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); michael@0: michael@0: NSString* nativeString; michael@0: if (data) michael@0: nativeString = [NSString stringWithCharacters:(const unichar*)data length:(dataSize / sizeof(char16_t))]; michael@0: else michael@0: nativeString = [NSString string]; michael@0: michael@0: // be nice to Carbon apps, normalize the receiver's contents using Form C. michael@0: nativeString = [nativeString precomposedStringWithCanonicalMapping]; michael@0: michael@0: [pasteboardOutputDict setObject:nativeString forKey:pboardType]; michael@0: michael@0: nsMemory::Free(data); michael@0: } michael@0: else if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || michael@0: flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime) || michael@0: flavorStr.EqualsLiteral(kNativeImageMime)) { michael@0: uint32_t dataSize = 0; michael@0: nsCOMPtr transferSupports; michael@0: aTransferable->GetTransferData(flavorStr, getter_AddRefs(transferSupports), &dataSize); michael@0: nsCOMPtr ptrPrimitive(do_QueryInterface(transferSupports)); michael@0: if (!ptrPrimitive) michael@0: continue; michael@0: michael@0: nsCOMPtr primitiveData; michael@0: ptrPrimitive->GetData(getter_AddRefs(primitiveData)); michael@0: michael@0: nsCOMPtr image(do_QueryInterface(primitiveData)); michael@0: if (!image) { michael@0: NS_WARNING("Image isn't an imgIContainer in transferable"); michael@0: continue; michael@0: } michael@0: michael@0: RefPtr surface = michael@0: image->GetFrame(imgIContainer::FRAME_CURRENT, michael@0: imgIContainer::FLAG_SYNC_DECODE); michael@0: if (!surface) { michael@0: continue; michael@0: } michael@0: CGImageRef imageRef = NULL; michael@0: nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef); michael@0: if (NS_FAILED(rv) || !imageRef) { michael@0: continue; michael@0: } michael@0: michael@0: // Convert the CGImageRef to TIFF data. michael@0: CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0); michael@0: CGImageDestinationRef destRef = CGImageDestinationCreateWithData(tiffData, michael@0: CFSTR("public.tiff"), michael@0: 1, michael@0: NULL); michael@0: CGImageDestinationAddImage(destRef, imageRef, NULL); michael@0: bool successfullyConverted = CGImageDestinationFinalize(destRef); michael@0: michael@0: CGImageRelease(imageRef); michael@0: if (destRef) michael@0: CFRelease(destRef); michael@0: michael@0: if (!successfullyConverted) { michael@0: if (tiffData) michael@0: CFRelease(tiffData); michael@0: continue; michael@0: } michael@0: michael@0: [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType]; michael@0: if (tiffData) michael@0: CFRelease(tiffData); michael@0: } michael@0: else if (flavorStr.EqualsLiteral(kFileMime)) { michael@0: uint32_t len = 0; michael@0: nsCOMPtr genericFile; michael@0: rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericFile), &len); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: michael@0: nsCOMPtr file(do_QueryInterface(genericFile)); michael@0: if (!file) { michael@0: nsCOMPtr ptr(do_QueryInterface(genericFile)); michael@0: michael@0: if (ptr) { michael@0: ptr->GetData(getter_AddRefs(genericFile)); michael@0: file = do_QueryInterface(genericFile); michael@0: } michael@0: } michael@0: michael@0: if (!file) { michael@0: continue; michael@0: } michael@0: michael@0: nsAutoString fileURI; michael@0: rv = file->GetPath(fileURI); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: michael@0: NSString* str = nsCocoaUtils::ToNSString(fileURI); michael@0: NSArray* fileList = [NSArray arrayWithObjects:str, nil]; michael@0: [pasteboardOutputDict setObject:fileList forKey:NSFilenamesPboardType]; michael@0: } michael@0: else if (flavorStr.EqualsLiteral(kFilePromiseMime)) { michael@0: [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:NSFilesPromisePboardType]; michael@0: } michael@0: else if (flavorStr.EqualsLiteral(kURLMime)) { michael@0: uint32_t len = 0; michael@0: nsCOMPtr genericURL; michael@0: rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericURL), &len); michael@0: nsCOMPtr urlObject(do_QueryInterface(genericURL)); michael@0: michael@0: nsAutoString url; michael@0: urlObject->GetData(url); michael@0: michael@0: // A newline embedded in the URL means that the form is actually URL + title. michael@0: int32_t newlinePos = url.FindChar(char16_t('\n')); michael@0: if (newlinePos >= 0) { michael@0: url.Truncate(newlinePos); michael@0: michael@0: nsAutoString urlTitle; michael@0: urlObject->GetData(urlTitle); michael@0: urlTitle.Mid(urlTitle, newlinePos + 1, len - (newlinePos + 1)); michael@0: michael@0: NSString *nativeTitle = [[NSString alloc] initWithCharacters:reinterpret_cast(urlTitle.get()) michael@0: length:urlTitle.Length()]; michael@0: // be nice to Carbon apps, normalize the receiver's contents using Form C. michael@0: [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urln]; michael@0: // Also put the title out as 'urld', since some recipients will look for that. michael@0: [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urld]; michael@0: [nativeTitle release]; michael@0: } michael@0: michael@0: // The Finder doesn't like getting random binary data aka michael@0: // Unicode, so change it into an escaped URL containing only michael@0: // ASCII. michael@0: nsAutoCString utf8Data = NS_ConvertUTF16toUTF8(url.get(), url.Length()); michael@0: nsAutoCString escData; michael@0: NS_EscapeURL(utf8Data.get(), utf8Data.Length(), esc_OnlyNonASCII|esc_AlwaysCopy, escData); michael@0: michael@0: // printf("Escaped url is %s, length %d\n", escData.get(), escData.Length()); michael@0: michael@0: NSString *nativeURL = [NSString stringWithUTF8String:escData.get()]; michael@0: [pasteboardOutputDict setObject:nativeURL forKey:kCorePboardType_url]; michael@0: } michael@0: // If it wasn't a type that we recognize as exportable we don't put it on the system michael@0: // clipboard. We'll just access it from our cached transferable when we need it. michael@0: } michael@0: michael@0: return pasteboardOutputDict; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType) michael@0: { michael@0: if (aMIMEType.EqualsLiteral(kUnicodeMime) || michael@0: aMIMEType.EqualsLiteral(kHTMLMime)) { michael@0: if (aMIMEType.EqualsLiteral(kUnicodeMime)) michael@0: *aPasteboardType = NSStringPboardType; michael@0: else michael@0: *aPasteboardType = NSHTMLPboardType; michael@0: return true; michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString) michael@0: { michael@0: NSString* wrapped = michael@0: [NSString stringWithFormat: michael@0: @"" michael@0: "" michael@0: "" michael@0: "" michael@0: "" michael@0: "%@" michael@0: "" michael@0: "", aString]; michael@0: return wrapped; michael@0: }