michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "gfxPlatform.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: michael@0: #include "nsClipboard.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsPrimitiveHelpers.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsStringStream.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: michael@0: #include "imgIContainer.h" michael@0: #include "gfxImageSurface.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard) michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // michael@0: // nsClipboard constructor michael@0: // michael@0: //------------------------------------------------------------------------- michael@0: nsClipboard::nsClipboard() : nsIClipboard(), michael@0: mSelectionOwner(nullptr), michael@0: mGlobalOwner(nullptr), michael@0: mSelectionTransferable(nullptr), michael@0: mGlobalTransferable(nullptr) michael@0: { michael@0: // No implementation needed michael@0: } michael@0: michael@0: //------------------------------------------------------------------------- michael@0: // michael@0: // nsClipboard destructor michael@0: // michael@0: //------------------------------------------------------------------------- michael@0: nsClipboard::~nsClipboard() michael@0: { michael@0: } michael@0: michael@0: static inline QImage::Format michael@0: _moz2dformat_to_qformat(SurfaceFormat aFormat) michael@0: { michael@0: switch (aFormat) { michael@0: case SurfaceFormat::B8G8R8A8: michael@0: return QImage::Format_ARGB32_Premultiplied; michael@0: case SurfaceFormat::B8G8R8X8: michael@0: return QImage::Format_ARGB32; michael@0: case SurfaceFormat::R5G6B5: michael@0: return QImage::Format_RGB16; michael@0: default: michael@0: return QImage::Format_Invalid; michael@0: } michael@0: } michael@0: michael@0: // nsClipboard::SetNativeClipboardData ie. Copy michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::SetNativeClipboardData( nsITransferable *aTransferable, michael@0: QClipboard::Mode clipboardMode ) michael@0: { michael@0: if (nullptr == aTransferable) michael@0: { michael@0: NS_WARNING("nsClipboard::SetNativeClipboardData(): no transferable!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // get flavor list that includes all flavors that can be written (including michael@0: // ones obtained through conversion) michael@0: nsCOMPtr flavorList; michael@0: nsresult rv = aTransferable->FlavorsTransferableCanExport( getter_AddRefs(flavorList) ); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: NS_WARNING("nsClipboard::SetNativeClipboardData(): no FlavorsTransferable !"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: QClipboard *cb = QGuiApplication::clipboard(); michael@0: QMimeData *mimeData = new QMimeData; michael@0: michael@0: uint32_t flavorCount = 0; michael@0: flavorList->Count(&flavorCount); michael@0: bool imageAdded = false; michael@0: michael@0: for (uint32_t i = 0; i < flavorCount; ++i) michael@0: { michael@0: nsCOMPtr genericFlavor; michael@0: flavorList->GetElementAt(i,getter_AddRefs(genericFlavor)); michael@0: nsCOMPtr currentFlavor(do_QueryInterface(genericFlavor)); michael@0: michael@0: if (currentFlavor) michael@0: { michael@0: // flavorStr is the mime type michael@0: nsXPIDLCString flavorStr; michael@0: currentFlavor->ToString(getter_Copies(flavorStr)); michael@0: michael@0: // Clip is the data which will be sent to the clipboard michael@0: nsCOMPtr clip; michael@0: // len is the length of the data michael@0: uint32_t len; michael@0: michael@0: // Unicode text? michael@0: if (!strcmp(flavorStr.get(), kUnicodeMime)) michael@0: { michael@0: rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len); michael@0: nsCOMPtr wideString; michael@0: wideString = do_QueryInterface(clip); michael@0: if (!wideString || NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: nsAutoString utf16string; michael@0: wideString->GetData(utf16string); michael@0: QString str = QString::fromUtf16((const ushort*)utf16string.get()); michael@0: michael@0: // Add text to the mimeData michael@0: mimeData->setText(str); michael@0: } michael@0: michael@0: // html? michael@0: else if (!strcmp(flavorStr.get(), kHTMLMime)) michael@0: { michael@0: rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len); michael@0: nsCOMPtr wideString; michael@0: wideString = do_QueryInterface(clip); michael@0: if (!wideString || NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: nsAutoString utf16string; michael@0: wideString->GetData(utf16string); michael@0: QString str = QString::fromUtf16((const ushort*)utf16string.get()); michael@0: michael@0: // Add html to the mimeData michael@0: mimeData->setHtml(str); michael@0: } michael@0: michael@0: // image? michael@0: else if (!imageAdded // image is added only once to the clipboard michael@0: && (!strcmp(flavorStr.get(), kNativeImageMime) michael@0: || !strcmp(flavorStr.get(), kPNGImageMime) michael@0: || !strcmp(flavorStr.get(), kJPEGImageMime) michael@0: || !strcmp(flavorStr.get(), kJPGImageMime) michael@0: || !strcmp(flavorStr.get(), kGIFImageMime)) michael@0: ) michael@0: { michael@0: // Look through our transfer data for the image michael@0: static const char* const imageMimeTypes[] = { michael@0: kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime }; michael@0: nsCOMPtr ptrPrimitive; michael@0: for (uint32_t i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++) michael@0: { michael@0: aTransferable->GetTransferData(imageMimeTypes[i], getter_AddRefs(clip), &len); michael@0: ptrPrimitive = do_QueryInterface(clip); michael@0: } michael@0: michael@0: if (!ptrPrimitive) michael@0: continue; michael@0: michael@0: nsCOMPtr primitiveData; michael@0: ptrPrimitive->GetData(getter_AddRefs(primitiveData)); michael@0: nsCOMPtr image(do_QueryInterface(primitiveData)); michael@0: if (!image) // Not getting an image for an image mime type!? michael@0: continue; michael@0: michael@0: RefPtr surface = michael@0: image->GetFrame(imgIContainer::FRAME_CURRENT, michael@0: imgIContainer::FLAG_SYNC_DECODE); michael@0: if (!surface) michael@0: continue; michael@0: michael@0: RefPtr dataSurface = michael@0: surface->GetDataSurface(); michael@0: if (!dataSurface) michael@0: continue; michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) michael@0: continue; michael@0: michael@0: QImage qImage(map.mData, michael@0: dataSurface->GetSize().width, michael@0: dataSurface->GetSize().height, michael@0: map.mStride, michael@0: _moz2dformat_to_qformat(dataSurface->GetFormat())); michael@0: michael@0: dataSurface->Unmap(); michael@0: michael@0: // Add image to the mimeData michael@0: mimeData->setImageData(qImage); michael@0: imageAdded = true; michael@0: } michael@0: michael@0: // Other flavors, adding data to clipboard "as is" michael@0: else michael@0: { michael@0: rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(clip), &len); michael@0: // nothing found? michael@0: if (!clip || NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: void *primitive_data = nullptr; michael@0: nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr.get(), clip, michael@0: &primitive_data, len); michael@0: michael@0: if (primitive_data) michael@0: { michael@0: QByteArray data ((const char *)primitive_data, len); michael@0: // Add data to the mimeData michael@0: mimeData->setData(flavorStr.get(), data); michael@0: nsMemory::Free(primitive_data); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // If we have some mime data, add it to the clipboard michael@0: if(!mimeData->formats().isEmpty()) michael@0: cb->setMimeData(mimeData, clipboardMode); michael@0: else michael@0: delete mimeData; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsClipboard::GetNativeClipboardData ie. Paste michael@0: // michael@0: NS_IMETHODIMP michael@0: nsClipboard::GetNativeClipboardData(nsITransferable *aTransferable, michael@0: QClipboard::Mode clipboardMode) michael@0: { michael@0: if (nullptr == aTransferable) michael@0: { michael@0: NS_WARNING("GetNativeClipboardData: Transferable is null!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // get flavor list that includes all acceptable flavors (including michael@0: // ones obtained through conversion) michael@0: nsCOMPtr flavorList; michael@0: nsresult errCode = aTransferable->FlavorsTransferableCanImport( michael@0: getter_AddRefs(flavorList)); michael@0: michael@0: if (NS_FAILED(errCode)) michael@0: { michael@0: NS_WARNING("nsClipboard::GetNativeClipboardData(): no FlavorsTransferable!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: QClipboard *cb = QGuiApplication::clipboard(); michael@0: const QMimeData *mimeData = cb->mimeData(clipboardMode); michael@0: michael@0: // Walk through flavors and see which flavor matches the one being pasted michael@0: uint32_t flavorCount; michael@0: flavorList->Count(&flavorCount); michael@0: nsAutoCString foundFlavor; michael@0: michael@0: for (uint32_t i = 0; i < flavorCount; ++i) michael@0: { michael@0: nsCOMPtr genericFlavor; michael@0: flavorList->GetElementAt(i,getter_AddRefs(genericFlavor)); michael@0: nsCOMPtr currentFlavor(do_QueryInterface( genericFlavor) ); michael@0: michael@0: if (currentFlavor) michael@0: { michael@0: nsXPIDLCString flavorStr; michael@0: currentFlavor->ToString(getter_Copies(flavorStr)); michael@0: michael@0: // Ok, so which flavor the data being pasted could be? michael@0: // Text? michael@0: if (!strcmp(flavorStr.get(), kUnicodeMime) && mimeData->hasText()) michael@0: { michael@0: // Clipboard has text and flavor accepts text, so lets michael@0: // handle the data as text michael@0: foundFlavor = nsAutoCString(flavorStr); michael@0: michael@0: // Get the text data from clipboard michael@0: QString text = mimeData->text(); michael@0: const QChar *unicode = text.unicode(); michael@0: // Is there a more correct way to get the size in UTF16? michael@0: uint32_t len = (uint32_t) 2*text.size(); michael@0: michael@0: // And then to genericDataWrapper michael@0: nsCOMPtr genericDataWrapper; michael@0: nsPrimitiveHelpers::CreatePrimitiveForData( michael@0: foundFlavor.get(), michael@0: (void*)unicode, michael@0: len, michael@0: getter_AddRefs(genericDataWrapper)); michael@0: // Data is good, set it to the transferable michael@0: aTransferable->SetTransferData(foundFlavor.get(), michael@0: genericDataWrapper,len); michael@0: // And thats all michael@0: break; michael@0: } michael@0: michael@0: // html? michael@0: if (!strcmp(flavorStr.get(), kHTMLMime) && mimeData->hasHtml()) michael@0: { michael@0: // Clipboard has text/html and flavor accepts text/html, so lets michael@0: // handle the data as text/html michael@0: foundFlavor = nsAutoCString(flavorStr); michael@0: michael@0: // Get the text data from clipboard michael@0: QString html = mimeData->html(); michael@0: const QChar *unicode = html.unicode(); michael@0: // Is there a more correct way to get the size in UTF16? michael@0: uint32_t len = (uint32_t) 2*html.size(); michael@0: michael@0: // And then to genericDataWrapper michael@0: nsCOMPtr genericDataWrapper; michael@0: nsPrimitiveHelpers::CreatePrimitiveForData( michael@0: foundFlavor.get(), michael@0: (void*)unicode, michael@0: len, michael@0: getter_AddRefs(genericDataWrapper)); michael@0: // Data is good, set it to the transferable michael@0: aTransferable->SetTransferData(foundFlavor.get(), michael@0: genericDataWrapper,len); michael@0: // And thats all michael@0: break; michael@0: } michael@0: michael@0: // Image? michael@0: if (( !strcmp(flavorStr.get(), kJPEGImageMime) michael@0: || !strcmp(flavorStr.get(), kJPGImageMime) michael@0: || !strcmp(flavorStr.get(), kPNGImageMime) michael@0: || !strcmp(flavorStr.get(), kGIFImageMime)) michael@0: && mimeData->hasImage()) michael@0: { michael@0: // Try to retrieve an image from clipboard michael@0: QImage image = cb->image(); michael@0: if(image.isNull()) michael@0: continue; michael@0: michael@0: // Lets set the image format michael@0: QByteArray imageFormat; michael@0: if (!strcmp(flavorStr.get(), kJPEGImageMime) || !strcmp(flavorStr.get(), kJPGImageMime)) michael@0: imageFormat = "jpeg"; michael@0: else if (!strcmp(flavorStr.get(), kPNGImageMime)) michael@0: imageFormat = "png"; michael@0: else if (!strcmp(flavorStr.get(), kGIFImageMime)) michael@0: imageFormat = "gif"; michael@0: else michael@0: continue; michael@0: michael@0: // Write image from clippboard to a QByteArrayBuffer michael@0: QByteArray imageData; michael@0: QBuffer imageBuffer(&imageData); michael@0: QImageWriter imageWriter(&imageBuffer, imageFormat); michael@0: if(!imageWriter.write(image)) michael@0: continue; michael@0: michael@0: // Add the data to inputstream michael@0: nsCOMPtr byteStream; michael@0: NS_NewByteInputStream(getter_AddRefs(byteStream), imageData.constData(), michael@0: imageData.size(), NS_ASSIGNMENT_COPY); michael@0: // Data is good, set it to the transferable michael@0: aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); michael@0: michael@0: imageBuffer.close(); michael@0: michael@0: // And thats all michael@0: break; michael@0: } michael@0: michael@0: // Other mimetype? michael@0: // Trying to forward the data "as is" michael@0: if(mimeData->hasFormat(flavorStr.get())) michael@0: { michael@0: // get the data from the clipboard michael@0: QByteArray clipboardData = mimeData->data(flavorStr.get()); michael@0: // And add it to genericDataWrapper michael@0: nsCOMPtr genericDataWrapper; michael@0: nsPrimitiveHelpers::CreatePrimitiveForData( michael@0: foundFlavor.get(), michael@0: (void*) clipboardData.data(), michael@0: clipboardData.size(), michael@0: getter_AddRefs(genericDataWrapper)); michael@0: michael@0: // Data is good, set it to the transferable michael@0: aTransferable->SetTransferData(foundFlavor.get(), michael@0: genericDataWrapper,clipboardData.size()); michael@0: // And thats all michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength, michael@0: int32_t aWhichClipboard, bool *_retval) michael@0: { michael@0: *_retval = false; michael@0: if (aWhichClipboard != kGlobalClipboard) michael@0: return NS_OK; michael@0: michael@0: // Which kind of data in the clipboard michael@0: QClipboard *cb = QGuiApplication::clipboard(); michael@0: const QMimeData *mimeData = cb->mimeData(); michael@0: const char *flavor=nullptr; michael@0: QStringList formats = mimeData->formats(); michael@0: for (uint32_t i = 0; i < aLength; ++i) michael@0: { michael@0: flavor = aFlavorList[i]; michael@0: if (flavor) michael@0: { michael@0: QString qflavor(flavor); michael@0: michael@0: if (strcmp(flavor,kTextMime) == 0) michael@0: { michael@0: NS_WARNING("DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD"); michael@0: } michael@0: michael@0: // QClipboard says it has text/plain, mozilla wants to michael@0: // know if the data is text/unicode -> interpret text/plain to text/unicode michael@0: if (formats.contains(qflavor) || michael@0: strcmp(flavor, kUnicodeMime) == 0) michael@0: { michael@0: // A match has been found, return' michael@0: *_retval = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Sets the transferable object michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsClipboard::SetData(nsITransferable *aTransferable, michael@0: nsIClipboardOwner *aOwner, michael@0: int32_t aWhichClipboard) michael@0: { michael@0: // See if we can short cut michael@0: if ( michael@0: (aWhichClipboard == kGlobalClipboard michael@0: && aTransferable == mGlobalTransferable.get() michael@0: && aOwner == mGlobalOwner.get() michael@0: ) michael@0: || michael@0: (aWhichClipboard == kSelectionClipboard michael@0: && aTransferable == mSelectionTransferable.get() michael@0: && aOwner == mSelectionOwner.get() michael@0: ) michael@0: ) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv; michael@0: if (!mPrivacyHandler) { michael@0: rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: EmptyClipboard(aWhichClipboard); michael@0: michael@0: QClipboard::Mode mode; michael@0: michael@0: if (kGlobalClipboard == aWhichClipboard) michael@0: { michael@0: mGlobalOwner = aOwner; michael@0: mGlobalTransferable = aTransferable; michael@0: michael@0: mode = QClipboard::Clipboard; michael@0: } michael@0: else michael@0: { michael@0: mSelectionOwner = aOwner; michael@0: mSelectionTransferable = aTransferable; michael@0: michael@0: mode = QClipboard::Selection; michael@0: } michael@0: return SetNativeClipboardData( aTransferable, mode ); michael@0: } michael@0: michael@0: /** michael@0: * Gets the transferable object michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) michael@0: { michael@0: if (nullptr != aTransferable) michael@0: { michael@0: QClipboard::Mode mode; michael@0: if (kGlobalClipboard == aWhichClipboard) michael@0: { michael@0: mode = QClipboard::Clipboard; michael@0: } michael@0: else michael@0: { michael@0: mode = QClipboard::Selection; michael@0: } michael@0: return GetNativeClipboardData(aTransferable, mode); michael@0: } michael@0: else michael@0: { michael@0: NS_WARNING("nsClipboard::GetData(), aTransferable is NULL."); michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::EmptyClipboard(int32_t aWhichClipboard) michael@0: { michael@0: if (aWhichClipboard == kSelectionClipboard) michael@0: { michael@0: if (mSelectionOwner) michael@0: { michael@0: mSelectionOwner->LosingOwnership(mSelectionTransferable); michael@0: mSelectionOwner = nullptr; michael@0: } michael@0: mSelectionTransferable = nullptr; michael@0: } michael@0: else michael@0: { michael@0: if (mGlobalOwner) michael@0: { michael@0: mGlobalOwner->LosingOwnership(mGlobalTransferable); michael@0: mGlobalOwner = nullptr; michael@0: } michael@0: mGlobalTransferable = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::SupportsSelectionClipboard(bool *_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: QClipboard *cb = QGuiApplication::clipboard(); michael@0: if (cb->supportsSelection()) michael@0: { michael@0: *_retval = true; // we support the selection clipboard michael@0: } michael@0: else michael@0: { michael@0: *_retval = false; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClipboard::SupportsFindClipboard(bool* _retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: *_retval = false; michael@0: return NS_OK; michael@0: }