Thu, 15 Jan 2015 15:59:08 +0100
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 }