widget/qt/nsClipboard.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:165fd992910d
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include <QGuiApplication>
6 #include <QMimeData>
7 #include <QString>
8 #include <QStringList>
9 #include <QByteArray>
10 #include <QImage>
11 #include <QImageWriter>
12 #include <QBuffer>
13
14 #include "gfxPlatform.h"
15 #include "mozilla/ArrayUtils.h"
16 #include "mozilla/gfx/2D.h"
17
18 #include "nsClipboard.h"
19 #include "nsISupportsPrimitives.h"
20 #include "nsXPIDLString.h"
21 #include "nsPrimitiveHelpers.h"
22 #include "nsIInputStream.h"
23 #include "nsReadableUtils.h"
24 #include "nsStringStream.h"
25 #include "nsComponentManagerUtils.h"
26
27 #include "imgIContainer.h"
28 #include "gfxImageSurface.h"
29
30 using namespace mozilla;
31 using namespace mozilla::gfx;
32
33 NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
34
35 //-------------------------------------------------------------------------
36 //
37 // nsClipboard constructor
38 //
39 //-------------------------------------------------------------------------
40 nsClipboard::nsClipboard() : nsIClipboard(),
41 mSelectionOwner(nullptr),
42 mGlobalOwner(nullptr),
43 mSelectionTransferable(nullptr),
44 mGlobalTransferable(nullptr)
45 {
46 // No implementation needed
47 }
48
49 //-------------------------------------------------------------------------
50 //
51 // nsClipboard destructor
52 //
53 //-------------------------------------------------------------------------
54 nsClipboard::~nsClipboard()
55 {
56 }
57
58 static inline QImage::Format
59 _moz2dformat_to_qformat(SurfaceFormat aFormat)
60 {
61 switch (aFormat) {
62 case SurfaceFormat::B8G8R8A8:
63 return QImage::Format_ARGB32_Premultiplied;
64 case SurfaceFormat::B8G8R8X8:
65 return QImage::Format_ARGB32;
66 case SurfaceFormat::R5G6B5:
67 return QImage::Format_RGB16;
68 default:
69 return QImage::Format_Invalid;
70 }
71 }
72
73 // nsClipboard::SetNativeClipboardData ie. Copy
74
75 NS_IMETHODIMP
76 nsClipboard::SetNativeClipboardData( nsITransferable *aTransferable,
77 QClipboard::Mode clipboardMode )
78 {
79 if (nullptr == aTransferable)
80 {
81 NS_WARNING("nsClipboard::SetNativeClipboardData(): no transferable!");
82 return NS_ERROR_FAILURE;
83 }
84
85 // get flavor list that includes all flavors that can be written (including
86 // ones obtained through conversion)
87 nsCOMPtr<nsISupportsArray> flavorList;
88 nsresult rv = aTransferable->FlavorsTransferableCanExport( getter_AddRefs(flavorList) );
89
90 if (NS_FAILED(rv))
91 {
92 NS_WARNING("nsClipboard::SetNativeClipboardData(): no FlavorsTransferable !");
93 return NS_ERROR_FAILURE;
94 }
95
96 QClipboard *cb = QGuiApplication::clipboard();
97 QMimeData *mimeData = new QMimeData;
98
99 uint32_t flavorCount = 0;
100 flavorList->Count(&flavorCount);
101 bool imageAdded = false;
102
103 for (uint32_t i = 0; i < flavorCount; ++i)
104 {
105 nsCOMPtr<nsISupports> genericFlavor;
106 flavorList->GetElementAt(i,getter_AddRefs(genericFlavor));
107 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
108
109 if (currentFlavor)
110 {
111 // flavorStr is the mime type
112 nsXPIDLCString flavorStr;
113 currentFlavor->ToString(getter_Copies(flavorStr));
114
115 // Clip is the data which will be sent to the clipboard
116 nsCOMPtr<nsISupports> clip;
117 // len is the length of the data
118 uint32_t len;
119
120 // Unicode text?
121 if (!strcmp(flavorStr.get(), kUnicodeMime))
122 {
123 rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len);
124 nsCOMPtr<nsISupportsString> wideString;
125 wideString = do_QueryInterface(clip);
126 if (!wideString || NS_FAILED(rv))
127 continue;
128
129 nsAutoString utf16string;
130 wideString->GetData(utf16string);
131 QString str = QString::fromUtf16((const ushort*)utf16string.get());
132
133 // Add text to the mimeData
134 mimeData->setText(str);
135 }
136
137 // html?
138 else if (!strcmp(flavorStr.get(), kHTMLMime))
139 {
140 rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len);
141 nsCOMPtr<nsISupportsString> wideString;
142 wideString = do_QueryInterface(clip);
143 if (!wideString || NS_FAILED(rv))
144 continue;
145
146 nsAutoString utf16string;
147 wideString->GetData(utf16string);
148 QString str = QString::fromUtf16((const ushort*)utf16string.get());
149
150 // Add html to the mimeData
151 mimeData->setHtml(str);
152 }
153
154 // image?
155 else if (!imageAdded // image is added only once to the clipboard
156 && (!strcmp(flavorStr.get(), kNativeImageMime)
157 || !strcmp(flavorStr.get(), kPNGImageMime)
158 || !strcmp(flavorStr.get(), kJPEGImageMime)
159 || !strcmp(flavorStr.get(), kJPGImageMime)
160 || !strcmp(flavorStr.get(), kGIFImageMime))
161 )
162 {
163 // Look through our transfer data for the image
164 static const char* const imageMimeTypes[] = {
165 kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime };
166 nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive;
167 for (uint32_t i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++)
168 {
169 aTransferable->GetTransferData(imageMimeTypes[i], getter_AddRefs(clip), &len);
170 ptrPrimitive = do_QueryInterface(clip);
171 }
172
173 if (!ptrPrimitive)
174 continue;
175
176 nsCOMPtr<nsISupports> primitiveData;
177 ptrPrimitive->GetData(getter_AddRefs(primitiveData));
178 nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData));
179 if (!image) // Not getting an image for an image mime type!?
180 continue;
181
182 RefPtr<SourceSurface> surface =
183 image->GetFrame(imgIContainer::FRAME_CURRENT,
184 imgIContainer::FLAG_SYNC_DECODE);
185 if (!surface)
186 continue;
187
188 RefPtr<DataSourceSurface> dataSurface =
189 surface->GetDataSurface();
190 if (!dataSurface)
191 continue;
192
193 DataSourceSurface::MappedSurface map;
194 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map))
195 continue;
196
197 QImage qImage(map.mData,
198 dataSurface->GetSize().width,
199 dataSurface->GetSize().height,
200 map.mStride,
201 _moz2dformat_to_qformat(dataSurface->GetFormat()));
202
203 dataSurface->Unmap();
204
205 // Add image to the mimeData
206 mimeData->setImageData(qImage);
207 imageAdded = true;
208 }
209
210 // Other flavors, adding data to clipboard "as is"
211 else
212 {
213 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(clip), &len);
214 // nothing found?
215 if (!clip || NS_FAILED(rv))
216 continue;
217
218 void *primitive_data = nullptr;
219 nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr.get(), clip,
220 &primitive_data, len);
221
222 if (primitive_data)
223 {
224 QByteArray data ((const char *)primitive_data, len);
225 // Add data to the mimeData
226 mimeData->setData(flavorStr.get(), data);
227 nsMemory::Free(primitive_data);
228 }
229 }
230 }
231 }
232
233 // If we have some mime data, add it to the clipboard
234 if(!mimeData->formats().isEmpty())
235 cb->setMimeData(mimeData, clipboardMode);
236 else
237 delete mimeData;
238
239 return NS_OK;
240 }
241
242 // nsClipboard::GetNativeClipboardData ie. Paste
243 //
244 NS_IMETHODIMP
245 nsClipboard::GetNativeClipboardData(nsITransferable *aTransferable,
246 QClipboard::Mode clipboardMode)
247 {
248 if (nullptr == aTransferable)
249 {
250 NS_WARNING("GetNativeClipboardData: Transferable is null!");
251 return NS_ERROR_FAILURE;
252 }
253
254 // get flavor list that includes all acceptable flavors (including
255 // ones obtained through conversion)
256 nsCOMPtr<nsISupportsArray> flavorList;
257 nsresult errCode = aTransferable->FlavorsTransferableCanImport(
258 getter_AddRefs(flavorList));
259
260 if (NS_FAILED(errCode))
261 {
262 NS_WARNING("nsClipboard::GetNativeClipboardData(): no FlavorsTransferable!");
263 return NS_ERROR_FAILURE;
264 }
265
266 QClipboard *cb = QGuiApplication::clipboard();
267 const QMimeData *mimeData = cb->mimeData(clipboardMode);
268
269 // Walk through flavors and see which flavor matches the one being pasted
270 uint32_t flavorCount;
271 flavorList->Count(&flavorCount);
272 nsAutoCString foundFlavor;
273
274 for (uint32_t i = 0; i < flavorCount; ++i)
275 {
276 nsCOMPtr<nsISupports> genericFlavor;
277 flavorList->GetElementAt(i,getter_AddRefs(genericFlavor));
278 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface( genericFlavor) );
279
280 if (currentFlavor)
281 {
282 nsXPIDLCString flavorStr;
283 currentFlavor->ToString(getter_Copies(flavorStr));
284
285 // Ok, so which flavor the data being pasted could be?
286 // Text?
287 if (!strcmp(flavorStr.get(), kUnicodeMime) && mimeData->hasText())
288 {
289 // Clipboard has text and flavor accepts text, so lets
290 // handle the data as text
291 foundFlavor = nsAutoCString(flavorStr);
292
293 // Get the text data from clipboard
294 QString text = mimeData->text();
295 const QChar *unicode = text.unicode();
296 // Is there a more correct way to get the size in UTF16?
297 uint32_t len = (uint32_t) 2*text.size();
298
299 // And then to genericDataWrapper
300 nsCOMPtr<nsISupports> genericDataWrapper;
301 nsPrimitiveHelpers::CreatePrimitiveForData(
302 foundFlavor.get(),
303 (void*)unicode,
304 len,
305 getter_AddRefs(genericDataWrapper));
306 // Data is good, set it to the transferable
307 aTransferable->SetTransferData(foundFlavor.get(),
308 genericDataWrapper,len);
309 // And thats all
310 break;
311 }
312
313 // html?
314 if (!strcmp(flavorStr.get(), kHTMLMime) && mimeData->hasHtml())
315 {
316 // Clipboard has text/html and flavor accepts text/html, so lets
317 // handle the data as text/html
318 foundFlavor = nsAutoCString(flavorStr);
319
320 // Get the text data from clipboard
321 QString html = mimeData->html();
322 const QChar *unicode = html.unicode();
323 // Is there a more correct way to get the size in UTF16?
324 uint32_t len = (uint32_t) 2*html.size();
325
326 // And then to genericDataWrapper
327 nsCOMPtr<nsISupports> genericDataWrapper;
328 nsPrimitiveHelpers::CreatePrimitiveForData(
329 foundFlavor.get(),
330 (void*)unicode,
331 len,
332 getter_AddRefs(genericDataWrapper));
333 // Data is good, set it to the transferable
334 aTransferable->SetTransferData(foundFlavor.get(),
335 genericDataWrapper,len);
336 // And thats all
337 break;
338 }
339
340 // Image?
341 if (( !strcmp(flavorStr.get(), kJPEGImageMime)
342 || !strcmp(flavorStr.get(), kJPGImageMime)
343 || !strcmp(flavorStr.get(), kPNGImageMime)
344 || !strcmp(flavorStr.get(), kGIFImageMime))
345 && mimeData->hasImage())
346 {
347 // Try to retrieve an image from clipboard
348 QImage image = cb->image();
349 if(image.isNull())
350 continue;
351
352 // Lets set the image format
353 QByteArray imageFormat;
354 if (!strcmp(flavorStr.get(), kJPEGImageMime) || !strcmp(flavorStr.get(), kJPGImageMime))
355 imageFormat = "jpeg";
356 else if (!strcmp(flavorStr.get(), kPNGImageMime))
357 imageFormat = "png";
358 else if (!strcmp(flavorStr.get(), kGIFImageMime))
359 imageFormat = "gif";
360 else
361 continue;
362
363 // Write image from clippboard to a QByteArrayBuffer
364 QByteArray imageData;
365 QBuffer imageBuffer(&imageData);
366 QImageWriter imageWriter(&imageBuffer, imageFormat);
367 if(!imageWriter.write(image))
368 continue;
369
370 // Add the data to inputstream
371 nsCOMPtr<nsIInputStream> byteStream;
372 NS_NewByteInputStream(getter_AddRefs(byteStream), imageData.constData(),
373 imageData.size(), NS_ASSIGNMENT_COPY);
374 // Data is good, set it to the transferable
375 aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*));
376
377 imageBuffer.close();
378
379 // And thats all
380 break;
381 }
382
383 // Other mimetype?
384 // Trying to forward the data "as is"
385 if(mimeData->hasFormat(flavorStr.get()))
386 {
387 // get the data from the clipboard
388 QByteArray clipboardData = mimeData->data(flavorStr.get());
389 // And add it to genericDataWrapper
390 nsCOMPtr<nsISupports> genericDataWrapper;
391 nsPrimitiveHelpers::CreatePrimitiveForData(
392 foundFlavor.get(),
393 (void*) clipboardData.data(),
394 clipboardData.size(),
395 getter_AddRefs(genericDataWrapper));
396
397 // Data is good, set it to the transferable
398 aTransferable->SetTransferData(foundFlavor.get(),
399 genericDataWrapper,clipboardData.size());
400 // And thats all
401 break;
402 }
403 }
404 }
405
406 return NS_OK;
407 }
408
409 NS_IMETHODIMP
410 nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength,
411 int32_t aWhichClipboard, bool *_retval)
412 {
413 *_retval = false;
414 if (aWhichClipboard != kGlobalClipboard)
415 return NS_OK;
416
417 // Which kind of data in the clipboard
418 QClipboard *cb = QGuiApplication::clipboard();
419 const QMimeData *mimeData = cb->mimeData();
420 const char *flavor=nullptr;
421 QStringList formats = mimeData->formats();
422 for (uint32_t i = 0; i < aLength; ++i)
423 {
424 flavor = aFlavorList[i];
425 if (flavor)
426 {
427 QString qflavor(flavor);
428
429 if (strcmp(flavor,kTextMime) == 0)
430 {
431 NS_WARNING("DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD");
432 }
433
434 // QClipboard says it has text/plain, mozilla wants to
435 // know if the data is text/unicode -> interpret text/plain to text/unicode
436 if (formats.contains(qflavor) ||
437 strcmp(flavor, kUnicodeMime) == 0)
438 {
439 // A match has been found, return'
440 *_retval = true;
441 break;
442 }
443 }
444 }
445 return NS_OK;
446 }
447
448 /**
449 * Sets the transferable object
450 */
451 NS_IMETHODIMP
452 nsClipboard::SetData(nsITransferable *aTransferable,
453 nsIClipboardOwner *aOwner,
454 int32_t aWhichClipboard)
455 {
456 // See if we can short cut
457 if (
458 (aWhichClipboard == kGlobalClipboard
459 && aTransferable == mGlobalTransferable.get()
460 && aOwner == mGlobalOwner.get()
461 )
462 ||
463 (aWhichClipboard == kSelectionClipboard
464 && aTransferable == mSelectionTransferable.get()
465 && aOwner == mSelectionOwner.get()
466 )
467 )
468 {
469 return NS_OK;
470 }
471
472 nsresult rv;
473 if (!mPrivacyHandler) {
474 rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler));
475 NS_ENSURE_SUCCESS(rv, rv);
476 }
477 rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable);
478 NS_ENSURE_SUCCESS(rv, rv);
479
480 EmptyClipboard(aWhichClipboard);
481
482 QClipboard::Mode mode;
483
484 if (kGlobalClipboard == aWhichClipboard)
485 {
486 mGlobalOwner = aOwner;
487 mGlobalTransferable = aTransferable;
488
489 mode = QClipboard::Clipboard;
490 }
491 else
492 {
493 mSelectionOwner = aOwner;
494 mSelectionTransferable = aTransferable;
495
496 mode = QClipboard::Selection;
497 }
498 return SetNativeClipboardData( aTransferable, mode );
499 }
500
501 /**
502 * Gets the transferable object
503 */
504 NS_IMETHODIMP
505 nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard)
506 {
507 if (nullptr != aTransferable)
508 {
509 QClipboard::Mode mode;
510 if (kGlobalClipboard == aWhichClipboard)
511 {
512 mode = QClipboard::Clipboard;
513 }
514 else
515 {
516 mode = QClipboard::Selection;
517 }
518 return GetNativeClipboardData(aTransferable, mode);
519 }
520 else
521 {
522 NS_WARNING("nsClipboard::GetData(), aTransferable is NULL.");
523 }
524 return NS_ERROR_FAILURE;
525 }
526
527 NS_IMETHODIMP
528 nsClipboard::EmptyClipboard(int32_t aWhichClipboard)
529 {
530 if (aWhichClipboard == kSelectionClipboard)
531 {
532 if (mSelectionOwner)
533 {
534 mSelectionOwner->LosingOwnership(mSelectionTransferable);
535 mSelectionOwner = nullptr;
536 }
537 mSelectionTransferable = nullptr;
538 }
539 else
540 {
541 if (mGlobalOwner)
542 {
543 mGlobalOwner->LosingOwnership(mGlobalTransferable);
544 mGlobalOwner = nullptr;
545 }
546 mGlobalTransferable = nullptr;
547 }
548
549 return NS_OK;
550 }
551
552 NS_IMETHODIMP
553 nsClipboard::SupportsSelectionClipboard(bool *_retval)
554 {
555 NS_ENSURE_ARG_POINTER(_retval);
556
557 QClipboard *cb = QGuiApplication::clipboard();
558 if (cb->supportsSelection())
559 {
560 *_retval = true; // we support the selection clipboard
561 }
562 else
563 {
564 *_retval = false;
565 }
566
567 return NS_OK;
568 }
569
570 NS_IMETHODIMP
571 nsClipboard::SupportsFindClipboard(bool* _retval)
572 {
573 NS_ENSURE_ARG_POINTER(_retval);
574
575 *_retval = false;
576 return NS_OK;
577 }

mercurial