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