|
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/. */ |
|
5 |
|
6 #ifdef MOZ_LOGGING |
|
7 #define FORCE_PR_LOG |
|
8 #endif |
|
9 #include "prlog.h" |
|
10 |
|
11 #include "gfxPlatform.h" |
|
12 #include "nsCOMPtr.h" |
|
13 #include "nsClipboard.h" |
|
14 #include "nsString.h" |
|
15 #include "nsISupportsPrimitives.h" |
|
16 #include "nsXPIDLString.h" |
|
17 #include "nsPrimitiveHelpers.h" |
|
18 #include "nsMemory.h" |
|
19 #include "nsIFile.h" |
|
20 #include "nsStringStream.h" |
|
21 #include "nsDragService.h" |
|
22 #include "nsEscape.h" |
|
23 #include "nsPrintfCString.h" |
|
24 #include "nsObjCExceptions.h" |
|
25 #include "imgIContainer.h" |
|
26 #include "nsCocoaUtils.h" |
|
27 |
|
28 using mozilla::gfx::DataSourceSurface; |
|
29 using mozilla::gfx::SourceSurface; |
|
30 using mozilla::RefPtr; |
|
31 |
|
32 // Screenshots use the (undocumented) png pasteboard type. |
|
33 #define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil |
|
34 |
|
35 #ifdef PR_LOGGING |
|
36 extern PRLogModuleInfo* sCocoaLog; |
|
37 #endif |
|
38 |
|
39 extern void EnsureLogInitialized(); |
|
40 |
|
41 nsClipboard::nsClipboard() : nsBaseClipboard() |
|
42 { |
|
43 mCachedClipboard = -1; |
|
44 mChangeCount = 0; |
|
45 |
|
46 EnsureLogInitialized(); |
|
47 } |
|
48 |
|
49 nsClipboard::~nsClipboard() |
|
50 { |
|
51 } |
|
52 |
|
53 // We separate this into its own function because after an @try, all local |
|
54 // variables within that function get marked as volatile, and our C++ type |
|
55 // system doesn't like volatile things. |
|
56 static NSData* |
|
57 GetDataFromPasteboard(NSPasteboard* aPasteboard, NSString* aType) |
|
58 { |
|
59 NSData *data = nil; |
|
60 @try { |
|
61 data = [aPasteboard dataForType:aType]; |
|
62 } @catch (NSException* e) { |
|
63 NS_WARNING(nsPrintfCString("Exception raised while getting data from the pasteboard: \"%s - %s\"", |
|
64 [[e name] UTF8String], [[e reason] UTF8String]).get()); |
|
65 } |
|
66 return data; |
|
67 } |
|
68 |
|
69 NS_IMETHODIMP |
|
70 nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard) |
|
71 { |
|
72 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
73 |
|
74 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !mTransferable) |
|
75 return NS_ERROR_FAILURE; |
|
76 |
|
77 mIgnoreEmptyNotification = true; |
|
78 |
|
79 NSDictionary* pasteboardOutputDict = PasteboardDictFromTransferable(mTransferable); |
|
80 if (!pasteboardOutputDict) |
|
81 return NS_ERROR_FAILURE; |
|
82 |
|
83 unsigned int outputCount = [pasteboardOutputDict count]; |
|
84 NSArray* outputKeys = [pasteboardOutputDict allKeys]; |
|
85 NSPasteboard* cocoaPasteboard; |
|
86 if (aWhichClipboard == kFindClipboard) { |
|
87 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; |
|
88 [cocoaPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; |
|
89 } else { |
|
90 // Write everything else out to the general pasteboard. |
|
91 cocoaPasteboard = [NSPasteboard generalPasteboard]; |
|
92 [cocoaPasteboard declareTypes:outputKeys owner:nil]; |
|
93 } |
|
94 |
|
95 for (unsigned int i = 0; i < outputCount; i++) { |
|
96 NSString* currentKey = [outputKeys objectAtIndex:i]; |
|
97 id currentValue = [pasteboardOutputDict valueForKey:currentKey]; |
|
98 if (aWhichClipboard == kFindClipboard) { |
|
99 if (currentKey == NSStringPboardType) |
|
100 [cocoaPasteboard setString:currentValue forType:currentKey]; |
|
101 } else { |
|
102 if (currentKey == NSStringPboardType || |
|
103 currentKey == kCorePboardType_url || |
|
104 currentKey == kCorePboardType_urld || |
|
105 currentKey == kCorePboardType_urln) { |
|
106 [cocoaPasteboard setString:currentValue forType:currentKey]; |
|
107 } else if (currentKey == NSHTMLPboardType) { |
|
108 [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) |
|
109 forType:currentKey]; |
|
110 } else { |
|
111 [cocoaPasteboard setData:currentValue forType:currentKey]; |
|
112 } |
|
113 } |
|
114 } |
|
115 |
|
116 mCachedClipboard = aWhichClipboard; |
|
117 mChangeCount = [cocoaPasteboard changeCount]; |
|
118 |
|
119 mIgnoreEmptyNotification = false; |
|
120 |
|
121 return NS_OK; |
|
122 |
|
123 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
124 } |
|
125 |
|
126 nsresult |
|
127 nsClipboard::TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteboard *cocoaPasteboard) |
|
128 { |
|
129 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
130 |
|
131 // get flavor list that includes all acceptable flavors (including ones obtained through conversion) |
|
132 nsCOMPtr<nsISupportsArray> flavorList; |
|
133 nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); |
|
134 if (NS_FAILED(rv)) |
|
135 return NS_ERROR_FAILURE; |
|
136 |
|
137 uint32_t flavorCount; |
|
138 flavorList->Count(&flavorCount); |
|
139 |
|
140 for (uint32_t i = 0; i < flavorCount; i++) { |
|
141 nsCOMPtr<nsISupports> genericFlavor; |
|
142 flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); |
|
143 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor)); |
|
144 if (!currentFlavor) |
|
145 continue; |
|
146 |
|
147 nsXPIDLCString flavorStr; |
|
148 currentFlavor->ToString(getter_Copies(flavorStr)); // i has a flavr |
|
149 |
|
150 // printf("looking for clipboard data of type %s\n", flavorStr.get()); |
|
151 |
|
152 NSString *pboardType = nil; |
|
153 if (nsClipboard::IsStringType(flavorStr, &pboardType)) { |
|
154 NSString* pString = [cocoaPasteboard stringForType:pboardType]; |
|
155 if (!pString) |
|
156 continue; |
|
157 |
|
158 NSData* stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; |
|
159 unsigned int dataLength = [stringData length]; |
|
160 void* clipboardDataPtr = malloc(dataLength); |
|
161 if (!clipboardDataPtr) |
|
162 return NS_ERROR_OUT_OF_MEMORY; |
|
163 [stringData getBytes:clipboardDataPtr]; |
|
164 |
|
165 // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. |
|
166 int32_t signedDataLength = dataLength; |
|
167 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength); |
|
168 dataLength = signedDataLength; |
|
169 |
|
170 // skip BOM (Byte Order Mark to distinguish little or big endian) |
|
171 char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr; |
|
172 if ((dataLength > 2) && |
|
173 ((clipboardDataPtrNoBOM[0] == 0xFEFF) || |
|
174 (clipboardDataPtrNoBOM[0] == 0xFFFE))) { |
|
175 dataLength -= sizeof(char16_t); |
|
176 clipboardDataPtrNoBOM += 1; |
|
177 } |
|
178 |
|
179 nsCOMPtr<nsISupports> genericDataWrapper; |
|
180 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, |
|
181 getter_AddRefs(genericDataWrapper)); |
|
182 aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); |
|
183 free(clipboardDataPtr); |
|
184 break; |
|
185 } |
|
186 else if (flavorStr.EqualsLiteral(kJPEGImageMime) || |
|
187 flavorStr.EqualsLiteral(kJPGImageMime) || |
|
188 flavorStr.EqualsLiteral(kPNGImageMime) || |
|
189 flavorStr.EqualsLiteral(kGIFImageMime)) { |
|
190 // Figure out if there's data on the pasteboard we can grab (sanity check) |
|
191 NSString *type = [cocoaPasteboard availableTypeFromArray:[NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; |
|
192 if (!type) |
|
193 continue; |
|
194 |
|
195 // Read data off the clipboard |
|
196 NSData *pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type); |
|
197 if (!pasteboardData) |
|
198 continue; |
|
199 |
|
200 // Figure out what type we're converting to |
|
201 CFStringRef outputType = NULL; |
|
202 if (flavorStr.EqualsLiteral(kJPEGImageMime) || |
|
203 flavorStr.EqualsLiteral(kJPGImageMime)) |
|
204 outputType = CFSTR("public.jpeg"); |
|
205 else if (flavorStr.EqualsLiteral(kPNGImageMime)) |
|
206 outputType = CFSTR("public.png"); |
|
207 else if (flavorStr.EqualsLiteral(kGIFImageMime)) |
|
208 outputType = CFSTR("com.compuserve.gif"); |
|
209 else |
|
210 continue; |
|
211 |
|
212 // Use ImageIO to interpret the data on the clipboard and transcode. |
|
213 // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely |
|
214 // and safely in most cases (like ObjC). A notable exception is CFRelease. |
|
215 NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: |
|
216 (NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat, |
|
217 (type == NSTIFFPboardType ? @"public.tiff" : @"public.png"), |
|
218 kCGImageSourceTypeIdentifierHint, nil]; |
|
219 |
|
220 CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, |
|
221 (CFDictionaryRef)options); |
|
222 NSMutableData *encodedData = [NSMutableData data]; |
|
223 CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData, |
|
224 outputType, |
|
225 1, NULL); |
|
226 CGImageDestinationAddImageFromSource(dest, source, 0, NULL); |
|
227 bool successfullyConverted = CGImageDestinationFinalize(dest); |
|
228 |
|
229 if (successfullyConverted) { |
|
230 // Put the converted data in a form Gecko can understand |
|
231 nsCOMPtr<nsIInputStream> byteStream; |
|
232 NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)[encodedData bytes], |
|
233 [encodedData length], NS_ASSIGNMENT_COPY); |
|
234 |
|
235 aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); |
|
236 } |
|
237 |
|
238 if (dest) |
|
239 CFRelease(dest); |
|
240 if (source) |
|
241 CFRelease(source); |
|
242 |
|
243 if (successfullyConverted) |
|
244 break; |
|
245 else |
|
246 continue; |
|
247 } |
|
248 } |
|
249 |
|
250 return NS_OK; |
|
251 |
|
252 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
253 } |
|
254 |
|
255 NS_IMETHODIMP |
|
256 nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, int32_t aWhichClipboard) |
|
257 { |
|
258 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
259 |
|
260 if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !aTransferable) |
|
261 return NS_ERROR_FAILURE; |
|
262 |
|
263 NSPasteboard* cocoaPasteboard; |
|
264 if (aWhichClipboard == kFindClipboard) { |
|
265 cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; |
|
266 } else { |
|
267 cocoaPasteboard = [NSPasteboard generalPasteboard]; |
|
268 } |
|
269 if (!cocoaPasteboard) |
|
270 return NS_ERROR_FAILURE; |
|
271 |
|
272 // get flavor list that includes all acceptable flavors (including ones obtained through conversion) |
|
273 nsCOMPtr<nsISupportsArray> flavorList; |
|
274 nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); |
|
275 if (NS_FAILED(rv)) |
|
276 return NS_ERROR_FAILURE; |
|
277 |
|
278 uint32_t flavorCount; |
|
279 flavorList->Count(&flavorCount); |
|
280 |
|
281 // If we were the last ones to put something on the pasteboard, then just use the cached |
|
282 // transferable. Otherwise clear it because it isn't relevant any more. |
|
283 if (mCachedClipboard == aWhichClipboard && |
|
284 mChangeCount == [cocoaPasteboard changeCount]) { |
|
285 if (mTransferable) { |
|
286 for (uint32_t i = 0; i < flavorCount; i++) { |
|
287 nsCOMPtr<nsISupports> genericFlavor; |
|
288 flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); |
|
289 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor)); |
|
290 if (!currentFlavor) |
|
291 continue; |
|
292 |
|
293 nsXPIDLCString flavorStr; |
|
294 currentFlavor->ToString(getter_Copies(flavorStr)); |
|
295 |
|
296 nsCOMPtr<nsISupports> dataSupports; |
|
297 uint32_t dataSize = 0; |
|
298 rv = mTransferable->GetTransferData(flavorStr, getter_AddRefs(dataSupports), &dataSize); |
|
299 if (NS_SUCCEEDED(rv)) { |
|
300 aTransferable->SetTransferData(flavorStr, dataSupports, dataSize); |
|
301 return NS_OK; // maybe try to fill in more types? Is there a point? |
|
302 } |
|
303 } |
|
304 } |
|
305 } else { |
|
306 nsBaseClipboard::EmptyClipboard(aWhichClipboard); |
|
307 } |
|
308 |
|
309 // at this point we can't satisfy the request from cache data so let's look |
|
310 // for things other people put on the system clipboard |
|
311 |
|
312 return nsClipboard::TransferableFromPasteboard(aTransferable, cocoaPasteboard); |
|
313 |
|
314 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
315 } |
|
316 |
|
317 // returns true if we have *any* of the passed in flavors available for pasting |
|
318 NS_IMETHODIMP |
|
319 nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength, |
|
320 int32_t aWhichClipboard, bool* outResult) |
|
321 { |
|
322 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
323 |
|
324 *outResult = false; |
|
325 |
|
326 if ((aWhichClipboard != kGlobalClipboard) || !aFlavorList) |
|
327 return NS_OK; |
|
328 |
|
329 // first see if we have data for this in our cached transferable |
|
330 if (mTransferable) { |
|
331 nsCOMPtr<nsISupportsArray> transferableFlavorList; |
|
332 nsresult rv = mTransferable->FlavorsTransferableCanImport(getter_AddRefs(transferableFlavorList)); |
|
333 if (NS_SUCCEEDED(rv)) { |
|
334 uint32_t transferableFlavorCount; |
|
335 transferableFlavorList->Count(&transferableFlavorCount); |
|
336 for (uint32_t j = 0; j < transferableFlavorCount; j++) { |
|
337 nsCOMPtr<nsISupports> transferableFlavorSupports; |
|
338 transferableFlavorList->GetElementAt(j, getter_AddRefs(transferableFlavorSupports)); |
|
339 nsCOMPtr<nsISupportsCString> currentTransferableFlavor(do_QueryInterface(transferableFlavorSupports)); |
|
340 if (!currentTransferableFlavor) |
|
341 continue; |
|
342 nsXPIDLCString transferableFlavorStr; |
|
343 currentTransferableFlavor->ToString(getter_Copies(transferableFlavorStr)); |
|
344 |
|
345 for (uint32_t k = 0; k < aLength; k++) { |
|
346 if (transferableFlavorStr.Equals(aFlavorList[k])) { |
|
347 *outResult = true; |
|
348 return NS_OK; |
|
349 } |
|
350 } |
|
351 } |
|
352 } |
|
353 } |
|
354 |
|
355 NSPasteboard* generalPBoard = [NSPasteboard generalPasteboard]; |
|
356 |
|
357 for (uint32_t i = 0; i < aLength; i++) { |
|
358 nsDependentCString mimeType(aFlavorList[i]); |
|
359 NSString *pboardType = nil; |
|
360 |
|
361 if (nsClipboard::IsStringType(mimeType, &pboardType)) { |
|
362 NSString* availableType = [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:pboardType]]; |
|
363 if (availableType && [availableType isEqualToString:pboardType]) { |
|
364 *outResult = true; |
|
365 break; |
|
366 } |
|
367 } else if (!strcmp(aFlavorList[i], kJPEGImageMime) || |
|
368 !strcmp(aFlavorList[i], kJPGImageMime) || |
|
369 !strcmp(aFlavorList[i], kPNGImageMime) || |
|
370 !strcmp(aFlavorList[i], kGIFImageMime)) { |
|
371 NSString* availableType = [generalPBoard availableTypeFromArray: |
|
372 [NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; |
|
373 if (availableType) { |
|
374 *outResult = true; |
|
375 break; |
|
376 } |
|
377 } |
|
378 } |
|
379 |
|
380 return NS_OK; |
|
381 |
|
382 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
383 } |
|
384 |
|
385 NS_IMETHODIMP |
|
386 nsClipboard::SupportsFindClipboard(bool *_retval) |
|
387 { |
|
388 NS_ENSURE_ARG_POINTER(_retval); |
|
389 *_retval = true; |
|
390 return NS_OK; |
|
391 } |
|
392 |
|
393 // This function converts anything that other applications might understand into the system format |
|
394 // and puts it into a dictionary which it returns. |
|
395 // static |
|
396 NSDictionary* |
|
397 nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) |
|
398 { |
|
399 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
|
400 |
|
401 if (!aTransferable) |
|
402 return nil; |
|
403 |
|
404 NSMutableDictionary* pasteboardOutputDict = [NSMutableDictionary dictionary]; |
|
405 |
|
406 nsCOMPtr<nsISupportsArray> flavorList; |
|
407 nsresult rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); |
|
408 if (NS_FAILED(rv)) |
|
409 return nil; |
|
410 |
|
411 uint32_t flavorCount; |
|
412 flavorList->Count(&flavorCount); |
|
413 for (uint32_t i = 0; i < flavorCount; i++) { |
|
414 nsCOMPtr<nsISupports> genericFlavor; |
|
415 flavorList->GetElementAt(i, getter_AddRefs(genericFlavor)); |
|
416 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor)); |
|
417 if (!currentFlavor) |
|
418 continue; |
|
419 |
|
420 nsXPIDLCString flavorStr; |
|
421 currentFlavor->ToString(getter_Copies(flavorStr)); |
|
422 |
|
423 PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("writing out clipboard data of type %s (%d)\n", flavorStr.get(), i)); |
|
424 |
|
425 NSString *pboardType = nil; |
|
426 |
|
427 if (nsClipboard::IsStringType(flavorStr, &pboardType)) { |
|
428 void* data = nullptr; |
|
429 uint32_t dataSize = 0; |
|
430 nsCOMPtr<nsISupports> genericDataWrapper; |
|
431 rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize); |
|
432 nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); |
|
433 |
|
434 NSString* nativeString; |
|
435 if (data) |
|
436 nativeString = [NSString stringWithCharacters:(const unichar*)data length:(dataSize / sizeof(char16_t))]; |
|
437 else |
|
438 nativeString = [NSString string]; |
|
439 |
|
440 // be nice to Carbon apps, normalize the receiver's contents using Form C. |
|
441 nativeString = [nativeString precomposedStringWithCanonicalMapping]; |
|
442 |
|
443 [pasteboardOutputDict setObject:nativeString forKey:pboardType]; |
|
444 |
|
445 nsMemory::Free(data); |
|
446 } |
|
447 else if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || |
|
448 flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime) || |
|
449 flavorStr.EqualsLiteral(kNativeImageMime)) { |
|
450 uint32_t dataSize = 0; |
|
451 nsCOMPtr<nsISupports> transferSupports; |
|
452 aTransferable->GetTransferData(flavorStr, getter_AddRefs(transferSupports), &dataSize); |
|
453 nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(transferSupports)); |
|
454 if (!ptrPrimitive) |
|
455 continue; |
|
456 |
|
457 nsCOMPtr<nsISupports> primitiveData; |
|
458 ptrPrimitive->GetData(getter_AddRefs(primitiveData)); |
|
459 |
|
460 nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData)); |
|
461 if (!image) { |
|
462 NS_WARNING("Image isn't an imgIContainer in transferable"); |
|
463 continue; |
|
464 } |
|
465 |
|
466 RefPtr<SourceSurface> surface = |
|
467 image->GetFrame(imgIContainer::FRAME_CURRENT, |
|
468 imgIContainer::FLAG_SYNC_DECODE); |
|
469 if (!surface) { |
|
470 continue; |
|
471 } |
|
472 CGImageRef imageRef = NULL; |
|
473 nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef); |
|
474 if (NS_FAILED(rv) || !imageRef) { |
|
475 continue; |
|
476 } |
|
477 |
|
478 // Convert the CGImageRef to TIFF data. |
|
479 CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0); |
|
480 CGImageDestinationRef destRef = CGImageDestinationCreateWithData(tiffData, |
|
481 CFSTR("public.tiff"), |
|
482 1, |
|
483 NULL); |
|
484 CGImageDestinationAddImage(destRef, imageRef, NULL); |
|
485 bool successfullyConverted = CGImageDestinationFinalize(destRef); |
|
486 |
|
487 CGImageRelease(imageRef); |
|
488 if (destRef) |
|
489 CFRelease(destRef); |
|
490 |
|
491 if (!successfullyConverted) { |
|
492 if (tiffData) |
|
493 CFRelease(tiffData); |
|
494 continue; |
|
495 } |
|
496 |
|
497 [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType]; |
|
498 if (tiffData) |
|
499 CFRelease(tiffData); |
|
500 } |
|
501 else if (flavorStr.EqualsLiteral(kFileMime)) { |
|
502 uint32_t len = 0; |
|
503 nsCOMPtr<nsISupports> genericFile; |
|
504 rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericFile), &len); |
|
505 if (NS_FAILED(rv)) { |
|
506 continue; |
|
507 } |
|
508 |
|
509 nsCOMPtr<nsIFile> file(do_QueryInterface(genericFile)); |
|
510 if (!file) { |
|
511 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericFile)); |
|
512 |
|
513 if (ptr) { |
|
514 ptr->GetData(getter_AddRefs(genericFile)); |
|
515 file = do_QueryInterface(genericFile); |
|
516 } |
|
517 } |
|
518 |
|
519 if (!file) { |
|
520 continue; |
|
521 } |
|
522 |
|
523 nsAutoString fileURI; |
|
524 rv = file->GetPath(fileURI); |
|
525 if (NS_FAILED(rv)) { |
|
526 continue; |
|
527 } |
|
528 |
|
529 NSString* str = nsCocoaUtils::ToNSString(fileURI); |
|
530 NSArray* fileList = [NSArray arrayWithObjects:str, nil]; |
|
531 [pasteboardOutputDict setObject:fileList forKey:NSFilenamesPboardType]; |
|
532 } |
|
533 else if (flavorStr.EqualsLiteral(kFilePromiseMime)) { |
|
534 [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:NSFilesPromisePboardType]; |
|
535 } |
|
536 else if (flavorStr.EqualsLiteral(kURLMime)) { |
|
537 uint32_t len = 0; |
|
538 nsCOMPtr<nsISupports> genericURL; |
|
539 rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericURL), &len); |
|
540 nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL)); |
|
541 |
|
542 nsAutoString url; |
|
543 urlObject->GetData(url); |
|
544 |
|
545 // A newline embedded in the URL means that the form is actually URL + title. |
|
546 int32_t newlinePos = url.FindChar(char16_t('\n')); |
|
547 if (newlinePos >= 0) { |
|
548 url.Truncate(newlinePos); |
|
549 |
|
550 nsAutoString urlTitle; |
|
551 urlObject->GetData(urlTitle); |
|
552 urlTitle.Mid(urlTitle, newlinePos + 1, len - (newlinePos + 1)); |
|
553 |
|
554 NSString *nativeTitle = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(urlTitle.get()) |
|
555 length:urlTitle.Length()]; |
|
556 // be nice to Carbon apps, normalize the receiver's contents using Form C. |
|
557 [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urln]; |
|
558 // Also put the title out as 'urld', since some recipients will look for that. |
|
559 [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urld]; |
|
560 [nativeTitle release]; |
|
561 } |
|
562 |
|
563 // The Finder doesn't like getting random binary data aka |
|
564 // Unicode, so change it into an escaped URL containing only |
|
565 // ASCII. |
|
566 nsAutoCString utf8Data = NS_ConvertUTF16toUTF8(url.get(), url.Length()); |
|
567 nsAutoCString escData; |
|
568 NS_EscapeURL(utf8Data.get(), utf8Data.Length(), esc_OnlyNonASCII|esc_AlwaysCopy, escData); |
|
569 |
|
570 // printf("Escaped url is %s, length %d\n", escData.get(), escData.Length()); |
|
571 |
|
572 NSString *nativeURL = [NSString stringWithUTF8String:escData.get()]; |
|
573 [pasteboardOutputDict setObject:nativeURL forKey:kCorePboardType_url]; |
|
574 } |
|
575 // If it wasn't a type that we recognize as exportable we don't put it on the system |
|
576 // clipboard. We'll just access it from our cached transferable when we need it. |
|
577 } |
|
578 |
|
579 return pasteboardOutputDict; |
|
580 |
|
581 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
|
582 } |
|
583 |
|
584 bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType) |
|
585 { |
|
586 if (aMIMEType.EqualsLiteral(kUnicodeMime) || |
|
587 aMIMEType.EqualsLiteral(kHTMLMime)) { |
|
588 if (aMIMEType.EqualsLiteral(kUnicodeMime)) |
|
589 *aPasteboardType = NSStringPboardType; |
|
590 else |
|
591 *aPasteboardType = NSHTMLPboardType; |
|
592 return true; |
|
593 } else { |
|
594 return false; |
|
595 } |
|
596 } |
|
597 |
|
598 NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString) |
|
599 { |
|
600 NSString* wrapped = |
|
601 [NSString stringWithFormat: |
|
602 @"<html>" |
|
603 "<head>" |
|
604 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">" |
|
605 "</head>" |
|
606 "<body>" |
|
607 "%@" |
|
608 "</body>" |
|
609 "</html>", aString]; |
|
610 return wrapped; |
|
611 } |