Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / widget / src / xpwidgets / nsTransferable.cpp
blob1bcce086dc67eb94221cd5d38f9bd8f8c68f2e79
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
41 Notes to self:
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"
51 #include "nsString.h"
52 #include "nsReadableUtils.h"
53 #include "nsVoidArray.h"
54 #include "nsIFormatConverter.h"
55 #include "nsIComponentManager.h"
56 #include "nsCOMPtr.h"
57 #include "nsXPCOM.h"
58 #include "nsISupportsPrimitives.h"
59 #include "nsMemory.h"
60 #include "nsPrimitiveHelpers.h"
61 #include "nsXPIDLString.h"
62 #include "nsDirectoryServiceDefs.h"
63 #include "nsDirectoryService.h"
64 #include "nsCRT.h"
65 #include "nsNetUtil.h"
66 #include "nsIOutputStream.h"
67 #include "nsIInputStream.h"
68 #include "nsIFile.h"
69 #include "nsAutoPtr.h"
71 NS_IMPL_ISUPPORTS1(nsTransferable, nsITransferable)
75 // DataStruct
77 // Holds a flavor (a mime type) that describes the data and the associated data.
79 struct DataStruct
81 DataStruct ( const char* aFlavor )
82 : mDataLen(0), mFlavor(aFlavor), mCacheFileName(nsnull) { }
83 ~DataStruct();
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); }
91 protected:
93 enum {
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
103 PRUint32 mDataLen;
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))
116 return data;
119 return nsnull;
122 //-------------------------------------------------------------------------
123 DataStruct::~DataStruct()
125 if (mCacheFileName) nsCRT::free(mCacheFileName);
127 //nsIFileSpec * cacheFile = GetFileSpec(mCacheFileName);
128 //cacheFile->Remove();
131 //-------------------------------------------------------------------------
132 void
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)) )
139 return;
140 else
141 NS_WARNING("Oh no, couldn't write data to the cache file");
144 mData = aData;
145 mDataLen = aDataLen;
149 //-------------------------------------------------------------------------
150 void
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)) )
158 return;
159 else {
160 // oh shit, something went horribly wrong here.
161 NS_WARNING("Oh no, couldn't read data in from the cache file");
162 *aData = nsnull;
163 *aDataLen = 0;
164 return;
168 *aData = mData;
169 if ( mData )
170 NS_ADDREF(*aData);
171 *aDataLen = mDataLen;
175 //-------------------------------------------------------------------------
176 nsIFile*
177 DataStruct::GetFileSpec(const char * aFileName)
179 nsIFile* cacheFile;
180 NS_GetSpecialDirectory(NS_OS_TEMP_DIR, &cacheFile);
182 if (cacheFile == nsnull)
183 return 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
188 if (!aFileName) {
189 cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
190 cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
191 } else {
192 cacheFile->AppendNative(nsDependentCString(aFileName));
195 return cacheFile;
199 //-------------------------------------------------------------------------
200 nsresult
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)) );
205 if (cacheFile) {
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
214 // to the file
215 //PRUint32 bytes;
216 nsCOMPtr<nsIOutputStream> outStr;
218 NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
219 cacheFile);
221 if (!outStr) return NS_ERROR_FAILURE;
223 void* buff = nsnull;
224 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
225 if ( buff ) {
226 PRUint32 ignored;
227 outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
228 nsMemory::Free(buff);
229 return NS_OK;
232 return NS_ERROR_FAILURE;
236 //-------------------------------------------------------------------------
237 nsresult
238 DataStruct::ReadCache(nsISupports** aData, PRUint32* aDataLen)
240 // if we don't have a cache filename we are out of luck
241 if (!mCacheFileName)
242 return NS_ERROR_FAILURE;
244 // get the path and file name
245 nsCOMPtr<nsIFile> cacheFile ( getter_AddRefs(GetFileSpec(mCacheFileName)) );
246 PRBool exists;
247 if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
248 // get the size of the file
249 PRInt64 fileSize;
250 PRInt64 max32(LL_INIT(0, 0xFFFFFFFF));
251 cacheFile->GetFileSize(&fileSize);
252 if (LL_CMP(fileSize, >, max32))
253 return NS_ERROR_OUT_OF_MEMORY;
254 PRUint32 size;
255 LL_L2UI(size, fileSize);
257 // create new memory for the large clipboard data
258 nsAutoArrayPtr<char> data(new char[size]);
259 if ( !data )
260 return NS_ERROR_OUT_OF_MEMORY;
262 // now read it all in
263 nsCOMPtr<nsIInputStream> inStr;
264 NS_NewLocalFileInputStream( getter_AddRefs(inStr),
265 cacheFile);
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
278 *aData = nsnull;
279 *aDataLen = 0;
282 return NS_ERROR_FAILURE;
286 #ifdef XP_MAC
287 #pragma mark -
288 #endif
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);
310 delete data;
312 delete mDataArray;
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.
323 nsresult
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 );
339 return NS_OK;
344 // GetTransferData
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.
350 NS_IMETHODIMP
351 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, PRUint32 *aDataLen)
353 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
355 nsresult rv = NS_OK;
357 // first look and see if the data is present in one of the intrinsic flavors
358 PRInt32 i;
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);
366 if (dataProvider) {
367 rv = dataProvider->GetFlavorData(this, aFlavor, aData, aDataLen);
368 if (NS_FAILED(rv))
369 break; // the provider failed. fall into the converter code below.
372 if (*aData && *aDataLen > 0)
373 return NS_OK;
375 break;
379 PRBool found = PR_FALSE;
381 // if not, try using a format converter to get the requested flavor
382 if ( mFormatConv ) {
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);
387 if ( canConvert ) {
388 nsCOMPtr<nsISupports> dataBytes;
389 PRUint32 len;
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);
394 if (dataProvider) {
395 rv = dataProvider->GetFlavorData(this, aFlavor, getter_AddRefs(dataBytes), &len);
396 if (NS_FAILED(rv))
397 break; // give up
400 mFormatConv->Convert(data->GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
401 found = PR_TRUE;
402 break;
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
414 // flavor string.
416 NS_IMETHODIMP
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);
426 return NS_OK;
430 return NS_ERROR_FAILURE;
435 // SetTransferData
439 NS_IMETHODIMP
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.
445 PRInt32 i = 0;
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 );
450 return NS_OK;
454 // if not, try using a format converter to find a flavor to put the data in
455 if ( mFormatConv ) {
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);
461 if ( 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);
466 return NS_OK;
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);
476 return result;
481 // AddDataFlavor
483 // Adds a data flavor to our list with no data. Error if it already exists.
485 NS_IMETHODIMP
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);
495 return NS_OK;
500 // RemoveDataFlavor
502 // Removes a data flavor (and causes the data to be destroyed). Error if
503 // the requested flavor is not present.
505 NS_IMETHODIMP
506 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
508 DataStruct* data = GetDataForFlavor (mDataArray, aDataFlavor);
510 if ( data ) {
511 mDataArray->RemoveElement (data);
512 delete data;
514 return NS_OK;
516 else
517 return NS_ERROR_FAILURE;
525 NS_IMETHODIMP
526 nsTransferable::IsLargeDataSet(PRBool *_retval)
528 NS_ENSURE_ARG_POINTER(_retval);
529 *_retval = PR_FALSE;
530 return NS_OK;
538 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
540 mFormatConv = aConverter;
541 return NS_OK;
549 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
551 NS_ENSURE_ARG_POINTER(aConverter);
552 *aConverter = mFormatConv;
553 NS_IF_ADDREF(*aConverter);
554 return NS_OK;
559 // FlavorsTransferableCanImport
561 // Computes a list of flavors that the transferable can accept into it, either through
562 // intrinsic knowledge or input data converters.
564 NS_IMETHODIMP
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));
575 if ( 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
597 return NS_OK;
598 } // FlavorsTransferableCanImport
602 // FlavorsTransferableCanExport
604 // Computes a list of flavors that the transferable can export, either through
605 // intrinsic knowledge or output data converters.
607 NS_IMETHODIMP
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));
618 if ( 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
640 return NS_OK;
641 } // FlavorsTransferableCanExport