1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/qt/nsClipboard.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,577 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <QGuiApplication> 1.9 +#include <QMimeData> 1.10 +#include <QString> 1.11 +#include <QStringList> 1.12 +#include <QByteArray> 1.13 +#include <QImage> 1.14 +#include <QImageWriter> 1.15 +#include <QBuffer> 1.16 + 1.17 +#include "gfxPlatform.h" 1.18 +#include "mozilla/ArrayUtils.h" 1.19 +#include "mozilla/gfx/2D.h" 1.20 + 1.21 +#include "nsClipboard.h" 1.22 +#include "nsISupportsPrimitives.h" 1.23 +#include "nsXPIDLString.h" 1.24 +#include "nsPrimitiveHelpers.h" 1.25 +#include "nsIInputStream.h" 1.26 +#include "nsReadableUtils.h" 1.27 +#include "nsStringStream.h" 1.28 +#include "nsComponentManagerUtils.h" 1.29 + 1.30 +#include "imgIContainer.h" 1.31 +#include "gfxImageSurface.h" 1.32 + 1.33 +using namespace mozilla; 1.34 +using namespace mozilla::gfx; 1.35 + 1.36 +NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard) 1.37 + 1.38 +//------------------------------------------------------------------------- 1.39 +// 1.40 +// nsClipboard constructor 1.41 +// 1.42 +//------------------------------------------------------------------------- 1.43 +nsClipboard::nsClipboard() : nsIClipboard(), 1.44 + mSelectionOwner(nullptr), 1.45 + mGlobalOwner(nullptr), 1.46 + mSelectionTransferable(nullptr), 1.47 + mGlobalTransferable(nullptr) 1.48 +{ 1.49 + // No implementation needed 1.50 +} 1.51 + 1.52 +//------------------------------------------------------------------------- 1.53 +// 1.54 +// nsClipboard destructor 1.55 +// 1.56 +//------------------------------------------------------------------------- 1.57 +nsClipboard::~nsClipboard() 1.58 +{ 1.59 +} 1.60 + 1.61 +static inline QImage::Format 1.62 +_moz2dformat_to_qformat(SurfaceFormat aFormat) 1.63 +{ 1.64 + switch (aFormat) { 1.65 + case SurfaceFormat::B8G8R8A8: 1.66 + return QImage::Format_ARGB32_Premultiplied; 1.67 + case SurfaceFormat::B8G8R8X8: 1.68 + return QImage::Format_ARGB32; 1.69 + case SurfaceFormat::R5G6B5: 1.70 + return QImage::Format_RGB16; 1.71 + default: 1.72 + return QImage::Format_Invalid; 1.73 + } 1.74 +} 1.75 + 1.76 +// nsClipboard::SetNativeClipboardData ie. Copy 1.77 + 1.78 +NS_IMETHODIMP 1.79 +nsClipboard::SetNativeClipboardData( nsITransferable *aTransferable, 1.80 + QClipboard::Mode clipboardMode ) 1.81 +{ 1.82 + if (nullptr == aTransferable) 1.83 + { 1.84 + NS_WARNING("nsClipboard::SetNativeClipboardData(): no transferable!"); 1.85 + return NS_ERROR_FAILURE; 1.86 + } 1.87 + 1.88 + // get flavor list that includes all flavors that can be written (including 1.89 + // ones obtained through conversion) 1.90 + nsCOMPtr<nsISupportsArray> flavorList; 1.91 + nsresult rv = aTransferable->FlavorsTransferableCanExport( getter_AddRefs(flavorList) ); 1.92 + 1.93 + if (NS_FAILED(rv)) 1.94 + { 1.95 + NS_WARNING("nsClipboard::SetNativeClipboardData(): no FlavorsTransferable !"); 1.96 + return NS_ERROR_FAILURE; 1.97 + } 1.98 + 1.99 + QClipboard *cb = QGuiApplication::clipboard(); 1.100 + QMimeData *mimeData = new QMimeData; 1.101 + 1.102 + uint32_t flavorCount = 0; 1.103 + flavorList->Count(&flavorCount); 1.104 + bool imageAdded = false; 1.105 + 1.106 + for (uint32_t i = 0; i < flavorCount; ++i) 1.107 + { 1.108 + nsCOMPtr<nsISupports> genericFlavor; 1.109 + flavorList->GetElementAt(i,getter_AddRefs(genericFlavor)); 1.110 + nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface(genericFlavor)); 1.111 + 1.112 + if (currentFlavor) 1.113 + { 1.114 + // flavorStr is the mime type 1.115 + nsXPIDLCString flavorStr; 1.116 + currentFlavor->ToString(getter_Copies(flavorStr)); 1.117 + 1.118 + // Clip is the data which will be sent to the clipboard 1.119 + nsCOMPtr<nsISupports> clip; 1.120 + // len is the length of the data 1.121 + uint32_t len; 1.122 + 1.123 + // Unicode text? 1.124 + if (!strcmp(flavorStr.get(), kUnicodeMime)) 1.125 + { 1.126 + rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len); 1.127 + nsCOMPtr<nsISupportsString> wideString; 1.128 + wideString = do_QueryInterface(clip); 1.129 + if (!wideString || NS_FAILED(rv)) 1.130 + continue; 1.131 + 1.132 + nsAutoString utf16string; 1.133 + wideString->GetData(utf16string); 1.134 + QString str = QString::fromUtf16((const ushort*)utf16string.get()); 1.135 + 1.136 + // Add text to the mimeData 1.137 + mimeData->setText(str); 1.138 + } 1.139 + 1.140 + // html? 1.141 + else if (!strcmp(flavorStr.get(), kHTMLMime)) 1.142 + { 1.143 + rv = aTransferable->GetTransferData(flavorStr,getter_AddRefs(clip),&len); 1.144 + nsCOMPtr<nsISupportsString> wideString; 1.145 + wideString = do_QueryInterface(clip); 1.146 + if (!wideString || NS_FAILED(rv)) 1.147 + continue; 1.148 + 1.149 + nsAutoString utf16string; 1.150 + wideString->GetData(utf16string); 1.151 + QString str = QString::fromUtf16((const ushort*)utf16string.get()); 1.152 + 1.153 + // Add html to the mimeData 1.154 + mimeData->setHtml(str); 1.155 + } 1.156 + 1.157 + // image? 1.158 + else if (!imageAdded // image is added only once to the clipboard 1.159 + && (!strcmp(flavorStr.get(), kNativeImageMime) 1.160 + || !strcmp(flavorStr.get(), kPNGImageMime) 1.161 + || !strcmp(flavorStr.get(), kJPEGImageMime) 1.162 + || !strcmp(flavorStr.get(), kJPGImageMime) 1.163 + || !strcmp(flavorStr.get(), kGIFImageMime)) 1.164 + ) 1.165 + { 1.166 + // Look through our transfer data for the image 1.167 + static const char* const imageMimeTypes[] = { 1.168 + kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime }; 1.169 + nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive; 1.170 + for (uint32_t i = 0; !ptrPrimitive && i < ArrayLength(imageMimeTypes); i++) 1.171 + { 1.172 + aTransferable->GetTransferData(imageMimeTypes[i], getter_AddRefs(clip), &len); 1.173 + ptrPrimitive = do_QueryInterface(clip); 1.174 + } 1.175 + 1.176 + if (!ptrPrimitive) 1.177 + continue; 1.178 + 1.179 + nsCOMPtr<nsISupports> primitiveData; 1.180 + ptrPrimitive->GetData(getter_AddRefs(primitiveData)); 1.181 + nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData)); 1.182 + if (!image) // Not getting an image for an image mime type!? 1.183 + continue; 1.184 + 1.185 + RefPtr<SourceSurface> surface = 1.186 + image->GetFrame(imgIContainer::FRAME_CURRENT, 1.187 + imgIContainer::FLAG_SYNC_DECODE); 1.188 + if (!surface) 1.189 + continue; 1.190 + 1.191 + RefPtr<DataSourceSurface> dataSurface = 1.192 + surface->GetDataSurface(); 1.193 + if (!dataSurface) 1.194 + continue; 1.195 + 1.196 + DataSourceSurface::MappedSurface map; 1.197 + if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) 1.198 + continue; 1.199 + 1.200 + QImage qImage(map.mData, 1.201 + dataSurface->GetSize().width, 1.202 + dataSurface->GetSize().height, 1.203 + map.mStride, 1.204 + _moz2dformat_to_qformat(dataSurface->GetFormat())); 1.205 + 1.206 + dataSurface->Unmap(); 1.207 + 1.208 + // Add image to the mimeData 1.209 + mimeData->setImageData(qImage); 1.210 + imageAdded = true; 1.211 + } 1.212 + 1.213 + // Other flavors, adding data to clipboard "as is" 1.214 + else 1.215 + { 1.216 + rv = aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(clip), &len); 1.217 + // nothing found? 1.218 + if (!clip || NS_FAILED(rv)) 1.219 + continue; 1.220 + 1.221 + void *primitive_data = nullptr; 1.222 + nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr.get(), clip, 1.223 + &primitive_data, len); 1.224 + 1.225 + if (primitive_data) 1.226 + { 1.227 + QByteArray data ((const char *)primitive_data, len); 1.228 + // Add data to the mimeData 1.229 + mimeData->setData(flavorStr.get(), data); 1.230 + nsMemory::Free(primitive_data); 1.231 + } 1.232 + } 1.233 + } 1.234 + } 1.235 + 1.236 + // If we have some mime data, add it to the clipboard 1.237 + if(!mimeData->formats().isEmpty()) 1.238 + cb->setMimeData(mimeData, clipboardMode); 1.239 + else 1.240 + delete mimeData; 1.241 + 1.242 + return NS_OK; 1.243 +} 1.244 + 1.245 +// nsClipboard::GetNativeClipboardData ie. Paste 1.246 +// 1.247 +NS_IMETHODIMP 1.248 +nsClipboard::GetNativeClipboardData(nsITransferable *aTransferable, 1.249 + QClipboard::Mode clipboardMode) 1.250 +{ 1.251 + if (nullptr == aTransferable) 1.252 + { 1.253 + NS_WARNING("GetNativeClipboardData: Transferable is null!"); 1.254 + return NS_ERROR_FAILURE; 1.255 + } 1.256 + 1.257 + // get flavor list that includes all acceptable flavors (including 1.258 + // ones obtained through conversion) 1.259 + nsCOMPtr<nsISupportsArray> flavorList; 1.260 + nsresult errCode = aTransferable->FlavorsTransferableCanImport( 1.261 + getter_AddRefs(flavorList)); 1.262 + 1.263 + if (NS_FAILED(errCode)) 1.264 + { 1.265 + NS_WARNING("nsClipboard::GetNativeClipboardData(): no FlavorsTransferable!"); 1.266 + return NS_ERROR_FAILURE; 1.267 + } 1.268 + 1.269 + QClipboard *cb = QGuiApplication::clipboard(); 1.270 + const QMimeData *mimeData = cb->mimeData(clipboardMode); 1.271 + 1.272 + // Walk through flavors and see which flavor matches the one being pasted 1.273 + uint32_t flavorCount; 1.274 + flavorList->Count(&flavorCount); 1.275 + nsAutoCString foundFlavor; 1.276 + 1.277 + for (uint32_t i = 0; i < flavorCount; ++i) 1.278 + { 1.279 + nsCOMPtr<nsISupports> genericFlavor; 1.280 + flavorList->GetElementAt(i,getter_AddRefs(genericFlavor)); 1.281 + nsCOMPtr<nsISupportsCString> currentFlavor(do_QueryInterface( genericFlavor) ); 1.282 + 1.283 + if (currentFlavor) 1.284 + { 1.285 + nsXPIDLCString flavorStr; 1.286 + currentFlavor->ToString(getter_Copies(flavorStr)); 1.287 + 1.288 + // Ok, so which flavor the data being pasted could be? 1.289 + // Text? 1.290 + if (!strcmp(flavorStr.get(), kUnicodeMime) && mimeData->hasText()) 1.291 + { 1.292 + // Clipboard has text and flavor accepts text, so lets 1.293 + // handle the data as text 1.294 + foundFlavor = nsAutoCString(flavorStr); 1.295 + 1.296 + // Get the text data from clipboard 1.297 + QString text = mimeData->text(); 1.298 + const QChar *unicode = text.unicode(); 1.299 + // Is there a more correct way to get the size in UTF16? 1.300 + uint32_t len = (uint32_t) 2*text.size(); 1.301 + 1.302 + // And then to genericDataWrapper 1.303 + nsCOMPtr<nsISupports> genericDataWrapper; 1.304 + nsPrimitiveHelpers::CreatePrimitiveForData( 1.305 + foundFlavor.get(), 1.306 + (void*)unicode, 1.307 + len, 1.308 + getter_AddRefs(genericDataWrapper)); 1.309 + // Data is good, set it to the transferable 1.310 + aTransferable->SetTransferData(foundFlavor.get(), 1.311 + genericDataWrapper,len); 1.312 + // And thats all 1.313 + break; 1.314 + } 1.315 + 1.316 + // html? 1.317 + if (!strcmp(flavorStr.get(), kHTMLMime) && mimeData->hasHtml()) 1.318 + { 1.319 + // Clipboard has text/html and flavor accepts text/html, so lets 1.320 + // handle the data as text/html 1.321 + foundFlavor = nsAutoCString(flavorStr); 1.322 + 1.323 + // Get the text data from clipboard 1.324 + QString html = mimeData->html(); 1.325 + const QChar *unicode = html.unicode(); 1.326 + // Is there a more correct way to get the size in UTF16? 1.327 + uint32_t len = (uint32_t) 2*html.size(); 1.328 + 1.329 + // And then to genericDataWrapper 1.330 + nsCOMPtr<nsISupports> genericDataWrapper; 1.331 + nsPrimitiveHelpers::CreatePrimitiveForData( 1.332 + foundFlavor.get(), 1.333 + (void*)unicode, 1.334 + len, 1.335 + getter_AddRefs(genericDataWrapper)); 1.336 + // Data is good, set it to the transferable 1.337 + aTransferable->SetTransferData(foundFlavor.get(), 1.338 + genericDataWrapper,len); 1.339 + // And thats all 1.340 + break; 1.341 + } 1.342 + 1.343 + // Image? 1.344 + if (( !strcmp(flavorStr.get(), kJPEGImageMime) 1.345 + || !strcmp(flavorStr.get(), kJPGImageMime) 1.346 + || !strcmp(flavorStr.get(), kPNGImageMime) 1.347 + || !strcmp(flavorStr.get(), kGIFImageMime)) 1.348 + && mimeData->hasImage()) 1.349 + { 1.350 + // Try to retrieve an image from clipboard 1.351 + QImage image = cb->image(); 1.352 + if(image.isNull()) 1.353 + continue; 1.354 + 1.355 + // Lets set the image format 1.356 + QByteArray imageFormat; 1.357 + if (!strcmp(flavorStr.get(), kJPEGImageMime) || !strcmp(flavorStr.get(), kJPGImageMime)) 1.358 + imageFormat = "jpeg"; 1.359 + else if (!strcmp(flavorStr.get(), kPNGImageMime)) 1.360 + imageFormat = "png"; 1.361 + else if (!strcmp(flavorStr.get(), kGIFImageMime)) 1.362 + imageFormat = "gif"; 1.363 + else 1.364 + continue; 1.365 + 1.366 + // Write image from clippboard to a QByteArrayBuffer 1.367 + QByteArray imageData; 1.368 + QBuffer imageBuffer(&imageData); 1.369 + QImageWriter imageWriter(&imageBuffer, imageFormat); 1.370 + if(!imageWriter.write(image)) 1.371 + continue; 1.372 + 1.373 + // Add the data to inputstream 1.374 + nsCOMPtr<nsIInputStream> byteStream; 1.375 + NS_NewByteInputStream(getter_AddRefs(byteStream), imageData.constData(), 1.376 + imageData.size(), NS_ASSIGNMENT_COPY); 1.377 + // Data is good, set it to the transferable 1.378 + aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); 1.379 + 1.380 + imageBuffer.close(); 1.381 + 1.382 + // And thats all 1.383 + break; 1.384 + } 1.385 + 1.386 + // Other mimetype? 1.387 + // Trying to forward the data "as is" 1.388 + if(mimeData->hasFormat(flavorStr.get())) 1.389 + { 1.390 + // get the data from the clipboard 1.391 + QByteArray clipboardData = mimeData->data(flavorStr.get()); 1.392 + // And add it to genericDataWrapper 1.393 + nsCOMPtr<nsISupports> genericDataWrapper; 1.394 + nsPrimitiveHelpers::CreatePrimitiveForData( 1.395 + foundFlavor.get(), 1.396 + (void*) clipboardData.data(), 1.397 + clipboardData.size(), 1.398 + getter_AddRefs(genericDataWrapper)); 1.399 + 1.400 + // Data is good, set it to the transferable 1.401 + aTransferable->SetTransferData(foundFlavor.get(), 1.402 + genericDataWrapper,clipboardData.size()); 1.403 + // And thats all 1.404 + break; 1.405 + } 1.406 + } 1.407 + } 1.408 + 1.409 + return NS_OK; 1.410 +} 1.411 + 1.412 +NS_IMETHODIMP 1.413 +nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength, 1.414 + int32_t aWhichClipboard, bool *_retval) 1.415 +{ 1.416 + *_retval = false; 1.417 + if (aWhichClipboard != kGlobalClipboard) 1.418 + return NS_OK; 1.419 + 1.420 + // Which kind of data in the clipboard 1.421 + QClipboard *cb = QGuiApplication::clipboard(); 1.422 + const QMimeData *mimeData = cb->mimeData(); 1.423 + const char *flavor=nullptr; 1.424 + QStringList formats = mimeData->formats(); 1.425 + for (uint32_t i = 0; i < aLength; ++i) 1.426 + { 1.427 + flavor = aFlavorList[i]; 1.428 + if (flavor) 1.429 + { 1.430 + QString qflavor(flavor); 1.431 + 1.432 + if (strcmp(flavor,kTextMime) == 0) 1.433 + { 1.434 + NS_WARNING("DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD"); 1.435 + } 1.436 + 1.437 + // QClipboard says it has text/plain, mozilla wants to 1.438 + // know if the data is text/unicode -> interpret text/plain to text/unicode 1.439 + if (formats.contains(qflavor) || 1.440 + strcmp(flavor, kUnicodeMime) == 0) 1.441 + { 1.442 + // A match has been found, return' 1.443 + *_retval = true; 1.444 + break; 1.445 + } 1.446 + } 1.447 + } 1.448 + return NS_OK; 1.449 +} 1.450 + 1.451 +/** 1.452 + * Sets the transferable object 1.453 + */ 1.454 +NS_IMETHODIMP 1.455 +nsClipboard::SetData(nsITransferable *aTransferable, 1.456 + nsIClipboardOwner *aOwner, 1.457 + int32_t aWhichClipboard) 1.458 +{ 1.459 + // See if we can short cut 1.460 + if ( 1.461 + (aWhichClipboard == kGlobalClipboard 1.462 + && aTransferable == mGlobalTransferable.get() 1.463 + && aOwner == mGlobalOwner.get() 1.464 + ) 1.465 + || 1.466 + (aWhichClipboard == kSelectionClipboard 1.467 + && aTransferable == mSelectionTransferable.get() 1.468 + && aOwner == mSelectionOwner.get() 1.469 + ) 1.470 + ) 1.471 + { 1.472 + return NS_OK; 1.473 + } 1.474 + 1.475 + nsresult rv; 1.476 + if (!mPrivacyHandler) { 1.477 + rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler)); 1.478 + NS_ENSURE_SUCCESS(rv, rv); 1.479 + } 1.480 + rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable); 1.481 + NS_ENSURE_SUCCESS(rv, rv); 1.482 + 1.483 + EmptyClipboard(aWhichClipboard); 1.484 + 1.485 + QClipboard::Mode mode; 1.486 + 1.487 + if (kGlobalClipboard == aWhichClipboard) 1.488 + { 1.489 + mGlobalOwner = aOwner; 1.490 + mGlobalTransferable = aTransferable; 1.491 + 1.492 + mode = QClipboard::Clipboard; 1.493 + } 1.494 + else 1.495 + { 1.496 + mSelectionOwner = aOwner; 1.497 + mSelectionTransferable = aTransferable; 1.498 + 1.499 + mode = QClipboard::Selection; 1.500 + } 1.501 + return SetNativeClipboardData( aTransferable, mode ); 1.502 +} 1.503 + 1.504 +/** 1.505 + * Gets the transferable object 1.506 + */ 1.507 +NS_IMETHODIMP 1.508 +nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) 1.509 +{ 1.510 + if (nullptr != aTransferable) 1.511 + { 1.512 + QClipboard::Mode mode; 1.513 + if (kGlobalClipboard == aWhichClipboard) 1.514 + { 1.515 + mode = QClipboard::Clipboard; 1.516 + } 1.517 + else 1.518 + { 1.519 + mode = QClipboard::Selection; 1.520 + } 1.521 + return GetNativeClipboardData(aTransferable, mode); 1.522 + } 1.523 + else 1.524 + { 1.525 + NS_WARNING("nsClipboard::GetData(), aTransferable is NULL."); 1.526 + } 1.527 + return NS_ERROR_FAILURE; 1.528 +} 1.529 + 1.530 +NS_IMETHODIMP 1.531 +nsClipboard::EmptyClipboard(int32_t aWhichClipboard) 1.532 +{ 1.533 + if (aWhichClipboard == kSelectionClipboard) 1.534 + { 1.535 + if (mSelectionOwner) 1.536 + { 1.537 + mSelectionOwner->LosingOwnership(mSelectionTransferable); 1.538 + mSelectionOwner = nullptr; 1.539 + } 1.540 + mSelectionTransferable = nullptr; 1.541 + } 1.542 + else 1.543 + { 1.544 + if (mGlobalOwner) 1.545 + { 1.546 + mGlobalOwner->LosingOwnership(mGlobalTransferable); 1.547 + mGlobalOwner = nullptr; 1.548 + } 1.549 + mGlobalTransferable = nullptr; 1.550 + } 1.551 + 1.552 + return NS_OK; 1.553 +} 1.554 + 1.555 +NS_IMETHODIMP 1.556 +nsClipboard::SupportsSelectionClipboard(bool *_retval) 1.557 +{ 1.558 + NS_ENSURE_ARG_POINTER(_retval); 1.559 + 1.560 + QClipboard *cb = QGuiApplication::clipboard(); 1.561 + if (cb->supportsSelection()) 1.562 + { 1.563 + *_retval = true; // we support the selection clipboard 1.564 + } 1.565 + else 1.566 + { 1.567 + *_retval = false; 1.568 + } 1.569 + 1.570 + return NS_OK; 1.571 +} 1.572 + 1.573 +NS_IMETHODIMP 1.574 +nsClipboard::SupportsFindClipboard(bool* _retval) 1.575 +{ 1.576 + NS_ENSURE_ARG_POINTER(_retval); 1.577 + 1.578 + *_retval = false; 1.579 + return NS_OK; 1.580 +}