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/. */
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.
16 #include "nsTransferable.h"
18 #include "nsReadableUtils.h"
20 #include "nsIFormatConverter.h"
21 #include "nsIComponentManager.h"
24 #include "nsISupportsPrimitives.h"
26 #include "nsPrimitiveHelpers.h"
27 #include "nsXPIDLString.h"
28 #include "nsDirectoryServiceDefs.h"
29 #include "nsDirectoryService.h"
31 #include "nsNetUtil.h"
32 #include "nsIOutputStream.h"
33 #include "nsIInputStream.h"
35 #include "nsILoadContext.h"
36 #include "nsAutoPtr.h"
38 NS_IMPL_ISUPPORTS(nsTransferable
, nsITransferable
)
40 size_t GetDataForFlavor (const nsTArray
<DataStruct
>& aArray
,
41 const char* aDataFlavor
)
43 for (size_t i
= 0 ; i
< aArray
.Length () ; ++i
) {
44 if (aArray
[i
].GetFlavor().Equals (aDataFlavor
))
48 return aArray
.NoIndex
;
51 //-------------------------------------------------------------------------
52 DataStruct::~DataStruct()
54 if (mCacheFileName
) free(mCacheFileName
);
57 //-------------------------------------------------------------------------
59 DataStruct::SetData ( nsISupports
* aData
, uint32_t aDataLen
, bool aIsPrivateData
)
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
)) )
68 NS_WARNING("Oh no, couldn't write data to the cache file");
76 //-------------------------------------------------------------------------
78 DataStruct::GetData ( nsISupports
** aData
, uint32_t *aDataLen
)
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
)) )
87 // oh shit, something went horribly wrong here.
88 NS_WARNING("Oh no, couldn't read data in from the cache file");
102 //-------------------------------------------------------------------------
103 already_AddRefed
<nsIFile
>
104 DataStruct::GetFileSpec(const char* aFileName
)
106 nsCOMPtr
<nsIFile
> cacheFile
;
107 NS_GetSpecialDirectory(NS_OS_TEMP_DIR
, getter_AddRefs(cacheFile
));
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
116 cacheFile
->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
117 cacheFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
119 cacheFile
->AppendNative(nsDependentCString(aFileName
));
122 return cacheFile
.forget();
126 //-------------------------------------------------------------------------
128 DataStruct::WriteCache(nsISupports
* aData
, uint32_t aDataLen
)
130 // Get a new path and file to the temp directory
131 nsCOMPtr
<nsIFile
> cacheFile
= GetFileSpec(mCacheFileName
);
133 // remember the file name
134 if (!mCacheFileName
) {
135 nsXPIDLCString fName
;
136 cacheFile
->GetNativeLeafName(fName
);
137 mCacheFileName
= strdup(fName
);
140 // write out the contents of the clipboard
143 nsCOMPtr
<nsIOutputStream
> outStr
;
145 NS_NewLocalFileOutputStream(getter_AddRefs(outStr
),
148 if (!outStr
) return NS_ERROR_FAILURE
;
150 void* buff
= nullptr;
151 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor
.get(), aData
, &buff
, aDataLen
);
154 outStr
->Write(reinterpret_cast<char*>(buff
), aDataLen
, &ignored
);
155 nsMemory::Free(buff
);
159 return NS_ERROR_FAILURE
;
163 //-------------------------------------------------------------------------
165 DataStruct::ReadCache(nsISupports
** aData
, uint32_t* aDataLen
)
167 // if we don't have a cache filename we are out of luck
169 return NS_ERROR_FAILURE
;
171 // get the path and file name
172 nsCOMPtr
<nsIFile
> cacheFile
= GetFileSpec(mCacheFileName
);
174 if ( cacheFile
&& NS_SUCCEEDED(cacheFile
->Exists(&exists
)) && exists
) {
175 // get the size of the file
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
]);
186 return NS_ERROR_OUT_OF_MEMORY
;
188 // now read it all in
189 nsCOMPtr
<nsIInputStream
> inStr
;
190 NS_NewLocalFileInputStream( getter_AddRefs(inStr
),
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
;
203 // zero the return params
208 return NS_ERROR_FAILURE
;
212 //-------------------------------------------------------------------------
214 // Transferable constructor
216 //-------------------------------------------------------------------------
217 nsTransferable::nsTransferable()
218 : mPrivateData(false)
220 , mInitialized(false)
225 //-------------------------------------------------------------------------
227 // Transferable destructor
229 //-------------------------------------------------------------------------
230 nsTransferable::~nsTransferable()
236 nsTransferable::Init(nsILoadContext
* aContext
)
238 MOZ_ASSERT(!mInitialized
);
241 mPrivateData
= aContext
->UsePrivateBrowsing();
250 // GetTransferDataFlavors
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.
257 nsTransferable::GetTransferDataFlavors(nsISupportsArray
** aDataFlavorList
)
259 MOZ_ASSERT(mInitialized
);
261 nsresult rv
= NS_NewISupportsArray ( aDataFlavorList
);
262 if (NS_FAILED(rv
)) return rv
;
264 for (size_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
);
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.
286 nsTransferable::GetTransferData(const char *aFlavor
, nsISupports
**aData
, uint32_t *aDataLen
)
288 MOZ_ASSERT(mInitialized
);
290 NS_ENSURE_ARG_POINTER(aFlavor
&& aData
&& aDataLen
);
293 nsCOMPtr
<nsISupports
> savedData
;
295 // first look and see if the data is present in one of the intrinsic flavors
296 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
297 DataStruct
& data
= mDataArray
.ElementAt(i
);
298 if ( data
.GetFlavor().Equals(aFlavor
) ) {
299 nsCOMPtr
<nsISupports
> dataBytes
;
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
);
306 rv
= dataProvider
->GetFlavorData(this, aFlavor
,
307 getter_AddRefs(dataBytes
), &len
);
309 break; // the provider failed. fall into the converter code below.
312 if (dataBytes
&& len
> 0) { // XXXmats why is zero length not ok?
314 dataBytes
.forget(aData
);
317 savedData
= dataBytes
; // return this if format converter fails
324 // if not, try using a format converter to get the requested flavor
326 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
327 DataStruct
& data
= mDataArray
.ElementAt(i
);
328 bool canConvert
= false;
329 mFormatConv
->CanConvert(data
.GetFlavor().get(), aFlavor
, &canConvert
);
331 nsCOMPtr
<nsISupports
> dataBytes
;
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
);
338 rv
= dataProvider
->GetFlavorData(this, aFlavor
,
339 getter_AddRefs(dataBytes
), &len
);
344 mFormatConv
->Convert(data
.GetFlavor().get(), dataBytes
, len
, aFlavor
, aData
, aDataLen
);
351 // for backward compatibility
353 savedData
.forget(aData
);
357 return found
? NS_OK
: NS_ERROR_FAILURE
;
362 // GetAnyTransferData
364 // Returns the data of the first flavor found. Caller is responsible for deleting the
368 nsTransferable::GetAnyTransferData(char **aFlavor
, nsISupports
**aData
, uint32_t *aDataLen
)
370 MOZ_ASSERT(mInitialized
);
372 NS_ENSURE_ARG_POINTER(aFlavor
&& aData
&& aDataLen
);
374 for (size_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
);
383 return NS_ERROR_FAILURE
;
393 nsTransferable::SetTransferData(const char *aFlavor
, nsISupports
*aData
, uint32_t aDataLen
)
395 MOZ_ASSERT(mInitialized
);
397 NS_ENSURE_ARG(aFlavor
);
399 // first check our intrinsic flavors to see if one has been registered.
400 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
401 DataStruct
& data
= mDataArray
.ElementAt(i
);
402 if ( data
.GetFlavor().Equals(aFlavor
) ) {
403 data
.SetData ( aData
, aDataLen
, mPrivateData
);
408 // if not, try using a format converter to find a flavor to put the data in
410 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
411 DataStruct
& data
= mDataArray
.ElementAt(i
);
412 bool canConvert
= false;
413 mFormatConv
->CanConvert(aFlavor
, data
.GetFlavor().get(), &canConvert
);
416 nsCOMPtr
<nsISupports
> ConvertedData
;
417 uint32_t ConvertedLen
;
418 mFormatConv
->Convert(aFlavor
, aData
, aDataLen
, data
.GetFlavor().get(), getter_AddRefs(ConvertedData
), &ConvertedLen
);
419 data
.SetData(ConvertedData
, ConvertedLen
, mPrivateData
);
425 // Can't set data neither directly nor through converter. Just add this flavor and try again
426 nsresult result
= NS_ERROR_FAILURE
;
427 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor
)) )
428 result
= SetTransferData (aFlavor
, aData
, aDataLen
);
437 // Adds a data flavor to our list with no data. Error if it already exists.
440 nsTransferable::AddDataFlavor(const char *aDataFlavor
)
442 MOZ_ASSERT(mInitialized
);
444 if (GetDataForFlavor (mDataArray
, aDataFlavor
) != mDataArray
.NoIndex
)
445 return NS_ERROR_FAILURE
;
447 // Create a new "slot" for the data
448 mDataArray
.AppendElement(DataStruct ( aDataFlavor
));
457 // Removes a data flavor (and causes the data to be destroyed). Error if
458 // the requested flavor is not present.
461 nsTransferable::RemoveDataFlavor(const char *aDataFlavor
)
463 MOZ_ASSERT(mInitialized
);
465 size_t idx
= GetDataForFlavor(mDataArray
, aDataFlavor
);
466 if (idx
!= mDataArray
.NoIndex
) {
467 mDataArray
.RemoveElementAt (idx
);
470 return NS_ERROR_FAILURE
;
479 nsTransferable::IsLargeDataSet(bool *_retval
)
481 MOZ_ASSERT(mInitialized
);
483 NS_ENSURE_ARG_POINTER(_retval
);
493 NS_IMETHODIMP
nsTransferable::SetConverter(nsIFormatConverter
* aConverter
)
495 MOZ_ASSERT(mInitialized
);
497 mFormatConv
= aConverter
;
506 NS_IMETHODIMP
nsTransferable::GetConverter(nsIFormatConverter
* *aConverter
)
508 MOZ_ASSERT(mInitialized
);
510 NS_ENSURE_ARG_POINTER(aConverter
);
511 *aConverter
= mFormatConv
;
512 NS_IF_ADDREF(*aConverter
);
518 // FlavorsTransferableCanImport
520 // Computes a list of flavors that the transferable can accept into it, either through
521 // intrinsic knowledge or input data converters.
524 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray
**_retval
)
526 MOZ_ASSERT(mInitialized
);
528 NS_ENSURE_ARG_POINTER(_retval
);
530 // Get the flavor list, and on to the end of it, append the list of flavors we
531 // can also get to through a converter. This is so that we can just walk the list
532 // in one go, looking for the desired flavor.
533 GetTransferDataFlavors(_retval
); // addrefs
534 nsCOMPtr
<nsIFormatConverter
> converter
;
535 GetConverter(getter_AddRefs(converter
));
537 nsCOMPtr
<nsISupportsArray
> convertedList
;
538 converter
->GetInputDataFlavors(getter_AddRefs(convertedList
));
540 if ( convertedList
) {
541 uint32_t importListLen
;
542 convertedList
->Count(&importListLen
);
544 for (uint32_t i
= 0; i
< importListLen
; ++i
) {
545 nsCOMPtr
<nsISupports
> genericFlavor
;
546 convertedList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
548 nsCOMPtr
<nsISupportsCString
> flavorWrapper ( do_QueryInterface (genericFlavor
) );
549 nsAutoCString flavorStr
;
550 flavorWrapper
->GetData( flavorStr
);
552 if (GetDataForFlavor (mDataArray
, flavorStr
.get())
553 == mDataArray
.NoIndex
) // Don't append if already in intrinsic list
554 (*_retval
)->AppendElement (genericFlavor
);
555 } // foreach flavor that can be converted to
557 } // if a converter exists
560 } // FlavorsTransferableCanImport
564 // FlavorsTransferableCanExport
566 // Computes a list of flavors that the transferable can export, either through
567 // intrinsic knowledge or output data converters.
570 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray
**_retval
)
572 MOZ_ASSERT(mInitialized
);
574 NS_ENSURE_ARG_POINTER(_retval
);
576 // Get the flavor list, and on to the end of it, append the list of flavors we
577 // can also get to through a converter. This is so that we can just walk the list
578 // in one go, looking for the desired flavor.
579 GetTransferDataFlavors(_retval
); // addrefs
580 nsCOMPtr
<nsIFormatConverter
> converter
;
581 GetConverter(getter_AddRefs(converter
));
583 nsCOMPtr
<nsISupportsArray
> convertedList
;
584 converter
->GetOutputDataFlavors(getter_AddRefs(convertedList
));
586 if ( convertedList
) {
587 uint32_t importListLen
;
588 convertedList
->Count(&importListLen
);
590 for ( uint32_t i
=0; i
< importListLen
; ++i
) {
591 nsCOMPtr
<nsISupports
> genericFlavor
;
592 convertedList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
594 nsCOMPtr
<nsISupportsCString
> flavorWrapper ( do_QueryInterface (genericFlavor
) );
595 nsAutoCString flavorStr
;
596 flavorWrapper
->GetData( flavorStr
);
598 if (GetDataForFlavor (mDataArray
, flavorStr
.get())
599 == mDataArray
.NoIndex
) // Don't append if already in intrinsic list
600 (*_retval
)->AppendElement (genericFlavor
);
601 } // foreach flavor that can be converted to
603 } // if a converter exists
606 } // FlavorsTransferableCanExport
609 nsTransferable::GetIsPrivateData(bool *aIsPrivateData
)
611 MOZ_ASSERT(mInitialized
);
613 NS_ENSURE_ARG_POINTER(aIsPrivateData
);
615 *aIsPrivateData
= mPrivateData
;
621 nsTransferable::SetIsPrivateData(bool aIsPrivateData
)
623 MOZ_ASSERT(mInitialized
);
625 mPrivateData
= aIsPrivateData
;
631 nsTransferable::GetRequestingNode(nsIDOMNode
** outRequestingNode
)
633 nsCOMPtr
<nsIDOMNode
> node
= do_QueryReferent(mRequestingNode
);
634 node
.forget(outRequestingNode
);
639 nsTransferable::SetRequestingNode(nsIDOMNode
* aRequestingNode
)
641 mRequestingNode
= do_GetWeakReference(aRequestingNode
);