Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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 //-------------------------------------------------------------------------
59 void
60 DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen, bool aIsPrivBrowsing )
61 {
62 // Now, check to see if we consider the data to be "too large"
63 // as well as ensuring that private browsing mode is disabled
64 if (aDataLen > kLargeDatasetSize && aIsPrivBrowsing == false) {
65 // if so, cache it to disk instead of memory
66 if ( NS_SUCCEEDED(WriteCache(aData, aDataLen)) )
67 return;
68 else
69 NS_WARNING("Oh no, couldn't write data to the cache file");
70 }
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 bool aIsPrivBrowsing = false;
404 for (i = 0; i < mDataArray.Length(); ++i) {
405 DataStruct& data = mDataArray.ElementAt(i);
406 if ( data.GetFlavor().Equals(aFlavor) ) {
407 if ( NS_SUCCEEDED(GetIsPrivateData(&aIsPrivBrowsing)) ) {
408 data.SetData ( aData, aDataLen, aIsPrivBrowsing );
409 return NS_OK;
410 }
411 else { // call to GetIsPrivateData() failed,
412 return NS_ERROR_FAILURE; // we didn't SetData(), generic error
413 }
414 }
415 }
417 // if not, try using a format converter to find a flavor to put the data in
418 if ( mFormatConv ) {
419 for (i = 0; i < mDataArray.Length(); ++i) {
420 DataStruct& data = mDataArray.ElementAt(i);
421 bool canConvert = false;
422 bool aIsPrivBrowsing = false;
423 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
425 if ( canConvert ) {
426 nsCOMPtr<nsISupports> ConvertedData;
427 uint32_t ConvertedLen;
428 mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
429 if (NS_SUCCEEDED(GetIsPrivateData(&aIsPrivBrowsing))) {
430 data.SetData(ConvertedData, ConvertedLen, aIsPrivBrowsing);
431 return NS_OK;
432 }
433 else { // call to GetIsPrivateData() failed,
434 return NS_ERROR_FAILURE; // we didn't SetData(), generic error
435 }
436 }
437 }
438 }
440 // Can't set data neither directly nor through converter. Just add this flavor and try again
441 nsresult result = NS_ERROR_FAILURE;
442 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
443 result = SetTransferData (aFlavor, aData, aDataLen);
445 return result;
446 }
449 //
450 // AddDataFlavor
451 //
452 // Adds a data flavor to our list with no data. Error if it already exists.
453 //
454 NS_IMETHODIMP
455 nsTransferable::AddDataFlavor(const char *aDataFlavor)
456 {
457 MOZ_ASSERT(mInitialized);
459 if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
460 return NS_ERROR_FAILURE;
462 // Create a new "slot" for the data
463 mDataArray.AppendElement(DataStruct ( aDataFlavor ));
465 return NS_OK;
466 }
469 //
470 // RemoveDataFlavor
471 //
472 // Removes a data flavor (and causes the data to be destroyed). Error if
473 // the requested flavor is not present.
474 //
475 NS_IMETHODIMP
476 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
477 {
478 MOZ_ASSERT(mInitialized);
480 uint32_t idx;
481 if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
482 mDataArray.RemoveElementAt (idx);
483 return NS_OK;
484 }
485 return NS_ERROR_FAILURE;
486 }
489 /**
490 *
491 *
492 */
493 NS_IMETHODIMP
494 nsTransferable::IsLargeDataSet(bool *_retval)
495 {
496 MOZ_ASSERT(mInitialized);
498 NS_ENSURE_ARG_POINTER(_retval);
499 *_retval = false;
500 return NS_OK;
501 }
504 /**
505 *
506 *
507 */
508 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
509 {
510 MOZ_ASSERT(mInitialized);
512 mFormatConv = aConverter;
513 return NS_OK;
514 }
517 /**
518 *
519 *
520 */
521 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
522 {
523 MOZ_ASSERT(mInitialized);
525 NS_ENSURE_ARG_POINTER(aConverter);
526 *aConverter = mFormatConv;
527 NS_IF_ADDREF(*aConverter);
528 return NS_OK;
529 }
532 //
533 // FlavorsTransferableCanImport
534 //
535 // Computes a list of flavors that the transferable can accept into it, either through
536 // intrinsic knowledge or input data converters.
537 //
538 NS_IMETHODIMP
539 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
540 {
541 MOZ_ASSERT(mInitialized);
543 NS_ENSURE_ARG_POINTER(_retval);
545 // Get the flavor list, and on to the end of it, append the list of flavors we
546 // can also get to through a converter. This is so that we can just walk the list
547 // in one go, looking for the desired flavor.
548 GetTransferDataFlavors(_retval); // addrefs
549 nsCOMPtr<nsIFormatConverter> converter;
550 GetConverter(getter_AddRefs(converter));
551 if ( converter ) {
552 nsCOMPtr<nsISupportsArray> convertedList;
553 converter->GetInputDataFlavors(getter_AddRefs(convertedList));
555 if ( convertedList ) {
556 uint32_t importListLen;
557 convertedList->Count(&importListLen);
559 for ( uint32_t i=0; i < importListLen; ++i ) {
560 nsCOMPtr<nsISupports> genericFlavor;
561 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
563 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
564 nsAutoCString flavorStr;
565 flavorWrapper->GetData( flavorStr );
567 if (GetDataForFlavor (mDataArray, flavorStr.get())
568 == mDataArray.NoIndex) // Don't append if already in intrinsic list
569 (*_retval)->AppendElement (genericFlavor);
570 } // foreach flavor that can be converted to
571 }
572 } // if a converter exists
574 return NS_OK;
575 } // FlavorsTransferableCanImport
578 //
579 // FlavorsTransferableCanExport
580 //
581 // Computes a list of flavors that the transferable can export, either through
582 // intrinsic knowledge or output data converters.
583 //
584 NS_IMETHODIMP
585 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
586 {
587 MOZ_ASSERT(mInitialized);
589 NS_ENSURE_ARG_POINTER(_retval);
591 // Get the flavor list, and on to the end of it, append the list of flavors we
592 // can also get to through a converter. This is so that we can just walk the list
593 // in one go, looking for the desired flavor.
594 GetTransferDataFlavors(_retval); // addrefs
595 nsCOMPtr<nsIFormatConverter> converter;
596 GetConverter(getter_AddRefs(converter));
597 if ( converter ) {
598 nsCOMPtr<nsISupportsArray> convertedList;
599 converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
601 if ( convertedList ) {
602 uint32_t importListLen;
603 convertedList->Count(&importListLen);
605 for ( uint32_t i=0; i < importListLen; ++i ) {
606 nsCOMPtr<nsISupports> genericFlavor;
607 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
609 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
610 nsAutoCString flavorStr;
611 flavorWrapper->GetData( flavorStr );
613 if (GetDataForFlavor (mDataArray, flavorStr.get())
614 == mDataArray.NoIndex) // Don't append if already in intrinsic list
615 (*_retval)->AppendElement (genericFlavor);
616 } // foreach flavor that can be converted to
617 }
618 } // if a converter exists
620 return NS_OK;
621 } // FlavorsTransferableCanExport
623 NS_IMETHODIMP
624 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
625 {
626 MOZ_ASSERT(mInitialized);
628 NS_ENSURE_ARG_POINTER(aIsPrivateData);
630 *aIsPrivateData = mPrivateData;
632 return NS_OK;
633 }
635 NS_IMETHODIMP
636 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
637 {
638 MOZ_ASSERT(mInitialized);
640 mPrivateData = aIsPrivateData;
642 return NS_OK;
643 }