Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 )
60 {
61 #if 0 // Remove unnecessary disk caching to accommodate https://www.torproject.org/projects/torbrowser/design/#disk-avoidance
62 // Now, check to see if we consider the data to be "too large"
63 if (aDataLen > kLargeDatasetSize) {
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 }
70 #endif // #if 0
72 mData = aData;
73 mDataLen = aDataLen;
74 }
77 //-------------------------------------------------------------------------
78 void
79 DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
80 {
81 // check here to see if the data is cached on disk
82 if ( !mData && mCacheFileName ) {
83 // if so, read it in and pass it back
84 // ReadCache creates memory and copies the data into it.
85 if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
86 return;
87 else {
88 // oh shit, something went horribly wrong here.
89 NS_WARNING("Oh no, couldn't read data in from the cache file");
90 *aData = nullptr;
91 *aDataLen = 0;
92 return;
93 }
94 }
96 *aData = mData;
97 if ( mData )
98 NS_ADDREF(*aData);
99 *aDataLen = mDataLen;
100 }
103 //-------------------------------------------------------------------------
104 already_AddRefed<nsIFile>
105 DataStruct::GetFileSpec(const char* aFileName)
106 {
107 nsCOMPtr<nsIFile> cacheFile;
108 NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(cacheFile));
110 if (!cacheFile)
111 return nullptr;
113 // if the param aFileName contains a name we should use that
114 // because the file probably already exists
115 // otherwise create a unique name
116 if (!aFileName) {
117 cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
118 cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
119 } else {
120 cacheFile->AppendNative(nsDependentCString(aFileName));
121 }
123 return cacheFile.forget();
124 }
127 //-------------------------------------------------------------------------
128 nsresult
129 DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
130 {
131 // Get a new path and file to the temp directory
132 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
133 if (cacheFile) {
134 // remember the file name
135 if (!mCacheFileName) {
136 nsXPIDLCString fName;
137 cacheFile->GetNativeLeafName(fName);
138 mCacheFileName = strdup(fName);
139 }
141 // write out the contents of the clipboard
142 // to the file
143 //uint32_t bytes;
144 nsCOMPtr<nsIOutputStream> outStr;
146 NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
147 cacheFile);
149 if (!outStr) return NS_ERROR_FAILURE;
151 void* buff = nullptr;
152 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
153 if ( buff ) {
154 uint32_t ignored;
155 outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
156 nsMemory::Free(buff);
157 return NS_OK;
158 }
159 }
160 return NS_ERROR_FAILURE;
161 }
164 //-------------------------------------------------------------------------
165 nsresult
166 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
167 {
168 // if we don't have a cache filename we are out of luck
169 if (!mCacheFileName)
170 return NS_ERROR_FAILURE;
172 // get the path and file name
173 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
174 bool exists;
175 if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
176 // get the size of the file
177 int64_t fileSize;
178 int64_t max32 = 0xFFFFFFFF;
179 cacheFile->GetFileSize(&fileSize);
180 if (fileSize > max32)
181 return NS_ERROR_OUT_OF_MEMORY;
183 uint32_t size = uint32_t(fileSize);
184 // create new memory for the large clipboard data
185 nsAutoArrayPtr<char> data(new char[size]);
186 if ( !data )
187 return NS_ERROR_OUT_OF_MEMORY;
189 // now read it all in
190 nsCOMPtr<nsIInputStream> inStr;
191 NS_NewLocalFileInputStream( getter_AddRefs(inStr),
192 cacheFile);
194 if (!cacheFile) return NS_ERROR_FAILURE;
196 nsresult rv = inStr->Read(data, fileSize, aDataLen);
198 // make sure we got all the data ok
199 if (NS_SUCCEEDED(rv) && *aDataLen == size) {
200 nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
201 return *aData ? NS_OK : NS_ERROR_FAILURE;
202 }
204 // zero the return params
205 *aData = nullptr;
206 *aDataLen = 0;
207 }
209 return NS_ERROR_FAILURE;
210 }
213 //-------------------------------------------------------------------------
214 //
215 // Transferable constructor
216 //
217 //-------------------------------------------------------------------------
218 nsTransferable::nsTransferable()
219 : mPrivateData(false)
220 #ifdef DEBUG
221 , mInitialized(false)
222 #endif
223 {
224 }
226 //-------------------------------------------------------------------------
227 //
228 // Transferable destructor
229 //
230 //-------------------------------------------------------------------------
231 nsTransferable::~nsTransferable()
232 {
233 }
236 NS_IMETHODIMP
237 nsTransferable::Init(nsILoadContext* aContext)
238 {
239 MOZ_ASSERT(!mInitialized);
241 if (aContext) {
242 mPrivateData = aContext->UsePrivateBrowsing();
243 }
244 #ifdef DEBUG
245 mInitialized = true;
246 #endif
247 return NS_OK;
248 }
250 //
251 // GetTransferDataFlavors
252 //
253 // Returns a copy of the internal list of flavors. This does NOT take into
254 // account any converter that may be registered. This list consists of
255 // nsISupportsCString objects so that the flavor list can be accessed from JS.
256 //
257 nsresult
258 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
259 {
260 MOZ_ASSERT(mInitialized);
262 nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
263 if (NS_FAILED(rv)) return rv;
265 for ( uint32_t i=0; i<mDataArray.Length(); ++i ) {
266 DataStruct& data = mDataArray.ElementAt(i);
267 nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
268 if ( flavorWrapper ) {
269 flavorWrapper->SetData ( data.GetFlavor() );
270 nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
271 (*aDataFlavorList)->AppendElement( genericWrapper );
272 }
273 }
275 return NS_OK;
276 }
279 //
280 // GetTransferData
281 //
282 // Returns the data of the requested flavor, obtained from either having the data on hand or
283 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
284 // accessible from JS.
285 //
286 NS_IMETHODIMP
287 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
288 {
289 MOZ_ASSERT(mInitialized);
291 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
293 nsresult rv = NS_OK;
294 nsCOMPtr<nsISupports> savedData;
296 // first look and see if the data is present in one of the intrinsic flavors
297 uint32_t i;
298 for (i = 0; i < mDataArray.Length(); ++i ) {
299 DataStruct& data = mDataArray.ElementAt(i);
300 if ( data.GetFlavor().Equals(aFlavor) ) {
301 nsCOMPtr<nsISupports> dataBytes;
302 uint32_t len;
303 data.GetData(getter_AddRefs(dataBytes), &len);
304 if (len == kFlavorHasDataProvider && dataBytes) {
305 // do we have a data provider?
306 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
307 if (dataProvider) {
308 rv = dataProvider->GetFlavorData(this, aFlavor,
309 getter_AddRefs(dataBytes), &len);
310 if (NS_FAILED(rv))
311 break; // the provider failed. fall into the converter code below.
312 }
313 }
314 if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
315 *aDataLen = len;
316 dataBytes.forget(aData);
317 return NS_OK;
318 }
319 savedData = dataBytes; // return this if format converter fails
320 break;
321 }
322 }
324 bool found = false;
326 // if not, try using a format converter to get the requested flavor
327 if ( mFormatConv ) {
328 for (i = 0; i < mDataArray.Length(); ++i) {
329 DataStruct& data = mDataArray.ElementAt(i);
330 bool canConvert = false;
331 mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
332 if ( canConvert ) {
333 nsCOMPtr<nsISupports> dataBytes;
334 uint32_t len;
335 data.GetData(getter_AddRefs(dataBytes), &len);
336 if (len == kFlavorHasDataProvider && dataBytes) {
337 // do we have a data provider?
338 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
339 if (dataProvider) {
340 rv = dataProvider->GetFlavorData(this, aFlavor,
341 getter_AddRefs(dataBytes), &len);
342 if (NS_FAILED(rv))
343 break; // give up
344 }
345 }
346 mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
347 found = true;
348 break;
349 }
350 }
351 }
353 // for backward compatibility
354 if (!found) {
355 savedData.forget(aData);
356 *aDataLen = 0;
357 }
359 return found ? NS_OK : NS_ERROR_FAILURE;
360 }
363 //
364 // GetAnyTransferData
365 //
366 // Returns the data of the first flavor found. Caller is responsible for deleting the
367 // flavor string.
368 //
369 NS_IMETHODIMP
370 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, uint32_t *aDataLen)
371 {
372 MOZ_ASSERT(mInitialized);
374 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
376 for ( uint32_t i=0; i < mDataArray.Length(); ++i ) {
377 DataStruct& data = mDataArray.ElementAt(i);
378 if (data.IsDataAvailable()) {
379 *aFlavor = ToNewCString(data.GetFlavor());
380 data.GetData(aData, aDataLen);
381 return NS_OK;
382 }
383 }
385 return NS_ERROR_FAILURE;
386 }
389 //
390 // SetTransferData
391 //
392 //
393 //
394 NS_IMETHODIMP
395 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
396 {
397 MOZ_ASSERT(mInitialized);
399 NS_ENSURE_ARG(aFlavor);
401 // first check our intrinsic flavors to see if one has been registered.
402 uint32_t i = 0;
403 for (i = 0; i < mDataArray.Length(); ++i) {
404 DataStruct& data = mDataArray.ElementAt(i);
405 if ( data.GetFlavor().Equals(aFlavor) ) {
406 data.SetData ( aData, aDataLen );
407 return NS_OK;
408 }
409 }
411 // if not, try using a format converter to find a flavor to put the data in
412 if ( mFormatConv ) {
413 for (i = 0; i < mDataArray.Length(); ++i) {
414 DataStruct& data = mDataArray.ElementAt(i);
415 bool canConvert = false;
416 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
418 if ( canConvert ) {
419 nsCOMPtr<nsISupports> ConvertedData;
420 uint32_t ConvertedLen;
421 mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
422 data.SetData(ConvertedData, ConvertedLen);
423 return NS_OK;
424 }
425 }
426 }
428 // Can't set data neither directly nor through converter. Just add this flavor and try again
429 nsresult result = NS_ERROR_FAILURE;
430 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
431 result = SetTransferData (aFlavor, aData, aDataLen);
433 return result;
434 }
437 //
438 // AddDataFlavor
439 //
440 // Adds a data flavor to our list with no data. Error if it already exists.
441 //
442 NS_IMETHODIMP
443 nsTransferable::AddDataFlavor(const char *aDataFlavor)
444 {
445 MOZ_ASSERT(mInitialized);
447 if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
448 return NS_ERROR_FAILURE;
450 // Create a new "slot" for the data
451 mDataArray.AppendElement(DataStruct ( aDataFlavor ));
453 return NS_OK;
454 }
457 //
458 // RemoveDataFlavor
459 //
460 // Removes a data flavor (and causes the data to be destroyed). Error if
461 // the requested flavor is not present.
462 //
463 NS_IMETHODIMP
464 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
465 {
466 MOZ_ASSERT(mInitialized);
468 uint32_t idx;
469 if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
470 mDataArray.RemoveElementAt (idx);
471 return NS_OK;
472 }
473 return NS_ERROR_FAILURE;
474 }
477 /**
478 *
479 *
480 */
481 NS_IMETHODIMP
482 nsTransferable::IsLargeDataSet(bool *_retval)
483 {
484 MOZ_ASSERT(mInitialized);
486 NS_ENSURE_ARG_POINTER(_retval);
487 *_retval = false;
488 return NS_OK;
489 }
492 /**
493 *
494 *
495 */
496 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
497 {
498 MOZ_ASSERT(mInitialized);
500 mFormatConv = aConverter;
501 return NS_OK;
502 }
505 /**
506 *
507 *
508 */
509 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
510 {
511 MOZ_ASSERT(mInitialized);
513 NS_ENSURE_ARG_POINTER(aConverter);
514 *aConverter = mFormatConv;
515 NS_IF_ADDREF(*aConverter);
516 return NS_OK;
517 }
520 //
521 // FlavorsTransferableCanImport
522 //
523 // Computes a list of flavors that the transferable can accept into it, either through
524 // intrinsic knowledge or input data converters.
525 //
526 NS_IMETHODIMP
527 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
528 {
529 MOZ_ASSERT(mInitialized);
531 NS_ENSURE_ARG_POINTER(_retval);
533 // Get the flavor list, and on to the end of it, append the list of flavors we
534 // can also get to through a converter. This is so that we can just walk the list
535 // in one go, looking for the desired flavor.
536 GetTransferDataFlavors(_retval); // addrefs
537 nsCOMPtr<nsIFormatConverter> converter;
538 GetConverter(getter_AddRefs(converter));
539 if ( converter ) {
540 nsCOMPtr<nsISupportsArray> convertedList;
541 converter->GetInputDataFlavors(getter_AddRefs(convertedList));
543 if ( convertedList ) {
544 uint32_t importListLen;
545 convertedList->Count(&importListLen);
547 for ( uint32_t i=0; i < importListLen; ++i ) {
548 nsCOMPtr<nsISupports> genericFlavor;
549 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
551 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
552 nsAutoCString flavorStr;
553 flavorWrapper->GetData( flavorStr );
555 if (GetDataForFlavor (mDataArray, flavorStr.get())
556 == mDataArray.NoIndex) // Don't append if already in intrinsic list
557 (*_retval)->AppendElement (genericFlavor);
558 } // foreach flavor that can be converted to
559 }
560 } // if a converter exists
562 return NS_OK;
563 } // FlavorsTransferableCanImport
566 //
567 // FlavorsTransferableCanExport
568 //
569 // Computes a list of flavors that the transferable can export, either through
570 // intrinsic knowledge or output data converters.
571 //
572 NS_IMETHODIMP
573 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
574 {
575 MOZ_ASSERT(mInitialized);
577 NS_ENSURE_ARG_POINTER(_retval);
579 // Get the flavor list, and on to the end of it, append the list of flavors we
580 // can also get to through a converter. This is so that we can just walk the list
581 // in one go, looking for the desired flavor.
582 GetTransferDataFlavors(_retval); // addrefs
583 nsCOMPtr<nsIFormatConverter> converter;
584 GetConverter(getter_AddRefs(converter));
585 if ( converter ) {
586 nsCOMPtr<nsISupportsArray> convertedList;
587 converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
589 if ( convertedList ) {
590 uint32_t importListLen;
591 convertedList->Count(&importListLen);
593 for ( uint32_t i=0; i < importListLen; ++i ) {
594 nsCOMPtr<nsISupports> genericFlavor;
595 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
597 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
598 nsAutoCString flavorStr;
599 flavorWrapper->GetData( flavorStr );
601 if (GetDataForFlavor (mDataArray, flavorStr.get())
602 == mDataArray.NoIndex) // Don't append if already in intrinsic list
603 (*_retval)->AppendElement (genericFlavor);
604 } // foreach flavor that can be converted to
605 }
606 } // if a converter exists
608 return NS_OK;
609 } // FlavorsTransferableCanExport
611 NS_IMETHODIMP
612 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
613 {
614 MOZ_ASSERT(mInitialized);
616 NS_ENSURE_ARG_POINTER(aIsPrivateData);
618 *aIsPrivateData = mPrivateData;
620 return NS_OK;
621 }
623 NS_IMETHODIMP
624 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
625 {
626 MOZ_ASSERT(mInitialized);
628 mPrivateData = aIsPrivateData;
630 return NS_OK;
631 }