Bug 867104 - Add a crashtest. r=ehsan
[gecko.git] / widget / qt / nsClipboard.cpp
blobe4cc1366b91b5a9ec947665a1fed324581c52fcb
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/. */
5 #include <QApplication>
6 #include <QMimeData>
7 #include <QString>
8 #include <QStringList>
9 #include <QByteArray>
10 #include <QImage>
11 #include <QImageWriter>
12 #include <QBuffer>
14 #include "mozilla/Util.h"
16 #include "nsClipboard.h"
17 #include "nsISupportsPrimitives.h"
18 #include "nsXPIDLString.h"
19 #include "nsPrimitiveHelpers.h"
20 #include "nsIInputStream.h"
21 #include "nsReadableUtils.h"
22 #include "nsStringStream.h"
23 #include "nsComponentManagerUtils.h"
25 #include "imgIContainer.h"
26 #include "gfxImageSurface.h"
28 using namespace mozilla;
30 NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
32 //-------------------------------------------------------------------------
34 // nsClipboard constructor
36 //-------------------------------------------------------------------------
37 nsClipboard::nsClipboard() : nsIClipboard(),
38 mSelectionOwner(nullptr),
39 mGlobalOwner(nullptr),
40 mSelectionTransferable(nullptr),
41 mGlobalTransferable(nullptr)
43 // No implementation needed
46 //-------------------------------------------------------------------------
48 // nsClipboard destructor
50 //-------------------------------------------------------------------------
51 nsClipboard::~nsClipboard()
55 static inline QImage::Format
56 _gfximage_to_qformat(gfxASurface::gfxImageFormat aFormat)
58 switch (aFormat) {
59 case gfxASurface::ImageFormatARGB32:
60 return QImage::Format_ARGB32_Premultiplied;
61 case gfxASurface::ImageFormatRGB24:
62 return QImage::Format_ARGB32;
63 case gfxASurface::ImageFormatRGB16_565:
64 return QImage::Format_RGB16;
65 default:
66 return QImage::Format_Invalid;
70 // nsClipboard::SetNativeClipboardData ie. Copy
72 NS_IMETHODIMP
73 nsClipboard::SetNativeClipboardData( nsITransferable *aTransferable,
74 QClipboard::Mode clipboardMode )
76 if (nullptr == aTransferable)
78 NS_WARNING("nsClipboard::SetNativeClipboardData(): no transferable!");
79 return NS_ERROR_FAILURE;
82 // get flavor list that includes all flavors that can be written (including
83 // ones obtained through conversion)
84 nsCOMPtr<nsISupportsArray> flavorList;
85 nsresult rv = aTransferable->FlavorsTransferableCanExport( getter_AddRefs(flavorList) );
87 if (NS_FAILED(rv))
89 NS_WARNING("nsClipboard::SetNativeClipboardData(): no FlavorsTransferable !");
90 return NS_ERROR_FAILURE;
93 QClipboard *cb = QApplication::clipboard();
94 QMimeData *mimeData = new QMimeData;
96 uint32_t flavorCount = 0;
97 flavorList->Count(&flavorCount);
98 bool imageAdded = false;
100 for (uint32_t i = 0; i < flavorCount; ++i)
102 nsCOMPtr<nsISupports> genericFlavor;
103 flavorList->GetElementAt(i,getter_AddRefs(genericFlavor));
104 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor));
106 if (currentFlavor)
108 // flavorStr is the mime type
109 nsXPIDLCString flavorStr;
110 currentFlavor->ToString(getter_Copies(flavorStr));
112 // Clip is the data which will be sent to the clipboard
113 nsCOMPtr<nsISupports> clip;
114 // len is the length of the data
115 uint32_t len;
117 // Unicode text?
118 if (!strcmp(flavorStr.get(), kUnicodeMime))
120 rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len);
121 nsCOMPtr<nsISupportsString> wideString;
122 wideString = do_QueryInterface(clip);
123 if (!wideString || NS_FAILED(rv))
124 continue;
126 nsAutoString utf16string;
127 wideString->GetData(utf16string);
128 QString str = QString::fromUtf16(utf16string.get());
130 // Add text to the mimeData
131 mimeData->setText(str);
134 // html?
135 else if (!strcmp(flavorStr.get(), kHTMLMime))
137 rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len);
138 nsCOMPtr<nsISupportsString> wideString;
139 wideString = do_QueryInterface(clip);
140 if (!wideString || NS_FAILED(rv))
141 continue;
143 nsAutoString utf16string;
144 wideString->GetData(utf16string);
145 QString str = QString::fromUtf16(utf16string.get());
147 // Add html to the mimeData
148 mimeData->setHtml(str);
151 // image?
152 else if (!imageAdded // image is added only once to the clipboard
153 && (!strcmp(flavorStr.get(), kNativeImageMime)
154 || !strcmp(flavorStr.get(), kPNGImageMime)
155 || !strcmp(flavorStr.get(), kJPEGImageMime)
156 || !strcmp(flavorStr.get(), kJPGImageMime)
157 || !strcmp(flavorStr.get(), kGIFImageMime))
160 // Look through our transfer data for the image
161 static const char* const imageMimeTypes[] = {
162 kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime };
163 nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive;
164 for (uint32_t i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++)
166 aTransferable->GetTransferData(imageMimeTypes[i], getter_AddRefs(clip), &len);
167 ptrPrimitive = do_QueryInterface(clip);
170 if (!ptrPrimitive)
171 continue;
173 nsCOMPtr<nsISupports> primitiveData;
174 ptrPrimitive->GetData(getter_AddRefs(primitiveData));
175 nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData));
176 if (!image) // Not getting an image for an image mime type!?
177 continue;
179 nsRefPtr<gfxASurface> surface;
180 image->GetFrame(imgIContainer::FRAME_CURRENT,
181 imgIContainer::FLAG_SYNC_DECODE,
182 getter_AddRefs(surface));
183 if (!surface)
184 continue;
186 nsRefPtr<gfxImageSurface> frame(surface->GetAsReadableARGB32ImageSurface());
187 if (!frame)
188 continue;
190 QImage qImage(frame->Data(),
191 frame->Width(),
192 frame->Height(),
193 frame->Stride(),
194 _gfximage_to_qformat(frame->Format()));
196 // Add image to the mimeData
197 mimeData->setImageData(qImage);
198 imageAdded = true;
201 // Other flavors, adding data to clipboard "as is"
202 else
204 rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(clip), &len);
205 // nothing found?
206 if (!clip || NS_FAILED(rv))
207 continue;
209 void *primitive_data = nullptr;
210 nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr.get(), clip,
211 &primitive_data, len);
213 if (primitive_data)
215 QByteArray data ((const char *)primitive_data, len);
216 // Add data to the mimeData
217 mimeData->setData(flavorStr.get(), data);
218 nsMemory::Free(primitive_data);
224 // If we have some mime data, add it to the clipboard
225 if(!mimeData->formats().isEmpty())
226 cb->setMimeData(mimeData, clipboardMode);
227 else
228 delete mimeData;
230 return NS_OK;
233 // nsClipboard::GetNativeClipboardData ie. Paste
235 NS_IMETHODIMP
236 nsClipboard::GetNativeClipboardData(nsITransferable *aTransferable,
237 QClipboard::Mode clipboardMode)
239 if (nullptr == aTransferable)
241 NS_WARNING("GetNativeClipboardData: Transferable is null!");
242 return NS_ERROR_FAILURE;
245 // get flavor list that includes all acceptable flavors (including
246 // ones obtained through conversion)
247 nsCOMPtr<nsISupportsArray> flavorList;
248 nsresult errCode = aTransferable->FlavorsTransferableCanImport(
249 getter_AddRefs(flavorList));
251 if (NS_FAILED(errCode))
253 NS_WARNING("nsClipboard::GetNativeClipboardData(): no FlavorsTransferable!");
254 return NS_ERROR_FAILURE;
257 QClipboard *cb = QApplication::clipboard();
258 const QMimeData *mimeData = cb->mimeData(clipboardMode);
260 // Walk through flavors and see which flavor matches the one being pasted
261 uint32_t flavorCount;
262 flavorList->Count(&flavorCount);
263 nsAutoCString foundFlavor;
265 for (uint32_t i = 0; i < flavorCount; ++i)
267 nsCOMPtr<nsISupports> genericFlavor;
268 flavorList->GetElementAt(i,getter_AddRefs(genericFlavor));
269 nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface( genericFlavor) );
271 if (currentFlavor)
273 nsXPIDLCString flavorStr;
274 currentFlavor->ToString(getter_Copies(flavorStr));
276 // Ok, so which flavor the data being pasted could be?
277 // Text?
278 if (!strcmp(flavorStr.get(), kUnicodeMime) && mimeData->hasText())
280 // Clipboard has text and flavor accepts text, so lets
281 // handle the data as text
282 foundFlavor = nsAutoCString(flavorStr);
284 // Get the text data from clipboard
285 QString text = mimeData->text();
286 const QChar *unicode = text.unicode();
287 // Is there a more correct way to get the size in UTF16?
288 uint32_t len = (uint32_t) 2*text.size();
290 // And then to genericDataWrapper
291 nsCOMPtr<nsISupports> genericDataWrapper;
292 nsPrimitiveHelpers::CreatePrimitiveForData(
293 foundFlavor.get(),
294 (void*)unicode,
295 len,
296 getter_AddRefs(genericDataWrapper));
297 // Data is good, set it to the transferable
298 aTransferable->SetTransferData(foundFlavor.get(),
299 genericDataWrapper,len);
300 // And thats all
301 break;
304 // html?
305 if (!strcmp(flavorStr.get(), kHTMLMime) && mimeData->hasHtml())
307 // Clipboard has text/html and flavor accepts text/html, so lets
308 // handle the data as text/html
309 foundFlavor = nsAutoCString(flavorStr);
311 // Get the text data from clipboard
312 QString html = mimeData->html();
313 const QChar *unicode = html.unicode();
314 // Is there a more correct way to get the size in UTF16?
315 uint32_t len = (uint32_t) 2*html.size();
317 // And then to genericDataWrapper
318 nsCOMPtr<nsISupports> genericDataWrapper;
319 nsPrimitiveHelpers::CreatePrimitiveForData(
320 foundFlavor.get(),
321 (void*)unicode,
322 len,
323 getter_AddRefs(genericDataWrapper));
324 // Data is good, set it to the transferable
325 aTransferable->SetTransferData(foundFlavor.get(),
326 genericDataWrapper,len);
327 // And thats all
328 break;
331 // Image?
332 if (( !strcmp(flavorStr.get(), kJPEGImageMime)
333 || !strcmp(flavorStr.get(), kJPGImageMime)
334 || !strcmp(flavorStr.get(), kPNGImageMime)
335 || !strcmp(flavorStr.get(), kGIFImageMime))
336 && mimeData->hasImage())
338 // Try to retrieve an image from clipboard
339 QImage image = cb->image();
340 if(image.isNull())
341 continue;
343 // Lets set the image format
344 QByteArray imageFormat;
345 if (!strcmp(flavorStr.get(), kJPEGImageMime) || !strcmp(flavorStr.get(), kJPGImageMime))
346 imageFormat = "jpeg";
347 else if (!strcmp(flavorStr.get(), kPNGImageMime))
348 imageFormat = "png";
349 else if (!strcmp(flavorStr.get(), kGIFImageMime))
350 imageFormat = "gif";
351 else
352 continue;
354 // Write image from clippboard to a QByteArrayBuffer
355 QByteArray imageData;
356 QBuffer imageBuffer(&imageData);
357 QImageWriter imageWriter(&imageBuffer, imageFormat);
358 if(!imageWriter.write(image))
359 continue;
361 // Add the data to inputstream
362 nsCOMPtr<nsIInputStream> byteStream;
363 NS_NewByteInputStream(getter_AddRefs(byteStream), imageData.constData(),
364 imageData.size(), NS_ASSIGNMENT_COPY);
365 // Data is good, set it to the transferable
366 aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*));
368 imageBuffer.close();
370 // And thats all
371 break;
374 // Other mimetype?
375 // Trying to forward the data "as is"
376 if(mimeData->hasFormat(flavorStr.get()))
378 // get the data from the clipboard
379 QByteArray clipboardData = mimeData->data(flavorStr.get());
380 // And add it to genericDataWrapper
381 nsCOMPtr<nsISupports> genericDataWrapper;
382 nsPrimitiveHelpers::CreatePrimitiveForData(
383 foundFlavor.get(),
384 (void*) clipboardData.data(),
385 clipboardData.size(),
386 getter_AddRefs(genericDataWrapper));
388 // Data is good, set it to the transferable
389 aTransferable->SetTransferData(foundFlavor.get(),
390 genericDataWrapper,clipboardData.size());
391 // And thats all
392 break;
397 return NS_OK;
400 NS_IMETHODIMP
401 nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength,
402 int32_t aWhichClipboard, bool *_retval)
404 *_retval = false;
405 if (aWhichClipboard != kGlobalClipboard)
406 return NS_OK;
408 // Which kind of data in the clipboard
409 QClipboard *cb = QApplication::clipboard();
410 const QMimeData *mimeData = cb->mimeData();
411 const char *flavor=NULL;
412 QStringList formats = mimeData->formats();
413 for (uint32_t i = 0; i < aLength; ++i)
415 flavor = aFlavorList[i];
416 if (flavor)
418 QString qflavor(flavor);
420 if (strcmp(flavor,kTextMime) == 0)
422 NS_WARNING("DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD");
425 // QClipboard says it has text/plain, mozilla wants to
426 // know if the data is text/unicode -> interpret text/plain to text/unicode
427 if (formats.contains(qflavor) ||
428 strcmp(flavor, kUnicodeMime) == 0)
430 // A match has been found, return'
431 *_retval = true;
432 break;
436 return NS_OK;
440 * Sets the transferable object
442 NS_IMETHODIMP
443 nsClipboard::SetData(nsITransferable *aTransferable,
444 nsIClipboardOwner *aOwner,
445 int32_t aWhichClipboard)
447 // See if we can short cut
448 if (
449 (aWhichClipboard == kGlobalClipboard
450 && aTransferable == mGlobalTransferable.get()
451 && aOwner == mGlobalOwner.get()
454 (aWhichClipboard == kSelectionClipboard
455 && aTransferable == mSelectionTransferable.get()
456 && aOwner == mSelectionOwner.get()
460 return NS_OK;
463 nsresult rv;
464 if (!mPrivacyHandler) {
465 rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler));
466 NS_ENSURE_SUCCESS(rv, rv);
468 rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable);
469 NS_ENSURE_SUCCESS(rv, rv);
471 EmptyClipboard(aWhichClipboard);
473 QClipboard::Mode mode;
475 if (kGlobalClipboard == aWhichClipboard)
477 mGlobalOwner = aOwner;
478 mGlobalTransferable = aTransferable;
480 mode = QClipboard::Clipboard;
482 else
484 mSelectionOwner = aOwner;
485 mSelectionTransferable = aTransferable;
487 mode = QClipboard::Selection;
489 return SetNativeClipboardData( aTransferable, mode );
493 * Gets the transferable object
495 NS_IMETHODIMP
496 nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard)
498 if (nullptr != aTransferable)
500 QClipboard::Mode mode;
501 if (kGlobalClipboard == aWhichClipboard)
503 mode = QClipboard::Clipboard;
505 else
507 mode = QClipboard::Selection;
509 return GetNativeClipboardData(aTransferable, mode);
511 else
513 NS_WARNING("nsClipboard::GetData(), aTransferable is NULL.");
515 return NS_ERROR_FAILURE;
518 NS_IMETHODIMP
519 nsClipboard::EmptyClipboard(int32_t aWhichClipboard)
521 if (aWhichClipboard == kSelectionClipboard)
523 if (mSelectionOwner)
525 mSelectionOwner->LosingOwnership(mSelectionTransferable);
526 mSelectionOwner = nullptr;
528 mSelectionTransferable = nullptr;
530 else
532 if (mGlobalOwner)
534 mGlobalOwner->LosingOwnership(mGlobalTransferable);
535 mGlobalOwner = nullptr;
537 mGlobalTransferable = nullptr;
540 return NS_OK;
543 NS_IMETHODIMP
544 nsClipboard::SupportsSelectionClipboard(bool *_retval)
546 NS_ENSURE_ARG_POINTER(_retval);
548 QClipboard *cb = QApplication::clipboard();
549 if (cb->supportsSelection())
551 *_retval = true; // we support the selection clipboard
553 else
555 *_retval = false;
558 return NS_OK;