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
)
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
)) )
67 NS_WARNING("Oh no, couldn't write data to the cache file");
75 //-------------------------------------------------------------------------
77 DataStruct::GetData ( nsISupports
** aData
, uint32_t *aDataLen
)
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
)) )
86 // oh shit, something went horribly wrong here.
87 NS_WARNING("Oh no, couldn't read data in from the cache file");
101 //-------------------------------------------------------------------------
102 already_AddRefed
<nsIFile
>
103 DataStruct::GetFileSpec(const char* aFileName
)
105 nsCOMPtr
<nsIFile
> cacheFile
;
106 NS_GetSpecialDirectory(NS_OS_TEMP_DIR
, getter_AddRefs(cacheFile
));
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
115 cacheFile
->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
116 cacheFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
118 cacheFile
->AppendNative(nsDependentCString(aFileName
));
121 return cacheFile
.forget();
125 //-------------------------------------------------------------------------
127 DataStruct::WriteCache(nsISupports
* aData
, uint32_t aDataLen
)
129 // Get a new path and file to the temp directory
130 nsCOMPtr
<nsIFile
> cacheFile
= GetFileSpec(mCacheFileName
);
132 // remember the file name
133 if (!mCacheFileName
) {
134 nsXPIDLCString fName
;
135 cacheFile
->GetNativeLeafName(fName
);
136 mCacheFileName
= strdup(fName
);
139 // write out the contents of the clipboard
142 nsCOMPtr
<nsIOutputStream
> outStr
;
144 NS_NewLocalFileOutputStream(getter_AddRefs(outStr
),
147 if (!outStr
) return NS_ERROR_FAILURE
;
149 void* buff
= nullptr;
150 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor
.get(), aData
, &buff
, aDataLen
);
153 outStr
->Write(reinterpret_cast<char*>(buff
), aDataLen
, &ignored
);
154 nsMemory::Free(buff
);
158 return NS_ERROR_FAILURE
;
162 //-------------------------------------------------------------------------
164 DataStruct::ReadCache(nsISupports
** aData
, uint32_t* aDataLen
)
166 // if we don't have a cache filename we are out of luck
168 return NS_ERROR_FAILURE
;
170 // get the path and file name
171 nsCOMPtr
<nsIFile
> cacheFile
= GetFileSpec(mCacheFileName
);
173 if ( cacheFile
&& NS_SUCCEEDED(cacheFile
->Exists(&exists
)) && exists
) {
174 // get the size of the file
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
]);
185 return NS_ERROR_OUT_OF_MEMORY
;
187 // now read it all in
188 nsCOMPtr
<nsIInputStream
> inStr
;
189 NS_NewLocalFileInputStream( getter_AddRefs(inStr
),
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
;
202 // zero the return params
207 return NS_ERROR_FAILURE
;
211 //-------------------------------------------------------------------------
213 // Transferable constructor
215 //-------------------------------------------------------------------------
216 nsTransferable::nsTransferable()
217 : mPrivateData(false)
219 , mInitialized(false)
224 //-------------------------------------------------------------------------
226 // Transferable destructor
228 //-------------------------------------------------------------------------
229 nsTransferable::~nsTransferable()
235 nsTransferable::Init(nsILoadContext
* aContext
)
237 MOZ_ASSERT(!mInitialized
);
240 mPrivateData
= aContext
->UsePrivateBrowsing();
249 // GetTransferDataFlavors
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.
256 nsTransferable::GetTransferDataFlavors(nsISupportsArray
** aDataFlavorList
)
258 MOZ_ASSERT(mInitialized
);
260 nsresult rv
= NS_NewISupportsArray ( aDataFlavorList
);
261 if (NS_FAILED(rv
)) return rv
;
263 for (size_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
);
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.
285 nsTransferable::GetTransferData(const char *aFlavor
, nsISupports
**aData
, uint32_t *aDataLen
)
287 MOZ_ASSERT(mInitialized
);
289 NS_ENSURE_ARG_POINTER(aFlavor
&& aData
&& aDataLen
);
292 nsCOMPtr
<nsISupports
> savedData
;
294 // first look and see if the data is present in one of the intrinsic flavors
295 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
296 DataStruct
& data
= mDataArray
.ElementAt(i
);
297 if ( data
.GetFlavor().Equals(aFlavor
) ) {
298 nsCOMPtr
<nsISupports
> dataBytes
;
300 data
.GetData(getter_AddRefs(dataBytes
), &len
);
301 if (len
== kFlavorHasDataProvider
&& dataBytes
) {
302 // do we have a data provider?
303 nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
= do_QueryInterface(dataBytes
);
305 rv
= dataProvider
->GetFlavorData(this, aFlavor
,
306 getter_AddRefs(dataBytes
), &len
);
308 break; // the provider failed. fall into the converter code below.
311 if (dataBytes
&& len
> 0) { // XXXmats why is zero length not ok?
313 dataBytes
.forget(aData
);
316 savedData
= dataBytes
; // return this if format converter fails
323 // if not, try using a format converter to get the requested flavor
325 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
326 DataStruct
& data
= mDataArray
.ElementAt(i
);
327 bool canConvert
= false;
328 mFormatConv
->CanConvert(data
.GetFlavor().get(), aFlavor
, &canConvert
);
330 nsCOMPtr
<nsISupports
> dataBytes
;
332 data
.GetData(getter_AddRefs(dataBytes
), &len
);
333 if (len
== kFlavorHasDataProvider
&& dataBytes
) {
334 // do we have a data provider?
335 nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
= do_QueryInterface(dataBytes
);
337 rv
= dataProvider
->GetFlavorData(this, aFlavor
,
338 getter_AddRefs(dataBytes
), &len
);
343 mFormatConv
->Convert(data
.GetFlavor().get(), dataBytes
, len
, aFlavor
, aData
, aDataLen
);
350 // for backward compatibility
352 savedData
.forget(aData
);
356 return found
? NS_OK
: NS_ERROR_FAILURE
;
361 // GetAnyTransferData
363 // Returns the data of the first flavor found. Caller is responsible for deleting the
367 nsTransferable::GetAnyTransferData(char **aFlavor
, nsISupports
**aData
, uint32_t *aDataLen
)
369 MOZ_ASSERT(mInitialized
);
371 NS_ENSURE_ARG_POINTER(aFlavor
&& aData
&& aDataLen
);
373 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
374 DataStruct
& data
= mDataArray
.ElementAt(i
);
375 if (data
.IsDataAvailable()) {
376 *aFlavor
= ToNewCString(data
.GetFlavor());
377 data
.GetData(aData
, aDataLen
);
382 return NS_ERROR_FAILURE
;
392 nsTransferable::SetTransferData(const char *aFlavor
, nsISupports
*aData
, uint32_t aDataLen
)
394 MOZ_ASSERT(mInitialized
);
396 NS_ENSURE_ARG(aFlavor
);
398 // first check our intrinsic flavors to see if one has been registered.
399 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
400 DataStruct
& data
= mDataArray
.ElementAt(i
);
401 if ( data
.GetFlavor().Equals(aFlavor
) ) {
402 data
.SetData ( aData
, aDataLen
);
407 // if not, try using a format converter to find a flavor to put the data in
409 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
410 DataStruct
& data
= mDataArray
.ElementAt(i
);
411 bool canConvert
= false;
412 mFormatConv
->CanConvert(aFlavor
, data
.GetFlavor().get(), &canConvert
);
415 nsCOMPtr
<nsISupports
> ConvertedData
;
416 uint32_t ConvertedLen
;
417 mFormatConv
->Convert(aFlavor
, aData
, aDataLen
, data
.GetFlavor().get(), getter_AddRefs(ConvertedData
), &ConvertedLen
);
418 data
.SetData(ConvertedData
, ConvertedLen
);
424 // Can't set data neither directly nor through converter. Just add this flavor and try again
425 nsresult result
= NS_ERROR_FAILURE
;
426 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor
)) )
427 result
= SetTransferData (aFlavor
, aData
, aDataLen
);
436 // Adds a data flavor to our list with no data. Error if it already exists.
439 nsTransferable::AddDataFlavor(const char *aDataFlavor
)
441 MOZ_ASSERT(mInitialized
);
443 if (GetDataForFlavor (mDataArray
, aDataFlavor
) != mDataArray
.NoIndex
)
444 return NS_ERROR_FAILURE
;
446 // Create a new "slot" for the data
447 mDataArray
.AppendElement(DataStruct ( aDataFlavor
));
456 // Removes a data flavor (and causes the data to be destroyed). Error if
457 // the requested flavor is not present.
460 nsTransferable::RemoveDataFlavor(const char *aDataFlavor
)
462 MOZ_ASSERT(mInitialized
);
464 size_t idx
= GetDataForFlavor(mDataArray
, aDataFlavor
);
465 if (idx
!= mDataArray
.NoIndex
) {
466 mDataArray
.RemoveElementAt (idx
);
469 return NS_ERROR_FAILURE
;
478 nsTransferable::IsLargeDataSet(bool *_retval
)
480 MOZ_ASSERT(mInitialized
);
482 NS_ENSURE_ARG_POINTER(_retval
);
492 NS_IMETHODIMP
nsTransferable::SetConverter(nsIFormatConverter
* aConverter
)
494 MOZ_ASSERT(mInitialized
);
496 mFormatConv
= aConverter
;
505 NS_IMETHODIMP
nsTransferable::GetConverter(nsIFormatConverter
* *aConverter
)
507 MOZ_ASSERT(mInitialized
);
509 NS_ENSURE_ARG_POINTER(aConverter
);
510 *aConverter
= mFormatConv
;
511 NS_IF_ADDREF(*aConverter
);
517 // FlavorsTransferableCanImport
519 // Computes a list of flavors that the transferable can accept into it, either through
520 // intrinsic knowledge or input data converters.
523 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray
**_retval
)
525 MOZ_ASSERT(mInitialized
);
527 NS_ENSURE_ARG_POINTER(_retval
);
529 // Get the flavor list, and on to the end of it, append the list of flavors we
530 // can also get to through a converter. This is so that we can just walk the list
531 // in one go, looking for the desired flavor.
532 GetTransferDataFlavors(_retval
); // addrefs
533 nsCOMPtr
<nsIFormatConverter
> converter
;
534 GetConverter(getter_AddRefs(converter
));
536 nsCOMPtr
<nsISupportsArray
> convertedList
;
537 converter
->GetInputDataFlavors(getter_AddRefs(convertedList
));
539 if ( convertedList
) {
540 uint32_t importListLen
;
541 convertedList
->Count(&importListLen
);
543 for (uint32_t i
= 0; i
< importListLen
; ++i
) {
544 nsCOMPtr
<nsISupports
> genericFlavor
;
545 convertedList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
547 nsCOMPtr
<nsISupportsCString
> flavorWrapper ( do_QueryInterface (genericFlavor
) );
548 nsAutoCString flavorStr
;
549 flavorWrapper
->GetData( flavorStr
);
551 if (GetDataForFlavor (mDataArray
, flavorStr
.get())
552 == mDataArray
.NoIndex
) // Don't append if already in intrinsic list
553 (*_retval
)->AppendElement (genericFlavor
);
554 } // foreach flavor that can be converted to
556 } // if a converter exists
559 } // FlavorsTransferableCanImport
563 // FlavorsTransferableCanExport
565 // Computes a list of flavors that the transferable can export, either through
566 // intrinsic knowledge or output data converters.
569 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray
**_retval
)
571 MOZ_ASSERT(mInitialized
);
573 NS_ENSURE_ARG_POINTER(_retval
);
575 // Get the flavor list, and on to the end of it, append the list of flavors we
576 // can also get to through a converter. This is so that we can just walk the list
577 // in one go, looking for the desired flavor.
578 GetTransferDataFlavors(_retval
); // addrefs
579 nsCOMPtr
<nsIFormatConverter
> converter
;
580 GetConverter(getter_AddRefs(converter
));
582 nsCOMPtr
<nsISupportsArray
> convertedList
;
583 converter
->GetOutputDataFlavors(getter_AddRefs(convertedList
));
585 if ( convertedList
) {
586 uint32_t importListLen
;
587 convertedList
->Count(&importListLen
);
589 for ( uint32_t i
=0; i
< importListLen
; ++i
) {
590 nsCOMPtr
<nsISupports
> genericFlavor
;
591 convertedList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
593 nsCOMPtr
<nsISupportsCString
> flavorWrapper ( do_QueryInterface (genericFlavor
) );
594 nsAutoCString flavorStr
;
595 flavorWrapper
->GetData( flavorStr
);
597 if (GetDataForFlavor (mDataArray
, flavorStr
.get())
598 == mDataArray
.NoIndex
) // Don't append if already in intrinsic list
599 (*_retval
)->AppendElement (genericFlavor
);
600 } // foreach flavor that can be converted to
602 } // if a converter exists
605 } // FlavorsTransferableCanExport
608 nsTransferable::GetIsPrivateData(bool *aIsPrivateData
)
610 MOZ_ASSERT(mInitialized
);
612 NS_ENSURE_ARG_POINTER(aIsPrivateData
);
614 *aIsPrivateData
= mPrivateData
;
620 nsTransferable::SetIsPrivateData(bool aIsPrivateData
)
622 MOZ_ASSERT(mInitialized
);
624 mPrivateData
= aIsPrivateData
;
630 nsTransferable::GetRequestingNode(nsIDOMNode
** outRequestingNode
)
632 nsCOMPtr
<nsIDOMNode
> node
= do_QueryReferent(mRequestingNode
);
633 node
.forget(outRequestingNode
);
638 nsTransferable::SetRequestingNode(nsIDOMNode
* aRequestingNode
)
640 mRequestingNode
= do_GetWeakReference(aRequestingNode
);