1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Mike Pinkerton (pinkerton@netscape.com)
24 * Dainis Jonitis (Dainis_Jonitis@swh-t.lv)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
43 - at some point, strings will be accessible from JS, so we won't have to wrap
44 flavors in an nsISupportsCString. Until then, we're kinda stuck with
45 this crappy API of nsISupportsArrays.
50 #include "nsTransferable.h"
52 #include "nsReadableUtils.h"
53 #include "nsVoidArray.h"
54 #include "nsIFormatConverter.h"
55 #include "nsIComponentManager.h"
58 #include "nsISupportsPrimitives.h"
60 #include "nsPrimitiveHelpers.h"
61 #include "nsXPIDLString.h"
62 #include "nsDirectoryServiceDefs.h"
63 #include "nsDirectoryService.h"
65 #include "nsNetUtil.h"
66 #include "nsIOutputStream.h"
67 #include "nsIInputStream.h"
69 #include "nsAutoPtr.h"
71 NS_IMPL_ISUPPORTS1(nsTransferable
, nsITransferable
)
77 // Holds a flavor (a mime type) that describes the data and the associated data.
81 DataStruct ( const char* aFlavor
)
82 : mDataLen(0), mFlavor(aFlavor
), mCacheFileName(nsnull
) { }
85 const nsCString
& GetFlavor() const { return mFlavor
; }
86 void SetData( nsISupports
* inData
, PRUint32 inDataLen
);
87 void GetData( nsISupports
** outData
, PRUint32
*outDataLen
);
88 nsIFile
* GetFileSpec(const char * aFileName
);
89 PRBool
IsDataAvailable() const { return (mData
&& mDataLen
> 0) || (!mData
&& mCacheFileName
); }
94 // The size of data over which we write the data to disk rather than
95 // keep it around in memory.
96 kLargeDatasetSize
= 1000000 // 1 million bytes
99 nsresult
WriteCache(nsISupports
* aData
, PRUint32 aDataLen
);
100 nsresult
ReadCache(nsISupports
** aData
, PRUint32
* aDataLen
);
102 nsCOMPtr
<nsISupports
> mData
; // OWNER - some varient of primitive wrapper
104 const nsCAutoString mFlavor
;
105 char * mCacheFileName
;
109 DataStruct
* GetDataForFlavor (const nsVoidArray
* pArray
, const char* aDataFlavor
);
111 DataStruct
* GetDataForFlavor (const nsVoidArray
* pArray
, const char* aDataFlavor
)
113 for (PRInt32 i
= 0 ; i
< pArray
->Count () ; ++i
) {
114 DataStruct
* data
= (DataStruct
*)pArray
->ElementAt (i
);
115 if (data
->GetFlavor().Equals (aDataFlavor
))
122 //-------------------------------------------------------------------------
123 DataStruct::~DataStruct()
125 if (mCacheFileName
) nsCRT::free(mCacheFileName
);
127 //nsIFileSpec * cacheFile = GetFileSpec(mCacheFileName);
128 //cacheFile->Remove();
131 //-------------------------------------------------------------------------
133 DataStruct::SetData ( nsISupports
* aData
, PRUint32 aDataLen
)
135 // Now, check to see if we consider the data to be "too large"
136 if (aDataLen
> kLargeDatasetSize
) {
137 // if so, cache it to disk instead of memory
138 if ( NS_SUCCEEDED(WriteCache(aData
, aDataLen
)) )
141 NS_WARNING("Oh no, couldn't write data to the cache file");
149 //-------------------------------------------------------------------------
151 DataStruct::GetData ( nsISupports
** aData
, PRUint32
*aDataLen
)
153 // check here to see if the data is cached on disk
154 if ( !mData
&& mCacheFileName
) {
155 // if so, read it in and pass it back
156 // ReadCache creates memory and copies the data into it.
157 if ( NS_SUCCEEDED(ReadCache(aData
, aDataLen
)) )
160 // oh shit, something went horribly wrong here.
161 NS_WARNING("Oh no, couldn't read data in from the cache file");
171 *aDataLen
= mDataLen
;
175 //-------------------------------------------------------------------------
177 DataStruct::GetFileSpec(const char * aFileName
)
180 NS_GetSpecialDirectory(NS_OS_TEMP_DIR
, &cacheFile
);
182 if (cacheFile
== nsnull
)
185 // if the param aFileName contains a name we should use that
186 // because the file probably already exists
187 // otherwise create a unique name
189 cacheFile
->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
190 cacheFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0600);
192 cacheFile
->AppendNative(nsDependentCString(aFileName
));
199 //-------------------------------------------------------------------------
201 DataStruct::WriteCache(nsISupports
* aData
, PRUint32 aDataLen
)
203 // Get a new path and file to the temp directory
204 nsCOMPtr
<nsIFile
> cacheFile ( getter_AddRefs(GetFileSpec(mCacheFileName
)) );
206 // remember the file name
207 if (!mCacheFileName
) {
208 nsXPIDLCString fName
;
209 cacheFile
->GetNativeLeafName(fName
);
210 mCacheFileName
= nsCRT::strdup(fName
);
213 // write out the contents of the clipboard
216 nsCOMPtr
<nsIOutputStream
> outStr
;
218 NS_NewLocalFileOutputStream(getter_AddRefs(outStr
),
221 if (!outStr
) return NS_ERROR_FAILURE
;
224 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor
.get(), aData
, &buff
, aDataLen
);
227 outStr
->Write(reinterpret_cast<char*>(buff
), aDataLen
, &ignored
);
228 nsMemory::Free(buff
);
232 return NS_ERROR_FAILURE
;
236 //-------------------------------------------------------------------------
238 DataStruct::ReadCache(nsISupports
** aData
, PRUint32
* aDataLen
)
240 // if we don't have a cache filename we are out of luck
242 return NS_ERROR_FAILURE
;
244 // get the path and file name
245 nsCOMPtr
<nsIFile
> cacheFile ( getter_AddRefs(GetFileSpec(mCacheFileName
)) );
247 if ( cacheFile
&& NS_SUCCEEDED(cacheFile
->Exists(&exists
)) && exists
) {
248 // get the size of the file
250 PRInt64
max32(LL_INIT(0, 0xFFFFFFFF));
251 cacheFile
->GetFileSize(&fileSize
);
252 if (LL_CMP(fileSize
, >, max32
))
253 return NS_ERROR_OUT_OF_MEMORY
;
255 LL_L2UI(size
, fileSize
);
257 // create new memory for the large clipboard data
258 nsAutoArrayPtr
<char> data(new char[size
]);
260 return NS_ERROR_OUT_OF_MEMORY
;
262 // now read it all in
263 nsCOMPtr
<nsIInputStream
> inStr
;
264 NS_NewLocalFileInputStream( getter_AddRefs(inStr
),
267 if (!cacheFile
) return NS_ERROR_FAILURE
;
269 nsresult rv
= inStr
->Read(data
, fileSize
, aDataLen
);
271 // make sure we got all the data ok
272 if (NS_SUCCEEDED(rv
) && *aDataLen
== size
) {
273 nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor
.get(), data
, fileSize
, aData
);
274 return *aData
? NS_OK
: NS_ERROR_FAILURE
;
277 // zero the return params
282 return NS_ERROR_FAILURE
;
291 //-------------------------------------------------------------------------
293 // Transferable constructor
295 //-------------------------------------------------------------------------
296 nsTransferable::nsTransferable()
298 mDataArray
= new nsVoidArray();
301 //-------------------------------------------------------------------------
303 // Transferable destructor
305 //-------------------------------------------------------------------------
306 nsTransferable::~nsTransferable()
308 for (PRInt32 i
=0;i
<mDataArray
->Count();i
++) {
309 DataStruct
* data
= (DataStruct
*)mDataArray
->ElementAt(i
);
317 // GetTransferDataFlavors
319 // Returns a copy of the internal list of flavors. This does NOT take into
320 // account any converter that may be registered. This list consists of
321 // nsISupportsCString objects so that the flavor list can be accessed from JS.
324 nsTransferable::GetTransferDataFlavors(nsISupportsArray
** aDataFlavorList
)
326 nsresult rv
= NS_NewISupportsArray ( aDataFlavorList
);
327 if (NS_FAILED(rv
)) return rv
;
329 for ( PRInt32 i
=0; i
<mDataArray
->Count(); ++i
) {
330 DataStruct
* data
= (DataStruct
*)mDataArray
->ElementAt(i
);
331 nsCOMPtr
<nsISupportsCString
> flavorWrapper
= do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID
);
332 if ( flavorWrapper
) {
333 flavorWrapper
->SetData ( data
->GetFlavor() );
334 nsCOMPtr
<nsISupports
> genericWrapper ( do_QueryInterface(flavorWrapper
) );
335 (*aDataFlavorList
)->AppendElement( genericWrapper
);
346 // Returns the data of the requested flavor, obtained from either having the data on hand or
347 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
348 // accessible from JS.
351 nsTransferable::GetTransferData(const char *aFlavor
, nsISupports
**aData
, PRUint32
*aDataLen
)
353 NS_ENSURE_ARG_POINTER(aFlavor
&& aData
&& aDataLen
);
357 // first look and see if the data is present in one of the intrinsic flavors
359 for (i
= 0; i
< mDataArray
->Count(); ++i
) {
360 DataStruct
* data
= (DataStruct
*)mDataArray
->ElementAt(i
);
361 if ( data
->GetFlavor().Equals(aFlavor
) ) {
362 data
->GetData(aData
, aDataLen
);
363 if (*aDataLen
== kFlavorHasDataProvider
) {
364 // do we have a data provider?
365 nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
= do_QueryInterface(*aData
);
367 rv
= dataProvider
->GetFlavorData(this, aFlavor
, aData
, aDataLen
);
369 break; // the provider failed. fall into the converter code below.
372 if (*aData
&& *aDataLen
> 0)
379 PRBool found
= PR_FALSE
;
381 // if not, try using a format converter to get the requested flavor
383 for (i
= 0; i
< mDataArray
->Count(); ++i
) {
384 DataStruct
* data
= (DataStruct
*)mDataArray
->ElementAt(i
);
385 PRBool canConvert
= PR_FALSE
;
386 mFormatConv
->CanConvert(data
->GetFlavor().get(), aFlavor
, &canConvert
);
388 nsCOMPtr
<nsISupports
> dataBytes
;
390 data
->GetData(getter_AddRefs(dataBytes
), &len
);
391 if (len
== kFlavorHasDataProvider
) {
392 // do we have a data provider?
393 nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
= do_QueryInterface(dataBytes
);
395 rv
= dataProvider
->GetFlavorData(this, aFlavor
, getter_AddRefs(dataBytes
), &len
);
400 mFormatConv
->Convert(data
->GetFlavor().get(), dataBytes
, len
, aFlavor
, aData
, aDataLen
);
406 return found
? NS_OK
: NS_ERROR_FAILURE
;
411 // GetAnyTransferData
413 // Returns the data of the first flavor found. Caller is responsible for deleting the
417 nsTransferable::GetAnyTransferData(char **aFlavor
, nsISupports
**aData
, PRUint32
*aDataLen
)
419 NS_ENSURE_ARG_POINTER(aFlavor
&& aData
&& aDataLen
);
421 for ( PRInt32 i
=0; i
< mDataArray
->Count(); ++i
) {
422 DataStruct
* data
= (DataStruct
*)mDataArray
->ElementAt(i
);
423 if (data
->IsDataAvailable()) {
424 *aFlavor
= ToNewCString(data
->GetFlavor());
425 data
->GetData(aData
, aDataLen
);
430 return NS_ERROR_FAILURE
;
440 nsTransferable::SetTransferData(const char *aFlavor
, nsISupports
*aData
, PRUint32 aDataLen
)
442 NS_ENSURE_ARG(aFlavor
);
444 // first check our intrinsic flavors to see if one has been registered.
446 for (i
= 0; i
< mDataArray
->Count(); ++i
) {
447 DataStruct
* data
= (DataStruct
*)mDataArray
->ElementAt(i
);
448 if ( data
->GetFlavor().Equals(aFlavor
) ) {
449 data
->SetData ( aData
, aDataLen
);
454 // if not, try using a format converter to find a flavor to put the data in
456 for (i
= 0; i
< mDataArray
->Count(); ++i
) {
457 DataStruct
* data
= (DataStruct
*)mDataArray
->ElementAt(i
);
458 PRBool canConvert
= PR_FALSE
;
459 mFormatConv
->CanConvert(aFlavor
, data
->GetFlavor().get(), &canConvert
);
462 nsCOMPtr
<nsISupports
> ConvertedData
;
463 PRUint32 ConvertedLen
;
464 mFormatConv
->Convert(aFlavor
, aData
, aDataLen
, data
->GetFlavor().get(), getter_AddRefs(ConvertedData
), &ConvertedLen
);
465 data
->SetData(ConvertedData
, ConvertedLen
);
471 // Can't set data neither directly nor through converter. Just add this flavor and try again
472 nsresult result
= NS_ERROR_FAILURE
;
473 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor
)) )
474 result
= SetTransferData (aFlavor
, aData
, aDataLen
);
483 // Adds a data flavor to our list with no data. Error if it already exists.
486 nsTransferable::AddDataFlavor(const char *aDataFlavor
)
488 if (GetDataForFlavor (mDataArray
, aDataFlavor
))
489 return NS_ERROR_FAILURE
;
491 // Create a new "slot" for the data
492 DataStruct
* data
= new DataStruct ( aDataFlavor
) ;
493 mDataArray
->AppendElement((void *)data
);
502 // Removes a data flavor (and causes the data to be destroyed). Error if
503 // the requested flavor is not present.
506 nsTransferable::RemoveDataFlavor(const char *aDataFlavor
)
508 DataStruct
* data
= GetDataForFlavor (mDataArray
, aDataFlavor
);
511 mDataArray
->RemoveElement (data
);
517 return NS_ERROR_FAILURE
;
526 nsTransferable::IsLargeDataSet(PRBool
*_retval
)
528 NS_ENSURE_ARG_POINTER(_retval
);
538 NS_IMETHODIMP
nsTransferable::SetConverter(nsIFormatConverter
* aConverter
)
540 mFormatConv
= aConverter
;
549 NS_IMETHODIMP
nsTransferable::GetConverter(nsIFormatConverter
* *aConverter
)
551 NS_ENSURE_ARG_POINTER(aConverter
);
552 *aConverter
= mFormatConv
;
553 NS_IF_ADDREF(*aConverter
);
559 // FlavorsTransferableCanImport
561 // Computes a list of flavors that the transferable can accept into it, either through
562 // intrinsic knowledge or input data converters.
565 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray
**_retval
)
567 NS_ENSURE_ARG_POINTER(_retval
);
569 // Get the flavor list, and on to the end of it, append the list of flavors we
570 // can also get to through a converter. This is so that we can just walk the list
571 // in one go, looking for the desired flavor.
572 GetTransferDataFlavors(_retval
); // addrefs
573 nsCOMPtr
<nsIFormatConverter
> converter
;
574 GetConverter(getter_AddRefs(converter
));
576 nsCOMPtr
<nsISupportsArray
> convertedList
;
577 converter
->GetInputDataFlavors(getter_AddRefs(convertedList
));
579 if ( convertedList
) {
580 PRUint32 importListLen
;
581 convertedList
->Count(&importListLen
);
583 for ( PRUint32 i
=0; i
< importListLen
; ++i
) {
584 nsCOMPtr
<nsISupports
> genericFlavor
;
585 convertedList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
587 nsCOMPtr
<nsISupportsCString
> flavorWrapper ( do_QueryInterface (genericFlavor
) );
588 nsCAutoString flavorStr
;
589 flavorWrapper
->GetData( flavorStr
);
591 if (!GetDataForFlavor (mDataArray
, flavorStr
.get())) // Don't append if already in intrinsic list
592 (*_retval
)->AppendElement (genericFlavor
);
593 } // foreach flavor that can be converted to
595 } // if a converter exists
598 } // FlavorsTransferableCanImport
602 // FlavorsTransferableCanExport
604 // Computes a list of flavors that the transferable can export, either through
605 // intrinsic knowledge or output data converters.
608 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray
**_retval
)
610 NS_ENSURE_ARG_POINTER(_retval
);
612 // Get the flavor list, and on to the end of it, append the list of flavors we
613 // can also get to through a converter. This is so that we can just walk the list
614 // in one go, looking for the desired flavor.
615 GetTransferDataFlavors(_retval
); // addrefs
616 nsCOMPtr
<nsIFormatConverter
> converter
;
617 GetConverter(getter_AddRefs(converter
));
619 nsCOMPtr
<nsISupportsArray
> convertedList
;
620 converter
->GetOutputDataFlavors(getter_AddRefs(convertedList
));
622 if ( convertedList
) {
623 PRUint32 importListLen
;
624 convertedList
->Count(&importListLen
);
626 for ( PRUint32 i
=0; i
< importListLen
; ++i
) {
627 nsCOMPtr
<nsISupports
> genericFlavor
;
628 convertedList
->GetElementAt ( i
, getter_AddRefs(genericFlavor
) );
630 nsCOMPtr
<nsISupportsCString
> flavorWrapper ( do_QueryInterface (genericFlavor
) );
631 nsCAutoString flavorStr
;
632 flavorWrapper
->GetData( flavorStr
);
634 if (!GetDataForFlavor (mDataArray
, flavorStr
.get())) // Don't append if already in intrinsic list
635 (*_retval
)->AppendElement (genericFlavor
);
636 } // foreach flavor that can be converted to
638 } // if a converter exists
641 } // FlavorsTransferableCanExport