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/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"
21 #include "nsStringStream.h"
22 #include "nsDragService.h"
24 #include "nsPrintfCString.h"
25 #include "nsObjCExceptions.h"
26 #include "imgIContainer.h"
27 #include "nsCocoaUtils.h"
29 using mozilla::gfx::DataSourceSurface;
30 using mozilla::gfx::SourceSurface;
31 using mozilla::LogLevel;
33 extern mozilla::LazyLogModule sCocoaLog;
35 mozilla::StaticRefPtr<nsITransferable> nsClipboard::sSelectionCache;
37 @implementation UTIHelper
39 + (NSString*)stringFromPboardType:(NSString*)aType {
40 if ([aType isEqualToString:kMozWildcardPboardType] ||
41 [aType isEqualToString:kMozCustomTypesPboardType] ||
42 [aType isEqualToString:kPublicUrlPboardType] ||
43 [aType isEqualToString:kPublicUrlNamePboardType] ||
44 [aType isEqualToString:kMozFileUrlsPboardType] ||
45 [aType isEqualToString:(NSString*)kPasteboardTypeFileURLPromise] ||
46 [aType isEqualToString:(NSString*)kPasteboardTypeFilePromiseContent] ||
47 [aType isEqualToString:(NSString*)kUTTypeFileURL] ||
48 [aType isEqualToString:NSStringPboardType] ||
49 [aType isEqualToString:NSPasteboardTypeString] ||
50 [aType isEqualToString:NSPasteboardTypeHTML] || [aType isEqualToString:NSPasteboardTypeRTF] ||
51 [aType isEqualToString:NSPasteboardTypeTIFF] || [aType isEqualToString:NSPasteboardTypePNG]) {
52 return [NSString stringWithString:aType];
54 NSString* dynamicType = (NSString*)UTTypeCreatePreferredIdentifierForTag(
55 kUTTagClassNSPboardType, (CFStringRef)aType, kUTTypeData);
56 NSString* result = [NSString stringWithString:dynamicType];
57 [dynamicType release];
63 nsClipboard::nsClipboard()
64 : mCachedClipboard(-1), mChangeCount(0), mIgnoreEmptyNotification(false) {}
66 nsClipboard::~nsClipboard() {
67 EmptyClipboard(kGlobalClipboard);
68 EmptyClipboard(kFindClipboard);
69 ClearSelectionCache();
72 NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
74 // We separate this into its own function because after an @try, all local
75 // variables within that function get marked as volatile, and our C++ type
76 // system doesn't like volatile things.
77 static NSData* GetDataFromPasteboard(NSPasteboard* aPasteboard, NSString* aType) {
80 data = [aPasteboard dataForType:aType];
81 } @catch (NSException* e) {
83 nsPrintfCString("Exception raised while getting data from the pasteboard: \"%s - %s\"",
84 [[e name] UTF8String], [[e reason] UTF8String])
91 void nsClipboard::SetSelectionCache(nsITransferable* aTransferable) {
92 sSelectionCache = aTransferable;
95 void nsClipboard::ClearSelectionCache() { sSelectionCache = nullptr; }
98 nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard) {
99 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
101 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !mTransferable)
102 return NS_ERROR_FAILURE;
104 mIgnoreEmptyNotification = true;
106 NSDictionary* pasteboardOutputDict = PasteboardDictFromTransferable(mTransferable);
107 if (!pasteboardOutputDict) return NS_ERROR_FAILURE;
109 unsigned int outputCount = [pasteboardOutputDict count];
110 NSArray* outputKeys = [pasteboardOutputDict allKeys];
111 NSPasteboard* cocoaPasteboard;
112 if (aWhichClipboard == kFindClipboard) {
113 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
114 NSString* stringType = [UTIHelper stringFromPboardType:NSPasteboardTypeString];
115 [cocoaPasteboard declareTypes:[NSArray arrayWithObject:stringType] owner:nil];
117 // Write everything else out to the general pasteboard.
118 cocoaPasteboard = [NSPasteboard generalPasteboard];
119 [cocoaPasteboard declareTypes:outputKeys owner:nil];
122 for (unsigned int i = 0; i < outputCount; i++) {
123 NSString* currentKey = [outputKeys objectAtIndex:i];
124 id currentValue = [pasteboardOutputDict valueForKey:currentKey];
125 if (aWhichClipboard == kFindClipboard) {
126 if ([currentKey isEqualToString:[UTIHelper stringFromPboardType:NSPasteboardTypeString]]) {
127 [cocoaPasteboard setString:currentValue forType:currentKey];
130 if ([currentKey isEqualToString:[UTIHelper stringFromPboardType:NSPasteboardTypeString]] ||
131 [currentKey isEqualToString:[UTIHelper stringFromPboardType:kPublicUrlPboardType]] ||
132 [currentKey isEqualToString:[UTIHelper stringFromPboardType:kPublicUrlNamePboardType]]) {
133 [cocoaPasteboard setString:currentValue forType:currentKey];
134 } else if ([currentKey
135 isEqualToString:[UTIHelper stringFromPboardType:kUrlsWithTitlesPboardType]]) {
136 [cocoaPasteboard setPropertyList:[pasteboardOutputDict valueForKey:currentKey]
138 } else if ([currentKey
139 isEqualToString:[UTIHelper stringFromPboardType:NSPasteboardTypeHTML]]) {
140 [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
142 } else if ([currentKey
143 isEqualToString:[UTIHelper stringFromPboardType:kMozFileUrlsPboardType]]) {
144 [cocoaPasteboard writeObjects:currentValue];
146 [cocoaPasteboard setData:currentValue forType:currentKey];
151 mCachedClipboard = aWhichClipboard;
152 mChangeCount = [cocoaPasteboard changeCount];
154 mIgnoreEmptyNotification = false;
158 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
161 nsresult nsClipboard::TransferableFromPasteboard(nsITransferable* aTransferable,
162 NSPasteboard* cocoaPasteboard) {
163 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
165 // get flavor list that includes all acceptable flavors (including ones obtained through
167 nsTArray<nsCString> flavors;
168 nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
169 if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
171 for (uint32_t i = 0; i < flavors.Length(); i++) {
172 nsCString& flavorStr = flavors[i];
174 // printf("looking for clipboard data of type %s\n", flavorStr.get());
176 NSString* pboardType = nil;
177 if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
178 NSString* pString = [cocoaPasteboard stringForType:pboardType];
180 [pboardType release];
185 if ([pboardType isEqualToString:[UTIHelper stringFromPboardType:NSPasteboardTypeRTF]]) {
186 stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
188 stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
190 unsigned int dataLength = [stringData length];
191 void* clipboardDataPtr = malloc(dataLength);
192 if (!clipboardDataPtr) {
193 [pboardType release];
194 return NS_ERROR_OUT_OF_MEMORY;
196 [stringData getBytes:clipboardDataPtr length:dataLength];
198 // The DOM only wants LF, so convert from MacOS line endings to DOM line endings.
199 int32_t signedDataLength = dataLength;
200 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr,
202 dataLength = signedDataLength;
204 // skip BOM (Byte Order Mark to distinguish little or big endian)
205 char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr;
206 if ((dataLength > 2) &&
207 ((clipboardDataPtrNoBOM[0] == 0xFEFF) || (clipboardDataPtrNoBOM[0] == 0xFFFE))) {
208 dataLength -= sizeof(char16_t);
209 clipboardDataPtrNoBOM += 1;
212 nsCOMPtr<nsISupports> genericDataWrapper;
213 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength,
214 getter_AddRefs(genericDataWrapper));
215 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
216 free(clipboardDataPtr);
217 [pboardType release];
219 } else if (flavorStr.EqualsLiteral(kCustomTypesMime)) {
220 NSString* type = [cocoaPasteboard
221 availableTypeFromArray:
222 [NSArray arrayWithObject:[UTIHelper stringFromPboardType:kMozCustomTypesPboardType]]];
227 NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type);
228 if (!pasteboardData) {
232 unsigned int dataLength = [pasteboardData length];
233 void* clipboardDataPtr = malloc(dataLength);
234 if (!clipboardDataPtr) {
235 return NS_ERROR_OUT_OF_MEMORY;
237 [pasteboardData getBytes:clipboardDataPtr length:dataLength];
239 nsCOMPtr<nsISupports> genericDataWrapper;
240 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength,
241 getter_AddRefs(genericDataWrapper));
243 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
244 free(clipboardDataPtr);
245 } else if (flavorStr.EqualsLiteral(kJPEGImageMime) || flavorStr.EqualsLiteral(kJPGImageMime) ||
246 flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime)) {
247 // Figure out if there's data on the pasteboard we can grab (sanity check)
248 NSString* type = [cocoaPasteboard
249 availableTypeFromArray:
250 [NSArray arrayWithObjects:[UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL],
251 [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF],
252 [UTIHelper stringFromPboardType:NSPasteboardTypePNG], nil]];
255 // Read data off the clipboard
256 NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type);
257 if (!pasteboardData) continue;
259 // Figure out what type we're converting to
260 CFStringRef outputType = NULL;
261 if (flavorStr.EqualsLiteral(kJPEGImageMime) || flavorStr.EqualsLiteral(kJPGImageMime))
262 outputType = CFSTR("public.jpeg");
263 else if (flavorStr.EqualsLiteral(kPNGImageMime))
264 outputType = CFSTR("public.png");
265 else if (flavorStr.EqualsLiteral(kGIFImageMime))
266 outputType = CFSTR("com.compuserve.gif");
270 // Use ImageIO to interpret the data on the clipboard and transcode.
271 // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely
272 // and safely in most cases (like ObjC). A notable exception is CFRelease.
273 NSDictionary* options = [NSDictionary
274 dictionaryWithObjectsAndKeys:(NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat,
275 type, kCGImageSourceTypeIdentifierHint, nil];
276 CGImageSourceRef source = nullptr;
277 if (type == [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL]) {
278 NSString* urlStr = [cocoaPasteboard stringForType:type];
279 NSURL* url = [NSURL URLWithString:urlStr];
280 source = CGImageSourceCreateWithURL((CFURLRef)url, (CFDictionaryRef)options);
282 source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, (CFDictionaryRef)options);
285 NSMutableData* encodedData = [NSMutableData data];
286 CGImageDestinationRef dest =
287 CGImageDestinationCreateWithData((CFMutableDataRef)encodedData, outputType, 1, NULL);
288 CGImageDestinationAddImageFromSource(dest, source, 0, NULL);
289 bool successfullyConverted = CGImageDestinationFinalize(dest);
291 if (successfullyConverted) {
292 // Put the converted data in a form Gecko can understand
293 nsCOMPtr<nsIInputStream> byteStream;
294 NS_NewByteInputStream(getter_AddRefs(byteStream),
295 mozilla::Span((const char*)[encodedData bytes], [encodedData length]),
298 aTransferable->SetTransferData(flavorStr.get(), byteStream);
301 if (dest) CFRelease(dest);
302 if (source) CFRelease(source);
304 if (successfullyConverted)
313 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
317 nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, int32_t aWhichClipboard) {
318 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
320 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !aTransferable)
321 return NS_ERROR_FAILURE;
323 NSPasteboard* cocoaPasteboard;
324 if (aWhichClipboard == kFindClipboard) {
325 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
327 cocoaPasteboard = [NSPasteboard generalPasteboard];
329 if (!cocoaPasteboard) return NS_ERROR_FAILURE;
331 // get flavor list that includes all acceptable flavors (including ones obtained through
333 nsTArray<nsCString> flavors;
334 nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
335 if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
337 // If we were the last ones to put something on the pasteboard, then just use the cached
338 // transferable. Otherwise clear it because it isn't relevant any more.
339 if (mCachedClipboard == aWhichClipboard && mChangeCount == [cocoaPasteboard changeCount]) {
341 for (uint32_t i = 0; i < flavors.Length(); i++) {
342 nsCString& flavorStr = flavors[i];
344 nsCOMPtr<nsISupports> dataSupports;
345 rv = mTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(dataSupports));
346 if (NS_SUCCEEDED(rv)) {
347 aTransferable->SetTransferData(flavorStr.get(), dataSupports);
348 return NS_OK; // maybe try to fill in more types? Is there a point?
353 EmptyClipboard(aWhichClipboard);
356 // at this point we can't satisfy the request from cache data so let's look
357 // for things other people put on the system clipboard
359 return nsClipboard::TransferableFromPasteboard(aTransferable, cocoaPasteboard);
361 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
364 // returns true if we have *any* of the passed in flavors available for pasting
366 nsClipboard::HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
368 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
372 if (aWhichClipboard != kGlobalClipboard) return NS_OK;
374 // first see if we have data for this in our cached transferable
376 nsTArray<nsCString> flavors;
377 nsresult rv = mTransferable->FlavorsTransferableCanImport(flavors);
378 if (NS_SUCCEEDED(rv)) {
379 for (uint32_t j = 0; j < flavors.Length(); j++) {
380 const nsCString& transferableFlavorStr = flavors[j];
382 for (uint32_t k = 0; k < aFlavorList.Length(); k++) {
383 if (transferableFlavorStr.Equals(aFlavorList[k])) {
392 NSPasteboard* generalPBoard = [NSPasteboard generalPasteboard];
394 for (auto& mimeType : aFlavorList) {
395 NSString* pboardType = nil;
396 if (nsClipboard::IsStringType(mimeType, &pboardType)) {
397 NSString* availableType =
398 [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:pboardType]];
399 if (availableType && [availableType isEqualToString:pboardType]) {
400 [pboardType release];
404 [pboardType release];
405 } else if (mimeType.EqualsLiteral(kCustomTypesMime)) {
406 NSString* availableType = [generalPBoard
407 availableTypeFromArray:
408 [NSArray arrayWithObject:[UTIHelper stringFromPboardType:kMozCustomTypesPboardType]]];
413 } else if (mimeType.EqualsLiteral(kJPEGImageMime) || mimeType.EqualsLiteral(kJPGImageMime) ||
414 mimeType.EqualsLiteral(kPNGImageMime) || mimeType.EqualsLiteral(kGIFImageMime)) {
415 NSString* availableType = [generalPBoard
416 availableTypeFromArray:
417 [NSArray arrayWithObjects:[UTIHelper stringFromPboardType:NSPasteboardTypeTIFF],
418 [UTIHelper stringFromPboardType:NSPasteboardTypePNG], nil]];
428 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
432 nsClipboard::SupportsFindClipboard(bool* _retval) {
433 NS_ENSURE_ARG_POINTER(_retval);
439 mozilla::Maybe<uint32_t> nsClipboard::FindIndexOfImageFlavor(
440 const nsTArray<nsCString>& aMIMETypes) {
441 for (uint32_t i = 0; i < aMIMETypes.Length(); ++i) {
442 if (nsClipboard::IsImageType(aMIMETypes[i])) {
443 return mozilla::Some(i);
447 return mozilla::Nothing();
450 // This function converts anything that other applications might understand into the system format
451 // and puts it into a dictionary which it returns.
453 NSDictionary* nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) {
454 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
456 if (!aTransferable) {
460 NSMutableDictionary* pasteboardOutputDict = [NSMutableDictionary dictionary];
462 nsTArray<nsCString> flavors;
463 nsresult rv = aTransferable->FlavorsTransferableCanExport(flavors);
468 const mozilla::Maybe<uint32_t> imageFlavorIndex = nsClipboard::FindIndexOfImageFlavor(flavors);
470 if (imageFlavorIndex) {
471 // When right-clicking and "Copy Image" is clicked on macOS, some apps expect the
472 // first flavor to be the image flavor. See bug 1689992. For other apps, the
473 // order shouldn't matter.
474 std::swap(*flavors.begin(), flavors[*imageFlavorIndex]);
476 for (uint32_t i = 0; i < flavors.Length(); i++) {
477 nsCString& flavorStr = flavors[i];
479 MOZ_LOG(sCocoaLog, LogLevel::Info,
480 ("writing out clipboard data of type %s (%d)\n", flavorStr.get(), i));
482 NSString* pboardType = nil;
483 if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
484 nsCOMPtr<nsISupports> genericDataWrapper;
485 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericDataWrapper));
487 [pboardType release];
492 if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(genericDataWrapper)) {
496 NSString* nativeString;
497 if (!data.IsEmpty()) {
498 nativeString = [NSString stringWithCharacters:(const unichar*)data.get()
499 length:data.Length()];
501 nativeString = [NSString string];
504 // be nice to Carbon apps, normalize the receiver's contents using Form C.
505 nativeString = [nativeString precomposedStringWithCanonicalMapping];
507 [pasteboardOutputDict setObject:nativeString forKey:pboardType];
509 [pboardType release];
510 } else if (flavorStr.EqualsLiteral(kCustomTypesMime)) {
511 nsCOMPtr<nsISupports> genericDataWrapper;
512 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericDataWrapper));
518 if (nsCOMPtr<nsISupportsCString> text = do_QueryInterface(genericDataWrapper)) {
522 if (!data.IsEmpty()) {
523 NSData* nativeData = [NSData dataWithBytes:data.get() length:data.Length()];
524 NSString* customType = [UTIHelper stringFromPboardType:kMozCustomTypesPboardType];
528 [pasteboardOutputDict setObject:nativeData forKey:customType];
530 } else if (nsClipboard::IsImageType(flavorStr)) {
531 nsCOMPtr<nsISupports> transferSupports;
532 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(transferSupports));
537 nsCOMPtr<imgIContainer> image(do_QueryInterface(transferSupports));
539 NS_WARNING("Image isn't an imgIContainer in transferable");
543 RefPtr<SourceSurface> surface =
544 image->GetFrame(imgIContainer::FRAME_CURRENT,
545 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
549 CGImageRef imageRef = NULL;
550 rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef);
551 if (NS_FAILED(rv) || !imageRef) {
555 // Convert the CGImageRef to TIFF data.
556 CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0);
557 CGImageDestinationRef destRef =
558 CGImageDestinationCreateWithData(tiffData, CFSTR("public.tiff"), 1, NULL);
559 CGImageDestinationAddImage(destRef, imageRef, NULL);
560 bool successfullyConverted = CGImageDestinationFinalize(destRef);
562 CGImageRelease(imageRef);
567 if (!successfullyConverted || !tiffData) {
574 NSString* tiffType = [UTIHelper stringFromPboardType:NSPasteboardTypeTIFF];
575 [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:tiffType];
577 } else if (flavorStr.EqualsLiteral(kFileMime)) {
578 nsCOMPtr<nsISupports> genericFile;
579 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericFile));
584 nsCOMPtr<nsIFile> file(do_QueryInterface(genericFile));
589 nsAutoString fileURI;
590 rv = file->GetPath(fileURI);
595 NSString* str = nsCocoaUtils::ToNSString(fileURI);
596 NSURL* url = [NSURL fileURLWithPath:str isDirectory:NO];
597 NSString* fileUTType = [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL];
598 if (!url || ![url absoluteString]) {
601 [pasteboardOutputDict setObject:[url absoluteString] forKey:fileUTType];
602 } else if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
603 NSString* urlPromise =
604 [UTIHelper stringFromPboardType:(NSString*)kPasteboardTypeFileURLPromise];
605 NSString* urlPromiseContent =
606 [UTIHelper stringFromPboardType:(NSString*)kPasteboardTypeFilePromiseContent];
607 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:urlPromise];
608 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:urlPromiseContent];
609 } else if (flavorStr.EqualsLiteral(kURLMime)) {
610 nsCOMPtr<nsISupports> genericURL;
611 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(genericURL));
612 nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL));
615 urlObject->GetData(url);
617 NSString* nativeTitle = nil;
619 // A newline embedded in the URL means that the form is actually URL +
620 // title. This embedding occurs in nsDragService::GetData.
621 int32_t newlinePos = url.FindChar(char16_t('\n'));
622 if (newlinePos >= 0) {
623 url.Truncate(newlinePos);
625 nsAutoString urlTitle;
626 urlObject->GetData(urlTitle);
627 urlTitle.Mid(urlTitle, newlinePos + 1, urlTitle.Length() - (newlinePos + 1));
630 [NSString stringWithCharacters:reinterpret_cast<const unichar*>(urlTitle.get())
631 length:urlTitle.Length()];
633 // The Finder doesn't like getting random binary data aka
634 // Unicode, so change it into an escaped URL containing only
636 nsAutoCString utf8Data = NS_ConvertUTF16toUTF8(url.get(), url.Length());
637 nsAutoCString escData;
638 NS_EscapeURL(utf8Data.get(), utf8Data.Length(), esc_OnlyNonASCII | esc_AlwaysCopy, escData);
640 NSString* nativeURL = [NSString stringWithUTF8String:escData.get()];
641 NSString* publicUrl = [UTIHelper stringFromPboardType:kPublicUrlPboardType];
645 [pasteboardOutputDict setObject:nativeURL forKey:publicUrl];
647 NSArray* urlsAndTitles = @[ @[ nativeURL ], @[ nativeTitle ] ];
648 NSString* urlName = [UTIHelper stringFromPboardType:kPublicUrlNamePboardType];
649 NSString* urlsWithTitles = [UTIHelper stringFromPboardType:kUrlsWithTitlesPboardType];
650 [pasteboardOutputDict setObject:nativeTitle forKey:urlName];
651 [pasteboardOutputDict setObject:urlsAndTitles forKey:urlsWithTitles];
654 // If it wasn't a type that we recognize as exportable we don't put it on the system
655 // clipboard. We'll just access it from our cached transferable when we need it.
658 return pasteboardOutputDict;
660 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
663 // aPasteboardType is being retained and needs to be released by the caller.
664 bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType) {
665 if (aMIMEType.EqualsLiteral(kUnicodeMime)) {
666 *aPasteboardType = [[UTIHelper stringFromPboardType:NSPasteboardTypeString] retain];
668 } else if (aMIMEType.EqualsLiteral(kRTFMime)) {
669 *aPasteboardType = [[UTIHelper stringFromPboardType:NSPasteboardTypeRTF] retain];
671 } else if (aMIMEType.EqualsLiteral(kHTMLMime)) {
672 *aPasteboardType = [[UTIHelper stringFromPboardType:NSPasteboardTypeHTML] retain];
680 bool nsClipboard::IsImageType(const nsACString& aMIMEType) {
681 return aMIMEType.EqualsLiteral(kPNGImageMime) || aMIMEType.EqualsLiteral(kJPEGImageMime) ||
682 aMIMEType.EqualsLiteral(kJPGImageMime) || aMIMEType.EqualsLiteral(kGIFImageMime) ||
683 aMIMEType.EqualsLiteral(kNativeImageMime);
686 NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString) {
687 NSString* wrapped = [NSString
688 stringWithFormat:@"<html>"
690 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"
701 * Sets the transferable object
705 nsClipboard::SetData(nsITransferable* aTransferable, nsIClipboardOwner* anOwner,
706 int32_t aWhichClipboard) {
707 NS_ASSERTION(aTransferable, "clipboard given a null transferable");
709 if (aWhichClipboard == kSelectionCache) {
711 SetSelectionCache(aTransferable);
714 return NS_ERROR_FAILURE;
717 if (aTransferable == mTransferable && anOwner == mClipboardOwner) {
720 bool selectClipPresent;
721 SupportsSelectionClipboard(&selectClipPresent);
722 bool findClipPresent;
723 SupportsFindClipboard(&findClipPresent);
724 if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) {
725 return NS_ERROR_FAILURE;
728 EmptyClipboard(aWhichClipboard);
730 mClipboardOwner = anOwner;
731 mTransferable = aTransferable;
733 nsresult rv = NS_ERROR_FAILURE;
735 rv = SetNativeClipboardData(aWhichClipboard);
742 * Gets the transferable object
746 nsClipboard::GetData(nsITransferable* aTransferable, int32_t aWhichClipboard) {
747 NS_ASSERTION(aTransferable, "clipboard given a null transferable");
749 bool selectClipPresent;
750 SupportsSelectionClipboard(&selectClipPresent);
751 bool findClipPresent;
752 SupportsFindClipboard(&findClipPresent);
753 if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard)
754 return NS_ERROR_FAILURE;
757 return GetNativeClipboardData(aTransferable, aWhichClipboard);
760 return NS_ERROR_FAILURE;
764 nsClipboard::EmptyClipboard(int32_t aWhichClipboard) {
765 if (aWhichClipboard == kSelectionCache) {
766 ClearSelectionCache();
770 bool selectClipPresent;
771 SupportsSelectionClipboard(&selectClipPresent);
772 bool findClipPresent;
773 SupportsFindClipboard(&findClipPresent);
774 if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) {
775 return NS_ERROR_FAILURE;
778 if (mIgnoreEmptyNotification) {
782 if (mClipboardOwner) {
783 mClipboardOwner->LosingOwnership(mTransferable);
784 mClipboardOwner = nullptr;
787 mTransferable = nullptr;
792 nsClipboard::SupportsSelectionClipboard(bool* _retval) {
793 *_retval = false; // we don't support the selection clipboard by default.