diff --git a/widget/src/cocoa/nsClipboard.h b/widget/src/cocoa/nsClipboard.h index 31803ab7ff60..356be8448183 100644 --- a/widget/src/cocoa/nsClipboard.h +++ b/widget/src/cocoa/nsClipboard.h @@ -58,13 +58,15 @@ public: // Helper methods, used also by nsDragService static NSDictionary* PasteboardDictFromTransferable(nsITransferable *aTransferable); - static nsresult CopyPasteboardDataToTransferable(NSPasteboard* aPasteboard, nsITransferable* aTransferable, PRUint32 aItemIndex); protected: // impelement the native clipboard behavior NS_IMETHOD SetNativeClipboardData(PRInt32 aWhichClipboard); NS_IMETHOD GetNativeClipboardData(nsITransferable * aTransferable, PRInt32 aWhichClipboard); + +private: + int mChangeCount; // this is always set to the native change count after any clipboard modifications }; diff --git a/widget/src/cocoa/nsClipboard.mm b/widget/src/cocoa/nsClipboard.mm index 7e5de0c26596..d00023106d8a 100644 --- a/widget/src/cocoa/nsClipboard.mm +++ b/widget/src/cocoa/nsClipboard.mm @@ -50,6 +50,7 @@ nsClipboard::nsClipboard() : nsBaseClipboard() { + mChangeCount = 0; } @@ -84,6 +85,8 @@ nsClipboard::SetNativeClipboardData(PRInt32 aWhichClipboard) [generalPBoard setData:currentValue forType:currentKey]; } + mChangeCount = [generalPBoard changeCount]; + mIgnoreEmptyNotification = PR_FALSE; return NS_OK; @@ -92,11 +95,110 @@ nsClipboard::SetNativeClipboardData(PRInt32 aWhichClipboard) NS_IMETHODIMP nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, PRInt32 aWhichClipboard) -{ +{ if ((aWhichClipboard != kGlobalClipboard) || !aTransferable) return NS_ERROR_FAILURE; - return CopyPasteboardDataToTransferable([NSPasteboard generalPasteboard], aTransferable, 0); + NSPasteboard* cocoaPasteboard = [NSPasteboard generalPasteboard]; + if (!cocoaPasteboard) + return NS_ERROR_FAILURE; + + // get flavor list that includes all acceptable flavors (including ones obtained through conversion) + nsCOMPtr flavorList; + nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + PRUint32 flavorCount; + flavorList->Count(&flavorCount); + + // If we were the last ones to put something on the pasteboard, then just use the cached + // transferable. Otherwise clear it because it isn't relevant any more. + if (mChangeCount == [cocoaPasteboard changeCount]) { + if (mTransferable) { + for (PRUint32 i = 0; i < flavorCount; i++) { + nsCOMPtr genericFlavor; + flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); + nsCOMPtr currentFlavor(do_QueryInterface(genericFlavor)); + if (!currentFlavor) + continue; + + nsXPIDLCString flavorStr; + currentFlavor->ToString(getter_Copies(flavorStr)); + + nsCOMPtr dataSupports; + PRUint32 dataSize = 0; + rv = mTransferable->GetTransferData(flavorStr, getter_AddRefs(dataSupports), &dataSize); + if (NS_SUCCEEDED(rv)) { + aTransferable->SetTransferData(flavorStr, dataSupports, dataSize); + return NS_OK; // maybe try to fill in more types? Is there a point? + } + } + } + } + else { + nsBaseClipboard::EmptyClipboard(kGlobalClipboard); + } + + // at this point we can't satisfy the request from cache data so let's look + // for things other people put on the system clipboard + + for (PRUint32 i = 0; i < flavorCount; i++) { + nsCOMPtr genericFlavor; + flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); + nsCOMPtr currentFlavor(do_QueryInterface(genericFlavor)); + if (!currentFlavor) + continue; + + nsXPIDLCString flavorStr; + currentFlavor->ToString(getter_Copies(flavorStr)); // i has a flavr + + // printf("looking for clipboard data of type %s\n", flavorStr.get()); + + if (flavorStr.EqualsLiteral(kUnicodeMime)) { + NSString* pString = [cocoaPasteboard stringForType:NSStringPboardType]; + if (!pString) + continue; + + NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; + unsigned int dataLength = [stringData length]; + void* clipboardDataPtr = malloc(dataLength); + if (!clipboardDataPtr) + return NS_ERROR_OUT_OF_MEMORY; + [stringData getBytes:clipboardDataPtr]; + + // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. + PRInt32 signedDataLength = dataLength; + nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength); + dataLength = signedDataLength; + + // skip BOM (Byte Order Mark to distinguish little or big endian) + PRUnichar* clipboardDataPtrNoBOM = (PRUnichar*)clipboardDataPtr; + if ((dataLength > 2) && + ((clipboardDataPtrNoBOM[0] == 0xFEFF) || + (clipboardDataPtrNoBOM[0] == 0xFFFE))) { + dataLength -= sizeof(PRUnichar); + clipboardDataPtrNoBOM += 1; + } + + nsCOMPtr genericDataWrapper; + nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, + getter_AddRefs(genericDataWrapper)); + aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); + free(clipboardDataPtr); + break; + } + + /* + if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || + flavorStr.EqualsLiteral(kGIFImageMime)) { + // We have never supported this on Mac OS X, we could someday but nobody does this. + break; + } + */ + } + + return NS_OK; } @@ -119,7 +221,7 @@ nsClipboard::HasDataMatchingFlavors(nsISupportsArray* aFlavorList, PRInt32 aWhic if (flavorWrapper) { nsXPIDLCString flavorStr; flavorWrapper->ToString(getter_Copies(flavorStr)); - if (strcmp(flavorStr, kUnicodeMime) == 0) { + if (flavorStr.EqualsLiteral(kUnicodeMime)) { NSString* availableType = [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; if (availableType && [availableType isEqualToString:NSStringPboardType]) { *outResult = PR_TRUE; @@ -140,6 +242,9 @@ nsClipboard::HasDataMatchingFlavors(nsISupportsArray* aFlavorList, PRInt32 aWhic return NS_OK; } + +// This function converts anything that other applications might understand into the system format +// and puts it into a dictionary which it returns. // static NSDictionary* nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) @@ -168,8 +273,22 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) // printf("writing out clipboard data of type %s\n", flavorStr.get()); - if (strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 || - strcmp(flavorStr, kGIFImageMime) == 0 || strcmp(flavorStr, kNativeImageMime) == 0) { + if (flavorStr.EqualsLiteral(kUnicodeMime)) { + void* data = nsnull; + PRUint32 dataSize = 0; + nsCOMPtr genericDataWrapper; + rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize); + nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); + + NSString* nativeString = [NSString stringWithCharacters:(const unichar*)data length:(dataSize / sizeof(PRUnichar))]; + // be nice to Carbon apps, normalize the receiver's contents using Form C. + nativeString = [nativeString precomposedStringWithCanonicalMapping]; + [pasteboardOutputDict setObject:nativeString forKey:NSStringPboardType]; + + nsMemory::Free(data); + } + else if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || + flavorStr.EqualsLiteral(kGIFImageMime) || flavorStr.EqualsLiteral(kNativeImageMime)) { PRUint32 dataSize = 0; nsCOMPtr transferSupports; aTransferable->GetTransferData(flavorStr, getter_AddRefs(transferSupports), &dataSize); @@ -231,147 +350,10 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) [pasteboardOutputDict setObject:tiffData forKey:NSTIFFPboardType]; } - else { - /* If it isn't an image, we just throw the data on the clipboard with the mime string - * as its key. If we recognize the data as something we want to export in standard - * terms, then we do that too. - */ - void* data = nsnull; - PRUint32 dataSize = 0; - nsCOMPtr genericDataWrapper; - rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize); - nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); - // if it is kUnicodeMime, it is text we want to export as standard NSStringPboardType - if (strcmp(flavorStr, kUnicodeMime) == 0) { - NSString* nativeString = [NSString stringWithCharacters:(const unichar*)data length:(dataSize / sizeof(PRUnichar))]; - // be nice to Carbon apps, normalize the receiverŐs contents using Form C. - nativeString = [nativeString precomposedStringWithCanonicalMapping]; - [pasteboardOutputDict setObject:nativeString forKey:NSStringPboardType]; - } - else { - NSString* key = [NSString stringWithUTF8String:flavorStr]; - NSData* value = [NSData dataWithBytes:data length:dataSize]; - [pasteboardOutputDict setObject:value forKey:key]; - } - - nsMemory::Free(data); - } + // If it wasn't a type that we recognize as exportable we don't put it on the system + // clipboard. We'll just access it from our cached transferable when we need it. } - + return pasteboardOutputDict; } - -// static -nsresult -nsClipboard::CopyPasteboardDataToTransferable(NSPasteboard* aPasteboard, nsITransferable* aTransferable, PRUint32 aItemIndex) -{ - if (!aTransferable || !aPasteboard) - return NS_ERROR_FAILURE; - - // get flavor list that includes all acceptable flavors (including ones obtained through conversion) - nsCOMPtr flavorList; - nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; - - PRUint32 flavorCount; - flavorList->Count(&flavorCount); - for (PRUint32 i = 0; i < flavorCount; i++) { - nsCOMPtr genericFlavor; - flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); - nsCOMPtr currentFlavor(do_QueryInterface(genericFlavor)); - - if (!currentFlavor) - continue; - - nsXPIDLCString flavorStr; - currentFlavor->ToString(getter_Copies(flavorStr)); - - // printf("looking for clipboard data of type %s\n", flavorStr.get()); - - if (strcmp(flavorStr, kFileMime) == 0) { - NSArray* pFiles = [aPasteboard propertyListForType:NSFilenamesPboardType]; - if (!pFiles || [pFiles count] < (aItemIndex + 1)) - continue; - - NSString* filePath = [pFiles objectAtIndex:aItemIndex]; - if (!filePath) - continue; - - unsigned int stringLength = [filePath length]; - unsigned int dataLength = (stringLength + 1) * sizeof(PRUnichar); // in bytes - PRUnichar* clipboardDataPtr = (PRUnichar*)malloc(dataLength); - [filePath getCharacters:clipboardDataPtr]; - clipboardDataPtr[stringLength] = 0; // null terminate - - nsCOMPtr file; - nsresult rv = NS_NewLocalFile(nsDependentString(clipboardDataPtr), PR_TRUE, getter_AddRefs(file)); - free(clipboardDataPtr); - if (NS_FAILED(rv)) - continue; - - nsCOMPtr genericDataWrapper; - genericDataWrapper = do_QueryInterface(file); - aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); - - break; - } - else if (strcmp(flavorStr, kUnicodeMime) == 0) { - NSString* pString = [aPasteboard stringForType:NSStringPboardType]; - if (!pString) - continue; - - NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; - unsigned int dataLength = [stringData length]; - unsigned char* clipboardDataPtr = (unsigned char*)malloc(dataLength); - [stringData getBytes:(void*)clipboardDataPtr]; - - // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. - nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, (void**)&clipboardDataPtr, (PRInt32*)&dataLength); - - // skip BOM (Byte Order Mark to distinguish little or big endian) - unsigned char* clipboardDataPtrNoBOM = clipboardDataPtr; - if ((dataLength > 2) && - ((clipboardDataPtr[0] == 0xFE && clipboardDataPtr[1] == 0xFF) || - (clipboardDataPtr[0] == 0xFF && clipboardDataPtr[1] == 0xFE))) { - dataLength -= sizeof(PRUnichar); - clipboardDataPtrNoBOM += sizeof(PRUnichar); - } - - nsCOMPtr genericDataWrapper; - nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, - getter_AddRefs(genericDataWrapper)); - aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); - free(clipboardDataPtr); - break; - } - else if (strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 || - strcmp(flavorStr, kGIFImageMime) == 0) { - // We have never supported this on Mac, we could someday but nobody does this. We want this - // test here so that we don't try to get this as a custom type. - } - else { - // this is some sort of data that mozilla put on the clipboard itself if it exists - NSData* pData = [aPasteboard dataForType:[NSString stringWithUTF8String:flavorStr]]; - if (!pData) - continue; - - unsigned int dataLength = [pData length]; - unsigned char* clipboardDataPtr = (unsigned char*)malloc(dataLength); - [pData getBytes:(void*)clipboardDataPtr]; - - // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. - nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, (void**)&clipboardDataPtr, (PRInt32*)&dataLength); - - nsCOMPtr genericDataWrapper; - nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength, - getter_AddRefs(genericDataWrapper)); - aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); - free(clipboardDataPtr); - break; - } - } - - return NS_OK; -} diff --git a/widget/src/cocoa/nsDragService.mm b/widget/src/cocoa/nsDragService.mm index 3faf063d56f1..d85603003bf3 100644 --- a/widget/src/cocoa/nsDragService.mm +++ b/widget/src/cocoa/nsDragService.mm @@ -96,6 +96,8 @@ static nsresult SetUpDragClipboard(nsISupportsArray* aTransferableArray) PRUint32 count = 0; aTransferableArray->Count(&count); + NSPasteboard* dragPBoard = [NSPasteboard pasteboardWithName:NSDragPboard]; + for (PRUint32 i = 0; i < count; i++) { nsCOMPtr currentTransferableSupports; aTransferableArray->GetElementAt(i, getter_AddRefs(currentTransferableSupports)); @@ -114,27 +116,25 @@ static nsresult SetUpDragClipboard(nsISupportsArray* aTransferableArray) // write everything out to the general pasteboard unsigned int outputCount = [pasteboardOutputDict count]; NSArray* outputKeys = [pasteboardOutputDict allKeys]; - NSPasteboard* generalPBoard = [NSPasteboard pasteboardWithName:NSDragPboard]; - [generalPBoard declareTypes:outputKeys owner:nil]; + [dragPBoard declareTypes:outputKeys owner:nil]; for (unsigned int i = 0; i < outputCount; i++) { NSString* currentKey = [outputKeys objectAtIndex:i]; id currentValue = [pasteboardOutputDict valueForKey:currentKey]; if (currentKey == NSStringPboardType) { - [generalPBoard setString:currentValue forType:currentKey]; + [dragPBoard setString:currentValue forType:currentKey]; } else if (currentKey == NSTIFFPboardType) { - [generalPBoard setData:currentValue forType:currentKey]; - } - else { - // Record miscellaneous types of data under a generic type - // that is always registered with Cocoa as draggable. This - // allows types dynamically synthesized from JS to work. - [generalPBoard setData:nil forType:kWildcardPboardType]; + [dragPBoard setData:currentValue forType:currentKey]; } } } - return NS_OK; + // Gecko is initiating this drag so we always want its own views to consider + // it. Add our wildcard type to the pasteboard to accomplish this. Note that the + // wildcard type is not declared above but it doesn't seem to matter. + [dragPBoard setData:nil forType:kWildcardPboardType]; + + return NS_OK; } @@ -334,7 +334,7 @@ nsDragService::GetData(nsITransferable* aTransferable, PRUint32 aItemIndex) // printf("looking for clipboard data of type %s\n", flavorStr.get()); - if (strcmp(flavorStr, kFileMime) == 0) { + if (flavorStr.EqualsLiteral(kFileMime)) { NSArray* pFiles = [globalDragPboard propertyListForType:NSFilenamesPboardType]; if (!pFiles || [pFiles count] < (aItemIndex + 1)) continue; @@ -364,7 +364,7 @@ nsDragService::GetData(nsITransferable* aTransferable, PRUint32 aItemIndex) break; } - if (strcmp(flavorStr, kUnicodeMime) == 0) { + if (flavorStr.EqualsLiteral(kUnicodeMime)) { NSString* pString = [globalDragPboard stringForType:NSStringPboardType]; if (!pString) continue; @@ -378,7 +378,7 @@ nsDragService::GetData(nsITransferable* aTransferable, PRUint32 aItemIndex) // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. PRInt32 signedDataLength = dataLength; - nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, (PRInt32*)&signedDataLength); + nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength); dataLength = signedDataLength; // skip BOM (Byte Order Mark to distinguish little or big endian) @@ -401,8 +401,8 @@ nsDragService::GetData(nsITransferable* aTransferable, PRUint32 aItemIndex) // We have never supported this on Mac OS X, we should someday. Normally dragging images // in is accomplished with a file path drag instead of the image data itself. /* - if (strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 || - strcmp(flavorStr, kGIFImageMime) == 0) { + if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || + flavorStr.EqualsLiteral(kGIFImageMime)) { } */ @@ -418,13 +418,15 @@ nsDragService::IsDataFlavorSupported(const char *aDataFlavor, PRBool *_retval) if (!globalDragPboard) return NS_ERROR_FAILURE; - - if (strcmp(aDataFlavor, kFileMime) == 0) { + + nsDependentCString dataFlavor(aDataFlavor); + + if (dataFlavor.EqualsLiteral(kFileMime)) { NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]]; if (availableType && [availableType isEqualToString:NSFilenamesPboardType]) *_retval = PR_TRUE; } - else if (strcmp(aDataFlavor, kUnicodeMime) == 0) { + else if (dataFlavor.EqualsLiteral(kUnicodeMime)) { NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; if (availableType && [availableType isEqualToString:NSStringPboardType]) *_retval = PR_TRUE;