Bumping manifests a=b2g-bump
[gecko.git] / widget / nsTransferable.cpp
blob4450836aaadc933cb24021cdf8d18af81e8a99f7
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 )
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)) )
65 return;
66 else
67 NS_WARNING("Oh no, couldn't write data to the cache file");
70 mData = aData;
71 mDataLen = aDataLen;
75 //-------------------------------------------------------------------------
76 void
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)) )
84 return;
85 else {
86 // oh shit, something went horribly wrong here.
87 NS_WARNING("Oh no, couldn't read data in from the cache file");
88 *aData = nullptr;
89 *aDataLen = 0;
90 return;
94 *aData = mData;
95 if ( mData )
96 NS_ADDREF(*aData);
97 *aDataLen = mDataLen;
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));
108 if (!cacheFile)
109 return nullptr;
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
114 if (!aFileName) {
115 cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
116 cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
117 } else {
118 cacheFile->AppendNative(nsDependentCString(aFileName));
121 return cacheFile.forget();
125 //-------------------------------------------------------------------------
126 nsresult
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);
131 if (cacheFile) {
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
140 // to the file
141 //uint32_t bytes;
142 nsCOMPtr<nsIOutputStream> outStr;
144 NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
145 cacheFile);
147 if (!outStr) return NS_ERROR_FAILURE;
149 void* buff = nullptr;
150 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
151 if ( buff ) {
152 uint32_t ignored;
153 outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
154 nsMemory::Free(buff);
155 return NS_OK;
158 return NS_ERROR_FAILURE;
162 //-------------------------------------------------------------------------
163 nsresult
164 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
166 // if we don't have a cache filename we are out of luck
167 if (!mCacheFileName)
168 return NS_ERROR_FAILURE;
170 // get the path and file name
171 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
172 bool exists;
173 if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
174 // get the size of the file
175 int64_t fileSize;
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]);
184 if ( !data )
185 return NS_ERROR_OUT_OF_MEMORY;
187 // now read it all in
188 nsCOMPtr<nsIInputStream> inStr;
189 NS_NewLocalFileInputStream( getter_AddRefs(inStr),
190 cacheFile);
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
203 *aData = nullptr;
204 *aDataLen = 0;
207 return NS_ERROR_FAILURE;
211 //-------------------------------------------------------------------------
213 // Transferable constructor
215 //-------------------------------------------------------------------------
216 nsTransferable::nsTransferable()
217 : mPrivateData(false)
218 #ifdef DEBUG
219 , mInitialized(false)
220 #endif
224 //-------------------------------------------------------------------------
226 // Transferable destructor
228 //-------------------------------------------------------------------------
229 nsTransferable::~nsTransferable()
234 NS_IMETHODIMP
235 nsTransferable::Init(nsILoadContext* aContext)
237 MOZ_ASSERT(!mInitialized);
239 if (aContext) {
240 mPrivateData = aContext->UsePrivateBrowsing();
242 #ifdef DEBUG
243 mInitialized = true;
244 #endif
245 return NS_OK;
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.
255 nsresult
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 );
273 return NS_OK;
278 // GetTransferData
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.
284 NS_IMETHODIMP
285 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
287 MOZ_ASSERT(mInitialized);
289 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
291 nsresult rv = NS_OK;
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;
299 uint32_t len;
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);
304 if (dataProvider) {
305 rv = dataProvider->GetFlavorData(this, aFlavor,
306 getter_AddRefs(dataBytes), &len);
307 if (NS_FAILED(rv))
308 break; // the provider failed. fall into the converter code below.
311 if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
312 *aDataLen = len;
313 dataBytes.forget(aData);
314 return NS_OK;
316 savedData = dataBytes; // return this if format converter fails
317 break;
321 bool found = false;
323 // if not, try using a format converter to get the requested flavor
324 if ( mFormatConv ) {
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);
329 if ( canConvert ) {
330 nsCOMPtr<nsISupports> dataBytes;
331 uint32_t len;
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);
336 if (dataProvider) {
337 rv = dataProvider->GetFlavorData(this, aFlavor,
338 getter_AddRefs(dataBytes), &len);
339 if (NS_FAILED(rv))
340 break; // give up
343 mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
344 found = true;
345 break;
350 // for backward compatibility
351 if (!found) {
352 savedData.forget(aData);
353 *aDataLen = 0;
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
364 // flavor string.
366 NS_IMETHODIMP
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);
378 return NS_OK;
382 return NS_ERROR_FAILURE;
387 // SetTransferData
391 NS_IMETHODIMP
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 );
403 return NS_OK;
407 // if not, try using a format converter to find a flavor to put the data in
408 if ( mFormatConv ) {
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);
414 if ( 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);
419 return NS_OK;
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);
429 return result;
434 // AddDataFlavor
436 // Adds a data flavor to our list with no data. Error if it already exists.
438 NS_IMETHODIMP
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 ));
449 return NS_OK;
454 // RemoveDataFlavor
456 // Removes a data flavor (and causes the data to be destroyed). Error if
457 // the requested flavor is not present.
459 NS_IMETHODIMP
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);
467 return NS_OK;
469 return NS_ERROR_FAILURE;
477 NS_IMETHODIMP
478 nsTransferable::IsLargeDataSet(bool *_retval)
480 MOZ_ASSERT(mInitialized);
482 NS_ENSURE_ARG_POINTER(_retval);
483 *_retval = false;
484 return NS_OK;
492 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
494 MOZ_ASSERT(mInitialized);
496 mFormatConv = aConverter;
497 return NS_OK;
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);
512 return NS_OK;
517 // FlavorsTransferableCanImport
519 // Computes a list of flavors that the transferable can accept into it, either through
520 // intrinsic knowledge or input data converters.
522 NS_IMETHODIMP
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));
535 if ( 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
558 return NS_OK;
559 } // FlavorsTransferableCanImport
563 // FlavorsTransferableCanExport
565 // Computes a list of flavors that the transferable can export, either through
566 // intrinsic knowledge or output data converters.
568 NS_IMETHODIMP
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));
581 if ( 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
604 return NS_OK;
605 } // FlavorsTransferableCanExport
607 NS_IMETHODIMP
608 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
610 MOZ_ASSERT(mInitialized);
612 NS_ENSURE_ARG_POINTER(aIsPrivateData);
614 *aIsPrivateData = mPrivateData;
616 return NS_OK;
619 NS_IMETHODIMP
620 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
622 MOZ_ASSERT(mInitialized);
624 mPrivateData = aIsPrivateData;
626 return NS_OK;
629 NS_IMETHODIMP
630 nsTransferable::GetRequestingNode(nsIDOMNode** outRequestingNode)
632 nsCOMPtr<nsIDOMNode> node = do_QueryReferent(mRequestingNode);
633 node.forget(outRequestingNode);
634 return NS_OK;
637 NS_IMETHODIMP
638 nsTransferable::SetRequestingNode(nsIDOMNode* aRequestingNode)
640 mRequestingNode = do_GetWeakReference(aRequestingNode);
641 return NS_OK;