widget/xpwidgets/nsTransferable.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
parent 3
141e0f1194b1
child 9
a63d609f5ebe
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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 "mozilla/Preferences.h"
    18 #include "nsString.h"
    19 #include "nsReadableUtils.h"
    20 #include "nsTArray.h"
    21 #include "nsIFormatConverter.h"
    22 #include "nsIComponentManager.h"
    23 #include "nsCOMPtr.h"
    24 #include "nsXPCOM.h"
    25 #include "nsISupportsPrimitives.h"
    26 #include "nsMemory.h"
    27 #include "nsPrimitiveHelpers.h"
    28 #include "nsXPIDLString.h"
    29 #include "nsDirectoryServiceDefs.h"
    30 #include "nsDirectoryService.h"
    31 #include "nsCRT.h" 
    32 #include "nsNetUtil.h"
    33 #include "nsIOutputStream.h"
    34 #include "nsIInputStream.h"
    35 #include "nsIFile.h"
    36 #include "nsILoadContext.h"
    37 #include "nsAutoPtr.h"
    39 NS_IMPL_ISUPPORTS(nsTransferable, nsITransferable)
    41 uint32_t GetDataForFlavor (const nsTArray<DataStruct>& aArray,
    42                            const char* aDataFlavor)
    43 {
    44   for (uint32_t i = 0 ; i < aArray.Length () ; ++i) {
    45     if (aArray[i].GetFlavor().Equals (aDataFlavor))
    46       return i;
    47   }
    49   return aArray.NoIndex;
    50 }
    52 //-------------------------------------------------------------------------
    53 DataStruct::~DataStruct() 
    54 { 
    55   if (mCacheFileName) free(mCacheFileName); 
    56 }
    58 //-------------------------------------------------------------------------
    59 void
    60 DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen )
    61 {
    62   // First, prepare for conditional caching according to isolation mode
    63   int32_t isolationState = mozilla::Preferences::GetInt("privacy.thirdparty.isolate");
    65   // Now, check to see if we consider the data to be "too large"
    66   if (aDataLen > kLargeDatasetSize && isolationState == 0) {
    67     // if so, cache it to disk instead of memory
    68     if ( NS_SUCCEEDED(WriteCache(aData, aDataLen)) )
    69       return;
    70     else
    71 			NS_WARNING("Oh no, couldn't write data to the cache file");   
    72   } 
    74   mData    = aData;
    75   mDataLen = aDataLen;  
    76 }
    79 //-------------------------------------------------------------------------
    80 void
    81 DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
    82 {
    83   // check here to see if the data is cached on disk
    84   if ( !mData && mCacheFileName ) {
    85     // if so, read it in and pass it back
    86     // ReadCache creates memory and copies the data into it.
    87     if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
    88       return;
    89     else {
    90       // oh shit, something went horribly wrong here.
    91       NS_WARNING("Oh no, couldn't read data in from the cache file");
    92       *aData = nullptr;
    93       *aDataLen = 0;
    94       return;
    95     }
    96   }
    98   *aData = mData;
    99   if ( mData )
   100     NS_ADDREF(*aData); 
   101   *aDataLen = mDataLen;
   102 }
   105 //-------------------------------------------------------------------------
   106 already_AddRefed<nsIFile>
   107 DataStruct::GetFileSpec(const char* aFileName)
   108 {
   109   nsCOMPtr<nsIFile> cacheFile;
   110   NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(cacheFile));
   112   if (!cacheFile)
   113     return nullptr;
   115   // if the param aFileName contains a name we should use that
   116   // because the file probably already exists
   117   // otherwise create a unique name
   118   if (!aFileName) {
   119     cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
   120     cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
   121   } else {
   122     cacheFile->AppendNative(nsDependentCString(aFileName));
   123   }
   125   return cacheFile.forget();
   126 }
   129 //-------------------------------------------------------------------------
   130 nsresult
   131 DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
   132 {
   133   // Get a new path and file to the temp directory
   134   nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
   135   if (cacheFile) {
   136     // remember the file name
   137     if (!mCacheFileName) {
   138       nsXPIDLCString fName;
   139       cacheFile->GetNativeLeafName(fName);
   140       mCacheFileName = strdup(fName);
   141     }
   143     // write out the contents of the clipboard
   144     // to the file
   145     //uint32_t bytes;
   146     nsCOMPtr<nsIOutputStream> outStr;
   148     NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
   149                                 cacheFile);
   151     if (!outStr) return NS_ERROR_FAILURE;
   153     void* buff = nullptr;
   154     nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
   155     if ( buff ) {
   156       uint32_t ignored;
   157       outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
   158       nsMemory::Free(buff);
   159       return NS_OK;
   160     }
   161   }
   162   return NS_ERROR_FAILURE;
   163 }
   166 //-------------------------------------------------------------------------
   167 nsresult
   168 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
   169 {
   170   // if we don't have a cache filename we are out of luck
   171   if (!mCacheFileName)
   172     return NS_ERROR_FAILURE;
   174   // get the path and file name
   175   nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
   176   bool exists;
   177   if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
   178     // get the size of the file
   179     int64_t fileSize;
   180     int64_t max32 = 0xFFFFFFFF;
   181     cacheFile->GetFileSize(&fileSize);
   182     if (fileSize > max32)
   183       return NS_ERROR_OUT_OF_MEMORY;
   185     uint32_t size = uint32_t(fileSize);
   186     // create new memory for the large clipboard data
   187     nsAutoArrayPtr<char> data(new char[size]);
   188     if ( !data )
   189       return NS_ERROR_OUT_OF_MEMORY;
   191     // now read it all in
   192     nsCOMPtr<nsIInputStream> inStr;
   193     NS_NewLocalFileInputStream( getter_AddRefs(inStr),
   194                                 cacheFile);
   196     if (!cacheFile) return NS_ERROR_FAILURE;
   198     nsresult rv = inStr->Read(data, fileSize, aDataLen);
   200     // make sure we got all the data ok
   201     if (NS_SUCCEEDED(rv) && *aDataLen == size) {
   202       nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
   203       return *aData ? NS_OK : NS_ERROR_FAILURE;
   204     }
   206     // zero the return params
   207     *aData    = nullptr;
   208     *aDataLen = 0;
   209   }
   211   return NS_ERROR_FAILURE;
   212 }
   215 //-------------------------------------------------------------------------
   216 //
   217 // Transferable constructor
   218 //
   219 //-------------------------------------------------------------------------
   220 nsTransferable::nsTransferable()
   221   : mPrivateData(false)
   222 #ifdef DEBUG
   223   , mInitialized(false)
   224 #endif
   225 {
   226 }
   228 //-------------------------------------------------------------------------
   229 //
   230 // Transferable destructor
   231 //
   232 //-------------------------------------------------------------------------
   233 nsTransferable::~nsTransferable()
   234 {
   235 }
   238 NS_IMETHODIMP
   239 nsTransferable::Init(nsILoadContext* aContext)
   240 {
   241   MOZ_ASSERT(!mInitialized);
   243   if (aContext) {
   244     mPrivateData = aContext->UsePrivateBrowsing();
   245   }
   246 #ifdef DEBUG
   247   mInitialized = true;
   248 #endif
   249   return NS_OK;
   250 }
   252 //
   253 // GetTransferDataFlavors
   254 //
   255 // Returns a copy of the internal list of flavors. This does NOT take into 
   256 // account any converter that may be registered. This list consists of
   257 // nsISupportsCString objects so that the flavor list can be accessed from JS.
   258 //
   259 nsresult
   260 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
   261 {
   262   MOZ_ASSERT(mInitialized);
   264   nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
   265   if (NS_FAILED(rv)) return rv;
   267   for ( uint32_t i=0; i<mDataArray.Length(); ++i ) {
   268     DataStruct& data = mDataArray.ElementAt(i);
   269     nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
   270     if ( flavorWrapper ) {
   271       flavorWrapper->SetData ( data.GetFlavor() );
   272       nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
   273       (*aDataFlavorList)->AppendElement( genericWrapper );
   274     }
   275   }
   277   return NS_OK;
   278 }
   281 //
   282 // GetTransferData
   283 //
   284 // Returns the data of the requested flavor, obtained from either having the data on hand or
   285 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
   286 // accessible from JS.
   287 //
   288 NS_IMETHODIMP
   289 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
   290 {
   291   MOZ_ASSERT(mInitialized);
   293   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
   295   nsresult rv = NS_OK;
   296   nsCOMPtr<nsISupports> savedData;
   298   // first look and see if the data is present in one of the intrinsic flavors
   299   uint32_t i;
   300   for (i = 0; i < mDataArray.Length(); ++i ) {
   301     DataStruct& data = mDataArray.ElementAt(i);
   302     if ( data.GetFlavor().Equals(aFlavor) ) {
   303       nsCOMPtr<nsISupports> dataBytes;
   304       uint32_t len;
   305       data.GetData(getter_AddRefs(dataBytes), &len);
   306       if (len == kFlavorHasDataProvider && dataBytes) {
   307         // do we have a data provider?
   308         nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
   309         if (dataProvider) {
   310           rv = dataProvider->GetFlavorData(this, aFlavor,
   311                                            getter_AddRefs(dataBytes), &len);
   312           if (NS_FAILED(rv))
   313             break;    // the provider failed. fall into the converter code below.
   314         }
   315       }
   316       if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
   317         *aDataLen = len;
   318         dataBytes.forget(aData);
   319         return NS_OK;
   320       }
   321       savedData = dataBytes;  // return this if format converter fails
   322       break;
   323     }
   324   }
   326   bool found = false;
   328   // if not, try using a format converter to get the requested flavor
   329   if ( mFormatConv ) {
   330     for (i = 0; i < mDataArray.Length(); ++i) {
   331       DataStruct& data = mDataArray.ElementAt(i);
   332       bool canConvert = false;
   333       mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
   334       if ( canConvert ) {
   335         nsCOMPtr<nsISupports> dataBytes;
   336         uint32_t len;
   337         data.GetData(getter_AddRefs(dataBytes), &len);
   338         if (len == kFlavorHasDataProvider && dataBytes) {
   339           // do we have a data provider?
   340           nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
   341           if (dataProvider) {
   342             rv = dataProvider->GetFlavorData(this, aFlavor,
   343                                              getter_AddRefs(dataBytes), &len);
   344             if (NS_FAILED(rv))
   345               break;  // give up
   346           }
   347         }
   348         mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
   349         found = true;
   350         break;
   351       }
   352     }
   353   }
   355   // for backward compatibility
   356   if (!found) {
   357     savedData.forget(aData);
   358     *aDataLen = 0;
   359   }
   361   return found ? NS_OK : NS_ERROR_FAILURE;
   362 }
   365 //
   366 // GetAnyTransferData
   367 //
   368 // Returns the data of the first flavor found. Caller is responsible for deleting the
   369 // flavor string.
   370 // 
   371 NS_IMETHODIMP
   372 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, uint32_t *aDataLen)
   373 {
   374   MOZ_ASSERT(mInitialized);
   376   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
   378   for ( uint32_t i=0; i < mDataArray.Length(); ++i ) {
   379     DataStruct& data = mDataArray.ElementAt(i);
   380     if (data.IsDataAvailable()) {
   381       *aFlavor = ToNewCString(data.GetFlavor());
   382       data.GetData(aData, aDataLen);
   383       return NS_OK;
   384     }
   385   }
   387   return NS_ERROR_FAILURE;
   388 }
   391 //
   392 // SetTransferData
   393 //
   394 //
   395 // 
   396 NS_IMETHODIMP
   397 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
   398 {
   399   MOZ_ASSERT(mInitialized);
   401   NS_ENSURE_ARG(aFlavor);
   403   // first check our intrinsic flavors to see if one has been registered.
   404   uint32_t i = 0;
   405   for (i = 0; i < mDataArray.Length(); ++i) {
   406     DataStruct& data = mDataArray.ElementAt(i);
   407     if ( data.GetFlavor().Equals(aFlavor) ) {
   408       data.SetData ( aData, aDataLen );
   409       return NS_OK;
   410     }
   411   }
   413   // if not, try using a format converter to find a flavor to put the data in
   414   if ( mFormatConv ) {
   415     for (i = 0; i < mDataArray.Length(); ++i) {
   416       DataStruct& data = mDataArray.ElementAt(i);
   417       bool canConvert = false;
   418       mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
   420       if ( canConvert ) {
   421         nsCOMPtr<nsISupports> ConvertedData;
   422         uint32_t ConvertedLen;
   423         mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
   424         data.SetData(ConvertedData, ConvertedLen);
   425         return NS_OK;
   426       }
   427     }
   428   }
   430   // Can't set data neither directly nor through converter. Just add this flavor and try again
   431   nsresult result = NS_ERROR_FAILURE;
   432   if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
   433     result = SetTransferData (aFlavor, aData, aDataLen);
   435   return result;
   436 }
   439 //
   440 // AddDataFlavor
   441 //
   442 // Adds a data flavor to our list with no data. Error if it already exists.
   443 // 
   444 NS_IMETHODIMP
   445 nsTransferable::AddDataFlavor(const char *aDataFlavor)
   446 {
   447   MOZ_ASSERT(mInitialized);
   449   if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
   450     return NS_ERROR_FAILURE;
   452   // Create a new "slot" for the data
   453   mDataArray.AppendElement(DataStruct ( aDataFlavor ));
   455   return NS_OK;
   456 }
   459 //
   460 // RemoveDataFlavor
   461 //
   462 // Removes a data flavor (and causes the data to be destroyed). Error if
   463 // the requested flavor is not present.
   464 //
   465 NS_IMETHODIMP
   466 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
   467 {
   468   MOZ_ASSERT(mInitialized);
   470   uint32_t idx;
   471   if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
   472     mDataArray.RemoveElementAt (idx);
   473     return NS_OK;
   474   }
   475   return NS_ERROR_FAILURE;
   476 }
   479 /**
   480   * 
   481   *
   482   */
   483 NS_IMETHODIMP
   484 nsTransferable::IsLargeDataSet(bool *_retval)
   485 {
   486   MOZ_ASSERT(mInitialized);
   488   NS_ENSURE_ARG_POINTER(_retval);
   489   *_retval = false;
   490   return NS_OK;
   491 }
   494 /**
   495   * 
   496   *
   497   */
   498 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
   499 {
   500   MOZ_ASSERT(mInitialized);
   502   mFormatConv = aConverter;
   503   return NS_OK;
   504 }
   507 /**
   508   * 
   509   *
   510   */
   511 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
   512 {
   513   MOZ_ASSERT(mInitialized);
   515   NS_ENSURE_ARG_POINTER(aConverter);
   516   *aConverter = mFormatConv;
   517   NS_IF_ADDREF(*aConverter);
   518   return NS_OK;
   519 }
   522 //
   523 // FlavorsTransferableCanImport
   524 //
   525 // Computes a list of flavors that the transferable can accept into it, either through
   526 // intrinsic knowledge or input data converters.
   527 //
   528 NS_IMETHODIMP
   529 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
   530 {
   531   MOZ_ASSERT(mInitialized);
   533   NS_ENSURE_ARG_POINTER(_retval);
   535   // Get the flavor list, and on to the end of it, append the list of flavors we
   536   // can also get to through a converter. This is so that we can just walk the list
   537   // in one go, looking for the desired flavor.
   538   GetTransferDataFlavors(_retval);                        // addrefs
   539   nsCOMPtr<nsIFormatConverter> converter;
   540   GetConverter(getter_AddRefs(converter));
   541   if ( converter ) {
   542     nsCOMPtr<nsISupportsArray> convertedList;
   543     converter->GetInputDataFlavors(getter_AddRefs(convertedList));
   545     if ( convertedList ) {
   546       uint32_t importListLen;
   547       convertedList->Count(&importListLen);
   549       for ( uint32_t i=0; i < importListLen; ++i ) {
   550         nsCOMPtr<nsISupports> genericFlavor;
   551         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   553         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
   554         nsAutoCString flavorStr;
   555         flavorWrapper->GetData( flavorStr );
   557         if (GetDataForFlavor (mDataArray, flavorStr.get())
   558             == mDataArray.NoIndex) // Don't append if already in intrinsic list
   559           (*_retval)->AppendElement (genericFlavor);
   560       } // foreach flavor that can be converted to
   561     }
   562   } // if a converter exists
   564   return NS_OK;  
   565 } // FlavorsTransferableCanImport
   568 //
   569 // FlavorsTransferableCanExport
   570 //
   571 // Computes a list of flavors that the transferable can export, either through
   572 // intrinsic knowledge or output data converters.
   573 //
   574 NS_IMETHODIMP
   575 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
   576 {
   577   MOZ_ASSERT(mInitialized);
   579   NS_ENSURE_ARG_POINTER(_retval);
   581   // Get the flavor list, and on to the end of it, append the list of flavors we
   582   // can also get to through a converter. This is so that we can just walk the list
   583   // in one go, looking for the desired flavor.
   584   GetTransferDataFlavors(_retval);  // addrefs
   585   nsCOMPtr<nsIFormatConverter> converter;
   586   GetConverter(getter_AddRefs(converter));
   587   if ( converter ) {
   588     nsCOMPtr<nsISupportsArray> convertedList;
   589     converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
   591     if ( convertedList ) {
   592       uint32_t importListLen;
   593       convertedList->Count(&importListLen);
   595       for ( uint32_t i=0; i < importListLen; ++i ) {
   596         nsCOMPtr<nsISupports> genericFlavor;
   597         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   599         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
   600         nsAutoCString flavorStr;
   601         flavorWrapper->GetData( flavorStr );
   603         if (GetDataForFlavor (mDataArray, flavorStr.get())
   604             == mDataArray.NoIndex) // Don't append if already in intrinsic list
   605           (*_retval)->AppendElement (genericFlavor);
   606       } // foreach flavor that can be converted to
   607     }
   608   } // if a converter exists
   610   return NS_OK;
   611 } // FlavorsTransferableCanExport
   613 NS_IMETHODIMP
   614 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
   615 {
   616   MOZ_ASSERT(mInitialized);
   618   NS_ENSURE_ARG_POINTER(aIsPrivateData);
   620   *aIsPrivateData = mPrivateData;
   622   return NS_OK;
   623 }
   625 NS_IMETHODIMP
   626 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
   627 {
   628   MOZ_ASSERT(mInitialized);
   630   mPrivateData = aIsPrivateData;
   632   return NS_OK;
   633 }

mercurial