Revert Bug 1951225 - for causing failures @test_clipboard_cache_chrome.html.

This reverts commit cfb55158a2.

Revert "Bug 1951225 - Part 2-5: Implement new GetNativeClipboardData() on geckoview; r=geckoview-reviewers,m_kato"

This reverts commit 720b17e231.

Revert "Bug 1951225 - Part 2-4: Implement new GetNativeClipboardData() on windows widget; r=win-reviewers,gstoll"

This reverts commit 5b37cd1b74.

Revert "Bug 1951225 - Part 2-3: Implement new GetNativeClipboardData() on gtk widget; r=stransky"

This reverts commit ed6a7ee1eb.

Revert "Bug 1951225 - Part 2-2: Implement new GetNativeClipboardData() on headless widget; r=spohl"

This reverts commit d05e31225f.

Revert "Bug 1951225 - Part 2-1: Implement new GetNativeClipboardData() on cocoa widget; r=mac-reviewers,bradwerth"

This reverts commit e54df44d9b.

Revert "Bug 1951225 - Part 1: Introduce new {Async}GetNativeClipboardData(); r=gstoll"

This reverts commit 516732b93f.
This commit is contained in:
agoloman
2025-05-13 08:16:11 +03:00
committed by agoloman@mozilla.com
parent 37f125f5e9
commit c3d6835e06
15 changed files with 768 additions and 764 deletions

View File

