Thu, 15 Jan 2015 21:13:52 +0100
Remove forgotten relic of ABI crash risk averse overloaded method change.
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, bool aIsPrivBrowsing )
60 {
61 // Now, check to see if we consider the data to be "too large"
62 // as well as ensuring that private browsing mode is disabled
63 if (aDataLen > kLargeDatasetSize && aIsPrivBrowsing == false) {
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 }
71 mData = aData;
72 mDataLen = aDataLen;
73 }
76 //-------------------------------------------------------------------------
77 void
78 DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
79 {
80 // check here to see if the data is cached on disk
81 if ( !mData && mCacheFileName ) {
82 // if so, read it in and pass it back
83 // ReadCache creates memory and copies the data into it.
84 if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
85 return;
86 else {
87 // oh shit, something went horribly wrong here.
88 NS_WARNING("Oh no, couldn't read data in from the cache file");
89 *aData = nullptr;
90 *aDataLen = 0;
91 return;
92 }
93 }
95 *aData = mData;
96 if ( mData )
97 NS_ADDREF(*aData);
98 *aDataLen = mDataLen;
99 }
102 //-------------------------------------------------------------------------
103 already_AddRefed<nsIFile>
104 DataStruct::GetFileSpec(const char* aFileName)
105 {
106 nsCOMPtr<nsIFile> cacheFile;
107 NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(cacheFile));
109 if (!cacheFile)
110 return nullptr;
112 // if the param aFileName contains a name we should use that
113 // because the file probably already exists
114 // otherwise create a unique name
115 if (!aFileName) {
116 cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
117 cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
118 } else {
119 cacheFile->AppendNative(nsDependentCString(aFileName));
120 }
122 return cacheFile.forget();
123 }
126 //-------------------------------------------------------------------------
127 nsresult
128 DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
129 {
130 // Get a new path and file to the temp directory
131 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
132 if (cacheFile) {
133 // remember the file name
134 if (!mCacheFileName) {
135 nsXPIDLCString fName;
136 cacheFile->GetNativeLeafName(fName);
137 mCacheFileName = strdup(fName);
138 }
140 // write out the contents of the clipboard
141 // to the file
142 //uint32_t bytes;
143 nsCOMPtr<nsIOutputStream> outStr;
145 NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
146 cacheFile);
148 if (!outStr) return NS_ERROR_FAILURE;
150 void* buff = nullptr;
151 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
152 if ( buff ) {
153 uint32_t ignored;
154 outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
155 nsMemory::Free(buff);
156 return NS_OK;
157 }
158 }
159 return NS_ERROR_FAILURE;
160 }
163 //-------------------------------------------------------------------------
164 nsresult
165 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
166 {
167 // if we don't have a cache filename we are out of luck
168 if (!mCacheFileName)
169 return NS_ERROR_FAILURE;
171 // get the path and file name
172 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
173 bool exists;
174 if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
175 // get the size of the file
176 int64_t fileSize;
177 int64_t max32 = 0xFFFFFFFF;
178 cacheFile->GetFileSize(&fileSize);
179 if (fileSize > max32)
180 return NS_ERROR_OUT_OF_MEMORY;
182 uint32_t size = uint32_t(fileSize);
183 // create new memory for the large clipboard data
184 nsAutoArrayPtr<char> data(new char[size]);
185 if ( !data )
186 return NS_ERROR_OUT_OF_MEMORY;
188 // now read it all in
189 nsCOMPtr<nsIInputStream> inStr;
190 NS_NewLocalFileInputStream( getter_AddRefs(inStr),
191 cacheFile);
193 if (!cacheFile) return NS_ERROR_FAILURE;
195 nsresult rv = inStr->Read(data, fileSize, aDataLen);
197 // make sure we got all the data ok
198 if (NS_SUCCEEDED(rv) && *aDataLen == size) {
199 nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
200 return *aData ? NS_OK : NS_ERROR_FAILURE;
201 }
203 // zero the return params
204 *aData = nullptr;
205 *aDataLen = 0;
206 }
208 return NS_ERROR_FAILURE;
209 }
212 //-------------------------------------------------------------------------
213 //
214 // Transferable constructor
215 //
216 //-------------------------------------------------------------------------
217 nsTransferable::nsTransferable()
218 : mPrivateData(false)
219 #ifdef DEBUG
220 , mInitialized(false)
221 #endif
222 {
223 }
225 //-------------------------------------------------------------------------
226 //
227 // Transferable destructor
228 //
229 //-------------------------------------------------------------------------
230 nsTransferable::~nsTransferable()
231 {
232 }
235 NS_IMETHODIMP
236 nsTransferable::Init(nsILoadContext* aContext)
237 {
238 MOZ_ASSERT(!mInitialized);
240 if (aContext) {
241 mPrivateData = aContext->UsePrivateBrowsing();
242 }
243 #ifdef DEBUG
244 mInitialized = true;
245 #endif
246 return NS_OK;
247 }
249 //
250 // GetTransferDataFlavors
251 //
252 // Returns a copy of the internal list of flavors. This does NOT take into
253 // account any converter that may be registered. This list consists of
254 // nsISupportsCString objects so that the flavor list can be accessed from JS.
255 //
256 nsresult
257 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
258 {
259 MOZ_ASSERT(mInitialized);
261 nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
262 if (NS_FAILED(rv)) return rv;
264 for ( uint32_t i=0; i<mDataArray.Length(); ++i ) {
265 DataStruct& data = mDataArray.ElementAt(i);
266 nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
267 if ( flavorWrapper ) {
268 flavorWrapper->SetData ( data.GetFlavor() );
269 nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
270 (*aDataFlavorList)->AppendElement( genericWrapper );
271 }
272 }
274 return NS_OK;
275 }
278 //
279 // GetTransferData
280 //
281 // Returns the data of the requested flavor, obtained from either having the data on hand or
282 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
283 // accessible from JS.
284 //
285 NS_IMETHODIMP
286 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
287 {
288 MOZ_ASSERT(mInitialized);
290 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
292 nsresult rv = NS_OK;
293 nsCOMPtr<nsISupports> savedData;
295 // first look and see if the data is present in one of the intrinsic flavors
296 uint32_t i;
297 for (i = 0; i < mDataArray.Length(); ++i ) {
298 DataStruct& data = mDataArray.ElementAt(i);
299 if ( data.GetFlavor().Equals(aFlavor) ) {
300 nsCOMPtr<nsISupports> dataBytes;
301 uint32_t len;
302 data.GetData(getter_AddRefs(dataBytes), &len);
303 if (len == kFlavorHasDataProvider && dataBytes) {
304 // do we have a data provider?
305 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
306 if (dataProvider) {
307 rv = dataProvider->GetFlavorData(this, aFlavor,
308 getter_AddRefs(dataBytes), &len);
309 if (NS_FAILED(rv))
310 break; // the provider failed. fall into the converter code below.
311 }
312 }
313 if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
314 *aDataLen = len;
315 dataBytes.forget(aData);
316 return NS_OK;
317 }
318 savedData = dataBytes; // return this if format converter fails
319 break;
320 }
321 }
323 bool found = false;
325 // if not, try using a format converter to get the requested flavor
326 if ( mFormatConv ) {
327 for (i = 0; i < mDataArray.Length(); ++i) {
328 DataStruct& data = mDataArray.ElementAt(i);
329 bool canConvert = false;
330 mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
331 if ( canConvert ) {
332 nsCOMPtr<nsISupports> dataBytes;
333 uint32_t len;
334 data.GetData(getter_AddRefs(dataBytes), &len);
335 if (len == kFlavorHasDataProvider && dataBytes) {
336 // do we have a data provider?
337 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
338 if (dataProvider) {
339 rv = dataProvider->GetFlavorData(this, aFlavor,
340 getter_AddRefs(dataBytes), &len);
341 if (NS_FAILED(rv))
342 break; // give up
343 }
344 }
345 mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
346 found = true;
347 break;
348 }
349 }
350 }
352 // for backward compatibility
353 if (!found) {
354 savedData.forget(aData);
355 *aDataLen = 0;
356 }
358 return found ? NS_OK : NS_ERROR_FAILURE;
359 }
362 //
363 // GetAnyTransferData
364 //
365 // Returns the data of the first flavor found. Caller is responsible for deleting the
366 // flavor string.
367 //
368 NS_IMETHODIMP
369 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, uint32_t *aDataLen)
370 {
371 MOZ_ASSERT(mInitialized);
373 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
375 for ( uint32_t i=0; i < mDataArray.Length(); ++i ) {
376 DataStruct& data = mDataArray.ElementAt(i);
377 if (data.IsDataAvailable()) {
378 *aFlavor = ToNewCString(data.GetFlavor());
379 data.GetData(aData, aDataLen);
380 return NS_OK;
381 }
382 }
384 return NS_ERROR_FAILURE;
385 }
388 //
389 // SetTransferData
390 //
391 //
392 //
393 NS_IMETHODIMP
394 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
395 {
396 MOZ_ASSERT(mInitialized);
398 NS_ENSURE_ARG(aFlavor);
400 // first check our intrinsic flavors to see if one has been registered.
401 uint32_t i = 0;
402 bool aIsPrivBrowsing = false;
403 for (i = 0; i < mDataArray.Length(); ++i) {
404 DataStruct& data = mDataArray.ElementAt(i);
405 if ( data.GetFlavor().Equals(aFlavor) ) {
406 if ( NS_SUCCEEDED(GetIsPrivateData(&aIsPrivBrowsing)) ) {
407 data.SetData ( aData, aDataLen, aIsPrivBrowsing );
408 return NS_OK;
409 }
410 else { // call to GetIsPrivateData() failed,
411 return NS_ERROR_FAILURE; // we didn't SetData(), generic error
412 }
413 }
414 }
416 // if not, try using a format converter to find a flavor to put the data in
417 if ( mFormatConv ) {
418 for (i = 0; i < mDataArray.Length(); ++i) {
419 DataStruct& data = mDataArray.ElementAt(i);
420 bool canConvert = false;
421 bool aIsPrivBrowsing = false;
422 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
424 if ( canConvert ) {
425 nsCOMPtr<nsISupports> ConvertedData;
426 uint32_t ConvertedLen;
427 mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
428 if (NS_SUCCEEDED(GetIsPrivateData(&aIsPrivBrowsing))) {
429 data.SetData(ConvertedData, ConvertedLen, aIsPrivBrowsing);
430 return NS_OK;
431 }
432 else { // call to GetIsPrivateData() failed,
433 return NS_ERROR_FAILURE; // we didn't SetData(), generic error
434 }
435 }
436 }
437 }
439 // Can't set data neither directly nor through converter. Just add this flavor and try again
440 nsresult result = NS_ERROR_FAILURE;
441 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
442 result = SetTransferData (aFlavor, aData, aDataLen);
444 return result;
445 }
448 //
449 // AddDataFlavor
450 //
451 // Adds a data flavor to our list with no data. Error if it already exists.
452 //
453 NS_IMETHODIMP
454 nsTransferable::AddDataFlavor(const char *aDataFlavor)
455 {
456 MOZ_ASSERT(mInitialized);
458 if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
459 return NS_ERROR_FAILURE;
461 // Create a new "slot" for the data
462 mDataArray.AppendElement(DataStruct ( aDataFlavor ));
464 return NS_OK;
465 }
468 //
469 // RemoveDataFlavor
470 //
471 // Removes a data flavor (and causes the data to be destroyed). Error if
472 // the requested flavor is not present.
473 //
474 NS_IMETHODIMP
475 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
476 {
477 MOZ_ASSERT(mInitialized);
479 uint32_t idx;
480 if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
481 mDataArray.RemoveElementAt (idx);
482 return NS_OK;
483 }
484 return NS_ERROR_FAILURE;
485 }
488 /**
489 *
490 *
491 */
492 NS_IMETHODIMP
493 nsTransferable::IsLargeDataSet(bool *_retval)
494 {
495 MOZ_ASSERT(mInitialized);
497 NS_ENSURE_ARG_POINTER(_retval);
498 *_retval = false;
499 return NS_OK;
500 }
503 /**
504 *
505 *
506 */
507 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
508 {
509 MOZ_ASSERT(mInitialized);
511 mFormatConv = aConverter;
512 return NS_OK;
513 }
516 /**
517 *
518 *
519 */
520 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
521 {
522 MOZ_ASSERT(mInitialized);
524 NS_ENSURE_ARG_POINTER(aConverter);
525 *aConverter = mFormatConv;
526 NS_IF_ADDREF(*aConverter);
527 return NS_OK;
528 }
531 //
532 // FlavorsTransferableCanImport
533 //
534 // Computes a list of flavors that the transferable can accept into it, either through
535 // intrinsic knowledge or input data converters.
536 //
537 NS_IMETHODIMP
538 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
539 {
540 MOZ_ASSERT(mInitialized);
542 NS_ENSURE_ARG_POINTER(_retval);
544 // Get the flavor list, and on to the end of it, append the list of flavors we
545 // can also get to through a converter. This is so that we can just walk the list
546 // in one go, looking for the desired flavor.
547 GetTransferDataFlavors(_retval); // addrefs
548 nsCOMPtr<nsIFormatConverter> converter;
549 GetConverter(getter_AddRefs(converter));
550 if ( converter ) {
551 nsCOMPtr<nsISupportsArray> convertedList;
552 converter->GetInputDataFlavors(getter_AddRefs(convertedList));
554 if ( convertedList ) {
555 uint32_t importListLen;
556 convertedList->Count(&importListLen);
558 for ( uint32_t i=0; i < importListLen; ++i ) {
559 nsCOMPtr<nsISupports> genericFlavor;
560 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
562 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
563 nsAutoCString flavorStr;
564 flavorWrapper->GetData( flavorStr );
566 if (GetDataForFlavor (mDataArray, flavorStr.get())
567 == mDataArray.NoIndex) // Don't append if already in intrinsic list
568 (*_retval)->AppendElement (genericFlavor);
569 } // foreach flavor that can be converted to
570 }
571 } // if a converter exists
573 return NS_OK;
574 } // FlavorsTransferableCanImport
577 //
578 // FlavorsTransferableCanExport
579 //
580 // Computes a list of flavors that the transferable can export, either through
581 // intrinsic knowledge or output data converters.
582 //
583 NS_IMETHODIMP
584 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
585 {
586 MOZ_ASSERT(mInitialized);
588 NS_ENSURE_ARG_POINTER(_retval);
590 // Get the flavor list, and on to the end of it, append the list of flavors we
591 // can also get to through a converter. This is so that we can just walk the list
592 // in one go, looking for the desired flavor.
593 GetTransferDataFlavors(_retval); // addrefs
594 nsCOMPtr<nsIFormatConverter> converter;
595 GetConverter(getter_AddRefs(converter));
596 if ( converter ) {
597 nsCOMPtr<nsISupportsArray> convertedList;
598 converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
600 if ( convertedList ) {
601 uint32_t importListLen;
602 convertedList->Count(&importListLen);
604 for ( uint32_t i=0; i < importListLen; ++i ) {
605 nsCOMPtr<nsISupports> genericFlavor;
606 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
608 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
609 nsAutoCString flavorStr;
610 flavorWrapper->GetData( flavorStr );
612 if (GetDataForFlavor (mDataArray, flavorStr.get())
613 == mDataArray.NoIndex) // Don't append if already in intrinsic list
614 (*_retval)->AppendElement (genericFlavor);
615 } // foreach flavor that can be converted to
616 }
617 } // if a converter exists
619 return NS_OK;
620 } // FlavorsTransferableCanExport
622 NS_IMETHODIMP
623 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
624 {
625 MOZ_ASSERT(mInitialized);
627 NS_ENSURE_ARG_POINTER(aIsPrivateData);
629 *aIsPrivateData = mPrivateData;
631 return NS_OK;
632 }
634 NS_IMETHODIMP
635 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
636 {
637 MOZ_ASSERT(mInitialized);
639 mPrivateData = aIsPrivateData;
641 return NS_OK;
642 }