Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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 // Now, check to see if we consider the data to be "too large"
62 if (aDataLen > kLargeDatasetSize) {
63 // if so, cache it to disk instead of memory
64 if ( NS_SUCCEEDED(WriteCache(aData, aDataLen)) )
65 return;
66 else
67 NS_WARNING("Oh no, couldn't write data to the cache file");
68 }
70 mData = aData;
71 mDataLen = aDataLen;
72 }
75 //-------------------------------------------------------------------------
76 void
77 DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
78 {
79 // check here to see if the data is cached on disk
80 if ( !mData && mCacheFileName ) {
81 // if so, read it in and pass it back
82 // ReadCache creates memory and copies the data into it.
83 if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
84 return;
85 else {
86 // oh shit, something went horribly wrong here.
87 NS_WARNING("Oh no, couldn't read data in from the cache file");
88 *aData = nullptr;
89 *aDataLen = 0;
90 return;
91 }
92 }
94 *aData = mData;
95 if ( mData )
96 NS_ADDREF(*aData);
97 *aDataLen = mDataLen;
98 }
101 //-------------------------------------------------------------------------
102 already_AddRefed<nsIFile>
103 DataStruct::GetFileSpec(const char* aFileName)
104 {
105 nsCOMPtr<nsIFile> cacheFile;
106 NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(cacheFile));
108 if (!cacheFile)
109 return nullptr;
111 // if the param aFileName contains a name we should use that
112 // because the file probably already exists
113 // otherwise create a unique name
114 if (!aFileName) {
115 cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
116 cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
117 } else {
118 cacheFile->AppendNative(nsDependentCString(aFileName));
119 }
121 return cacheFile.forget();
122 }
125 //-------------------------------------------------------------------------
126 nsresult
127 DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
128 {
129 // Get a new path and file to the temp directory
130 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
131 if (cacheFile) {
132 // remember the file name
133 if (!mCacheFileName) {
134 nsXPIDLCString fName;
135 cacheFile->GetNativeLeafName(fName);
136 mCacheFileName = strdup(fName);
137 }
139 // write out the contents of the clipboard
140 // to the file
141 //uint32_t bytes;
142 nsCOMPtr<nsIOutputStream> outStr;
144 NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
145 cacheFile);
147 if (!outStr) return NS_ERROR_FAILURE;
149 void* buff = nullptr;
150 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
151 if ( buff ) {
152 uint32_t ignored;
153 outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
154 nsMemory::Free(buff);
155 return NS_OK;
156 }
157 }
158 return NS_ERROR_FAILURE;
159 }
162 //-------------------------------------------------------------------------
163 nsresult
164 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
165 {
166 // if we don't have a cache filename we are out of luck
167 if (!mCacheFileName)
168 return NS_ERROR_FAILURE;
170 // get the path and file name
171 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
172 bool exists;
173 if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
174 // get the size of the file
175 int64_t fileSize;
176 int64_t max32 = 0xFFFFFFFF;
177 cacheFile->GetFileSize(&fileSize);
178 if (fileSize > max32)
179 return NS_ERROR_OUT_OF_MEMORY;
181 uint32_t size = uint32_t(fileSize);
182 // create new memory for the large clipboard data
183 nsAutoArrayPtr<char> data(new char[size]);
184 if ( !data )
185 return NS_ERROR_OUT_OF_MEMORY;
187 // now read it all in
188 nsCOMPtr<nsIInputStream> inStr;
189 NS_NewLocalFileInputStream( getter_AddRefs(inStr),
190 cacheFile);
192 if (!cacheFile) return NS_ERROR_FAILURE;
194 nsresult rv = inStr->Read(data, fileSize, aDataLen);
196 // make sure we got all the data ok
197 if (NS_SUCCEEDED(rv) && *aDataLen == size) {
198 nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
199 return *aData ? NS_OK : NS_ERROR_FAILURE;
200 }
202 // zero the return params
203 *aData = nullptr;
204 *aDataLen = 0;
205 }
207 return NS_ERROR_FAILURE;
208 }
211 //-------------------------------------------------------------------------
212 //
213 // Transferable constructor
214 //
215 //-------------------------------------------------------------------------
216 nsTransferable::nsTransferable()
217 : mPrivateData(false)
218 #ifdef DEBUG
219 , mInitialized(false)
220 #endif
221 {
222 }
224 //-------------------------------------------------------------------------
225 //
226 // Transferable destructor
227 //
228 //-------------------------------------------------------------------------
229 nsTransferable::~nsTransferable()
230 {
231 }
234 NS_IMETHODIMP
235 nsTransferable::Init(nsILoadContext* aContext)
236 {
237 MOZ_ASSERT(!mInitialized);
239 if (aContext) {
240 mPrivateData = aContext->UsePrivateBrowsing();
241 }
242 #ifdef DEBUG
243 mInitialized = true;
244 #endif
245 return NS_OK;
246 }
248 //
249 // GetTransferDataFlavors
250 //
251 // Returns a copy of the internal list of flavors. This does NOT take into
252 // account any converter that may be registered. This list consists of
253 // nsISupportsCString objects so that the flavor list can be accessed from JS.
254 //
255 nsresult
256 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
257 {
258 MOZ_ASSERT(mInitialized);
260 nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
261 if (NS_FAILED(rv)) return rv;
263 for ( uint32_t i=0; i<mDataArray.Length(); ++i ) {
264 DataStruct& data = mDataArray.ElementAt(i);
265 nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
266 if ( flavorWrapper ) {
267 flavorWrapper->SetData ( data.GetFlavor() );
268 nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
269 (*aDataFlavorList)->AppendElement( genericWrapper );
270 }
271 }
273 return NS_OK;
274 }
277 //
278 // GetTransferData
279 //
280 // Returns the data of the requested flavor, obtained from either having the data on hand or
281 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
282 // accessible from JS.
283 //
284 NS_IMETHODIMP
285 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
286 {
287 MOZ_ASSERT(mInitialized);
289 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
291 nsresult rv = NS_OK;
292 nsCOMPtr<nsISupports> savedData;
294 // first look and see if the data is present in one of the intrinsic flavors
295 uint32_t i;
296 for (i = 0; i < mDataArray.Length(); ++i ) {
297 DataStruct& data = mDataArray.ElementAt(i);
298 if ( data.GetFlavor().Equals(aFlavor) ) {
299 nsCOMPtr<nsISupports> dataBytes;
300 uint32_t len;
301 data.GetData(getter_AddRefs(dataBytes), &len);
302 if (len == kFlavorHasDataProvider && dataBytes) {
303 // do we have a data provider?
304 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
305 if (dataProvider) {
306 rv = dataProvider->GetFlavorData(this, aFlavor,
307 getter_AddRefs(dataBytes), &len);
308 if (NS_FAILED(rv))
309 break; // the provider failed. fall into the converter code below.
310 }
311 }
312 if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
313 *aDataLen = len;
314 dataBytes.forget(aData);
315 return NS_OK;
316 }
317 savedData = dataBytes; // return this if format converter fails
318 break;
319 }
320 }
322 bool found = false;
324 // if not, try using a format converter to get the requested flavor
325 if ( mFormatConv ) {
326 for (i = 0; i < mDataArray.Length(); ++i) {
327 DataStruct& data = mDataArray.ElementAt(i);
328 bool canConvert = false;
329 mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
330 if ( canConvert ) {
331 nsCOMPtr<nsISupports> dataBytes;
332 uint32_t len;
333 data.GetData(getter_AddRefs(dataBytes), &len);
334 if (len == kFlavorHasDataProvider && dataBytes) {
335 // do we have a data provider?
336 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
337 if (dataProvider) {
338 rv = dataProvider->GetFlavorData(this, aFlavor,
339 getter_AddRefs(dataBytes), &len);
340 if (NS_FAILED(rv))
341 break; // give up
342 }
343 }
344 mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
345 found = true;
346 break;
347 }
348 }
349 }
351 // for backward compatibility
352 if (!found) {
353 savedData.forget(aData);
354 *aDataLen = 0;
355 }
357 return found ? NS_OK : NS_ERROR_FAILURE;
358 }
361 //
362 // GetAnyTransferData
363 //
364 // Returns the data of the first flavor found. Caller is responsible for deleting the
365 // flavor string.
366 //
367 NS_IMETHODIMP
368 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, uint32_t *aDataLen)
369 {
370 MOZ_ASSERT(mInitialized);
372 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
374 for ( uint32_t i=0; i < mDataArray.Length(); ++i ) {
375 DataStruct& data = mDataArray.ElementAt(i);
376 if (data.IsDataAvailable()) {
377 *aFlavor = ToNewCString(data.GetFlavor());
378 data.GetData(aData, aDataLen);
379 return NS_OK;
380 }
381 }
383 return NS_ERROR_FAILURE;
384 }
387 //
388 // SetTransferData
389 //
390 //
391 //
392 NS_IMETHODIMP
393 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
394 {
395 MOZ_ASSERT(mInitialized);
397 NS_ENSURE_ARG(aFlavor);
399 // first check our intrinsic flavors to see if one has been registered.
400 uint32_t i = 0;
401 for (i = 0; i < mDataArray.Length(); ++i) {
402 DataStruct& data = mDataArray.ElementAt(i);
403 if ( data.GetFlavor().Equals(aFlavor) ) {
404 data.SetData ( aData, aDataLen );
405 return NS_OK;
406 }
407 }
409 // if not, try using a format converter to find a flavor to put the data in
410 if ( mFormatConv ) {
411 for (i = 0; i < mDataArray.Length(); ++i) {
412 DataStruct& data = mDataArray.ElementAt(i);
413 bool canConvert = false;
414 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
416 if ( canConvert ) {
417 nsCOMPtr<nsISupports> ConvertedData;
418 uint32_t ConvertedLen;
419 mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
420 data.SetData(ConvertedData, ConvertedLen);
421 return NS_OK;
422 }
423 }
424 }
426 // Can't set data neither directly nor through converter. Just add this flavor and try again
427 nsresult result = NS_ERROR_FAILURE;
428 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
429 result = SetTransferData (aFlavor, aData, aDataLen);
431 return result;
432 }
435 //
436 // AddDataFlavor
437 //
438 // Adds a data flavor to our list with no data. Error if it already exists.
439 //
440 NS_IMETHODIMP
441 nsTransferable::AddDataFlavor(const char *aDataFlavor)
442 {
443 MOZ_ASSERT(mInitialized);
445 if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
446 return NS_ERROR_FAILURE;
448 // Create a new "slot" for the data
449 mDataArray.AppendElement(DataStruct ( aDataFlavor ));
451 return NS_OK;
452 }
455 //
456 // RemoveDataFlavor
457 //
458 // Removes a data flavor (and causes the data to be destroyed). Error if
459 // the requested flavor is not present.
460 //
461 NS_IMETHODIMP
462 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
463 {
464 MOZ_ASSERT(mInitialized);
466 uint32_t idx;
467 if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
468 mDataArray.RemoveElementAt (idx);
469 return NS_OK;
470 }
471 return NS_ERROR_FAILURE;
472 }
475 /**
476 *
477 *
478 */
479 NS_IMETHODIMP
480 nsTransferable::IsLargeDataSet(bool *_retval)
481 {
482 MOZ_ASSERT(mInitialized);
484 NS_ENSURE_ARG_POINTER(_retval);
485 *_retval = false;
486 return NS_OK;
487 }
490 /**
491 *
492 *
493 */
494 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
495 {
496 MOZ_ASSERT(mInitialized);
498 mFormatConv = aConverter;
499 return NS_OK;
500 }
503 /**
504 *
505 *
506 */
507 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
508 {
509 MOZ_ASSERT(mInitialized);
511 NS_ENSURE_ARG_POINTER(aConverter);
512 *aConverter = mFormatConv;
513 NS_IF_ADDREF(*aConverter);
514 return NS_OK;
515 }
518 //
519 // FlavorsTransferableCanImport
520 //
521 // Computes a list of flavors that the transferable can accept into it, either through
522 // intrinsic knowledge or input data converters.
523 //
524 NS_IMETHODIMP
525 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
526 {
527 MOZ_ASSERT(mInitialized);
529 NS_ENSURE_ARG_POINTER(_retval);
531 // Get the flavor list, and on to the end of it, append the list of flavors we
532 // can also get to through a converter. This is so that we can just walk the list
533 // in one go, looking for the desired flavor.
534 GetTransferDataFlavors(_retval); // addrefs
535 nsCOMPtr<nsIFormatConverter> converter;
536 GetConverter(getter_AddRefs(converter));
537 if ( converter ) {
538 nsCOMPtr<nsISupportsArray> convertedList;
539 converter->GetInputDataFlavors(getter_AddRefs(convertedList));
541 if ( convertedList ) {
542 uint32_t importListLen;
543 convertedList->Count(&importListLen);
545 for ( uint32_t i=0; i < importListLen; ++i ) {
546 nsCOMPtr<nsISupports> genericFlavor;
547 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
549 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
550 nsAutoCString flavorStr;
551 flavorWrapper->GetData( flavorStr );
553 if (GetDataForFlavor (mDataArray, flavorStr.get())
554 == mDataArray.NoIndex) // Don't append if already in intrinsic list
555 (*_retval)->AppendElement (genericFlavor);
556 } // foreach flavor that can be converted to
557 }
558 } // if a converter exists
560 return NS_OK;
561 } // FlavorsTransferableCanImport
564 //
565 // FlavorsTransferableCanExport
566 //
567 // Computes a list of flavors that the transferable can export, either through
568 // intrinsic knowledge or output data converters.
569 //
570 NS_IMETHODIMP
571 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
572 {
573 MOZ_ASSERT(mInitialized);
575 NS_ENSURE_ARG_POINTER(_retval);
577 // Get the flavor list, and on to the end of it, append the list of flavors we
578 // can also get to through a converter. This is so that we can just walk the list
579 // in one go, looking for the desired flavor.
580 GetTransferDataFlavors(_retval); // addrefs
581 nsCOMPtr<nsIFormatConverter> converter;
582 GetConverter(getter_AddRefs(converter));
583 if ( converter ) {
584 nsCOMPtr<nsISupportsArray> convertedList;
585 converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
587 if ( convertedList ) {
588 uint32_t importListLen;
589 convertedList->Count(&importListLen);
591 for ( uint32_t i=0; i < importListLen; ++i ) {
592 nsCOMPtr<nsISupports> genericFlavor;
593 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
595 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
596 nsAutoCString flavorStr;
597 flavorWrapper->GetData( flavorStr );
599 if (GetDataForFlavor (mDataArray, flavorStr.get())
600 == mDataArray.NoIndex) // Don't append if already in intrinsic list
601 (*_retval)->AppendElement (genericFlavor);
602 } // foreach flavor that can be converted to
603 }
604 } // if a converter exists
606 return NS_OK;
607 } // FlavorsTransferableCanExport
609 NS_IMETHODIMP
610 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
611 {
612 MOZ_ASSERT(mInitialized);
614 NS_ENSURE_ARG_POINTER(aIsPrivateData);
616 *aIsPrivateData = mPrivateData;
618 return NS_OK;
619 }
621 NS_IMETHODIMP
622 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
623 {
624 MOZ_ASSERT(mInitialized);
626 mPrivateData = aIsPrivateData;
628 return NS_OK;
629 }