widget/qt/nsClipboard.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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 <QGuiApplication>
     6 #include <QMimeData>
     7 #include <QString>
     8 #include <QStringList>
     9 #include <QByteArray>
    10 #include <QImage>
    11 #include <QImageWriter>
    12 #include <QBuffer>
    14 #include "gfxPlatform.h"
    15 #include "mozilla/ArrayUtils.h"
    16 #include "mozilla/gfx/2D.h"
    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"
    27 #include "imgIContainer.h"
    28 #include "gfxImageSurface.h"
    30 using namespace mozilla;
    31 using namespace mozilla::gfx;
    33 NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard)
    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 }
    49 //-------------------------------------------------------------------------
    50 //
    51 // nsClipboard destructor
    52 //
    53 //-------------------------------------------------------------------------
    54 nsClipboard::~nsClipboard()
    55 {
    56 }
    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 }
    73 // nsClipboard::SetNativeClipboardData ie. Copy
    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     }
    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) );
    90     if (NS_FAILED(rv))
    91     {
    92         NS_WARNING("nsClipboard::SetNativeClipboardData(): no FlavorsTransferable !");
    93         return NS_ERROR_FAILURE;
    94     }
    96     QClipboard *cb = QGuiApplication::clipboard();
    97     QMimeData *mimeData = new QMimeData;
    99     uint32_t flavorCount = 0;
   100     flavorList->Count(&flavorCount);
   101     bool imageAdded = false;
   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));
   109         if (currentFlavor)
   110         {
   111             // flavorStr is the mime type
   112             nsXPIDLCString flavorStr;
   113             currentFlavor->ToString(getter_Copies(flavorStr));
   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;
   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;
   129                 nsAutoString utf16string;
   130                 wideString->GetData(utf16string);
   131                 QString str = QString::fromUtf16((const ushort*)utf16string.get());
   133                 // Add text to the mimeData
   134                 mimeData->setText(str);
   135             }
   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;
   146                 nsAutoString utf16string;
   147                 wideString->GetData(utf16string);
   148                 QString str = QString::fromUtf16((const ushort*)utf16string.get());
   150                 // Add html to the mimeData
   151                 mimeData->setHtml(str);
   152             }
   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                 }
   173                 if (!ptrPrimitive)
   174                     continue;
   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;
   182                 RefPtr<SourceSurface> surface =
   183                   image->GetFrame(imgIContainer::FRAME_CURRENT,
   184                                   imgIContainer::FLAG_SYNC_DECODE);
   185                 if (!surface)
   186                   continue;
   188                 RefPtr<DataSourceSurface> dataSurface =
   189                   surface->GetDataSurface();
   190                 if (!dataSurface)
   191                   continue;
   193                 DataSourceSurface::MappedSurface map;
   194                 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map))
   195                   continue;
   197                 QImage qImage(map.mData,
   198                               dataSurface->GetSize().width,
   199                               dataSurface->GetSize().height,
   200                               map.mStride,
   201                               _moz2dformat_to_qformat(dataSurface->GetFormat()));
   203                 dataSurface->Unmap();
   205                 // Add image to the mimeData
   206                 mimeData->setImageData(qImage);
   207                 imageAdded = true;
   208             }
   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;
   218                 void *primitive_data = nullptr;
   219                 nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr.get(), clip,
   220                                                             &primitive_data, len);
   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     }
   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;
   239     return NS_OK;
   240 }
   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     }
   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));
   260     if (NS_FAILED(errCode))
   261     {
   262         NS_WARNING("nsClipboard::GetNativeClipboardData(): no FlavorsTransferable!");
   263         return NS_ERROR_FAILURE;
   264     }
   266     QClipboard *cb = QGuiApplication::clipboard();
   267     const QMimeData *mimeData = cb->mimeData(clipboardMode);
   269     // Walk through flavors and see which flavor matches the one being pasted
   270     uint32_t flavorCount;
   271     flavorList->Count(&flavorCount);
   272     nsAutoCString foundFlavor;
   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) );
   280         if (currentFlavor)
   281         {
   282             nsXPIDLCString flavorStr;
   283             currentFlavor->ToString(getter_Copies(flavorStr));
   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);
   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();
   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             }
   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);
   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();
   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             }
   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;
   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;
   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;
   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*));
   377                 imageBuffer.close();
   379                 // And thats all
   380                 break;
   381             }
   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));
   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     }
   406     return NS_OK;
   407 }
   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;
   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);
   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             }
   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 }
   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     }
   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);
   480     EmptyClipboard(aWhichClipboard);
   482     QClipboard::Mode mode;
   484     if (kGlobalClipboard == aWhichClipboard)
   485     {
   486         mGlobalOwner = aOwner;
   487         mGlobalTransferable = aTransferable;
   489         mode = QClipboard::Clipboard;
   490     }
   491     else
   492     {
   493         mSelectionOwner = aOwner;
   494         mSelectionTransferable = aTransferable;
   496         mode = QClipboard::Selection;
   497     }
   498     return SetNativeClipboardData( aTransferable, mode );
   499 }
   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 }
   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     }
   549     return NS_OK;
   550 }
   552 NS_IMETHODIMP
   553 nsClipboard::SupportsSelectionClipboard(bool *_retval)
   554 {
   555     NS_ENSURE_ARG_POINTER(_retval);
   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     }
   567     return NS_OK;
   568 }
   570 NS_IMETHODIMP
   571 nsClipboard::SupportsFindClipboard(bool* _retval)
   572 {
   573   NS_ENSURE_ARG_POINTER(_retval);
   575   *_retval = false;
   576   return NS_OK;
   577 }

mercurial