Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 aIsPrivateData )
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 && !aIsPrivateData) {
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 for (i = 0; i < mDataArray.Length(); ++i) {
403 DataStruct& data = mDataArray.ElementAt(i);
404 if ( data.GetFlavor().Equals(aFlavor) ) {
405 data.SetData ( aData, aDataLen, mPrivateData );
406 return NS_OK;
407 }
408 }
410 // if not, try using a format converter to find a flavor to put the data in
411 if ( mFormatConv ) {
412 for (i = 0; i < mDataArray.Length(); ++i) {
413 DataStruct& data = mDataArray.ElementAt(i);
414 bool canConvert = false;
415 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
417 if ( canConvert ) {
418 nsCOMPtr<nsISupports> ConvertedData;
419 uint32_t ConvertedLen;
420 mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
421 data.SetData(ConvertedData, ConvertedLen, mPrivateData);
422 return NS_OK;
423 }
424 }
425 }
427 // Can't set data neither directly nor through converter. Just add this flavor and try again
428 nsresult result = NS_ERROR_FAILURE;
429 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
430 result = SetTransferData (aFlavor, aData, aDataLen);
432 return result;
433 }
436 //
437 // AddDataFlavor
438 //
439 // Adds a data flavor to our list with no data. Error if it already exists.
440 //
441 NS_IMETHODIMP
442 nsTransferable::AddDataFlavor(const char *aDataFlavor)
443 {
444 MOZ_ASSERT(mInitialized);
446 if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
447 return NS_ERROR_FAILURE;
449 // Create a new "slot" for the data
450 mDataArray.AppendElement(DataStruct ( aDataFlavor ));
452 return NS_OK;
453 }
456 //
457 // RemoveDataFlavor
458 //
459 // Removes a data flavor (and causes the data to be destroyed). Error if
460 // the requested flavor is not present.
461 //
462 NS_IMETHODIMP
463 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
464 {
465 MOZ_ASSERT(mInitialized);
467 uint32_t idx;
468 if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
469 mDataArray.RemoveElementAt (idx);
470 return NS_OK;
471 }
472 return NS_ERROR_FAILURE;
473 }
476 /**
477 *
478 *
479 */
480 NS_IMETHODIMP
481 nsTransferable::IsLargeDataSet(bool *_retval)
482 {
483 MOZ_ASSERT(mInitialized);
485 NS_ENSURE_ARG_POINTER(_retval);
486 *_retval = false;
487 return NS_OK;
488 }
491 /**
492 *
493 *
494 */
495 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
496 {
497 MOZ_ASSERT(mInitialized);
499 mFormatConv = aConverter;
500 return NS_OK;
501 }
504 /**
505 *
506 *
507 */
508 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
509 {
510 MOZ_ASSERT(mInitialized);
512 NS_ENSURE_ARG_POINTER(aConverter);
513 *aConverter = mFormatConv;
514 NS_IF_ADDREF(*aConverter);
515 return NS_OK;
516 }
519 //
520 // FlavorsTransferableCanImport
521 //
522 // Computes a list of flavors that the transferable can accept into it, either through
523 // intrinsic knowledge or input data converters.
524 //
525 NS_IMETHODIMP
526 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
527 {
528 MOZ_ASSERT(mInitialized);
530 NS_ENSURE_ARG_POINTER(_retval);
532 // Get the flavor list, and on to the end of it, append the list of flavors we
533 // can also get to through a converter. This is so that we can just walk the list
534 // in one go, looking for the desired flavor.
535 GetTransferDataFlavors(_retval); // addrefs
536 nsCOMPtr<nsIFormatConverter> converter;
537 GetConverter(getter_AddRefs(converter));
538 if ( converter ) {
539 nsCOMPtr<nsISupportsArray> convertedList;
540 converter->GetInputDataFlavors(getter_AddRefs(convertedList));
542 if ( convertedList ) {
543 uint32_t importListLen;
544 convertedList->Count(&importListLen);
546 for ( uint32_t i=0; i < importListLen; ++i ) {
547 nsCOMPtr<nsISupports> genericFlavor;
548 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
550 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
551 nsAutoCString flavorStr;
552 flavorWrapper->GetData( flavorStr );
554 if (GetDataForFlavor (mDataArray, flavorStr.get())
555 == mDataArray.NoIndex) // Don't append if already in intrinsic list
556 (*_retval)->AppendElement (genericFlavor);
557 } // foreach flavor that can be converted to
558 }
559 } // if a converter exists
561 return NS_OK;
562 } // FlavorsTransferableCanImport
565 //
566 // FlavorsTransferableCanExport
567 //
568 // Computes a list of flavors that the transferable can export, either through
569 // intrinsic knowledge or output data converters.
570 //
571 NS_IMETHODIMP
572 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
573 {
574 MOZ_ASSERT(mInitialized);
576 NS_ENSURE_ARG_POINTER(_retval);
578 // Get the flavor list, and on to the end of it, append the list of flavors we
579 // can also get to through a converter. This is so that we can just walk the list
580 // in one go, looking for the desired flavor.
581 GetTransferDataFlavors(_retval); // addrefs
582 nsCOMPtr<nsIFormatConverter> converter;
583 GetConverter(getter_AddRefs(converter));
584 if ( converter ) {
585 nsCOMPtr<nsISupportsArray> convertedList;
586 converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
588 if ( convertedList ) {
589 uint32_t importListLen;
590 convertedList->Count(&importListLen);
592 for ( uint32_t i=0; i < importListLen; ++i ) {
593 nsCOMPtr<nsISupports> genericFlavor;
594 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
596 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
597 nsAutoCString flavorStr;
598 flavorWrapper->GetData( flavorStr );
600 if (GetDataForFlavor (mDataArray, flavorStr.get())
601 == mDataArray.NoIndex) // Don't append if already in intrinsic list
602 (*_retval)->AppendElement (genericFlavor);
603 } // foreach flavor that can be converted to
604 }
605 } // if a converter exists
607 return NS_OK;
608 } // FlavorsTransferableCanExport
610 NS_IMETHODIMP
611 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
612 {
613 MOZ_ASSERT(mInitialized);
615 NS_ENSURE_ARG_POINTER(aIsPrivateData);
617 *aIsPrivateData = mPrivateData;
619 return NS_OK;
620 }
622 NS_IMETHODIMP
623 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
624 {
625 MOZ_ASSERT(mInitialized);
627 mPrivateData = aIsPrivateData;
629 return NS_OK;
630 }