1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/gfx/2D.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/Unused.h"
12 #include "gfxPlatform.h"
13 #include "nsArrayUtils.h"
15 #include "nsClipboard.h"
17 #include "nsISupportsPrimitives.h"
18 #include "nsPrimitiveHelpers.h"
20 #include "nsStringStream.h"
22 #include "nsPrintfCString.h"
23 #include "nsObjCExceptions.h"
24 #include "imgIContainer.h"
25 #include "nsCocoaUtils.h"
27 using mozilla::gfx::DataSourceSurface;
28 using mozilla::gfx::SourceSurface;
30 mozilla::StaticRefPtr<nsITransferable> nsClipboard::sSelectionCache;
31 int32_t nsClipboard::sSelectionCacheChangeCount = 0;
33 nsClipboard::nsClipboard()
34 : nsBaseClipboard(mozilla::dom::ClipboardCapabilities(
35 false /* supportsSelectionClipboard */,
36 true /* supportsFindClipboard */,
37 true /* supportsSelectionCache */)) {}
39 nsClipboard::~nsClipboard() { ClearSelectionCache(); }
41 NS_IMPL_ISUPPORTS_INHERITED0(nsClipboard, nsBaseClipboard)
45 // We separate this into its own function because after an @try, all local
46 // variables within that function get marked as volatile, and our C++ type
47 // system doesn't like volatile things.
48 static NSData* GetDataFromPasteboard(NSPasteboard* aPasteboard,
52 data = [aPasteboard dataForType:aType];
53 } @catch (NSException* e) {
54 NS_WARNING(nsPrintfCString("Exception raised while getting data from the "
55 "pasteboard: \"%s - %s\"",
56 [[e name] UTF8String], [[e reason] UTF8String])
63 static NSPasteboard* GetPasteboard(int32_t aWhichClipboard) {
64 switch (aWhichClipboard) {
65 case nsIClipboard::kGlobalClipboard:
66 return [NSPasteboard generalPasteboard];
67 case nsIClipboard::kFindClipboard:
68 return [NSPasteboard pasteboardWithName:NSPasteboardNameFind];
76 void nsClipboard::SetSelectionCache(nsITransferable* aTransferable) {
77 sSelectionCacheChangeCount++;
78 sSelectionCache = aTransferable;
81 void nsClipboard::ClearSelectionCache() { SetSelectionCache(nullptr); }
84 nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable,
85 int32_t aWhichClipboard) {
86 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
88 MOZ_ASSERT(aTransferable);
89 MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
91 if (aWhichClipboard == kSelectionCache) {
92 SetSelectionCache(aTransferable);
96 NSDictionary* pasteboardOutputDict =
97 PasteboardDictFromTransferable(aTransferable);
98 if (!pasteboardOutputDict) return NS_ERROR_FAILURE;
100 unsigned int outputCount = [pasteboardOutputDict count];
101 NSArray* outputKeys = [pasteboardOutputDict allKeys];
102 NSPasteboard* cocoaPasteboard = GetPasteboard(aWhichClipboard);
103 MOZ_ASSERT(cocoaPasteboard);
104 if (aWhichClipboard == kFindClipboard) {
105 NSString* stringType =
106 [UTIHelper stringFromPboardType:NSPasteboardTypeString];
107 [cocoaPasteboard declareTypes:[NSArray arrayWithObject:stringType]
110 // Write everything else out to the general pasteboard.
111 MOZ_ASSERT(aWhichClipboard == kGlobalClipboard);
112 [cocoaPasteboard declareTypes:outputKeys owner:nil];
115 for (unsigned int i = 0; i < outputCount; i++) {
116 NSString* currentKey = [outputKeys objectAtIndex:i];
117 id currentValue = [pasteboardOutputDict valueForKey:currentKey];
118 if (aWhichClipboard == kFindClipboard) {
119 if ([currentKey isEqualToString:[UTIHelper stringFromPboardType:
120 NSPasteboardTypeString]]) {
121 [cocoaPasteboard setString:currentValue forType:currentKey];
124 if ([currentKey isEqualToString:[UTIHelper stringFromPboardType:
125 NSPasteboardTypeString]] ||
127 isEqualToString:[UTIHelper
128 stringFromPboardType:kPublicUrlPboardType]] ||
131 [UTIHelper stringFromPboardType:kPublicUrlNamePboardType]]) {
132 [cocoaPasteboard setString:currentValue forType:currentKey];
133 } else if ([currentKey
136 stringFromPboardType:kUrlsWithTitlesPboardType]]) {
138 setPropertyList:[pasteboardOutputDict valueForKey:currentKey]
140 } else if ([currentKey
141 isEqualToString:[UTIHelper stringFromPboardType:
142 NSPasteboardTypeHTML]]) {
144 setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
146 } else if ([currentKey
147 isEqualToString:[UTIHelper stringFromPboardType:
148 kMozFileUrlsPboardType]]) {
149 [cocoaPasteboard writeObjects:currentValue];
150 } else if ([currentKey
153 stringFromPboardType:(NSString*)kUTTypeFileURL]]) {
154 [cocoaPasteboard setString:currentValue forType:currentKey];
155 } else if ([currentKey
158 stringFromPboardType:kPasteboardConcealedType]]) {
159 // It's fine to set the data to null for this field - this field is an
160 // addition to a value's other type and works like a flag.
161 [cocoaPasteboard setData:NULL forType:currentKey];
163 [cocoaPasteboard setData:currentValue forType:currentKey];
170 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
173 nsresult nsClipboard::TransferableFromPasteboard(
174 nsITransferable* aTransferable, NSPasteboard* cocoaPasteboard) {
175 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
177 // get flavor list that includes all acceptable flavors (including ones
178 // obtained through conversion)
179 nsTArray<nsCString> flavors;
180 nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
181 if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
183 for (uint32_t i = 0; i < flavors.Length(); i++) {
184 nsCString& flavorStr = flavors[i];
186 // printf("looking for clipboard data of type %s\n", flavorStr.get());
188 NSString* pboardType = nil;
189 if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
190 NSString* pString = [cocoaPasteboard stringForType:pboardType];
196 bool isRTF = [pboardType
197 isEqualToString:[UTIHelper stringFromPboardType:NSPasteboardTypeRTF]];
199 stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
201 stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
203 unsigned int dataLength = [stringData length];
204 void* clipboardDataPtr = malloc(dataLength);
205 if (!clipboardDataPtr) {
206 return NS_ERROR_OUT_OF_MEMORY;
208 [stringData getBytes:clipboardDataPtr length:dataLength];
210 // The DOM only wants LF, so convert from MacOS line endings to DOM line
212 int32_t signedDataLength = dataLength;
213 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(
214 isRTF, &clipboardDataPtr, &signedDataLength);
215 dataLength = signedDataLength;
217 // skip BOM (Byte Order Mark to distinguish little or big endian)
218 char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr;
219 if ((dataLength > 2) && ((clipboardDataPtrNoBOM[0] == 0xFEFF) ||
220 (clipboardDataPtrNoBOM[0] == 0xFFFE))) {
221 dataLength -= sizeof(char16_t);
222 clipboardDataPtrNoBOM += 1;
225 nsCOMPtr<nsISupports> genericDataWrapper;
226 nsPrimitiveHelpers::CreatePrimitiveForData(
227 flavorStr, clipboardDataPtrNoBOM, dataLength,
228 getter_AddRefs(genericDataWrapper));
229 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
230 free(clipboardDataPtr);
232 } else if (flavorStr.EqualsLiteral(kFileMime)) {
233 NSArray* items = [cocoaPasteboard pasteboardItems];
234 if (!items || [items count] <= 0) {
238 // XXX we don't support multiple clipboard item on DOM and XPCOM interface
239 // for now, so we only get the data from the first pasteboard item.
240 NSPasteboardItem* item = [items objectAtIndex:0];
245 nsCocoaUtils::SetTransferDataForTypeFromPasteboardItem(aTransferable,
247 } else if (flavorStr.EqualsLiteral(kCustomTypesMime)) {
248 NSString* type = [cocoaPasteboard
249 availableTypeFromArray:
251 arrayWithObject:[UTIHelper stringFromPboardType:
252 kMozCustomTypesPboardType]]];
257 NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type);
258 if (!pasteboardData) {
262 unsigned int dataLength = [pasteboardData length];
263 void* clipboardDataPtr = malloc(dataLength);
264 if (!clipboardDataPtr) {
265 return NS_ERROR_OUT_OF_MEMORY;
267 [pasteboardData getBytes:clipboardDataPtr length:dataLength];
269 nsCOMPtr<nsISupports> genericDataWrapper;
270 nsPrimitiveHelpers::CreatePrimitiveForData(
271 flavorStr, clipboardDataPtr, dataLength,
272 getter_AddRefs(genericDataWrapper));
274 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
275 free(clipboardDataPtr);
276 } else if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
277 flavorStr.EqualsLiteral(kJPGImageMime) ||
278 flavorStr.EqualsLiteral(kPNGImageMime) ||
279 flavorStr.EqualsLiteral(kGIFImageMime)) {
280 // Figure out if there's data on the pasteboard we can grab (sanity check)
281 NSString* type = [cocoaPasteboard
282 availableTypeFromArray:
286 stringFromPboardType:(NSString*)kUTTypeFileURL],
287 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF],
288 [UTIHelper stringFromPboardType:NSPasteboardTypePNG],
292 // Read data off the clipboard
293 NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type);
294 if (!pasteboardData) continue;
296 // Figure out what type we're converting to
297 CFStringRef outputType = NULL;
298 if (flavorStr.EqualsLiteral(kJPEGImageMime) ||
299 flavorStr.EqualsLiteral(kJPGImageMime))
300 outputType = CFSTR("public.jpeg");
301 else if (flavorStr.EqualsLiteral(kPNGImageMime))
302 outputType = CFSTR("public.png");
303 else if (flavorStr.EqualsLiteral(kGIFImageMime))
304 outputType = CFSTR("com.compuserve.gif");
308 // Use ImageIO to interpret the data on the clipboard and transcode.
309 // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely
310 // and safely in most cases (like ObjC). A notable exception is CFRelease.
311 NSDictionary* options = [NSDictionary
312 dictionaryWithObjectsAndKeys:(NSNumber*)kCFBooleanTrue,
313 kCGImageSourceShouldAllowFloat, type,
314 kCGImageSourceTypeIdentifierHint, nil];
315 CGImageSourceRef source = nullptr;
316 if (type == [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL]) {
317 NSString* urlStr = [cocoaPasteboard stringForType:type];
318 NSURL* url = [NSURL URLWithString:urlStr];
320 CGImageSourceCreateWithURL((CFURLRef)url, (CFDictionaryRef)options);
322 source = CGImageSourceCreateWithData((CFDataRef)pasteboardData,
323 (CFDictionaryRef)options);
326 NSMutableData* encodedData = [NSMutableData data];
327 CGImageDestinationRef dest = CGImageDestinationCreateWithData(
328 (CFMutableDataRef)encodedData, outputType, 1, NULL);
329 CGImageDestinationAddImageFromSource(dest, source, 0, NULL);
330 bool successfullyConverted = CGImageDestinationFinalize(dest);
332 if (successfullyConverted) {
333 // Put the converted data in a form Gecko can understand
334 nsCOMPtr<nsIInputStream> byteStream;
335 NS_NewByteInputStream(getter_AddRefs(byteStream),
336 mozilla::Span((const char*)[encodedData bytes],
337 [encodedData length]),
340 aTransferable->SetTransferData(flavorStr.get(), byteStream);
343 if (dest) CFRelease(dest);
344 if (source) CFRelease(source);
346 if (successfullyConverted) {
347 // XXX Maybe try to fill in more types? Is there a point?
357 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
361 nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable,
362 int32_t aWhichClipboard) {
363 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
365 MOZ_DIAGNOSTIC_ASSERT(aTransferable);
366 MOZ_DIAGNOSTIC_ASSERT(
367 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
369 if (kSelectionCache == aWhichClipboard) {
370 if (!sSelectionCache) {
374 // get flavor list that includes all acceptable flavors (including ones
375 // obtained through conversion)
376 nsTArray<nsCString> flavors;
377 nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
379 return NS_ERROR_FAILURE;
382 for (const auto& flavor : flavors) {
383 nsCOMPtr<nsISupports> dataSupports;
384 rv = sSelectionCache->GetTransferData(flavor.get(),
385 getter_AddRefs(dataSupports));
386 if (NS_SUCCEEDED(rv)) {
387 MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__,
389 aTransferable->SetTransferData(flavor.get(), dataSupports);
390 // XXX Maybe try to fill in more types? Is there a point?
397 NSPasteboard* cocoaPasteboard = GetPasteboard(aWhichClipboard);
398 if (!cocoaPasteboard) {
399 return NS_ERROR_FAILURE;
402 return TransferableFromPasteboard(aTransferable, cocoaPasteboard);
404 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
407 // returns true if we have *any* of the passed in flavors available for pasting
408 mozilla::Result<bool, nsresult>
409 nsClipboard::HasNativeClipboardDataMatchingFlavors(
410 const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) {
411 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
413 MOZ_DIAGNOSTIC_ASSERT(
414 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
416 if (kSelectionCache == aWhichClipboard) {
417 nsTArray<nsCString> transferableFlavors;
418 if (sSelectionCache &&
419 NS_SUCCEEDED(sSelectionCache->FlavorsTransferableCanExport(
420 transferableFlavors))) {
421 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
422 MOZ_CLIPBOARD_LOG(" SelectionCache types (nums %zu)\n",
423 transferableFlavors.Length());
424 for (const auto& transferableFlavor : transferableFlavors) {
425 MOZ_CLIPBOARD_LOG(" MIME %s", transferableFlavor.get());
429 for (const auto& transferableFlavor : transferableFlavors) {
430 for (const auto& flavor : aFlavorList) {
431 if (transferableFlavor.Equals(flavor)) {
432 MOZ_CLIPBOARD_LOG(" has %s", flavor.get());
439 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
440 MOZ_CLIPBOARD_LOG(" no targets at clipboard (bad match)\n");
446 NSPasteboard* cocoaPasteboard = GetPasteboard(aWhichClipboard);
447 MOZ_ASSERT(cocoaPasteboard);
448 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
449 NSArray* types = [cocoaPasteboard types];
450 uint32_t count = [types count];
451 MOZ_CLIPBOARD_LOG(" Pasteboard types (nums %d)\n", count);
452 for (uint32_t i = 0; i < count; i++) {
453 NSPasteboardType type = [types objectAtIndex:i];
455 MOZ_CLIPBOARD_LOG(" failed to get MIME\n");
458 MOZ_CLIPBOARD_LOG(" MIME %s\n", [type UTF8String]);
462 for (auto& mimeType : aFlavorList) {
463 NSString* pboardType = nil;
464 if (nsClipboard::IsStringType(mimeType, &pboardType)) {
465 NSString* availableType = [cocoaPasteboard
466 availableTypeFromArray:[NSArray arrayWithObject:pboardType]];
467 if (availableType && [availableType isEqualToString:pboardType]) {
468 MOZ_CLIPBOARD_LOG(" has %s\n", mimeType.get());
471 } else if (mimeType.EqualsLiteral(kCustomTypesMime)) {
472 NSString* availableType = [cocoaPasteboard
473 availableTypeFromArray:
475 arrayWithObject:[UTIHelper stringFromPboardType:
476 kMozCustomTypesPboardType]]];
478 MOZ_CLIPBOARD_LOG(" has %s\n", mimeType.get());
481 } else if (mimeType.EqualsLiteral(kJPEGImageMime) ||
482 mimeType.EqualsLiteral(kJPGImageMime) ||
483 mimeType.EqualsLiteral(kPNGImageMime) ||
484 mimeType.EqualsLiteral(kGIFImageMime)) {
485 NSString* availableType = [cocoaPasteboard
486 availableTypeFromArray:
489 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF],
490 [UTIHelper stringFromPboardType:NSPasteboardTypePNG],
493 MOZ_CLIPBOARD_LOG(" has %s\n", mimeType.get());
496 } else if (mimeType.EqualsLiteral(kFileMime)) {
497 NSArray* items = [cocoaPasteboard pasteboardItems];
498 if (items && [items count] > 0) {
499 // XXX we only check the first pasteboard item as we only get data from
500 // first item in TransferableFromPasteboard for now.
501 if (NSPasteboardItem* item = [items objectAtIndex:0]) {
502 if ([item availableTypeFromArray:
504 arrayWithObjects:[UTIHelper
505 stringFromPboardType:
506 (NSString*)kUTTypeFileURL],
508 MOZ_CLIPBOARD_LOG(" has %s\n", mimeType.get());
516 if (MOZ_CLIPBOARD_LOG_ENABLED()) {
517 MOZ_CLIPBOARD_LOG(" no targets at clipboard (bad match)\n");
522 NS_OBJC_END_TRY_BLOCK_RETURN(mozilla::Err(NS_ERROR_FAILURE));
526 mozilla::Maybe<uint32_t> nsClipboard::FindIndexOfImageFlavor(
527 const nsTArray<nsCString>& aMIMETypes) {
528 for (uint32_t i = 0; i < aMIMETypes.Length(); ++i) {
529 if (nsClipboard::IsImageType(aMIMETypes[i])) {
530 return mozilla::Some(i);
534 return mozilla::Nothing();
537 // This function converts anything that other applications might understand into
538 // the system format and puts it into a dictionary which it returns. static
539 NSDictionary* nsClipboard::PasteboardDictFromTransferable(
540 nsITransferable* aTransferable) {
541 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
543 if (!aTransferable) {
547 NSMutableDictionary* pasteboardOutputDict = [NSMutableDictionary dictionary];
549 nsTArray<nsCString> flavors;
550 nsresult rv = aTransferable->FlavorsTransferableCanExport(flavors);
555 const mozilla::Maybe<uint32_t> imageFlavorIndex =
556 nsClipboard::FindIndexOfImageFlavor(flavors);
558 if (imageFlavorIndex) {
559 // When right-clicking and "Copy Image" is clicked on macOS, some apps
560 // expect the first flavor to be the image flavor. See bug 1689992. For
561 // other apps, the order shouldn't matter.
562 std::swap(*flavors.begin(), flavors[*imageFlavorIndex]);
564 for (uint32_t i = 0; i < flavors.Length(); i++) {
565 nsCString& flavorStr = flavors[i];
567 MOZ_CLIPBOARD_LOG("writing out clipboard data of type %s (%d)\n",
570 NSString* pboardType = nil;
571 if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
572 nsCOMPtr<nsISupports> genericDataWrapper;
573 rv = aTransferable->GetTransferData(flavorStr.get(),
574 getter_AddRefs(genericDataWrapper));
580 if (nsCOMPtr<nsISupportsString> text =
581 do_QueryInterface(genericDataWrapper)) {
585 NSString* nativeString;
586 if (!data.IsEmpty()) {
587 nativeString = [NSString stringWithCharacters:(const unichar*)data.get()
588 length:data.Length()];
590 nativeString = [NSString string];
593 // be nice to Carbon apps, normalize the receiver's contents using Form C.
594 nativeString = [nativeString precomposedStringWithCanonicalMapping];
596 [pasteboardOutputDict setObject:nativeString forKey:pboardType];
599 if (aTransferable->GetIsPrivateData()) {
600 // In the case of password strings, we want to include the key for
601 // concealed type. These will be flagged as private data.
602 [pasteboardOutputDict
603 setObject:nativeString
605 stringFromPboardType:kPasteboardConcealedType]];
607 } else if (flavorStr.EqualsLiteral(kCustomTypesMime)) {
608 nsCOMPtr<nsISupports> genericDataWrapper;
609 rv = aTransferable->GetTransferData(flavorStr.get(),
610 getter_AddRefs(genericDataWrapper));
616 if (nsCOMPtr<nsISupportsCString> text =
617 do_QueryInterface(genericDataWrapper)) {
621 if (!data.IsEmpty()) {
622 NSData* nativeData = [NSData dataWithBytes:data.get()
623 length:data.Length()];
624 NSString* customType =
625 [UTIHelper stringFromPboardType:kMozCustomTypesPboardType];
629 [pasteboardOutputDict setObject:nativeData forKey:customType];
631 } else if (nsClipboard::IsImageType(flavorStr)) {
632 nsCOMPtr<nsISupports> transferSupports;
633 rv = aTransferable->GetTransferData(flavorStr.get(),
634 getter_AddRefs(transferSupports));
639 nsCOMPtr<imgIContainer> image(do_QueryInterface(transferSupports));
641 NS_WARNING("Image isn't an imgIContainer in transferable");
645 RefPtr<SourceSurface> surface = image->GetFrame(
646 imgIContainer::FRAME_CURRENT,
647 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
651 CGImageRef imageRef = NULL;
652 rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef);
653 if (NS_FAILED(rv) || !imageRef) {
657 // Convert the CGImageRef to TIFF and PNG data.
658 CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0);
659 CFMutableDataRef pngData = CFDataCreateMutable(kCFAllocatorDefault, 0);
660 CGImageDestinationRef destRefTIFF = CGImageDestinationCreateWithData(
661 tiffData, CFSTR("public.tiff"), 1, NULL);
662 CGImageDestinationRef destRefPNG = CGImageDestinationCreateWithData(
663 pngData, CFSTR("public.png"), 1, NULL);
664 CGImageDestinationAddImage(destRefTIFF, imageRef, NULL);
665 CGImageDestinationAddImage(destRefPNG, imageRef, NULL);
666 const bool successfullyConvertedTIFF =
667 CGImageDestinationFinalize(destRefTIFF);
668 const bool successfullyConvertedPNG =
669 CGImageDestinationFinalize(destRefPNG);
671 CGImageRelease(imageRef);
673 CFRelease(destRefTIFF);
676 CFRelease(destRefPNG);
679 if (successfullyConvertedTIFF && tiffData) {
681 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF];
682 [pasteboardOutputDict setObject:(NSMutableData*)tiffData
685 if (successfullyConvertedPNG && pngData) {
687 [UTIHelper stringFromPboardType:NSPasteboardTypePNG];
689 [pasteboardOutputDict setObject:(NSMutableData*)pngData forKey:pngType];
697 } else if (flavorStr.EqualsLiteral(kFileMime)) {
698 nsCOMPtr<nsISupports> genericFile;
699 rv = aTransferable->GetTransferData(flavorStr.get(),
700 getter_AddRefs(genericFile));
705 nsCOMPtr<nsIFile> file(do_QueryInterface(genericFile));
710 nsAutoString fileURI;
711 rv = file->GetPath(fileURI);
716 NSString* str = nsCocoaUtils::ToNSString(fileURI);
717 NSURL* url = [NSURL fileURLWithPath:str isDirectory:NO];
718 if (!url || ![url absoluteString]) {
721 NSString* fileUTType =
722 [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL];
723 [pasteboardOutputDict setObject:[url absoluteString] forKey:fileUTType];
724 } else if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
725 NSString* urlPromise = [UTIHelper
726 stringFromPboardType:(NSString*)kPasteboardTypeFileURLPromise];
727 NSString* urlPromiseContent = [UTIHelper
728 stringFromPboardType:(NSString*)kPasteboardTypeFilePromiseContent];
729 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""]
731 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""]
732 forKey:urlPromiseContent];
733 } else if (flavorStr.EqualsLiteral(kURLMime)) {
734 nsCOMPtr<nsISupports> genericURL;
735 rv = aTransferable->GetTransferData(flavorStr.get(),
736 getter_AddRefs(genericURL));
737 nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL));
740 urlObject->GetData(url);
742 NSString* nativeTitle = nil;
744 // A newline embedded in the URL means that the form is actually URL +
745 // title. This embedding occurs in nsDragService::GetData.
746 int32_t newlinePos = url.FindChar(char16_t('\n'));
747 if (newlinePos >= 0) {
748 url.Truncate(newlinePos);
750 nsAutoString urlTitle;
751 urlObject->GetData(urlTitle);
752 urlTitle.Mid(urlTitle, newlinePos + 1,
753 urlTitle.Length() - (newlinePos + 1));
756 [NSString stringWithCharacters:reinterpret_cast<const unichar*>(
758 length:urlTitle.Length()];
760 // The Finder doesn't like getting random binary data aka
761 // Unicode, so change it into an escaped URL containing only
763 nsAutoCString utf8Data = NS_ConvertUTF16toUTF8(url.get(), url.Length());
764 nsAutoCString escData;
765 NS_EscapeURL(utf8Data.get(), utf8Data.Length(),
766 esc_OnlyNonASCII | esc_AlwaysCopy, escData);
768 NSString* nativeURL = [NSString stringWithUTF8String:escData.get()];
769 NSString* publicUrl =
770 [UTIHelper stringFromPboardType:kPublicUrlPboardType];
774 [pasteboardOutputDict setObject:nativeURL forKey:publicUrl];
776 NSArray* urlsAndTitles = @[ @[ nativeURL ], @[ nativeTitle ] ];
778 [UTIHelper stringFromPboardType:kPublicUrlNamePboardType];
779 NSString* urlsWithTitles =
780 [UTIHelper stringFromPboardType:kUrlsWithTitlesPboardType];
781 [pasteboardOutputDict setObject:nativeTitle forKey:urlName];
782 [pasteboardOutputDict setObject:urlsAndTitles forKey:urlsWithTitles];
785 // If it wasn't a type that we recognize as exportable we don't put it on
786 // the system clipboard. We'll just access it from our cached transferable
790 return pasteboardOutputDict;
792 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
795 bool nsClipboard::IsStringType(const nsCString& aMIMEType,
796 NSString** aPboardType) {
797 if (aMIMEType.EqualsLiteral(kTextMime)) {
798 *aPboardType = [UTIHelper stringFromPboardType:NSPasteboardTypeString];
800 } else if (aMIMEType.EqualsLiteral(kRTFMime)) {
801 *aPboardType = [UTIHelper stringFromPboardType:NSPasteboardTypeRTF];
803 } else if (aMIMEType.EqualsLiteral(kHTMLMime)) {
804 *aPboardType = [UTIHelper stringFromPboardType:NSPasteboardTypeHTML];
812 bool nsClipboard::IsImageType(const nsACString& aMIMEType) {
813 return aMIMEType.EqualsLiteral(kPNGImageMime) ||
814 aMIMEType.EqualsLiteral(kJPEGImageMime) ||
815 aMIMEType.EqualsLiteral(kJPGImageMime) ||
816 aMIMEType.EqualsLiteral(kGIFImageMime) ||
817 aMIMEType.EqualsLiteral(kNativeImageMime);
820 NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString) {
822 [NSString stringWithFormat:@"<html>"
824 "<meta http-equiv=\"content-type\" "
825 "content=\"text/html; charset=utf-8\">"
835 nsresult nsClipboard::EmptyNativeClipboardData(int32_t aWhichClipboard) {
836 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
838 MOZ_DIAGNOSTIC_ASSERT(
839 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
841 if (kSelectionCache == aWhichClipboard) {
842 ClearSelectionCache();
846 if (NSPasteboard* cocoaPasteboard = GetPasteboard(aWhichClipboard)) {
847 [cocoaPasteboard clearContents];
852 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
855 mozilla::Result<int32_t, nsresult>
856 nsClipboard::GetNativeClipboardSequenceNumber(int32_t aWhichClipboard) {
857 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
859 MOZ_DIAGNOSTIC_ASSERT(
860 nsIClipboard::IsClipboardTypeSupported(aWhichClipboard));
862 if (kSelectionCache == aWhichClipboard) {
863 return sSelectionCacheChangeCount;
866 NSPasteboard* cocoaPasteboard = GetPasteboard(aWhichClipboard);
867 if (!cocoaPasteboard) {
868 return mozilla::Err(NS_ERROR_FAILURE);
871 return [cocoaPasteboard changeCount];
873 NS_OBJC_END_TRY_BLOCK_RETURN(mozilla::Err(NS_ERROR_FAILURE));