widget/xpwidgets/nsTransferable.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
parent 9
a63d609f5ebe
child 11
deefc01c0e14
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 /* -*- 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 // The SetData method is overloaded to indicate private browsing mode
    59 // while achieving some semblance of (private) interface compatibility.
    60 //-------------------------------------------------------------------------
    61 void
    62 DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen, bool aIsPrivBrowsing )
    63 {
    64   // Now, check to see if we consider the data to be "too large"
    65   // as well as ensuring that private browsing mode is disabled
    66   if (aDataLen > kLargeDatasetSize && aIsPrivBrowsing == false) {
    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::SetData ( nsISupports* aData, uint32_t aDataLen )
    82 {
    83   // Now, check to see if we consider the data to be "too large"
    84   if (aDataLen > kLargeDatasetSize) {
    85     // if so, cache it to disk instead of memory
    86     if ( NS_SUCCEEDED(WriteCache(aData, aDataLen)) )
    87       return;
    88     else
    89 			NS_WARNING("Oh no, couldn't write data to the cache file");   
    90   } 
    92   mData    = aData;
    93   mDataLen = aDataLen;  
    94 }
    97 //-------------------------------------------------------------------------
    98 void
    99 DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
   100 {
   101   // check here to see if the data is cached on disk
   102   if ( !mData && mCacheFileName ) {
   103     // if so, read it in and pass it back
   104     // ReadCache creates memory and copies the data into it.
   105     if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
   106       return;
   107     else {
   108       // oh shit, something went horribly wrong here.
   109       NS_WARNING("Oh no, couldn't read data in from the cache file");
   110       *aData = nullptr;
   111       *aDataLen = 0;
   112       return;
   113     }
   114   }
   116   *aData = mData;
   117   if ( mData )
   118     NS_ADDREF(*aData); 
   119   *aDataLen = mDataLen;
   120 }
   123 //-------------------------------------------------------------------------
   124 already_AddRefed<nsIFile>
   125 DataStruct::GetFileSpec(const char* aFileName)
   126 {
   127   nsCOMPtr<nsIFile> cacheFile;
   128   NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(cacheFile));
   130   if (!cacheFile)
   131     return nullptr;
   133   // if the param aFileName contains a name we should use that
   134   // because the file probably already exists
   135   // otherwise create a unique name
   136   if (!aFileName) {
   137     cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
   138     cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
   139   } else {
   140     cacheFile->AppendNative(nsDependentCString(aFileName));
   141   }
   143   return cacheFile.forget();
   144 }
   147 //-------------------------------------------------------------------------
   148 nsresult
   149 DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
   150 {
   151   // Get a new path and file to the temp directory
   152   nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
   153   if (cacheFile) {
   154     // remember the file name
   155     if (!mCacheFileName) {
   156       nsXPIDLCString fName;
   157       cacheFile->GetNativeLeafName(fName);
   158       mCacheFileName = strdup(fName);
   159     }
   161     // write out the contents of the clipboard
   162     // to the file
   163     //uint32_t bytes;
   164     nsCOMPtr<nsIOutputStream> outStr;
   166     NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
   167                                 cacheFile);
   169     if (!outStr) return NS_ERROR_FAILURE;
   171     void* buff = nullptr;
   172     nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
   173     if ( buff ) {
   174       uint32_t ignored;
   175       outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
   176       nsMemory::Free(buff);
   177       return NS_OK;
   178     }
   179   }
   180   return NS_ERROR_FAILURE;
   181 }
   184 //-------------------------------------------------------------------------
   185 nsresult
   186 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
   187 {
   188   // if we don't have a cache filename we are out of luck
   189   if (!mCacheFileName)
   190     return NS_ERROR_FAILURE;
   192   // get the path and file name
   193   nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
   194   bool exists;
   195   if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
   196     // get the size of the file
   197     int64_t fileSize;
   198     int64_t max32 = 0xFFFFFFFF;
   199     cacheFile->GetFileSize(&fileSize);
   200     if (fileSize > max32)
   201       return NS_ERROR_OUT_OF_MEMORY;
   203     uint32_t size = uint32_t(fileSize);
   204     // create new memory for the large clipboard data
   205     nsAutoArrayPtr<char> data(new char[size]);
   206     if ( !data )
   207       return NS_ERROR_OUT_OF_MEMORY;
   209     // now read it all in
   210     nsCOMPtr<nsIInputStream> inStr;
   211     NS_NewLocalFileInputStream( getter_AddRefs(inStr),
   212                                 cacheFile);
   214     if (!cacheFile) return NS_ERROR_FAILURE;
   216     nsresult rv = inStr->Read(data, fileSize, aDataLen);
   218     // make sure we got all the data ok
   219     if (NS_SUCCEEDED(rv) && *aDataLen == size) {
   220       nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
   221       return *aData ? NS_OK : NS_ERROR_FAILURE;
   222     }
   224     // zero the return params
   225     *aData    = nullptr;
   226     *aDataLen = 0;
   227   }
   229   return NS_ERROR_FAILURE;
   230 }
   233 //-------------------------------------------------------------------------
   234 //
   235 // Transferable constructor
   236 //
   237 //-------------------------------------------------------------------------
   238 nsTransferable::nsTransferable()
   239   : mPrivateData(false)
   240 #ifdef DEBUG
   241   , mInitialized(false)
   242 #endif
   243 {
   244 }
   246 //-------------------------------------------------------------------------
   247 //
   248 // Transferable destructor
   249 //
   250 //-------------------------------------------------------------------------
   251 nsTransferable::~nsTransferable()
   252 {
   253 }
   256 NS_IMETHODIMP
   257 nsTransferable::Init(nsILoadContext* aContext)
   258 {
   259   MOZ_ASSERT(!mInitialized);
   261   if (aContext) {
   262     mPrivateData = aContext->UsePrivateBrowsing();
   263   }
   264 #ifdef DEBUG
   265   mInitialized = true;
   266 #endif
   267   return NS_OK;
   268 }
   270 //
   271 // GetTransferDataFlavors
   272 //
   273 // Returns a copy of the internal list of flavors. This does NOT take into 
   274 // account any converter that may be registered. This list consists of
   275 // nsISupportsCString objects so that the flavor list can be accessed from JS.
   276 //
   277 nsresult
   278 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
   279 {
   280   MOZ_ASSERT(mInitialized);
   282   nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
   283   if (NS_FAILED(rv)) return rv;
   285   for ( uint32_t i=0; i<mDataArray.Length(); ++i ) {
   286     DataStruct& data = mDataArray.ElementAt(i);
   287     nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
   288     if ( flavorWrapper ) {
   289       flavorWrapper->SetData ( data.GetFlavor() );
   290       nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
   291       (*aDataFlavorList)->AppendElement( genericWrapper );
   292     }
   293   }
   295   return NS_OK;
   296 }
   299 //
   300 // GetTransferData
   301 //
   302 // Returns the data of the requested flavor, obtained from either having the data on hand or
   303 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
   304 // accessible from JS.
   305 //
   306 NS_IMETHODIMP
   307 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
   308 {
   309   MOZ_ASSERT(mInitialized);
   311   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
   313   nsresult rv = NS_OK;
   314   nsCOMPtr<nsISupports> savedData;
   316   // first look and see if the data is present in one of the intrinsic flavors
   317   uint32_t i;
   318   for (i = 0; i < mDataArray.Length(); ++i ) {
   319     DataStruct& data = mDataArray.ElementAt(i);
   320     if ( data.GetFlavor().Equals(aFlavor) ) {
   321       nsCOMPtr<nsISupports> dataBytes;
   322       uint32_t len;
   323       data.GetData(getter_AddRefs(dataBytes), &len);
   324       if (len == kFlavorHasDataProvider && dataBytes) {
   325         // do we have a data provider?
   326         nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
   327         if (dataProvider) {
   328           rv = dataProvider->GetFlavorData(this, aFlavor,
   329                                            getter_AddRefs(dataBytes), &len);
   330           if (NS_FAILED(rv))
   331             break;    // the provider failed. fall into the converter code below.
   332         }
   333       }
   334       if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
   335         *aDataLen = len;
   336         dataBytes.forget(aData);
   337         return NS_OK;
   338       }
   339       savedData = dataBytes;  // return this if format converter fails
   340       break;
   341     }
   342   }
   344   bool found = false;
   346   // if not, try using a format converter to get the requested flavor
   347   if ( mFormatConv ) {
   348     for (i = 0; i < mDataArray.Length(); ++i) {
   349       DataStruct& data = mDataArray.ElementAt(i);
   350       bool canConvert = false;
   351       mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
   352       if ( canConvert ) {
   353         nsCOMPtr<nsISupports> dataBytes;
   354         uint32_t len;
   355         data.GetData(getter_AddRefs(dataBytes), &len);
   356         if (len == kFlavorHasDataProvider && dataBytes) {
   357           // do we have a data provider?
   358           nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
   359           if (dataProvider) {
   360             rv = dataProvider->GetFlavorData(this, aFlavor,
   361                                              getter_AddRefs(dataBytes), &len);
   362             if (NS_FAILED(rv))
   363               break;  // give up
   364           }
   365         }
   366         mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
   367         found = true;
   368         break;
   369       }
   370     }
   371   }
   373   // for backward compatibility
   374   if (!found) {
   375     savedData.forget(aData);
   376     *aDataLen = 0;
   377   }
   379   return found ? NS_OK : NS_ERROR_FAILURE;
   380 }
   383 //
   384 // GetAnyTransferData
   385 //
   386 // Returns the data of the first flavor found. Caller is responsible for deleting the
   387 // flavor string.
   388 // 
   389 NS_IMETHODIMP
   390 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, uint32_t *aDataLen)
   391 {
   392   MOZ_ASSERT(mInitialized);
   394   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
   396   for ( uint32_t i=0; i < mDataArray.Length(); ++i ) {
   397     DataStruct& data = mDataArray.ElementAt(i);
   398     if (data.IsDataAvailable()) {
   399       *aFlavor = ToNewCString(data.GetFlavor());
   400       data.GetData(aData, aDataLen);
   401       return NS_OK;
   402     }
   403   }
   405   return NS_ERROR_FAILURE;
   406 }
   409 //
   410 // SetTransferData
   411 //
   412 //
   413 // 
   414 NS_IMETHODIMP
   415 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
   416 {
   417   MOZ_ASSERT(mInitialized);
   419   NS_ENSURE_ARG(aFlavor);
   421   // first check our intrinsic flavors to see if one has been registered.
   422   uint32_t i = 0;
   423   bool aIsPrivBrowsing = false;
   424   for (i = 0; i < mDataArray.Length(); ++i) {
   425     DataStruct& data = mDataArray.ElementAt(i);
   426     if ( data.GetFlavor().Equals(aFlavor) ) {
   427       if ( NS_SUCCEEDED(GetIsPrivateData(&aIsPrivBrowsing)) ) {
   428         data.SetData ( aData, aDataLen, aIsPrivBrowsing );
   429         return NS_OK;
   430       }
   431       else {                     // call to GetIsPrivateData() failed,
   432         return NS_ERROR_FAILURE; // we didn't SetData(), generic error
   433       }
   434     }
   435   }
   437   // if not, try using a format converter to find a flavor to put the data in
   438   if ( mFormatConv ) {
   439     for (i = 0; i < mDataArray.Length(); ++i) {
   440       DataStruct& data = mDataArray.ElementAt(i);
   441       bool canConvert = false;
   442       mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
   444       if ( canConvert ) {
   445         nsCOMPtr<nsISupports> ConvertedData;
   446         uint32_t ConvertedLen;
   447         mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
   448         data.SetData(ConvertedData, ConvertedLen);
   449         return NS_OK;
   450       }
   451     }
   452   }
   454   // Can't set data neither directly nor through converter. Just add this flavor and try again
   455   nsresult result = NS_ERROR_FAILURE;
   456   if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
   457     result = SetTransferData (aFlavor, aData, aDataLen);
   459   return result;
   460 }
   463 //
   464 // AddDataFlavor
   465 //
   466 // Adds a data flavor to our list with no data. Error if it already exists.
   467 // 
   468 NS_IMETHODIMP
   469 nsTransferable::AddDataFlavor(const char *aDataFlavor)
   470 {
   471   MOZ_ASSERT(mInitialized);
   473   if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
   474     return NS_ERROR_FAILURE;
   476   // Create a new "slot" for the data
   477   mDataArray.AppendElement(DataStruct ( aDataFlavor ));
   479   return NS_OK;
   480 }
   483 //
   484 // RemoveDataFlavor
   485 //
   486 // Removes a data flavor (and causes the data to be destroyed). Error if
   487 // the requested flavor is not present.
   488 //
   489 NS_IMETHODIMP
   490 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
   491 {
   492   MOZ_ASSERT(mInitialized);
   494   uint32_t idx;
   495   if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
   496     mDataArray.RemoveElementAt (idx);
   497     return NS_OK;
   498   }
   499   return NS_ERROR_FAILURE;
   500 }
   503 /**
   504   * 
   505   *
   506   */
   507 NS_IMETHODIMP
   508 nsTransferable::IsLargeDataSet(bool *_retval)
   509 {
   510   MOZ_ASSERT(mInitialized);
   512   NS_ENSURE_ARG_POINTER(_retval);
   513   *_retval = false;
   514   return NS_OK;
   515 }
   518 /**
   519   * 
   520   *
   521   */
   522 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
   523 {
   524   MOZ_ASSERT(mInitialized);
   526   mFormatConv = aConverter;
   527   return NS_OK;
   528 }
   531 /**
   532   * 
   533   *
   534   */
   535 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
   536 {
   537   MOZ_ASSERT(mInitialized);
   539   NS_ENSURE_ARG_POINTER(aConverter);
   540   *aConverter = mFormatConv;
   541   NS_IF_ADDREF(*aConverter);
   542   return NS_OK;
   543 }
   546 //
   547 // FlavorsTransferableCanImport
   548 //
   549 // Computes a list of flavors that the transferable can accept into it, either through
   550 // intrinsic knowledge or input data converters.
   551 //
   552 NS_IMETHODIMP
   553 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
   554 {
   555   MOZ_ASSERT(mInitialized);
   557   NS_ENSURE_ARG_POINTER(_retval);
   559   // Get the flavor list, and on to the end of it, append the list of flavors we
   560   // can also get to through a converter. This is so that we can just walk the list
   561   // in one go, looking for the desired flavor.
   562   GetTransferDataFlavors(_retval);                        // addrefs
   563   nsCOMPtr<nsIFormatConverter> converter;
   564   GetConverter(getter_AddRefs(converter));
   565   if ( converter ) {
   566     nsCOMPtr<nsISupportsArray> convertedList;
   567     converter->GetInputDataFlavors(getter_AddRefs(convertedList));
   569     if ( convertedList ) {
   570       uint32_t importListLen;
   571       convertedList->Count(&importListLen);
   573       for ( uint32_t i=0; i < importListLen; ++i ) {
   574         nsCOMPtr<nsISupports> genericFlavor;
   575         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   577         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
   578         nsAutoCString flavorStr;
   579         flavorWrapper->GetData( flavorStr );
   581         if (GetDataForFlavor (mDataArray, flavorStr.get())
   582             == mDataArray.NoIndex) // Don't append if already in intrinsic list
   583           (*_retval)->AppendElement (genericFlavor);
   584       } // foreach flavor that can be converted to
   585     }
   586   } // if a converter exists
   588   return NS_OK;  
   589 } // FlavorsTransferableCanImport
   592 //
   593 // FlavorsTransferableCanExport
   594 //
   595 // Computes a list of flavors that the transferable can export, either through
   596 // intrinsic knowledge or output data converters.
   597 //
   598 NS_IMETHODIMP
   599 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
   600 {
   601   MOZ_ASSERT(mInitialized);
   603   NS_ENSURE_ARG_POINTER(_retval);
   605   // Get the flavor list, and on to the end of it, append the list of flavors we
   606   // can also get to through a converter. This is so that we can just walk the list
   607   // in one go, looking for the desired flavor.
   608   GetTransferDataFlavors(_retval);  // addrefs
   609   nsCOMPtr<nsIFormatConverter> converter;
   610   GetConverter(getter_AddRefs(converter));
   611   if ( converter ) {
   612     nsCOMPtr<nsISupportsArray> convertedList;
   613     converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
   615     if ( convertedList ) {
   616       uint32_t importListLen;
   617       convertedList->Count(&importListLen);
   619       for ( uint32_t i=0; i < importListLen; ++i ) {
   620         nsCOMPtr<nsISupports> genericFlavor;
   621         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   623         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
   624         nsAutoCString flavorStr;
   625         flavorWrapper->GetData( flavorStr );
   627         if (GetDataForFlavor (mDataArray, flavorStr.get())
   628             == mDataArray.NoIndex) // Don't append if already in intrinsic list
   629           (*_retval)->AppendElement (genericFlavor);
   630       } // foreach flavor that can be converted to
   631     }
   632   } // if a converter exists
   634   return NS_OK;
   635 } // FlavorsTransferableCanExport
   637 NS_IMETHODIMP
   638 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
   639 {
   640   MOZ_ASSERT(mInitialized);
   642   NS_ENSURE_ARG_POINTER(aIsPrivateData);
   644   *aIsPrivateData = mPrivateData;
   646   return NS_OK;
   647 }
   649 NS_IMETHODIMP
   650 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
   651 {
   652   MOZ_ASSERT(mInitialized);
   654   mPrivateData = aIsPrivateData;
   656   return NS_OK;
   657 }

mercurial