rewrite much of the Mac OS X clipboard service to fix several bugs and improve performance. also a lot of cleanup. b=386225 r=smorgan sr=pav

This commit is contained in:
joshmoz@gmail.com 2007-07-03 00:16:19 -07:00
parent dd6a9f754d
commit 2792f72167
3 changed files with 151 additions and 165 deletions

View File

@ -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
};

View File

@ -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<nsISupportsArray> 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<nsISupports> genericFlavor;
flavorList->GetElementAt(i, getter_AddRefs(genericFlavor));
nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
if (!currentFlavor)
continue;
nsXPIDLCString flavorStr;
currentFlavor->ToString(getter_Copies(flavorStr));
nsCOMPtr<nsISupports> 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<nsISupports> genericFlavor;
flavorList->GetElementAt(i, getter_AddRefs(genericFlavor));
nsCOMPtr<nsISupportsCString> 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<nsISupports> 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<nsISupports> 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<nsISupports> 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<nsISupports> 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<nsISupportsArray> 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<nsISupports> genericFlavor;
flavorList->GetElementAt(i, getter_AddRefs(genericFlavor));
nsCOMPtr<nsISupportsCString> 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<nsILocalFile> file;
nsresult rv = NS_NewLocalFile(nsDependentString(clipboardDataPtr), PR_TRUE, getter_AddRefs(file));
free(clipboardDataPtr);
if (NS_FAILED(rv))
continue;
nsCOMPtr<nsISupports> 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<nsISupports> 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<nsISupports> genericDataWrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength,
getter_AddRefs(genericDataWrapper));
aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength);
free(clipboardDataPtr);
break;
}
}
return NS_OK;
}

View File

@ -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<nsISupports> 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;