widget/xpwidgets/nsTransferable.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
parent 14
925c144e1f1f
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7 Notes to self:
     9 - at some point, strings will be accessible from JS, so we won't have to wrap
    10    flavors in an nsISupportsCString. Until then, we're kinda stuck with
    11    this crappy API of nsISupportsArrays.
    13 */
    16 #include "nsTransferable.h"
    17 #include "nsString.h"
    18 #include "nsReadableUtils.h"
    19 #include "nsTArray.h"
    20 #include "nsIFormatConverter.h"
    21 #include "nsIComponentManager.h"
    22 #include "nsCOMPtr.h"
    23 #include "nsXPCOM.h"
    24 #include "nsISupportsPrimitives.h"
    25 #include "nsMemory.h"
    26 #include "nsPrimitiveHelpers.h"
    27 #include "nsXPIDLString.h"
    28 #include "nsDirectoryServiceDefs.h"
    29 #include "nsDirectoryService.h"
    30 #include "nsCRT.h" 
    31 #include "nsNetUtil.h"
    32 #include "nsIOutputStream.h"
    33 #include "nsIInputStream.h"
    34 #include "nsIFile.h"
    35 #include "nsILoadContext.h"
    36 #include "nsAutoPtr.h"
    38 NS_IMPL_ISUPPORTS(nsTransferable, nsITransferable)
    40 uint32_t GetDataForFlavor (const nsTArray<DataStruct>& aArray,
    41                            const char* aDataFlavor)
    42 {
    43   for (uint32_t i = 0 ; i < aArray.Length () ; ++i) {
    44     if (aArray[i].GetFlavor().Equals (aDataFlavor))
    45       return i;
    46   }
    48   return aArray.NoIndex;
    49 }
    51 //-------------------------------------------------------------------------
    52 DataStruct::~DataStruct() 
    53 { 
    54   if (mCacheFileName) free(mCacheFileName); 
    55 }
    57 //-------------------------------------------------------------------------
    58 void
    59 DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen, bool aIsPrivateData )
    60 {
    61   // Now, check to see if we consider the data to be "too large"
    62   // as well as ensuring that private browsing mode is disabled
    63   if (aDataLen > kLargeDatasetSize && !aIsPrivateData) {
    64     // if so, cache it to disk instead of memory
    65     if ( NS_SUCCEEDED(WriteCache(aData, aDataLen)) )
    66       return;
    67     else
    68       NS_WARNING("Oh no, couldn't write data to the cache file");
    69   }
    71   mData    = aData;
    72   mDataLen = aDataLen;
    73 }
    76 //-------------------------------------------------------------------------
    77 void
    78 DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
    79 {
    80   // check here to see if the data is cached on disk
    81   if ( !mData && mCacheFileName ) {
    82     // if so, read it in and pass it back
    83     // ReadCache creates memory and copies the data into it.
    84     if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
    85       return;
    86     else {
    87       // oh shit, something went horribly wrong here.
    88       NS_WARNING("Oh no, couldn't read data in from the cache file");
    89       *aData = nullptr;
    90       *aDataLen = 0;
    91       return;
    92     }
    93   }
    95   *aData = mData;
    96   if ( mData )
    97     NS_ADDREF(*aData); 
    98   *aDataLen = mDataLen;
    99 }
   102 //-------------------------------------------------------------------------
   103 already_AddRefed<nsIFile>
   104 DataStruct::GetFileSpec(const char* aFileName)
   105 {
   106   nsCOMPtr<nsIFile> cacheFile;
   107   NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(cacheFile));
   109   if (!cacheFile)
   110     return nullptr;
   112   // if the param aFileName contains a name we should use that
   113   // because the file probably already exists
   114   // otherwise create a unique name
   115   if (!aFileName) {
   116     cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
   117     cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
   118   } else {
   119     cacheFile->AppendNative(nsDependentCString(aFileName));
   120   }
   122   return cacheFile.forget();
   123 }
   126 //-------------------------------------------------------------------------
   127 nsresult
   128 DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
   129 {
   130   // Get a new path and file to the temp directory
   131   nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
   132   if (cacheFile) {
   133     // remember the file name
   134     if (!mCacheFileName) {
   135       nsXPIDLCString fName;
   136       cacheFile->GetNativeLeafName(fName);
   137       mCacheFileName = strdup(fName);
   138     }
   140     // write out the contents of the clipboard
   141     // to the file
   142     //uint32_t bytes;
   143     nsCOMPtr<nsIOutputStream> outStr;
   145     NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
   146                                 cacheFile);
   148     if (!outStr) return NS_ERROR_FAILURE;
   150     void* buff = nullptr;
   151     nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
   152     if ( buff ) {
   153       uint32_t ignored;
   154       outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
   155       nsMemory::Free(buff);
   156       return NS_OK;
   157     }
   158   }
   159   return NS_ERROR_FAILURE;
   160 }
   163 //-------------------------------------------------------------------------
   164 nsresult
   165 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
   166 {
   167   // if we don't have a cache filename we are out of luck
   168   if (!mCacheFileName)
   169     return NS_ERROR_FAILURE;
   171   // get the path and file name
   172   nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
   173   bool exists;
   174   if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
   175     // get the size of the file
   176     int64_t fileSize;
   177     int64_t max32 = 0xFFFFFFFF;
   178     cacheFile->GetFileSize(&fileSize);
   179     if (fileSize > max32)
   180       return NS_ERROR_OUT_OF_MEMORY;
   182     uint32_t size = uint32_t(fileSize);
   183     // create new memory for the large clipboard data
   184     nsAutoArrayPtr<char> data(new char[size]);
   185     if ( !data )
   186       return NS_ERROR_OUT_OF_MEMORY;
   188     // now read it all in
   189     nsCOMPtr<nsIInputStream> inStr;
   190     NS_NewLocalFileInputStream( getter_AddRefs(inStr),
   191                                 cacheFile);
   193     if (!cacheFile) return NS_ERROR_FAILURE;
   195     nsresult rv = inStr->Read(data, fileSize, aDataLen);
   197     // make sure we got all the data ok
   198     if (NS_SUCCEEDED(rv) && *aDataLen == size) {
   199       nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
   200       return *aData ? NS_OK : NS_ERROR_FAILURE;
   201     }
   203     // zero the return params
   204     *aData    = nullptr;
   205     *aDataLen = 0;
   206   }
   208   return NS_ERROR_FAILURE;
   209 }
   212 //-------------------------------------------------------------------------
   213 //
   214 // Transferable constructor
   215 //
   216 //-------------------------------------------------------------------------
   217 nsTransferable::nsTransferable()
   218   : mPrivateData(false)
   219 #ifdef DEBUG
   220   , mInitialized(false)
   221 #endif
   222 {
   223 }
   225 //-------------------------------------------------------------------------
   226 //
   227 // Transferable destructor
   228 //
   229 //-------------------------------------------------------------------------
   230 nsTransferable::~nsTransferable()
   231 {
   232 }
   235 NS_IMETHODIMP
   236 nsTransferable::Init(nsILoadContext* aContext)
   237 {
   238   MOZ_ASSERT(!mInitialized);
   240   if (aContext) {
   241     mPrivateData = aContext->UsePrivateBrowsing();
   242   }
   243 #ifdef DEBUG
   244   mInitialized = true;
   245 #endif
   246   return NS_OK;
   247 }
   249 //
   250 // GetTransferDataFlavors
   251 //
   252 // Returns a copy of the internal list of flavors. This does NOT take into 
   253 // account any converter that may be registered. This list consists of
   254 // nsISupportsCString objects so that the flavor list can be accessed from JS.
   255 //
   256 nsresult
   257 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
   258 {
   259   MOZ_ASSERT(mInitialized);
   261   nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
   262   if (NS_FAILED(rv)) return rv;
   264   for ( uint32_t i=0; i<mDataArray.Length(); ++i ) {
   265     DataStruct& data = mDataArray.ElementAt(i);
   266     nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
   267     if ( flavorWrapper ) {
   268       flavorWrapper->SetData ( data.GetFlavor() );
   269       nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
   270       (*aDataFlavorList)->AppendElement( genericWrapper );
   271     }
   272   }
   274   return NS_OK;
   275 }
   278 //
   279 // GetTransferData
   280 //
   281 // Returns the data of the requested flavor, obtained from either having the data on hand or
   282 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
   283 // accessible from JS.
   284 //
   285 NS_IMETHODIMP
   286 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
   287 {
   288   MOZ_ASSERT(mInitialized);
   290   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
   292   nsresult rv = NS_OK;
   293   nsCOMPtr<nsISupports> savedData;
   295   // first look and see if the data is present in one of the intrinsic flavors
   296   uint32_t i;
   297   for (i = 0; i < mDataArray.Length(); ++i ) {
   298     DataStruct& data = mDataArray.ElementAt(i);
   299     if ( data.GetFlavor().Equals(aFlavor) ) {
   300       nsCOMPtr<nsISupports> dataBytes;
   301       uint32_t len;
   302       data.GetData(getter_AddRefs(dataBytes), &len);
   303       if (len == kFlavorHasDataProvider && dataBytes) {
   304         // do we have a data provider?
   305         nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
   306         if (dataProvider) {
   307           rv = dataProvider->GetFlavorData(this, aFlavor,
   308                                            getter_AddRefs(dataBytes), &len);
   309           if (NS_FAILED(rv))
   310             break;    // the provider failed. fall into the converter code below.
   311         }
   312       }
   313       if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
   314         *aDataLen = len;
   315         dataBytes.forget(aData);
   316         return NS_OK;
   317       }
   318       savedData = dataBytes;  // return this if format converter fails
   319       break;
   320     }
   321   }
   323   bool found = false;
   325   // if not, try using a format converter to get the requested flavor
   326   if ( mFormatConv ) {
   327     for (i = 0; i < mDataArray.Length(); ++i) {
   328       DataStruct& data = mDataArray.ElementAt(i);
   329       bool canConvert = false;
   330       mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
   331       if ( canConvert ) {
   332         nsCOMPtr<nsISupports> dataBytes;
   333         uint32_t len;
   334         data.GetData(getter_AddRefs(dataBytes), &len);
   335         if (len == kFlavorHasDataProvider && dataBytes) {
   336           // do we have a data provider?
   337           nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
   338           if (dataProvider) {
   339             rv = dataProvider->GetFlavorData(this, aFlavor,
   340                                              getter_AddRefs(dataBytes), &len);
   341             if (NS_FAILED(rv))
   342               break;  // give up
   343           }
   344         }
   345         mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
   346         found = true;
   347         break;
   348       }
   349     }
   350   }
   352   // for backward compatibility
   353   if (!found) {
   354     savedData.forget(aData);
   355     *aDataLen = 0;
   356   }
   358   return found ? NS_OK : NS_ERROR_FAILURE;
   359 }
   362 //
   363 // GetAnyTransferData
   364 //
   365 // Returns the data of the first flavor found. Caller is responsible for deleting the
   366 // flavor string.
   367 // 
   368 NS_IMETHODIMP
   369 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, uint32_t *aDataLen)
   370 {
   371   MOZ_ASSERT(mInitialized);
   373   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
   375   for ( uint32_t i=0; i < mDataArray.Length(); ++i ) {
   376     DataStruct& data = mDataArray.ElementAt(i);
   377     if (data.IsDataAvailable()) {
   378       *aFlavor = ToNewCString(data.GetFlavor());
   379       data.GetData(aData, aDataLen);
   380       return NS_OK;
   381     }
   382   }
   384   return NS_ERROR_FAILURE;
   385 }
   388 //
   389 // SetTransferData
   390 //
   391 //
   392 // 
   393 NS_IMETHODIMP
   394 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
   395 {
   396   MOZ_ASSERT(mInitialized);
   398   NS_ENSURE_ARG(aFlavor);
   400   // first check our intrinsic flavors to see if one has been registered.
   401   uint32_t i = 0;
   402   for (i = 0; i < mDataArray.Length(); ++i) {
   403     DataStruct& data = mDataArray.ElementAt(i);
   404     if ( data.GetFlavor().Equals(aFlavor) ) {
   405       data.SetData ( aData, aDataLen, mPrivateData );
   406       return NS_OK;
   407     }
   408   }
   410   // if not, try using a format converter to find a flavor to put the data in
   411   if ( mFormatConv ) {
   412     for (i = 0; i < mDataArray.Length(); ++i) {
   413       DataStruct& data = mDataArray.ElementAt(i);
   414       bool canConvert = false;
   415       mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
   417       if ( canConvert ) {
   418         nsCOMPtr<nsISupports> ConvertedData;
   419         uint32_t ConvertedLen;
   420         mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
   421         data.SetData(ConvertedData, ConvertedLen, mPrivateData);
   422         return NS_OK;
   423       }
   424     }
   425   }
   427   // Can't set data neither directly nor through converter. Just add this flavor and try again
   428   nsresult result = NS_ERROR_FAILURE;
   429   if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
   430     result = SetTransferData (aFlavor, aData, aDataLen);
   432   return result;
   433 }
   436 //
   437 // AddDataFlavor
   438 //
   439 // Adds a data flavor to our list with no data. Error if it already exists.
   440 // 
   441 NS_IMETHODIMP
   442 nsTransferable::AddDataFlavor(const char *aDataFlavor)
   443 {
   444   MOZ_ASSERT(mInitialized);
   446   if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
   447     return NS_ERROR_FAILURE;
   449   // Create a new "slot" for the data
   450   mDataArray.AppendElement(DataStruct ( aDataFlavor ));
   452   return NS_OK;
   453 }
   456 //
   457 // RemoveDataFlavor
   458 //
   459 // Removes a data flavor (and causes the data to be destroyed). Error if
   460 // the requested flavor is not present.
   461 //
   462 NS_IMETHODIMP
   463 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
   464 {
   465   MOZ_ASSERT(mInitialized);
   467   uint32_t idx;
   468   if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
   469     mDataArray.RemoveElementAt (idx);
   470     return NS_OK;
   471   }
   472   return NS_ERROR_FAILURE;
   473 }
   476 /**
   477   * 
   478   *
   479   */
   480 NS_IMETHODIMP
   481 nsTransferable::IsLargeDataSet(bool *_retval)
   482 {
   483   MOZ_ASSERT(mInitialized);
   485   NS_ENSURE_ARG_POINTER(_retval);
   486   *_retval = false;
   487   return NS_OK;
   488 }
   491 /**
   492   * 
   493   *
   494   */
   495 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
   496 {
   497   MOZ_ASSERT(mInitialized);
   499   mFormatConv = aConverter;
   500   return NS_OK;
   501 }
   504 /**
   505   * 
   506   *
   507   */
   508 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
   509 {
   510   MOZ_ASSERT(mInitialized);
   512   NS_ENSURE_ARG_POINTER(aConverter);
   513   *aConverter = mFormatConv;
   514   NS_IF_ADDREF(*aConverter);
   515   return NS_OK;
   516 }
   519 //
   520 // FlavorsTransferableCanImport
   521 //
   522 // Computes a list of flavors that the transferable can accept into it, either through
   523 // intrinsic knowledge or input data converters.
   524 //
   525 NS_IMETHODIMP
   526 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
   527 {
   528   MOZ_ASSERT(mInitialized);
   530   NS_ENSURE_ARG_POINTER(_retval);
   532   // Get the flavor list, and on to the end of it, append the list of flavors we
   533   // can also get to through a converter. This is so that we can just walk the list
   534   // in one go, looking for the desired flavor.
   535   GetTransferDataFlavors(_retval);                        // addrefs
   536   nsCOMPtr<nsIFormatConverter> converter;
   537   GetConverter(getter_AddRefs(converter));
   538   if ( converter ) {
   539     nsCOMPtr<nsISupportsArray> convertedList;
   540     converter->GetInputDataFlavors(getter_AddRefs(convertedList));
   542     if ( convertedList ) {
   543       uint32_t importListLen;
   544       convertedList->Count(&importListLen);
   546       for ( uint32_t i=0; i < importListLen; ++i ) {
   547         nsCOMPtr<nsISupports> genericFlavor;
   548         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   550         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
   551         nsAutoCString flavorStr;
   552         flavorWrapper->GetData( flavorStr );
   554         if (GetDataForFlavor (mDataArray, flavorStr.get())
   555             == mDataArray.NoIndex) // Don't append if already in intrinsic list
   556           (*_retval)->AppendElement (genericFlavor);
   557       } // foreach flavor that can be converted to
   558     }
   559   } // if a converter exists
   561   return NS_OK;  
   562 } // FlavorsTransferableCanImport
   565 //
   566 // FlavorsTransferableCanExport
   567 //
   568 // Computes a list of flavors that the transferable can export, either through
   569 // intrinsic knowledge or output data converters.
   570 //
   571 NS_IMETHODIMP
   572 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
   573 {
   574   MOZ_ASSERT(mInitialized);
   576   NS_ENSURE_ARG_POINTER(_retval);
   578   // Get the flavor list, and on to the end of it, append the list of flavors we
   579   // can also get to through a converter. This is so that we can just walk the list
   580   // in one go, looking for the desired flavor.
   581   GetTransferDataFlavors(_retval);  // addrefs
   582   nsCOMPtr<nsIFormatConverter> converter;
   583   GetConverter(getter_AddRefs(converter));
   584   if ( converter ) {
   585     nsCOMPtr<nsISupportsArray> convertedList;
   586     converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
   588     if ( convertedList ) {
   589       uint32_t importListLen;
   590       convertedList->Count(&importListLen);
   592       for ( uint32_t i=0; i < importListLen; ++i ) {
   593         nsCOMPtr<nsISupports> genericFlavor;
   594         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   596         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
   597         nsAutoCString flavorStr;
   598         flavorWrapper->GetData( flavorStr );
   600         if (GetDataForFlavor (mDataArray, flavorStr.get())
   601             == mDataArray.NoIndex) // Don't append if already in intrinsic list
   602           (*_retval)->AppendElement (genericFlavor);
   603       } // foreach flavor that can be converted to
   604     }
   605   } // if a converter exists
   607   return NS_OK;
   608 } // FlavorsTransferableCanExport
   610 NS_IMETHODIMP
   611 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
   612 {
   613   MOZ_ASSERT(mInitialized);
   615   NS_ENSURE_ARG_POINTER(aIsPrivateData);
   617   *aIsPrivateData = mPrivateData;
   619   return NS_OK;
   620 }
   622 NS_IMETHODIMP
   623 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
   624 {
   625   MOZ_ASSERT(mInitialized);
   627   mPrivateData = aIsPrivateData;
   629   return NS_OK;
   630 }

mercurial