@@ -108,49 +108,65 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
return NS_ERROR_FAILURE;
}
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
nsClipboard::GetNativeClipboardData(const nsACString& aFlavor,
NS_IMETHODIMP
nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (!jni::IsAvailable()) {
return Err(NS_ERROR_NOT_AVAILABLE);
return NS_ERROR_NOT_AVAILABLE;
}
if (aFlavor.EqualsLiteral(kTextMime) || aFlavor.EqualsLiteral(kHTMLMime)) {
auto text = java::Clipboard::GetTextData(
java::GeckoAppShell::GetApplicationContext(), aFlavor);
if (!text) {
return nsCOMPtr<nsISupports>{};
nsTArray<nsCString> flavors;
aTransferable->FlavorsTransferableCanImport(flavors);
for (auto& flavorStr : flavors) {
if (flavorStr.EqualsLiteral(kTextMime) ||
flavorStr.EqualsLiteral(kHTMLMime)) {
auto text = java::Clipboard::GetTextData(
java::GeckoAppShell::GetApplicationContext(), flavorStr);
if (!text) {
continue;
}
nsString buffer = text->ToString();
if (buffer.IsEmpty()) {
continue;
}
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, buffer.get(),
buffer.Length() * 2,
getter_AddRefs(wrapper));
if (wrapper) {
aTransferable->SetTransferData(flavorStr.get(), wrapper);
return NS_OK;
}
continue;
}
nsString buffer = text->ToString();
if (buffer.IsEmpty()) {
return nsCOMPtr<nsISupports>{};
mozilla::jni::ByteArray::LocalRef bytes;
nsresult rv = java::Clipboard::GetRawData(flavorStr, &bytes);
if (NS_FAILED(rv) || !bytes) {
continue;
}
nsCOMPtr<nsIInputStream> byteStream;
rv = NS_NewByteInputStream(
getter_AddRefs(byteStream),
mozilla::Span(
reinterpret_cast<const char*>(bytes->GetElements().Elements()),
bytes->Length()),
NS_ASSIGNMENT_COPY);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
rv = aTransferable->SetTransferData(flavorStr.get(), byteStream);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, buffer.get(), buffer.Length() * 2, getter_AddRefs(wrapper));
return std::move(wrapper);
}
mozilla::jni::ByteArray::LocalRef bytes;
nsresult rv = java::Clipboard::GetRawData(aFlavor, &bytes);
if (NS_FAILED(rv) || !bytes) {
return nsCOMPtr<nsISupports>{};
}
nsCOMPtr<nsIInputStream> byteStream;
rv = NS_NewByteInputStream(getter_AddRefs(byteStream),
mozilla::Span(reinterpret_cast<const char*>(
bytes->GetElements().Elements()),
bytes->Length()),
NS_ASSIGNMENT_COPY);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nsCOMPtr<nsISupports>{};
}
return nsCOMPtr<nsISupports>(std::move(byteStream));
return NS_OK;
}
nsresult nsClipboard::EmptyNativeClipboardData(ClipboardType aWhichClipboard) {

View File

@@ -27,9 +27,8 @@ class nsClipboard final : public nsBaseClipboard {
// Implement the native clipboard behavior.
NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
virtual mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
GetNativeClipboardData(const nsACString& aFlavor,
ClipboardType aWhichClipboard) override;
NS_IMETHOD GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
nsresult EmptyNativeClipboardData(ClipboardType aWhichClipboard) override;
mozilla::Result<bool, nsresult> HasNativeClipboardDataMatchingFlavors(
const nsTArray<nsCString>& aFlavorList,

View File

@@ -17,7 +17,7 @@
class nsITransferable;
class nsClipboard final : public nsBaseClipboard {
class nsClipboard : public nsBaseClipboard {
public:
nsClipboard();
@@ -33,14 +33,12 @@ class nsClipboard final : public nsBaseClipboard {
static NSDictionary* PasteboardDictFromTransferable(
nsITransferable* aTransferable);
// aPasteboardType is being retained and needs to be released by the caller.
static bool IsStringType(const nsACString& aMIMEType,
static bool IsStringType(const nsCString& aMIMEType,
NSString** aPasteboardType);
static bool IsImageType(const nsACString& aMIMEType);
static NSString* WrapHtmlForSystemPasteboard(NSString* aString);
static nsresult TransferableFromPasteboard(nsITransferable* aTransferable,
NSPasteboard* pboard);
static mozilla::Result<nsCOMPtr<nsISupports>, nsresult> GetDataFromPasteboard(
const nsACString& aFlavor, NSPasteboard* aPasteboard);
mozilla::Result<int32_t, nsresult> GetNativeClipboardSequenceNumber(
ClipboardType aWhichClipboard) override;
@@ -48,8 +46,8 @@ class nsClipboard final : public nsBaseClipboard {
// Implement the native clipboard behavior.
NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
mozilla::Result<nsCOMPtr<nsISupports>, nsresult> GetNativeClipboardData(
const nsACString& aFlavor, ClipboardType aWhichClipboard) override;
NS_IMETHOD GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
nsresult EmptyNativeClipboardData(ClipboardType aWhichClipboard) override;
mozilla::Result<bool, nsresult> HasNativeClipboardDataMatchingFlavors(
const nsTArray<nsCString>& aFlavorList,

View File

@@ -45,8 +45,8 @@ namespace {
// We separate this into its own function because after an @try, all local
// variables within that function get marked as volatile, and our C++ type
// system doesn't like volatile things.
static NSData* GetNSDataFromPasteboard(NSPasteboard* aPasteboard,
NSString* aType) {
static NSData* GetDataFromPasteboard(NSPasteboard* aPasteboard,
NSString* aType) {
NSData* data = nil;
@try {
data = [aPasteboard dataForType:aType];
@@ -179,22 +179,177 @@ nsresult nsClipboard::TransferableFromPasteboard(
// obtained through conversion)
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
for (uint32_t i = 0; i < flavors.Length(); i++) {
nsCString& flavorStr = flavors[i];
auto dataOrError = GetDataFromPasteboard(flavorStr, cocoaPasteboard);
if (dataOrError.isErr()) {
continue;
}
// printf("looking for clipboard data of type %s\n", flavorStr.get());
if (auto data = dataOrError.inspect()) {
aTransferable->SetTransferData(flavorStr.get(), data);
// XXX Maybe try to fill in more types? Is there a point?
NSString* pboardType = nil;
if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
NSString* pString = [cocoaPasteboard stringForType:pboardType];
if (!pString) {
continue;
}
NSData* stringData;
bool isRTF = [pboardType
isEqualToString:[UTIHelper stringFromPboardType:NSPasteboardTypeRTF]];
if (isRTF) {
stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
} else {
stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
}
unsigned int dataLength = [stringData length];
void* clipboardDataPtr = malloc(dataLength);
if (!clipboardDataPtr) {
return NS_ERROR_OUT_OF_MEMORY;
}
[stringData getBytes:clipboardDataPtr length:dataLength];
// The DOM only wants LF, so convert from MacOS line endings to DOM line
// endings.
int32_t signedDataLength = dataLength;
nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(
isRTF, &clipboardDataPtr, &signedDataLength);
dataLength = signedDataLength;
// skip BOM (Byte Order Mark to distinguish little or big endian)
char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr;
if ((dataLength > 2) && ((clipboardDataPtrNoBOM[0] == 0xFEFF) ||
(clipboardDataPtrNoBOM[0] == 0xFFFE))) {
dataLength -= sizeof(char16_t);
clipboardDataPtrNoBOM += 1;
}
nsCOMPtr<nsISupports> genericDataWrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
flavorStr, clipboardDataPtrNoBOM, dataLength,
getter_AddRefs(genericDataWrapper));
aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
free(clipboardDataPtr);
break;
} else if (flavorStr.EqualsLiteral(kFileMime)) {
NSArray* items = [cocoaPasteboard pasteboardItems];
if (!items || [items count] <= 0) {
continue;
}
// XXX we don't support multiple clipboard item on DOM and XPCOM interface
// for now, so we only get the data from the first pasteboard item.
NSPasteboardItem* item = [items objectAtIndex:0];
if (!item) {
continue;
}
nsCocoaUtils::SetTransferDataForTypeFromPasteboardItem(aTransferable,
flavorStr, item);
} else if (flavorStr.EqualsLiteral(kCustomTypesMime)) {
NSString* type = [cocoaPasteboard
availableTypeFromArray:
[NSArray
arrayWithObject:[UTIHelper stringFromPboardType:
kMozCustomTypesPboardType]]];
if (!type) {
continue;
}
NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type);
if (!pasteboardData) {
continue;
}
unsigned int dataLength = [pasteboardData length];
void* clipboardDataPtr = malloc(dataLength);
if (!clipboardDataPtr) {
return NS_ERROR_OUT_OF_MEMORY;
}
[pasteboardData getBytes:clipboardDataPtr length:dataLength];
nsCOMPtr<nsISupports> genericDataWrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
flavorStr, clipboardDataPtr, dataLength,
getter_AddRefs(genericDataWrapper));
aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
free(clipboardDataPtr);
} else if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
flavorStr.EqualsLiteral(kJPGImageMime) ||
flavorStr.EqualsLiteral(kPNGImageMime) ||
flavorStr.EqualsLiteral(kGIFImageMime)) {
// Figure out if there's data on the pasteboard we can grab (sanity check)
NSString* type = [cocoaPasteboard
availableTypeFromArray:
[NSArray
arrayWithObjects:
[UTIHelper
stringFromPboardType:(NSString*)kUTTypeFileURL],
[UTIHelper stringFromPboardType:NSPasteboardTypeTIFF],
[UTIHelper stringFromPboardType:NSPasteboardTypePNG],
nil]];
if (!type) continue;
// Read data off the clipboard
NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type);
if (!pasteboardData) continue;
// Figure out what type we're converting to
CFStringRef outputType = NULL;
if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
flavorStr.EqualsLiteral(kJPGImageMime))
outputType = CFSTR("public.jpeg");
else if (flavorStr.EqualsLiteral(kPNGImageMime))
outputType = CFSTR("public.png");
else if (flavorStr.EqualsLiteral(kGIFImageMime))
outputType = CFSTR("com.compuserve.gif");
else
continue;
// Use ImageIO to interpret the data on the clipboard and transcode.
// Note that ImageIO, like all CF APIs, allows NULLs to propagate freely
// and safely in most cases (like ObjC). A notable exception is CFRelease.
NSDictionary* options = [NSDictionary
dictionaryWithObjectsAndKeys:(NSNumber*)kCFBooleanTrue,
kCGImageSourceShouldAllowFloat, type,
kCGImageSourceTypeIdentifierHint, nil];
CGImageSourceRef source = nullptr;
if (type == [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL]) {
NSString* urlStr = [cocoaPasteboard stringForType:type];
NSURL* url = [NSURL URLWithString:urlStr];
source =
CGImageSourceCreateWithURL((CFURLRef)url, (CFDictionaryRef)options);
} else {
source = CGImageSourceCreateWithData((CFDataRef)pasteboardData,
(CFDictionaryRef)options);
}
NSMutableData* encodedData = [NSMutableData data];
CGImageDestinationRef dest = CGImageDestinationCreateWithData(
(CFMutableDataRef)encodedData, outputType, 1, NULL);
CGImageDestinationAddImageFromSource(dest, source, 0, NULL);
bool successfullyConverted = CGImageDestinationFinalize(dest);
if (successfullyConverted) {
// Put the converted data in a form Gecko can understand
nsCOMPtr<nsIInputStream> byteStream;
NS_NewByteInputStream(getter_AddRefs(byteStream),
mozilla::Span((const char*)[encodedData bytes],
[encodedData length]),
NS_ASSIGNMENT_COPY);
aTransferable->SetTransferData(flavorStr.get(), byteStream);
}
if (dest) CFRelease(dest);
if (source) CFRelease(source);
if (successfullyConverted) {
// XXX Maybe try to fill in more types? Is there a point?
break;
} else {
continue;
}
}
}
@@ -203,221 +358,51 @@ nsresult nsClipboard::TransferableFromPasteboard(
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
nsClipboard::GetDataFromPasteboard(const nsACString& aFlavor,
NSPasteboard* aPasteboard) {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
NSString* pboardType = nil;
if (nsClipboard::IsStringType(aFlavor, &pboardType)) {
NSString* pString = [aPasteboard stringForType:pboardType];
if (!pString) {
return nsCOMPtr<nsISupports>{};
}
NSData* stringData;
bool isRTF = [pboardType
isEqualToString:[UTIHelper stringFromPboardType:NSPasteboardTypeRTF]];
if (isRTF) {
stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
} else {
stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
}
unsigned int dataLength = [stringData length];
void* clipboardDataPtr = malloc(dataLength);
if (!clipboardDataPtr) {
return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
}
[stringData getBytes:clipboardDataPtr length:dataLength];
// The DOM only wants LF, so convert from MacOS line endings to DOM line
// endings.
int32_t signedDataLength = dataLength;
nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(isRTF, &clipboardDataPtr,
&signedDataLength);
dataLength = signedDataLength;
// skip BOM (Byte Order Mark to distinguish little or big endian)
char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr;
if ((dataLength > 2) && ((clipboardDataPtrNoBOM[0] == 0xFEFF) ||
(clipboardDataPtrNoBOM[0] == 0xFFFE))) {
dataLength -= sizeof(char16_t);
clipboardDataPtrNoBOM += 1;
}
nsCOMPtr<nsISupports> genericDataWrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, clipboardDataPtrNoBOM, dataLength,
getter_AddRefs(genericDataWrapper));
free(clipboardDataPtr);
return std::move(genericDataWrapper);
}
if (aFlavor.EqualsLiteral(kFileMime)) {
NSArray* items = [aPasteboard pasteboardItems];
if (!items || [items count] <= 0) {
return nsCOMPtr<nsISupports>{};
}
// XXX we don't support multiple clipboard item on DOM and XPCOM interface
// for now, so we only get the data from the first pasteboard item.
NSPasteboardItem* item = [items objectAtIndex:0];
if (!item) {
return nsCOMPtr<nsISupports>{};
}
return nsCOMPtr<nsISupports>(
nsCocoaUtils::GetDataFromPasteboardItem(aFlavor, item));
}
if (aFlavor.EqualsLiteral(kCustomTypesMime)) {
NSString* type = [aPasteboard
availableTypeFromArray:
[NSArray
arrayWithObject:[UTIHelper stringFromPboardType:
kMozCustomTypesPboardType]]];
if (!type) {
return nsCOMPtr<nsISupports>{};
}
NSData* pasteboardData = GetNSDataFromPasteboard(aPasteboard, type);
if (!pasteboardData) {
return nsCOMPtr<nsISupports>{};
}
unsigned int dataLength = [pasteboardData length];
void* clipboardDataPtr = malloc(dataLength);
if (!clipboardDataPtr) {
return mozilla::Err(NS_ERROR_OUT_OF_MEMORY);
}
[pasteboardData getBytes:clipboardDataPtr length:dataLength];
nsCOMPtr<nsISupports> genericDataWrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, clipboardDataPtr, dataLength,
getter_AddRefs(genericDataWrapper));
free(clipboardDataPtr);
return std::move(genericDataWrapper);
}
if (aFlavor.EqualsLiteral(kJPEGImageMime) ||
aFlavor.EqualsLiteral(kJPGImageMime) ||
aFlavor.EqualsLiteral(kPNGImageMime) ||
aFlavor.EqualsLiteral(kGIFImageMime)) {
// Figure out if there's data on the pasteboard we can grab (sanity check)
NSString* type = [aPasteboard
availableTypeFromArray:
[NSArray
arrayWithObjects:[UTIHelper
stringFromPboardType:(NSString*)
kUTTypeFileURL],
[UTIHelper
stringFromPboardType:NSPasteboardTypeTIFF],
[UTIHelper
stringFromPboardType:NSPasteboardTypePNG],
nil]];
if (!type) {
return nsCOMPtr<nsISupports>{};
}
// Read data off the clipboard
NSData* pasteboardData = GetNSDataFromPasteboard(aPasteboard, type);
if (!pasteboardData) {
return nsCOMPtr<nsISupports>{};
}
// Figure out what type we're converting to
CFStringRef outputType = NULL;
if (aFlavor.EqualsLiteral(kJPEGImageMime) ||
aFlavor.EqualsLiteral(kJPGImageMime)) {
outputType = CFSTR("public.jpeg");
} else if (aFlavor.EqualsLiteral(kPNGImageMime)) {
outputType = CFSTR("public.png");
} else if (aFlavor.EqualsLiteral(kGIFImageMime)) {
outputType = CFSTR("com.compuserve.gif");
} else {
return nsCOMPtr<nsISupports>{};
}
// Use ImageIO to interpret the data on the clipboard and transcode.
// Note that ImageIO, like all CF APIs, allows NULLs to propagate freely
// and safely in most cases (like ObjC). A notable exception is CFRelease.
NSDictionary* options = [NSDictionary
dictionaryWithObjectsAndKeys:(NSNumber*)kCFBooleanTrue,
kCGImageSourceShouldAllowFloat, type,
kCGImageSourceTypeIdentifierHint, nil];
CGImageSourceRef source = nullptr;
if (type == [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL]) {
NSString* urlStr = [aPasteboard stringForType:type];
NSURL* url = [NSURL URLWithString:urlStr];
source =
CGImageSourceCreateWithURL((CFURLRef)url, (CFDictionaryRef)options);
} else {
source = CGImageSourceCreateWithData((CFDataRef)pasteboardData,
(CFDictionaryRef)options);
}
NSMutableData* encodedData = [NSMutableData data];
CGImageDestinationRef dest = CGImageDestinationCreateWithData(
(CFMutableDataRef)encodedData, outputType, 1, NULL);
CGImageDestinationAddImageFromSource(dest, source, 0, NULL);
nsCOMPtr<nsIInputStream> byteStream;
if (CGImageDestinationFinalize(dest)) {
// Put the converted data in a form Gecko can understand
NS_NewByteInputStream(
getter_AddRefs(byteStream),
mozilla::Span((const char*)[encodedData bytes], [encodedData length]),
NS_ASSIGNMENT_COPY);
}
if (dest) {
CFRelease(dest);
}
if (source) {
CFRelease(source);
}
return nsCOMPtr<nsISupports>(std::move(byteStream));
}
return nsCOMPtr<nsISupports>{};
NS_OBJC_END_TRY_BLOCK_RETURN(mozilla::Err(NS_ERROR_FAILURE));
}
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
nsClipboard::GetNativeClipboardData(const nsACString& aFlavor,
NS_IMETHODIMP
nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
if (kSelectionCache == aWhichClipboard) {
if (!sSelectionCache) {
return nsCOMPtr<nsISupports>{};
return NS_OK;
}
nsCOMPtr<nsISupports> dataSupports;
if (NS_FAILED(sSelectionCache->GetTransferData(
PromiseFlatCString(aFlavor).get(), getter_AddRefs(dataSupports)))) {
return nsCOMPtr<nsISupports>{};
// get flavor list that includes all acceptable flavors (including ones
// obtained through conversion)
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__,
PromiseFlatCString(aFlavor).get());
return std::move(dataSupports);
for (const auto& flavor : flavors) {
nsCOMPtr<nsISupports> dataSupports;
rv = sSelectionCache->GetTransferData(flavor.get(),
getter_AddRefs(dataSupports));
if (NS_SUCCEEDED(rv)) {
MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__,
flavor.get());
aTransferable->SetTransferData(flavor.get(), dataSupports);
// XXX Maybe try to fill in more types? Is there a point?
break;
}
}
return NS_OK;
}
NSPasteboard* cocoaPasteboard = GetPasteboard(aWhichClipboard);
if (!cocoaPasteboard) {
return mozilla::Err(NS_ERROR_FAILURE);
return NS_ERROR_FAILURE;
}
return GetDataFromPasteboard(aFlavor, cocoaPasteboard);
return TransferableFromPasteboard(aTransferable, cocoaPasteboard);
NS_OBJC_END_TRY_BLOCK_RETURN(mozilla::Err(NS_ERROR_FAILURE));
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}
// returns true if we have *any* of the passed in flavors available for pasting
@@ -513,7 +498,7 @@ nsClipboard::HasNativeClipboardDataMatchingFlavors(
NSArray* items = [cocoaPasteboard pasteboardItems];
if (items && [items count] > 0) {
// XXX we only check the first pasteboard item as we only get data from
// first item in GetDataFromPasteboard for now.
// first item in TransferableFromPasteboard for now.
if (NSPasteboardItem* item = [items objectAtIndex:0]) {
if ([item availableTypeFromArray:
[NSArray
@@ -808,7 +793,7 @@ NSDictionary* nsClipboard::PasteboardDictFromTransferable(
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
bool nsClipboard::IsStringType(const nsACString& aMIMEType,
bool nsClipboard::IsStringType(const nsCString& aMIMEType,
NSString** aPboardType) {
if (aMIMEType.EqualsLiteral(kTextMime)) {
*aPboardType = [UTIHelper stringFromPboardType:NSPasteboardTypeString];

View File

@@ -509,12 +509,6 @@ class nsCocoaUtils {
static bool IsValidPasteboardType(NSString* aAvailableType,
bool aAllowFileURL);
/**
* Get data for specific type from NSPasteboardItem.
*/
static already_AddRefed<nsISupports> GetDataFromPasteboardItem(
const nsACString& aFlavor, NSPasteboardItem* aItem);
/**
* Set data for specific type from NSPasteboardItem to Transferable.
*/

View File

@@ -1697,23 +1697,25 @@ NSString* nsCocoaUtils::GetTitleForURLFromPasteboardItem(
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
const nsACString& aFlavor, NSPasteboardItem* aItem) {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
void nsCocoaUtils::SetTransferDataForTypeFromPasteboardItem(
nsITransferable* aTransferable, const nsCString& aFlavor,
NSPasteboardItem* aItem) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (!aItem) {
return nullptr;
if (!aTransferable || !aItem) {
return;
}
MOZ_LOG(gCocoaUtilsLog, LogLevel::Info,
("nsCocoaUtils::GetDataFromPasteboardItem: looking for pasteboard "
"data of type %s\n",
PromiseFlatCString(aFlavor).get()));
("nsCocoaUtils::SetTransferDataForTypeFromPasteboardItem: looking "
"for pasteboard data of "
"type %s\n",
aFlavor.get()));
if (aFlavor.EqualsLiteral(kFileMime)) {
NSString* filePath = nsCocoaUtils::GetFilePathFromPasteboardItem(aItem);
if (!filePath) {
return nullptr;
return;
}
unsigned int stringLength = [filePath length];
@@ -1721,7 +1723,7 @@ already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
(stringLength + 1) * sizeof(char16_t); // in bytes
char16_t* clipboardDataPtr = (char16_t*)malloc(dataLength);
if (!clipboardDataPtr) {
return nullptr;
return;
}
[filePath getCharacters:reinterpret_cast<unichar*>(clipboardDataPtr)];
@@ -1732,10 +1734,11 @@ already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
getter_AddRefs(file));
free(clipboardDataPtr);
if (NS_FAILED(rv)) {
return nullptr;
return;
}
return file.forget();
aTransferable->SetTransferData(aFlavor.get(), file);
return;
}
if (aFlavor.EqualsLiteral(kCustomTypesMime)) {
@@ -1744,17 +1747,17 @@ already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
arrayWithObject:kMozCustomTypesPboardType]];
if (!availableType ||
!nsCocoaUtils::IsValidPasteboardType(availableType, false)) {
return nullptr;
return;
}
NSData* pasteboardData = [aItem dataForType:availableType];
if (!pasteboardData) {
return nullptr;
return;
}
unsigned int dataLength = [pasteboardData length];
void* clipboardDataPtr = malloc(dataLength);
if (!clipboardDataPtr) {
return nullptr;
return;
}
[pasteboardData getBytes:clipboardDataPtr length:dataLength];
@@ -1763,8 +1766,9 @@ already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
aFlavor, clipboardDataPtr, dataLength,
getter_AddRefs(genericDataWrapper));
aTransferable->SetTransferData(aFlavor.get(), genericDataWrapper);
free(clipboardDataPtr);
return genericDataWrapper.forget();
return;
}
NSString* pString = nil;
@@ -1804,7 +1808,7 @@ already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
unsigned int dataLength = [stringData length];
void* clipboardDataPtr = malloc(dataLength);
if (!clipboardDataPtr) {
return nullptr;
return;
}
[stringData getBytes:clipboardDataPtr length:dataLength];
@@ -1827,8 +1831,9 @@ already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, clipboardDataPtrNoBOM, dataLength,
getter_AddRefs(genericDataWrapper));
aTransferable->SetTransferData(aFlavor.get(), genericDataWrapper);
free(clipboardDataPtr);
return genericDataWrapper.forget();
return;
}
// We have never supported this on Mac OS X, we should someday. Normally
@@ -1842,28 +1847,5 @@ already_AddRefed<nsISupports> nsCocoaUtils::GetDataFromPasteboardItem(
}
*/
return nullptr;
NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
}
void nsCocoaUtils::SetTransferDataForTypeFromPasteboardItem(
nsITransferable* aTransferable, const nsCString& aFlavor,
NSPasteboardItem* aItem) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (!aTransferable || !aItem) {
return;
}
MOZ_LOG(gCocoaUtilsLog, LogLevel::Info,
("nsCocoaUtils::SetTransferDataForTypeFromPasteboardItem: looking "
"for pasteboard data of type %s\n",
aFlavor.get()));
if (nsCOMPtr<nsISupports> data = GetDataFromPasteboardItem(aFlavor, aItem)) {
aTransferable->SetTransferData(aFlavor.get(), data);
}
NS_OBJC_END_TRY_IGNORE_BLOCK;
}

View File

@@ -84,6 +84,18 @@ static void clipboard_owner_change_cb(GtkClipboard* aGtkClipboard,
static bool GetHTMLCharset(Span<const char> aData, nsCString& str);
static void SetTransferableData(nsITransferable* aTransferable,
const nsACString& aFlavor,
const char* aClipboardData,
uint32_t aClipboardDataLength) {
MOZ_CLIPBOARD_LOG("SetTransferableData MIME %s\n",
PromiseFlatCString(aFlavor).get());
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, aClipboardData, aClipboardDataLength, getter_AddRefs(wrapper));
aTransferable->SetTransferData(PromiseFlatCString(aFlavor).get(), wrapper);
}
ClipboardTargets ClipboardTargets::Clone() {
ClipboardTargets ret;
ret.mCount = mCount;
@@ -368,17 +380,27 @@ nsClipboard::GetNativeClipboardSequenceNumber(ClipboardType aWhichClipboard) {
: mGlobalSequenceNumber;
}
static bool IsMIMEAtFlavourList(const nsTArray<nsCString>& aFlavourList,
const char* aMime) {
for (const auto& flavorStr : aFlavourList) {
if (flavorStr.Equals(aMime)) {
return true;
}
}
return false;
}
// When clipboard contains only images, X11/Gtk tries to convert them
// to text when we request text instead of just fail to provide the data.
// So if clipboard contains images only remove text MIME offer.
bool nsClipboard::HasSuitableData(int32_t aWhichClipboard,
const nsACString& aFlavor) {
MOZ_CLIPBOARD_LOG("nsClipboard::HasSuitableData");
bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard,
nsTArray<nsCString>& aFlavors) {
MOZ_CLIPBOARD_LOG("nsClipboard::FilterImportedFlavors");
auto targets = mContext->GetTargets(aWhichClipboard);
if (!targets) {
MOZ_CLIPBOARD_LOG(" X11: no targes at clipboard (null), quit.\n");
return false;
return true;
}
for (const auto& atom : targets.AsSpan()) {
@@ -408,34 +430,68 @@ bool nsClipboard::HasSuitableData(int32_t aWhichClipboard,
}
// So make sure we offer only types we have at clipboard.
nsTArray<nsCString> clipboardFlavors;
for (const auto& atom : targets.AsSpan()) {
GUniquePtr<gchar> atom_name(gdk_atom_name(atom));
if (!atom_name) {
continue;
}
if (aFlavor.Equals(atom_name.get())) {
return true;
if (IsMIMEAtFlavourList(aFlavors, atom_name.get())) {
clipboardFlavors.AppendElement(nsCString(atom_name.get()));
}
}
MOZ_CLIPBOARD_LOG(" X11: no suitable data in clipboard, quit.\n");
return false;
aFlavors.SwapElements(clipboardFlavors);
#ifdef MOZ_LOGGING
MOZ_CLIPBOARD_LOG(" X11: Flavors which match clipboard content:\n");
for (uint32_t i = 0; i < aFlavors.Length(); i++) {
MOZ_CLIPBOARD_LOG(" %s\n", aFlavors[i].get());
}
#endif
return true;
}
static already_AddRefed<nsIFile> GetFileData(const nsACString& aURIList) {
nsCOMPtr<nsIFile> file;
static nsresult GetTransferableFlavors(nsITransferable* aTransferable,
nsTArray<nsCString>& aFlavors) {
if (!aTransferable) {
return NS_ERROR_FAILURE;
}
// Get a list of flavors this transferable can import
nsresult rv = aTransferable->FlavorsTransferableCanImport(aFlavors);
if (NS_FAILED(rv)) {
MOZ_CLIPBOARD_LOG(" FlavorsTransferableCanImport falied!\n");
return rv;
}
#ifdef MOZ_LOGGING
MOZ_CLIPBOARD_LOG(" Flavors which can be imported:");
for (const auto& flavor : aFlavors) {
MOZ_CLIPBOARD_LOG(" %s", flavor.get());
}
#endif
return NS_OK;
}
static bool TransferableSetFile(nsITransferable* aTransferable,
const nsACString& aURIList) {
nsresult rv;
nsTArray<nsCString> uris = mozilla::widget::ParseTextURIList(aURIList);
if (!uris.IsEmpty()) {
nsCOMPtr<nsIURI> fileURI;
NS_NewURI(getter_AddRefs(fileURI), uris[0]);
if (nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI)) {
fileURL->GetFile(getter_AddRefs(file));
if (nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI, &rv)) {
nsCOMPtr<nsIFile> file;
rv = fileURL->GetFile(getter_AddRefs(file));
if (NS_SUCCEEDED(rv)) {
aTransferable->SetTransferData(kFileMime, file);
MOZ_CLIPBOARD_LOG(" successfully set file to clipboard\n");
return true;
}
}
}
return file.forget();
return false;
}
static already_AddRefed<nsISupports> GetHTMLData(Span<const char> aData) {
static bool TransferableSetHTML(nsITransferable* aTransferable,
Span<const char> aData) {
nsLiteralCString mimeType(kHTMLMime);
// Convert text/html into our text format
@@ -447,14 +503,16 @@ static already_AddRefed<nsISupports> GetHTMLData(Span<const char> aData) {
charset.AssignLiteral("utf-8");
}
MOZ_CLIPBOARD_LOG("GetHTMLData: HTML detected charset %s", charset.get());
MOZ_CLIPBOARD_LOG("TransferableSetHTML: HTML detected charset %s",
charset.get());
// app which use "text/html" to copy&paste
// get the decoder
auto encoding = Encoding::ForLabelNoReplacement(charset);
if (!encoding) {
MOZ_CLIPBOARD_LOG("GetHTMLData: get unicode decoder error (charset: %s)",
charset.get());
return nullptr;
MOZ_CLIPBOARD_LOG(
"TransferableSetHTML: get unicode decoder error (charset: %s)",
charset.get());
return false;
}
// According to spec html UTF-16BE/LE should be switched to UTF-8
@@ -477,142 +535,151 @@ static already_AddRefed<nsISupports> GetHTMLData(Span<const char> aData) {
if (enc != UTF_8_ENCODING && MOZ_CLIPBOARD_LOG_ENABLED()) {
nsCString decoderName;
enc->Name(decoderName);
MOZ_CLIPBOARD_LOG("GetHTMLData: expected UTF-8 decoder but got %s",
MOZ_CLIPBOARD_LOG("TransferableSetHTML: expected UTF-8 decoder but got %s",
decoderName.get());
}
#endif
if (NS_FAILED(rv)) {
MOZ_CLIPBOARD_LOG("GetHTMLData: failed to decode HTML");
return nullptr;
MOZ_CLIPBOARD_LOG("TransferableSetHTML: failed to decode HTML");
return false;
}
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
mimeType, (const char*)unicodeData.BeginReading(),
unicodeData.Length() * sizeof(char16_t), getter_AddRefs(wrapper));
return wrapper.forget();
SetTransferableData(aTransferable, mimeType,
(const char*)unicodeData.BeginReading(),
unicodeData.Length() * sizeof(char16_t));
return true;
}
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
nsClipboard::GetNativeClipboardData(const nsACString& aFlavor,
NS_IMETHODIMP
nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
MOZ_CLIPBOARD_LOG(
"nsClipboard::GetNativeClipboardData (%s) for %s\n",
aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard",
PromiseFlatCString(aFlavor).get());
"nsClipboard::GetNativeClipboardData (%s)\n",
aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard");
// TODO: Ensure we don't re-enter here.
if (!mContext) {
return Err(NS_ERROR_FAILURE);
return NS_ERROR_FAILURE;
}
nsTArray<nsCString> flavors;
nsresult rv = GetTransferableFlavors(aTransferable, flavors);
NS_ENSURE_SUCCESS(rv, rv);
// Filter out MIME types on X11 to prevent unwanted conversions,
// see Bug 1611407
if (widget::GdkIsX11Display() && !HasSuitableData(aWhichClipboard, aFlavor)) {
if (widget::GdkIsX11Display() &&
!FilterImportedFlavors(aWhichClipboard, flavors)) {
MOZ_CLIPBOARD_LOG(" Missing suitable clipboard data, quit.");
return nsCOMPtr<nsISupports>{};
return NS_OK;
}
if (aFlavor.EqualsLiteral(kJPEGImageMime) ||
aFlavor.EqualsLiteral(kJPGImageMime) ||
aFlavor.EqualsLiteral(kPNGImageMime) ||
aFlavor.EqualsLiteral(kGIFImageMime)) {
// Emulate support for image/jpg
nsAutoCString flavor(aFlavor.EqualsLiteral(kJPGImageMime)
? kJPEGImageMime
: PromiseFlatCString(aFlavor).get());
MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data\n",
flavor.get());
for (uint32_t i = 0; i < flavors.Length(); i++) {
nsCString& flavorStr = flavors[i];
if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
flavorStr.EqualsLiteral(kJPGImageMime) ||
flavorStr.EqualsLiteral(kPNGImageMime) ||
flavorStr.EqualsLiteral(kGIFImageMime)) {
// Emulate support for image/jpg
if (flavorStr.EqualsLiteral(kJPGImageMime)) {
flavorStr.Assign(kJPEGImageMime);
}
MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data\n",
flavorStr.get());
auto clipboardData =
mContext->GetClipboardData(flavorStr.get(), aWhichClipboard);
if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr.get());
continue;
}
nsCOMPtr<nsIInputStream> byteStream;
NS_NewByteInputStream(getter_AddRefs(byteStream), clipboardData.AsSpan(),
NS_ASSIGNMENT_COPY);
aTransferable->SetTransferData(flavorStr.get(), byteStream);
MOZ_CLIPBOARD_LOG(" got %s MIME data\n", flavorStr.get());
return NS_OK;
}
// Special case text/plain since we can convert any
// string into text/plain
if (flavorStr.EqualsLiteral(kTextMime)) {
MOZ_CLIPBOARD_LOG(" Getting text %s MIME clipboard data\n",
flavorStr.get());
auto clipboardData = mContext->GetClipboardText(aWhichClipboard);
if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" failed to get text data\n");
// If the type was text/plain and we couldn't get
// text off the clipboard, run the next loop
// iteration.
continue;
}
// Convert utf-8 into our text format.
NS_ConvertUTF8toUTF16 ucs2string(clipboardData.get());
SetTransferableData(aTransferable, flavorStr,
(const char*)ucs2string.BeginReading(),
ucs2string.Length() * 2);
MOZ_CLIPBOARD_LOG(" got text data, length %zd\n", ucs2string.Length());
return NS_OK;
}
if (flavorStr.EqualsLiteral(kFileMime)) {
MOZ_CLIPBOARD_LOG(" Getting %s file clipboard data\n",
flavorStr.get());
auto clipboardData =
mContext->GetClipboardData(kURIListMime, aWhichClipboard);
if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" text/uri-list type is missing\n");
continue;
}
nsDependentCSubstring fileName(clipboardData.AsSpan());
if (!TransferableSetFile(aTransferable, fileName)) {
continue;
}
return NS_OK;
}
MOZ_CLIPBOARD_LOG(" Getting %s MIME clipboard data\n", flavorStr.get());
auto clipboardData =
mContext->GetClipboardData(flavor.get(), aWhichClipboard);
mContext->GetClipboardData(flavorStr.get(), aWhichClipboard);
#ifdef MOZ_LOGGING
if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavor.get());
return nsCOMPtr<nsISupports>{};
MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr.get());
}
#endif
nsCOMPtr<nsIInputStream> byteStream;
NS_NewByteInputStream(getter_AddRefs(byteStream), clipboardData.AsSpan(),
NS_ASSIGNMENT_COPY);
if (clipboardData) {
MOZ_CLIPBOARD_LOG(" got %s mime type data.\n", flavorStr.get());
MOZ_CLIPBOARD_LOG(" got %s MIME data\n", flavor.get());
return nsCOMPtr<nsISupports>(std::move(byteStream));
}
// Special case text/plain since we can convert any
// string into text/plain
if (aFlavor.EqualsLiteral(kTextMime)) {
MOZ_CLIPBOARD_LOG(" Getting text %s MIME clipboard data\n",
PromiseFlatCString(aFlavor).get());
auto clipboardData = mContext->GetClipboardText(aWhichClipboard);
if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" failed to get text data\n");
// If the type was text/plain and we couldn't get
// text off the clipboard, run the next loop
// iteration.
return nsCOMPtr<nsISupports>{};
// Special case text/html since we can convert into UCS2
if (flavorStr.EqualsLiteral(kHTMLMime)) {
if (!TransferableSetHTML(aTransferable, clipboardData.AsSpan())) {
continue;
}
} else {
auto span = clipboardData.AsSpan();
SetTransferableData(aTransferable, flavorStr, span.data(),
span.Length());
}
return NS_OK;
}
// Convert utf-8 into our text format.
NS_ConvertUTF8toUTF16 ucs2string(clipboardData.get());
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, (const char*)ucs2string.BeginReading(),
ucs2string.Length() * 2, getter_AddRefs(wrapper));
MOZ_CLIPBOARD_LOG(" got text data, length %zd\n", ucs2string.Length());
return wrapper;
}
if (aFlavor.EqualsLiteral(kFileMime)) {
MOZ_CLIPBOARD_LOG(" Getting file %s MIME clipboard data\n",
PromiseFlatCString(aFlavor).get());
auto clipboardData =
mContext->GetClipboardData(kURIListMime, aWhichClipboard);
if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" text/uri-list type is missing\n");
return nsCOMPtr<nsISupports>{};
}
nsDependentCSubstring fileName(clipboardData.AsSpan());
if (nsCOMPtr<nsIFile> file = GetFileData(fileName)) {
MOZ_CLIPBOARD_LOG(" got file data\n");
return nsCOMPtr<nsISupports>(std::move(file));
}
MOZ_CLIPBOARD_LOG(" failed to get file data\n");
return nsCOMPtr<nsISupports>{};
}
MOZ_CLIPBOARD_LOG(" Getting %s MIME clipboard data\n",
PromiseFlatCString(aFlavor).get());
auto clipboardData = mContext->GetClipboardData(
PromiseFlatCString(aFlavor).get(), aWhichClipboard);
if (!clipboardData) {
MOZ_CLIPBOARD_LOG(" failed to get clipboard content.\n");
return nsCOMPtr<nsISupports>{};
}
MOZ_CLIPBOARD_LOG(" got %s mime type data.\n",
PromiseFlatCString(aFlavor).get());
// Special case text/html since we can convert into UCS2
auto span = clipboardData.AsSpan();
if (aFlavor.EqualsLiteral(kHTMLMime)) {
return nsCOMPtr<nsISupports>(GetHTMLData(span));
}
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, span.data(), span.Length(), getter_AddRefs(wrapper));
return wrapper;
MOZ_CLIPBOARD_LOG(" failed to get clipboard content.\n");
return NS_OK;
}
enum DataType {
@@ -623,14 +690,17 @@ enum DataType {
};
struct DataCallbackHandler {
nsBaseClipboard::GetNativeDataCallback mDataCallback;
RefPtr<nsITransferable> mTransferable;
nsBaseClipboard::GetDataCallback mDataCallback;
nsCString mMimeType;
DataType mDataType;
DataCallbackHandler(nsBaseClipboard::GetNativeDataCallback&& aDataCallback,
const nsACString& aMimeType,
DataType aDataType = DATATYPE_RAW)
: mDataCallback(std::move(aDataCallback)),
explicit DataCallbackHandler(RefPtr<nsITransferable> aTransferable,
nsBaseClipboard::GetDataCallback&& aDataCallback,
const char* aMimeType,
DataType aDataType = DATATYPE_RAW)
: mTransferable(std::move(aTransferable)),
mDataCallback(std::move(aDataCallback)),
mMimeType(aMimeType),
mDataType(aDataType) {
MOZ_COUNT_CTOR(DataCallbackHandler);
@@ -643,9 +713,9 @@ struct DataCallbackHandler {
}
};
static void AsyncGetTextImpl(
int32_t aWhichClipboard,
nsBaseClipboard::GetNativeDataCallback&& aCallback) {
static void AsyncGetTextImpl(nsITransferable* aTransferable,
int32_t aWhichClipboard,
nsBaseClipboard::GetDataCallback&& aCallback) {
MOZ_CLIPBOARD_LOG("AsyncGetText() type '%s'",
aWhichClipboard == nsClipboard::kSelectionClipboard
? "primary"
@@ -661,41 +731,47 @@ static void AsyncGetTextImpl(
size_t dataLength = aText ? strlen(aText) : 0;
if (dataLength <= 0) {
MOZ_CLIPBOARD_LOG(" quit, text is not available");
ref->mDataCallback(nsCOMPtr<nsISupports>{});
ref->mDataCallback(NS_OK);
return;
}
// Convert utf-8 into our unicode format.
NS_ConvertUTF8toUTF16 utf16string(aText, dataLength);
nsLiteralCString flavor(kTextMime);
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
flavor, (const char*)utf16string.BeginReading(),
utf16string.Length() * 2, getter_AddRefs(wrapper));
SetTransferableData(ref->mTransferable, flavor,
(const char*)utf16string.BeginReading(),
utf16string.Length() * 2);
MOZ_CLIPBOARD_LOG(" text is set, length = %d", (int)dataLength);
ref->mDataCallback(wrapper);
ref->mDataCallback(NS_OK);
},
new DataCallbackHandler(std::move(aCallback),
nsLiteralCString(kTextMime)));
new DataCallbackHandler(aTransferable, std::move(aCallback), kTextMime));
}
static void AsyncGetDataImpl(
int32_t aWhichClipboard, const nsACString& aMimeType, DataType aDataType,
nsBaseClipboard::GetNativeDataCallback&& aCallback) {
static void AsyncGetDataImpl(nsITransferable* aTransferable,
int32_t aWhichClipboard, const char* aMimeType,
DataType aDataType,
nsBaseClipboard::GetDataCallback&& aCallback) {
MOZ_CLIPBOARD_LOG("AsyncGetData() type '%s'",
aWhichClipboard == nsClipboard::kSelectionClipboard
? "primary"
: "clipboard");
const char* gtkMIMEType = nullptr;
switch (aDataType) {
case DATATYPE_FILE:
// Don't ask Gtk for application/x-moz-file
gtkMIMEType = kURIListMime;
break;
case DATATYPE_IMAGE:
case DATATYPE_HTML:
case DATATYPE_RAW:
gtkMIMEType = aMimeType;
break;
}
gtk_clipboard_request_contents(
gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)),
// Don't ask Gtk for application/x-moz-file.
gdk_atom_intern((aDataType == DATATYPE_FILE)
? kURIListMime
: PromiseFlatCString(aMimeType).get(),
FALSE),
gdk_atom_intern(gtkMIMEType, FALSE),
[](GtkClipboard* aClipboard, GtkSelectionData* aSelection,
gpointer aData) -> void {
UniquePtr<DataCallbackHandler> ref(
@@ -705,120 +781,130 @@ static void AsyncGetDataImpl(
int dataLength = gtk_selection_data_get_length(aSelection);
if (dataLength <= 0) {
ref->mDataCallback(nsCOMPtr<nsISupports>{});
ref->mDataCallback(NS_OK);
return;
}
const char* data = (const char*)gtk_selection_data_get_data(aSelection);
if (!data) {
ref->mDataCallback(nsCOMPtr<nsISupports>{});
ref->mDataCallback(NS_OK);
return;
}
switch (ref->mDataType) {
case DATATYPE_IMAGE: {
MOZ_CLIPBOARD_LOG(" get image clipboard data");
MOZ_CLIPBOARD_LOG(" set image clipboard data");
nsCOMPtr<nsIInputStream> byteStream;
NS_NewByteInputStream(getter_AddRefs(byteStream),
Span(data, dataLength), NS_ASSIGNMENT_COPY);
ref->mDataCallback(nsCOMPtr<nsISupports>(byteStream));
return;
ref->mTransferable->SetTransferData(ref->mMimeType.get(),
byteStream);
break;
}
case DATATYPE_FILE: {
MOZ_CLIPBOARD_LOG(" get file clipboard data");
nsDependentCSubstring uriList(data, dataLength);
if (nsCOMPtr<nsIFile> file = GetFileData(uriList)) {
MOZ_CLIPBOARD_LOG(" successfully get file data\n");
ref->mDataCallback(nsCOMPtr<nsISupports>(file));
return;
}
MOZ_CLIPBOARD_LOG(" set file clipboard data");
nsDependentCSubstring file(data, dataLength);
TransferableSetFile(ref->mTransferable, file);
break;
}
case DATATYPE_HTML: {
MOZ_CLIPBOARD_LOG(" html clipboard data");
Span dataSpan(data, dataLength);
if (nsCOMPtr<nsISupports> data = GetHTMLData(dataSpan)) {
MOZ_CLIPBOARD_LOG(" successfully get HTML data\n");
ref->mDataCallback(nsCOMPtr<nsISupports>(data));
return;
}
TransferableSetHTML(ref->mTransferable, dataSpan);
break;
}
case DATATYPE_RAW: {
MOZ_CLIPBOARD_LOG(" raw clipboard data %s", ref->mMimeType.get());
nsCOMPtr<nsISupports> wrapper;
nsPrimitiveHelpers::CreatePrimitiveForData(
ref->mMimeType, data, dataLength, getter_AddRefs(wrapper));
ref->mDataCallback(nsCOMPtr<nsISupports>(wrapper));
return;
SetTransferableData(ref->mTransferable, ref->mMimeType, data,
dataLength);
break;
}
}
ref->mDataCallback(nsCOMPtr<nsISupports>{});
ref->mDataCallback(NS_OK);
},
new DataCallbackHandler(std::move(aCallback), aMimeType, aDataType));
new DataCallbackHandler(aTransferable, std::move(aCallback), aMimeType,
aDataType));
}
static void AsyncGetDataFlavor(
int32_t aWhichClipboard, const nsACString& aFlavorStr,
nsBaseClipboard::GetNativeDataCallback&& aCallback) {
static void AsyncGetDataFlavor(nsITransferable* aTransferable,
int32_t aWhichClipboard, nsCString& aFlavorStr,
nsBaseClipboard::GetDataCallback&& aCallback) {
if (aFlavorStr.EqualsLiteral(kJPEGImageMime) ||
aFlavorStr.EqualsLiteral(kJPGImageMime) ||
aFlavorStr.EqualsLiteral(kPNGImageMime) ||
aFlavorStr.EqualsLiteral(kGIFImageMime)) {
// Emulate support for image/jpg
nsAutoCString flavor(aFlavorStr.EqualsLiteral(kJPGImageMime)
? kJPEGImageMime
: PromiseFlatCString(aFlavorStr).get());
MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data", flavor.get());
AsyncGetDataImpl(aWhichClipboard, flavor, DATATYPE_IMAGE,
std::move(aCallback));
if (aFlavorStr.EqualsLiteral(kJPGImageMime)) {
aFlavorStr.Assign(kJPEGImageMime);
}
MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data",
aFlavorStr.get());
AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
DATATYPE_IMAGE, std::move(aCallback));
return;
}
// Special case text/plain since we can convert any
// string into text/plain
if (aFlavorStr.EqualsLiteral(kTextMime)) {
MOZ_CLIPBOARD_LOG(" Getting unicode clipboard data");
AsyncGetTextImpl(aWhichClipboard, std::move(aCallback));
AsyncGetTextImpl(aTransferable, aWhichClipboard, std::move(aCallback));
return;
}
if (aFlavorStr.EqualsLiteral(kFileMime)) {
MOZ_CLIPBOARD_LOG(" Getting file clipboard data\n");
AsyncGetDataImpl(aWhichClipboard, aFlavorStr, DATATYPE_FILE,
std::move(aCallback));
AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
DATATYPE_FILE, std::move(aCallback));
return;
}
if (aFlavorStr.EqualsLiteral(kHTMLMime)) {
MOZ_CLIPBOARD_LOG(" Getting HTML clipboard data");
AsyncGetDataImpl(aWhichClipboard, aFlavorStr, DATATYPE_HTML,
std::move(aCallback));
AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
DATATYPE_HTML, std::move(aCallback));
return;
}
MOZ_CLIPBOARD_LOG(" Getting raw %s MIME clipboard data\n",
PromiseFlatCString(aFlavorStr).get());
AsyncGetDataImpl(aWhichClipboard, aFlavorStr, DATATYPE_RAW,
std::move(aCallback));
MOZ_CLIPBOARD_LOG(" Getting raw %s MIME clipboard data\n", aFlavorStr.get());
AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(),
DATATYPE_RAW, std::move(aCallback));
}
void nsClipboard::AsyncGetNativeClipboardData(
const nsACString& aFlavor, ClipboardType aWhichClipboard,
GetNativeDataCallback&& aCallback) {
void nsClipboard::AsyncGetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard,
GetDataCallback&& aCallback) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
MOZ_CLIPBOARD_LOG("nsClipboard::AsyncGetNativeClipboardData (%s) for %s",
MOZ_CLIPBOARD_LOG("nsClipboard::AsyncGetNativeClipboardData (%s)",
aWhichClipboard == nsClipboard::kSelectionClipboard
? "primary"
: "clipboard",
PromiseFlatCString(aFlavor).get());
: "clipboard");
nsTArray<nsCString> importedFlavors;
nsresult rv = GetTransferableFlavors(aTransferable, importedFlavors);
if (NS_FAILED(rv)) {
aCallback(rv);
return;
}
auto flavorsNum = importedFlavors.Length();
if (!flavorsNum) {
aCallback(NS_OK);
return;
}
#ifdef MOZ_LOGGING
if (flavorsNum > 1) {
MOZ_CLIPBOARD_LOG(
" Only first MIME type (%s) will be imported from clipboard!",
importedFlavors[0].get());
}
#endif
// Filter out MIME types on X11 to prevent unwanted conversions,
// see Bug 1611407
if (widget::GdkIsX11Display()) {
AsyncHasNativeClipboardDataMatchingFlavors(
nsTArray<nsCString>{PromiseFlatCString(aFlavor)}, aWhichClipboard,
[aWhichClipboard,
importedFlavors, aWhichClipboard,
[aWhichClipboard, transferable = nsCOMPtr{aTransferable},
callback = std::move(aCallback)](auto aResultOrError) mutable {
if (aResultOrError.isErr()) {
callback(Err(aResultOrError.unwrapErr()));
callback(aResultOrError.unwrapErr());
return;
}
@@ -826,18 +912,19 @@ void nsClipboard::AsyncGetNativeClipboardData(
std::move(aResultOrError.unwrap());
if (!clipboardFlavors.Length()) {
MOZ_CLIPBOARD_LOG(" no flavors in clipboard, quit.");
callback(nsCOMPtr<nsISupports>{});
callback(NS_OK);
return;
}
AsyncGetDataFlavor(aWhichClipboard, clipboardFlavors[0],
AsyncGetDataFlavor(transferable, aWhichClipboard, clipboardFlavors[0],
std::move(callback));
});
return;
}
// Read clipboard directly on Wayland
AsyncGetDataFlavor(aWhichClipboard, aFlavor, std::move(aCallback));
AsyncGetDataFlavor(aTransferable, aWhichClipboard, importedFlavors[0],
std::move(aCallback));
}
nsresult nsClipboard::EmptyNativeClipboardData(ClipboardType aWhichClipboard) {

View File

@@ -94,7 +94,7 @@ class nsRetrievalContext {
static ClipboardTargets sPrimaryTargets;
};
class nsClipboard final : public nsBaseClipboard, public nsIObserver {
class nsClipboard : public nsBaseClipboard, public nsIObserver {
public:
nsClipboard();
@@ -121,11 +121,11 @@ class nsClipboard final : public nsBaseClipboard, public nsIObserver {
// Implement the native clipboard behavior.
NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
mozilla::Result<nsCOMPtr<nsISupports>, nsresult> GetNativeClipboardData(
const nsACString& aFlavor, ClipboardType aWhichClipboard) override;
void AsyncGetNativeClipboardData(const nsACString& aFlavor,
NS_IMETHOD GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
void AsyncGetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard,
GetNativeDataCallback&& aCallback) override;
GetDataCallback&& aCallback) override;
nsresult EmptyNativeClipboardData(ClipboardType aWhichClipboard) override;
mozilla::Result<bool, nsresult> HasNativeClipboardDataMatchingFlavors(
const nsTArray<nsCString>& aFlavorList,
@@ -144,7 +144,8 @@ class nsClipboard final : public nsBaseClipboard, public nsIObserver {
void ClearTransferable(int32_t aWhichClipboard);
void ClearCachedTargets(int32_t aWhichClipboard);
bool HasSuitableData(int32_t aWhichClipboard, const nsACString& aFlavor);
bool FilterImportedFlavors(int32_t aWhichClipboard,
nsTArray<nsCString>& aFlavors);
// Hang on to our transferables so we can transfer data when asked.
nsCOMPtr<nsITransferable> mSelectionTransferable;

View File

@@ -66,34 +66,51 @@ HeadlessClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
return NS_OK;
}
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
HeadlessClipboard::GetNativeClipboardData(const nsACString& aFlavor,
NS_IMETHODIMP
HeadlessClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
auto& clipboard = mClipboards[aWhichClipboard];
MOZ_ASSERT(clipboard);
if (!aFlavor.EqualsLiteral(kTextMime) && !aFlavor.EqualsLiteral(kHTMLMime)) {
return nsCOMPtr<nsISupports>{};
for (const auto& flavor : flavors) {
if (!flavor.EqualsLiteral(kTextMime) && !flavor.EqualsLiteral(kHTMLMime)) {
continue;
}
bool isText = flavor.EqualsLiteral(kTextMime);
if (!(isText ? clipboard->HasText() : clipboard->HasHTML())) {
continue;
}
nsCOMPtr<nsISupportsString> dataWrapper =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
rv = dataWrapper->SetData(isText ? clipboard->GetText()
: clipboard->GetHTML());
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCOMPtr<nsISupports> genericDataWrapper = do_QueryInterface(dataWrapper);
rv = aTransferable->SetTransferData(flavor.get(), genericDataWrapper);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
// XXX Other platforms only fill the first available type, too.
break;
}
bool isText = aFlavor.EqualsLiteral(kTextMime);
if (!(isText ? clipboard->HasText() : clipboard->HasHTML())) {
return nsCOMPtr<nsISupports>{};
}
nsresult rv;
nsCOMPtr<nsISupportsString> dataWrapper =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
rv = dataWrapper->SetData(isText ? clipboard->GetText()
: clipboard->GetHTML());
if (NS_WARN_IF(NS_FAILED(rv))) {
return nsCOMPtr<nsISupports>{};
}
return nsCOMPtr<nsISupports>(std::move(dataWrapper));
return NS_OK;
}
nsresult HeadlessClipboard::EmptyNativeClipboardData(

View File

@@ -28,8 +28,8 @@ class HeadlessClipboard final : public nsBaseClipboard {
// Implement the native clipboard behavior.
NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
mozilla::Result<nsCOMPtr<nsISupports>, nsresult> GetNativeClipboardData(
const nsACString& aFlavor, ClipboardType aWhichClipboard) override;
NS_IMETHOD GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
nsresult EmptyNativeClipboardData(ClipboardType aWhichClipboard) override;
mozilla::Result<bool, nsresult> HasNativeClipboardDataMatchingFlavors(
const nsTArray<nsCString>& aFlavorList,

View File

@@ -463,26 +463,10 @@ NS_IMETHODIMP nsBaseClipboard::GetData(
// 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
}
nsTArray<nsCString> flavors;
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
nsresult rv = GetNativeClipboardData(aTransferable, aWhichClipboard);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
return rv;
}
for (const auto& flavor : flavors) {
auto dataOrError = GetNativeClipboardData(flavor, aWhichClipboard);
if (dataOrError.isErr()) {
continue;
}
if (dataOrError.inspect()) {
aTransferable->SetTransferData(flavor.get(), dataOrError.inspect());
// XXX Maybe try to fill in more types? Is there a point?
break;
}
}
if (!mozilla::contentanalysis::ContentAnalysis::
CheckClipboardContentAnalysisSync(this, aWindowContext->Canonical(),
aTransferable, aWhichClipboard)) {
@@ -889,9 +873,9 @@ void nsBaseClipboard::AsyncHasNativeClipboardDataMatchingFlavors(
}
void nsBaseClipboard::AsyncGetNativeClipboardData(
const nsACString& aFlavor, ClipboardType aWhichClipboard,
GetNativeDataCallback&& aCallback) {
aCallback(GetNativeClipboardData(aFlavor, aWhichClipboard));
nsITransferable* aTransferable, ClipboardType aWhichClipboard,
GetDataCallback&& aCallback) {
aCallback(GetNativeClipboardData(aTransferable, aWhichClipboard));
}
void nsBaseClipboard::ClearClipboardCache(ClipboardType aClipboardType) {
@@ -1067,8 +1051,8 @@ NS_IMETHODIMP nsBaseClipboard::ClipboardDataSnapshot::GetData(
// Since this is an async operation, we need to check if the data is still
// valid after we get the result.
GetDataInternal(
std::move(flavors), 0, aTransferable,
mClipboard->AsyncGetNativeClipboardData(
aTransferable, mClipboardType,
[callback = nsCOMPtr{aCallback}, self = RefPtr{this},
transferable = nsCOMPtr{aTransferable},
contentAnalysisCallback =
@@ -1147,18 +1131,9 @@ NS_IMETHODIMP nsBaseClipboard::ClipboardDataSnapshot::GetDataSync(
// for things other people put on the system clipboard.
}
for (const auto& flavor : flavors) {
auto dataOrError =
mClipboard->GetNativeClipboardData(flavor, mClipboardType);
if (dataOrError.isErr()) {
continue;
}
if (dataOrError.inspect()) {
aTransferable->SetTransferData(flavor.get(), dataOrError.inspect());
// XXX Maybe try to fill in more types? Is there a point?
break;
}
rv = mClipboard->GetNativeClipboardData(aTransferable, mClipboardType);
if (NS_FAILED(rv)) {
return rv;
}
bool shouldAllowContent = mozilla::contentanalysis::ContentAnalysis::
@@ -1207,46 +1182,6 @@ bool nsBaseClipboard::ClipboardDataSnapshot::IsValid() {
return true;
}
void nsBaseClipboard::ClipboardDataSnapshot::GetDataInternal(
nsTArray<nsCString>&& aTypes, nsTArray<nsCString>::index_type aIndex,
nsITransferable* aTransferable, GetDataInternalCallback&& aCallback) {
MOZ_ASSERT(aIndex < aTypes.Length());
// Since this is an async operation, we need to check if the data is still
// valid after we get the result.
mClipboard->AsyncGetNativeClipboardData(
aTypes[aIndex], mClipboardType,
[self = RefPtr{this}, types = std::move(aTypes), index = aIndex,
transferable = nsCOMPtr{aTransferable}, callback = std::move(aCallback)](
mozilla::Result<nsCOMPtr<nsISupports>, nsresult> aResult) mutable {
MOZ_ASSERT(index < types.Length());
// `IsValid()` checks the clipboard sequence number to ensure the data
// we are requesting is still valid.
if (!self->IsValid()) {
callback(NS_ERROR_NOT_AVAILABLE);
return;
}
if (!aResult.isErr() && aResult.inspect()) {
MOZ_ASSERT(index < types.Length());
transferable->SetTransferData(types[index].get(), aResult.inspect());
callback(NS_OK);
return;
}
// No more types to try.
if (++index >= types.Length()) {
callback(NS_OK);
return;
}
// Recursively call GetDataInternal to try the next type.
self->GetDataInternal(std::move(types), index, transferable,
std::move(callback));
});
}
NS_IMPL_ISUPPORTS(nsBaseClipboard::ClipboardPopulatedDataSnapshot,
nsIClipboardDataSnapshot)

View File

@@ -76,8 +76,7 @@ class nsBaseClipboard : public nsIClipboard {
mozilla::dom::WindowContext* aRequestingWindowContext,
nsIClipboardGetDataSnapshotCallback* aCallback);
using GetNativeDataCallback = mozilla::MoveOnlyFunction<void(
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>)>;
using GetDataCallback = mozilla::MoveOnlyFunction<void(nsresult)>;
using HasMatchingFlavorsCallback = mozilla::MoveOnlyFunction<void(
mozilla::Result<nsTArray<nsCString>, nsresult>)>;
@@ -105,12 +104,11 @@ class nsBaseClipboard : public nsIClipboard {
// Implement the native clipboard behavior.
NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) = 0;
virtual mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
GetNativeClipboardData(const nsACString& aFlavor,
ClipboardType aWhichClipboard) = 0;
virtual void AsyncGetNativeClipboardData(const nsACString& aFlavor,
NS_IMETHOD GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) = 0;
virtual void AsyncGetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard,
GetNativeDataCallback&& aCallback);
GetDataCallback&& aCallback);
virtual nsresult EmptyNativeClipboardData(ClipboardType aWhichClipboard) = 0;
virtual mozilla::Result<bool, nsresult> HasNativeClipboardDataMatchingFlavors(
const nsTArray<nsCString>& aFlavorList,
@@ -170,12 +168,6 @@ class nsBaseClipboard : public nsIClipboard {
virtual ~ClipboardDataSnapshot() = default;
bool IsValid();
using GetDataInternalCallback = mozilla::MoveOnlyFunction<void(nsresult)>;
void GetDataInternal(nsTArray<nsCString>&& aTypes,
nsTArray<nsCString>::index_type aIndex,
nsITransferable* aTransferable,
GetDataInternalCallback&& aCallback);
// The clipboard type defined in nsIClipboard.
const nsIClipboard::ClipboardType mClipboardType;
// The sequence number associated with the clipboard content for this

View File

@@ -165,7 +165,7 @@ interface nsIClipboard : nsISupports
/**
* Filters the flavors aTransferable can import (see
* `nsITransferable::flavorsTransferableCanImport`) and gets the data for the
* first available flavor. That data is set for aTransferable.
* first flavor. That data is set for aTransferable.
*
* @param aTransferable The transferable
* @param aWhichClipboard Specifies the clipboard to which this operation applies.

View File

@@ -960,125 +960,6 @@ nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject* aDataObject,
}
//-------------------------------------------------------------------------
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
nsClipboard::GetDataFromDataObject(IDataObject* aDataObject, UINT anIndex,
nsIWidget* aWindow,
const nsCString& aFlavor) {
MOZ_CLIPBOARD_LOG("%s", __FUNCTION__);
UINT format = GetFormat(aFlavor.get());
// Try to get the data using the desired flavor. This might fail, but all is
// not lost.
void* data = nullptr;
uint32_t dataLen = 0;
bool dataFound = false;
if (nullptr != aDataObject) {
if (NS_SUCCEEDED(GetNativeDataOffClipboard(
aDataObject, anIndex, format, aFlavor.get(), &data, &dataLen))) {
dataFound = true;
}
} else if (nullptr != aWindow) {
if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format, &data,
&dataLen))) {
dataFound = true;
}
}
// This is our second chance to try to find some data, having not found it
// when directly asking for the flavor. Let's try digging around in other
// flavors to help satisfy our craving for data.
if (!dataFound) {
if (aFlavor.EqualsLiteral(kTextMime)) {
dataFound =
FindUnicodeFromPlainText(aDataObject, anIndex, &data, &dataLen);
} else if (aFlavor.EqualsLiteral(kURLMime)) {
// drags from other windows apps expose the native
// CFSTR_INETURL{A,W} flavor
dataFound = FindURLFromNativeURL(aDataObject, anIndex, &data, &dataLen);
if (!dataFound) {
dataFound = FindURLFromLocalFile(aDataObject, anIndex, &data, &dataLen);
}
} else {
mozilla::Maybe<UINT> secondaryFormat = GetSecondaryFormat(aFlavor.get());
if (secondaryFormat) {
// Fall back to secondary format
dataFound = NS_SUCCEEDED(GetNativeDataOffClipboard(
aDataObject, anIndex, secondaryFormat.value(), aFlavor.get(), &data,
&dataLen));
}
}
} // if we try one last ditch effort to find our data
if (!dataFound) {
return nsCOMPtr<nsISupports>{};
}
// Hopefully by this point we've found it and can go about our business
nsCOMPtr<nsISupports> genericDataWrapper;
if (aFlavor.EqualsLiteral(kFileMime)) {
// we have a file path in |data|. Create an nsLocalFile object.
nsDependentString filepath(reinterpret_cast<char16_t*>(data));
nsCOMPtr<nsIFile> file;
if (NS_SUCCEEDED(NS_NewLocalFile(filepath, getter_AddRefs(file)))) {
genericDataWrapper = do_QueryInterface(file);
}
free(data);
} else if (aFlavor.EqualsLiteral(kNativeHTMLMime)) {
uint32_t dummy;
// the editor folks want CF_HTML exactly as it's on the clipboard, no
// conversions, no fancy stuff. Pull it off the clipboard, stuff it into
// a wrapper and hand it back to them.
if (FindPlatformHTML(aDataObject, anIndex, &data, &dummy, &dataLen)) {
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, data, dataLen, getter_AddRefs(genericDataWrapper));
}
free(data);
} else if (aFlavor.EqualsLiteral(kHTMLMime)) {
uint32_t startOfData = 0;
// The JS folks want CF_HTML exactly as it is on the clipboard, but
// minus the CF_HTML header index information.
// It also needs to be converted to UTF16 and have linebreaks changed.
if (FindPlatformHTML(aDataObject, anIndex, &data, &startOfData, &dataLen)) {
dataLen -= startOfData;
nsPrimitiveHelpers::CreatePrimitiveForCFHTML(
static_cast<char*>(data) + startOfData, &dataLen,
getter_AddRefs(genericDataWrapper));
}
free(data);
} else if (aFlavor.EqualsLiteral(kJPEGImageMime) ||
aFlavor.EqualsLiteral(kJPGImageMime) ||
aFlavor.EqualsLiteral(kPNGImageMime)) {
nsIInputStream* imageStream = reinterpret_cast<nsIInputStream*>(data);
genericDataWrapper = do_QueryInterface(imageStream);
NS_IF_RELEASE(imageStream);
} else {
// Treat custom types as a string of bytes.
if (!aFlavor.EqualsLiteral(kCustomTypesMime)) {
bool isRTF = aFlavor.EqualsLiteral(kRTFMime);
// we probably have some form of text. The DOM only wants LF, so
// convert from Win32 line endings to DOM line endings.
int32_t signedLen = static_cast<int32_t>(dataLen);
nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(isRTF, &data,
&signedLen);
dataLen = signedLen;
if (isRTF) {
// RTF on Windows is known to sometimes deliver an extra null byte.
if (dataLen > 0 && static_cast<char*>(data)[dataLen - 1] == '\0') {
dataLen--;
}
}
}
nsPrimitiveHelpers::CreatePrimitiveForData(
aFlavor, data, dataLen, getter_AddRefs(genericDataWrapper));
free(data);
}
return std::move(genericDataWrapper);
}
nsresult nsClipboard::GetDataFromDataObject(IDataObject* aDataObject,
UINT anIndex, nsIWidget* aWindow,
nsITransferable* aTransferable) {
@@ -1089,10 +970,12 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject* aDataObject,
return NS_ERROR_INVALID_ARG;
}
nsresult res = NS_ERROR_FAILURE;
// get flavor list that includes all flavors that can be written (including
// ones obtained through conversion)
nsTArray<nsCString> flavors;
nsresult res = aTransferable->FlavorsTransferableCanImport(flavors);
res = aTransferable->FlavorsTransferableCanImport(flavors);
if (NS_FAILED(res)) {
return NS_ERROR_FAILURE;
}
@@ -1100,22 +983,137 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject* aDataObject,
// Walk through flavors and see which flavor is on the clipboard them on the
// native clipboard,
for (uint32_t i = 0; i < flavors.Length(); i++) {
const nsCString& flavorStr = flavors[i];
nsCString& flavorStr = flavors[i];
UINT format = GetFormat(flavorStr.get());
auto dataOrError =
GetDataFromDataObject(aDataObject, anIndex, aWindow, flavorStr);
if (dataOrError.isErr() || !dataOrError.inspect()) {
continue;
// Try to get the data using the desired flavor. This might fail, but all is
// not lost.
void* data = nullptr;
uint32_t dataLen = 0;
bool dataFound = false;
if (nullptr != aDataObject) {
if (NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format,
flavorStr.get(), &data,
&dataLen))) {
dataFound = true;
}
} else if (nullptr != aWindow) {
if (NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format,
&data, &dataLen))) {
dataFound = true;
}
}
NS_ASSERTION(dataOrError.inspect(),
"About to put null data into the transferable");
aTransferable->SetTransferData(flavorStr.get(), dataOrError.inspect());
// we found one, get out of the loop
break;
// This is our second chance to try to find some data, having not found it
// when directly asking for the flavor. Let's try digging around in other
// flavors to help satisfy our craving for data.
if (!dataFound) {
if (flavorStr.EqualsLiteral(kTextMime)) {
dataFound =
FindUnicodeFromPlainText(aDataObject, anIndex, &data, &dataLen);
} else if (flavorStr.EqualsLiteral(kURLMime)) {
// drags from other windows apps expose the native
// CFSTR_INETURL{A,W} flavor
dataFound = FindURLFromNativeURL(aDataObject, anIndex, &data, &dataLen);
if (!dataFound) {
dataFound =
FindURLFromLocalFile(aDataObject, anIndex, &data, &dataLen);
}
} else {
mozilla::Maybe<UINT> secondaryFormat =
GetSecondaryFormat(flavorStr.get());
if (secondaryFormat) {
// Fall back to secondary format
dataFound = NS_SUCCEEDED(GetNativeDataOffClipboard(
aDataObject, anIndex, secondaryFormat.value(), flavorStr.get(),
&data, &dataLen));
}
}
} // if we try one last ditch effort to find our data
// Hopefully by this point we've found it and can go about our business
if (dataFound) {
nsCOMPtr<nsISupports> genericDataWrapper;
if (flavorStr.EqualsLiteral(kFileMime)) {
// we have a file path in |data|. Create an nsLocalFile object.
nsDependentString filepath(reinterpret_cast<char16_t*>(data));
nsCOMPtr<nsIFile> file;
if (NS_SUCCEEDED(NS_NewLocalFile(filepath, getter_AddRefs(file)))) {
genericDataWrapper = do_QueryInterface(file);
}
free(data);
} else if (flavorStr.EqualsLiteral(kNativeHTMLMime)) {
uint32_t dummy;
// the editor folks want CF_HTML exactly as it's on the clipboard, no
// conversions, no fancy stuff. Pull it off the clipboard, stuff it into
// a wrapper and hand it back to them.
if (FindPlatformHTML(aDataObject, anIndex, &data, &dummy, &dataLen)) {
nsPrimitiveHelpers::CreatePrimitiveForData(
flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper));
} else {
free(data);
continue; // something wrong with this flavor, keep looking for other
// data
}
free(data);
} else if (flavorStr.EqualsLiteral(kHTMLMime)) {
uint32_t startOfData = 0;
// The JS folks want CF_HTML exactly as it is on the clipboard, but
// minus the CF_HTML header index information.
// It also needs to be converted to UTF16 and have linebreaks changed.
if (FindPlatformHTML(aDataObject, anIndex, &data, &startOfData,
&dataLen)) {
dataLen -= startOfData;
nsPrimitiveHelpers::CreatePrimitiveForCFHTML(
static_cast<char*>(data) + startOfData, &dataLen,
getter_AddRefs(genericDataWrapper));
} else {
free(data);
continue; // something wrong with this flavor, keep looking for other
// data
}
free(data);
} else if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
flavorStr.EqualsLiteral(kJPGImageMime) ||
flavorStr.EqualsLiteral(kPNGImageMime)) {
nsIInputStream* imageStream = reinterpret_cast<nsIInputStream*>(data);
genericDataWrapper = do_QueryInterface(imageStream);
NS_IF_RELEASE(imageStream);
} else {
// Treat custom types as a string of bytes.
if (!flavorStr.EqualsLiteral(kCustomTypesMime)) {
bool isRTF = flavorStr.EqualsLiteral(kRTFMime);
// we probably have some form of text. The DOM only wants LF, so
// convert from Win32 line endings to DOM line endings.
int32_t signedLen = static_cast<int32_t>(dataLen);
nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(isRTF, &data,
&signedLen);
dataLen = signedLen;
if (isRTF) {
// RTF on Windows is known to sometimes deliver an extra null byte.
if (dataLen > 0 && static_cast<char*>(data)[dataLen - 1] == '\0') {
dataLen--;
}
}
}
nsPrimitiveHelpers::CreatePrimitiveForData(
flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper));
free(data);
}
NS_ASSERTION(genericDataWrapper,
"About to put null data into the transferable");
aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
res = NS_OK;
// we found one, get out of the loop
break;
}
} // foreach flavor
return NS_OK;
return res;
}
//
@@ -1384,24 +1382,24 @@ bool nsClipboard ::IsInternetShortcut(const nsAString& inFileName) {
} // IsInternetShortcut
//-------------------------------------------------------------------------
mozilla::Result<nsCOMPtr<nsISupports>, nsresult>
nsClipboard::GetNativeClipboardData(const nsACString& aFlavor,
NS_IMETHODIMP
nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) {
MOZ_DIAGNOSTIC_ASSERT(aTransferable);
MOZ_DIAGNOSTIC_ASSERT(
nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
MOZ_CLIPBOARD_LOG("%s aWhichClipboard=%i", __FUNCTION__, aWhichClipboard);
nsresult res;
// This makes sure we can use the OLE functionality for the clipboard
IDataObject* dataObj;
if (S_OK == RepeatedlyTryOleGetClipboard(&dataObj)) {
auto dataObjRelease = mozilla::MakeScopeExit([&] { dataObj->Release(); });
// Use OLE IDataObject for clipboard operations
MOZ_CLIPBOARD_LOG(" use OLE IDataObject:");
if (MOZ_CLIPBOARD_LOG_ENABLED()) {
IEnumFORMATETC* pEnum = nullptr;
if (S_OK == dataObj->EnumFormatEtc(DATADIR_GET, &pEnum)) {
auto pEnumRelease = mozilla::MakeScopeExit([&] { pEnum->Release(); });
FORMATETC fEtc;
while (S_OK == pEnum->Next(1, &fEtc, nullptr)) {
nsAutoString format;
@@ -1411,13 +1409,16 @@ nsClipboard::GetNativeClipboardData(const nsACString& aFlavor,
NS_ConvertUTF16toUTF8(format).get());
}
}
pEnum->Release();
}
return GetDataFromDataObject(dataObj, 0, nullptr, PromiseFlatCString(aFlavor));
res = GetDataFromDataObject(dataObj, 0, nullptr, aTransferable);
dataObj->Release();
} else {
// do it the old manual way
res = GetDataFromDataObject(nullptr, 0, mWindow, aTransferable);
}
// do it the old manual way
return GetDataFromDataObject(nullptr, 0, mWindow, PromiseFlatCString(aFlavor));
return res;
}
nsresult nsClipboard::EmptyNativeClipboardData(ClipboardType aWhichClipboard) {

View File

@@ -22,7 +22,7 @@ struct IDataObject;
* Native Win32 Clipboard wrapper
*/
class nsClipboard final : public nsBaseClipboard, public nsIObserver {
class nsClipboard : public nsBaseClipboard, public nsIObserver {
virtual ~nsClipboard();
public:
@@ -41,9 +41,6 @@ class nsClipboard final : public nsBaseClipboard, public nsIObserver {
static nsresult SetupNativeDataObject(nsITransferable* aTransferable,
IDataObject* aDataObj,
MightNeedToFlush* = nullptr);
static mozilla::Result<nsCOMPtr<nsISupports>, nsresult> GetDataFromDataObject(
IDataObject* aDataObject, UINT anIndex, nsIWidget* aWindow,
const nsCString& aFlavor);
static nsresult GetDataFromDataObject(IDataObject* aDataObject, UINT anIndex,
nsIWidget* aWindow,
nsITransferable* aTransferable);
@@ -81,8 +78,8 @@ class nsClipboard final : public nsBaseClipboard, public nsIObserver {
// Implement the native clipboard behavior.
NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
mozilla::Result<nsCOMPtr<nsISupports>, nsresult> GetNativeClipboardData(
const nsACString& aFlavor, ClipboardType aWhichClipboard) override;
NS_IMETHOD GetNativeClipboardData(nsITransferable* aTransferable,
ClipboardType aWhichClipboard) override;
nsresult EmptyNativeClipboardData(ClipboardType aWhichClipboard) override;
mozilla::Result<bool, nsresult> HasNativeClipboardDataMatchingFlavors(
const nsTArray<nsCString>& aFlavorList,