Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / widget / nsTransferable.cpp
blob08af4ed4e02df6fca57b398d5ee7a9bdfc210936
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.
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 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))
45 return i;
48 return aArray.NoIndex;
51 //-------------------------------------------------------------------------
52 DataStruct::~DataStruct()
54 if (mCacheFileName) free(mCacheFileName);
57 //-------------------------------------------------------------------------
58 void
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)) )
66 return;
67 else
68 NS_WARNING("Oh no, couldn't write data to the cache file");
71 mData = aData;
72 mDataLen = aDataLen;
76 //-------------------------------------------------------------------------
77 void
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)) )
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;
95 *aData = mData;
96 if ( mData )
97 NS_ADDREF(*aData);
98 *aDataLen = mDataLen;
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));
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));
122 return cacheFile.forget();
126 //-------------------------------------------------------------------------
127 nsresult
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);
132 if (cacheFile) {
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
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;
159 return NS_ERROR_FAILURE;
163 //-------------------------------------------------------------------------
164 nsresult
165 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
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;
203 // zero the return params
204 *aData = nullptr;
205 *aDataLen = 0;
208 return NS_ERROR_FAILURE;
212 //-------------------------------------------------------------------------
214 // Transferable constructor
216 //-------------------------------------------------------------------------
217 nsTransferable::nsTransferable()
218 : mPrivateData(false)
219 #ifdef DEBUG
220 , mInitialized(false)
221 #endif
225 //-------------------------------------------------------------------------
227 // Transferable destructor
229 //-------------------------------------------------------------------------
230 nsTransferable::~nsTransferable()
235 NS_IMETHODIMP
236 nsTransferable::Init(nsILoadContext* aContext)
238 MOZ_ASSERT(!mInitialized);
240 if (aContext) {
241 mPrivateData = aContext->UsePrivateBrowsing();
243 #ifdef DEBUG
244 mInitialized = true;
245 #endif
246 return NS_OK;
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.
256 nsresult
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 );
274 return NS_OK;
279 // GetTransferData
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.
285 NS_IMETHODIMP
286 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
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 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;
300 uint32_t len;
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);
305 if (dataProvider) {
306 rv = dataProvider->GetFlavorData(this, aFlavor,
307 getter_AddRefs(dataBytes), &len);
308 if (NS_FAILED(rv))
309 break; // the provider failed. fall into the converter code below.
312 if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
313 *aDataLen = len;
314 dataBytes.forget(aData);
315 return NS_OK;
317 savedData = dataBytes; // return this if format converter fails
318 break;
322 bool found = false;
324 // if not, try using a format converter to get the requested flavor
325 if ( mFormatConv ) {
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);
330 if ( canConvert ) {
331 nsCOMPtr<nsISupports> dataBytes;
332 uint32_t len;
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);
337 if (dataProvider) {
338 rv = dataProvider->GetFlavorData(this, aFlavor,
339 getter_AddRefs(dataBytes), &len);
340 if (NS_FAILED(rv))
341 break; // give up
344 mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
345 found = true;
346 break;
351 // for backward compatibility
352 if (!found) {
353 savedData.forget(aData);
354 *aDataLen = 0;
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
365 // flavor string.
367 NS_IMETHODIMP
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);
379 return NS_OK;
383 return NS_ERROR_FAILURE;
388 // SetTransferData
392 NS_IMETHODIMP
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 );
404 return NS_OK;
408 // if not, try using a format converter to find a flavor to put the data in
409 if ( mFormatConv ) {
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);
415 if ( 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);
420 return NS_OK;
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);
430 return result;
435 // AddDataFlavor
437 // Adds a data flavor to our list with no data. Error if it already exists.
439 NS_IMETHODIMP
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 ));
450 return NS_OK;
455 // RemoveDataFlavor
457 // Removes a data flavor (and causes the data to be destroyed). Error if
458 // the requested flavor is not present.
460 NS_IMETHODIMP
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);
468 return NS_OK;
470 return NS_ERROR_FAILURE;
478 NS_IMETHODIMP
479 nsTransferable::IsLargeDataSet(bool *_retval)
481 MOZ_ASSERT(mInitialized);
483 NS_ENSURE_ARG_POINTER(_retval);
484 *_retval = false;
485 return NS_OK;
493 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
495 MOZ_ASSERT(mInitialized);
497 mFormatConv = aConverter;
498 return NS_OK;
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);
513 return NS_OK;
518 // FlavorsTransferableCanImport
520 // Computes a list of flavors that the transferable can accept into it, either through
521 // intrinsic knowledge or input data converters.
523 NS_IMETHODIMP
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));
536 if ( 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
559 return NS_OK;
560 } // FlavorsTransferableCanImport
564 // FlavorsTransferableCanExport
566 // Computes a list of flavors that the transferable can export, either through
567 // intrinsic knowledge or output data converters.
569 NS_IMETHODIMP
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));
582 if ( 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
605 return NS_OK;
606 } // FlavorsTransferableCanExport
608 NS_IMETHODIMP
609 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
611 MOZ_ASSERT(mInitialized);
613 NS_ENSURE_ARG_POINTER(aIsPrivateData);
615 *aIsPrivateData = mPrivateData;
617 return NS_OK;
620 NS_IMETHODIMP
621 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
623 MOZ_ASSERT(mInitialized);
625 mPrivateData = aIsPrivateData;
627 return NS_OK;
630 NS_IMETHODIMP
631 nsTransferable::GetRequestingNode(nsIDOMNode** outRequestingNode)
633 nsCOMPtr<nsIDOMNode> node = do_QueryReferent(mRequestingNode);
634 node.forget(outRequestingNode);
635 return NS_OK;
638 NS_IMETHODIMP
639 nsTransferable::SetRequestingNode(nsIDOMNode* aRequestingNode)
641 mRequestingNode = do_GetWeakReference(aRequestingNode);
642 return NS_OK;