widget/xpwidgets/nsTransferable.cpp

changeset 0
6474c204b198
child 3
141e0f1194b1
equal deleted inserted replaced
-1:000000000000 0:71842db4e593
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/. */
5
6 /*
7 Notes to self:
8
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.
12
13 */
14
15
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"
37
38 NS_IMPL_ISUPPORTS(nsTransferable, nsITransferable)
39
40 uint32_t GetDataForFlavor (const nsTArray<DataStruct>& aArray,
41 const char* aDataFlavor)
42 {
43 for (uint32_t i = 0 ; i < aArray.Length () ; ++i) {
44 if (aArray[i].GetFlavor().Equals (aDataFlavor))
45 return i;
46 }
47
48 return aArray.NoIndex;
49 }
50
51 //-------------------------------------------------------------------------
52 DataStruct::~DataStruct()
53 {
54 if (mCacheFileName) free(mCacheFileName);
55 }
56
57 //-------------------------------------------------------------------------
58 void
59 DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen )
60 {
61 #if 0 // Remove unnecessary disk caching to accommodate https://www.torproject.org/projects/torbrowser/design/#disk-avoidance
62 // Now, check to see if we consider the data to be "too large"
63 if (aDataLen > kLargeDatasetSize) {
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");
69 }
70 #endif // #if 0
71
72 mData = aData;
73 mDataLen = aDataLen;
74 }
75
76
77 //-------------------------------------------------------------------------
78 void
79 DataStruct::GetData ( nsISupports** aData, uint32_t *aDataLen )
80 {
81 // check here to see if the data is cached on disk
82 if ( !mData && mCacheFileName ) {
83 // if so, read it in and pass it back
84 // ReadCache creates memory and copies the data into it.
85 if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
86 return;
87 else {
88 // oh shit, something went horribly wrong here.
89 NS_WARNING("Oh no, couldn't read data in from the cache file");
90 *aData = nullptr;
91 *aDataLen = 0;
92 return;
93 }
94 }
95
96 *aData = mData;
97 if ( mData )
98 NS_ADDREF(*aData);
99 *aDataLen = mDataLen;
100 }
101
102
103 //-------------------------------------------------------------------------
104 already_AddRefed<nsIFile>
105 DataStruct::GetFileSpec(const char* aFileName)
106 {
107 nsCOMPtr<nsIFile> cacheFile;
108 NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(cacheFile));
109
110 if (!cacheFile)
111 return nullptr;
112
113 // if the param aFileName contains a name we should use that
114 // because the file probably already exists
115 // otherwise create a unique name
116 if (!aFileName) {
117 cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
118 cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
119 } else {
120 cacheFile->AppendNative(nsDependentCString(aFileName));
121 }
122
123 return cacheFile.forget();
124 }
125
126
127 //-------------------------------------------------------------------------
128 nsresult
129 DataStruct::WriteCache(nsISupports* aData, uint32_t aDataLen)
130 {
131 // Get a new path and file to the temp directory
132 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
133 if (cacheFile) {
134 // remember the file name
135 if (!mCacheFileName) {
136 nsXPIDLCString fName;
137 cacheFile->GetNativeLeafName(fName);
138 mCacheFileName = strdup(fName);
139 }
140
141 // write out the contents of the clipboard
142 // to the file
143 //uint32_t bytes;
144 nsCOMPtr<nsIOutputStream> outStr;
145
146 NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
147 cacheFile);
148
149 if (!outStr) return NS_ERROR_FAILURE;
150
151 void* buff = nullptr;
152 nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
153 if ( buff ) {
154 uint32_t ignored;
155 outStr->Write(reinterpret_cast<char*>(buff), aDataLen, &ignored);
156 nsMemory::Free(buff);
157 return NS_OK;
158 }
159 }
160 return NS_ERROR_FAILURE;
161 }
162
163
164 //-------------------------------------------------------------------------
165 nsresult
166 DataStruct::ReadCache(nsISupports** aData, uint32_t* aDataLen)
167 {
168 // if we don't have a cache filename we are out of luck
169 if (!mCacheFileName)
170 return NS_ERROR_FAILURE;
171
172 // get the path and file name
173 nsCOMPtr<nsIFile> cacheFile = GetFileSpec(mCacheFileName);
174 bool exists;
175 if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
176 // get the size of the file
177 int64_t fileSize;
178 int64_t max32 = 0xFFFFFFFF;
179 cacheFile->GetFileSize(&fileSize);
180 if (fileSize > max32)
181 return NS_ERROR_OUT_OF_MEMORY;
182
183 uint32_t size = uint32_t(fileSize);
184 // create new memory for the large clipboard data
185 nsAutoArrayPtr<char> data(new char[size]);
186 if ( !data )
187 return NS_ERROR_OUT_OF_MEMORY;
188
189 // now read it all in
190 nsCOMPtr<nsIInputStream> inStr;
191 NS_NewLocalFileInputStream( getter_AddRefs(inStr),
192 cacheFile);
193
194 if (!cacheFile) return NS_ERROR_FAILURE;
195
196 nsresult rv = inStr->Read(data, fileSize, aDataLen);
197
198 // make sure we got all the data ok
199 if (NS_SUCCEEDED(rv) && *aDataLen == size) {
200 nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
201 return *aData ? NS_OK : NS_ERROR_FAILURE;
202 }
203
204 // zero the return params
205 *aData = nullptr;
206 *aDataLen = 0;
207 }
208
209 return NS_ERROR_FAILURE;
210 }
211
212
213 //-------------------------------------------------------------------------
214 //
215 // Transferable constructor
216 //
217 //-------------------------------------------------------------------------
218 nsTransferable::nsTransferable()
219 : mPrivateData(false)
220 #ifdef DEBUG
221 , mInitialized(false)
222 #endif
223 {
224 }
225
226 //-------------------------------------------------------------------------
227 //
228 // Transferable destructor
229 //
230 //-------------------------------------------------------------------------
231 nsTransferable::~nsTransferable()
232 {
233 }
234
235
236 NS_IMETHODIMP
237 nsTransferable::Init(nsILoadContext* aContext)
238 {
239 MOZ_ASSERT(!mInitialized);
240
241 if (aContext) {
242 mPrivateData = aContext->UsePrivateBrowsing();
243 }
244 #ifdef DEBUG
245 mInitialized = true;
246 #endif
247 return NS_OK;
248 }
249
250 //
251 // GetTransferDataFlavors
252 //
253 // Returns a copy of the internal list of flavors. This does NOT take into
254 // account any converter that may be registered. This list consists of
255 // nsISupportsCString objects so that the flavor list can be accessed from JS.
256 //
257 nsresult
258 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
259 {
260 MOZ_ASSERT(mInitialized);
261
262 nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
263 if (NS_FAILED(rv)) return rv;
264
265 for ( uint32_t i=0; i<mDataArray.Length(); ++i ) {
266 DataStruct& data = mDataArray.ElementAt(i);
267 nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
268 if ( flavorWrapper ) {
269 flavorWrapper->SetData ( data.GetFlavor() );
270 nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
271 (*aDataFlavorList)->AppendElement( genericWrapper );
272 }
273 }
274
275 return NS_OK;
276 }
277
278
279 //
280 // GetTransferData
281 //
282 // Returns the data of the requested flavor, obtained from either having the data on hand or
283 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
284 // accessible from JS.
285 //
286 NS_IMETHODIMP
287 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, uint32_t *aDataLen)
288 {
289 MOZ_ASSERT(mInitialized);
290
291 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
292
293 nsresult rv = NS_OK;
294 nsCOMPtr<nsISupports> savedData;
295
296 // first look and see if the data is present in one of the intrinsic flavors
297 uint32_t i;
298 for (i = 0; i < mDataArray.Length(); ++i ) {
299 DataStruct& data = mDataArray.ElementAt(i);
300 if ( data.GetFlavor().Equals(aFlavor) ) {
301 nsCOMPtr<nsISupports> dataBytes;
302 uint32_t len;
303 data.GetData(getter_AddRefs(dataBytes), &len);
304 if (len == kFlavorHasDataProvider && dataBytes) {
305 // do we have a data provider?
306 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
307 if (dataProvider) {
308 rv = dataProvider->GetFlavorData(this, aFlavor,
309 getter_AddRefs(dataBytes), &len);
310 if (NS_FAILED(rv))
311 break; // the provider failed. fall into the converter code below.
312 }
313 }
314 if (dataBytes && len > 0) { // XXXmats why is zero length not ok?
315 *aDataLen = len;
316 dataBytes.forget(aData);
317 return NS_OK;
318 }
319 savedData = dataBytes; // return this if format converter fails
320 break;
321 }
322 }
323
324 bool found = false;
325
326 // if not, try using a format converter to get the requested flavor
327 if ( mFormatConv ) {
328 for (i = 0; i < mDataArray.Length(); ++i) {
329 DataStruct& data = mDataArray.ElementAt(i);
330 bool canConvert = false;
331 mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
332 if ( canConvert ) {
333 nsCOMPtr<nsISupports> dataBytes;
334 uint32_t len;
335 data.GetData(getter_AddRefs(dataBytes), &len);
336 if (len == kFlavorHasDataProvider && dataBytes) {
337 // do we have a data provider?
338 nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
339 if (dataProvider) {
340 rv = dataProvider->GetFlavorData(this, aFlavor,
341 getter_AddRefs(dataBytes), &len);
342 if (NS_FAILED(rv))
343 break; // give up
344 }
345 }
346 mFormatConv->Convert(data.GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
347 found = true;
348 break;
349 }
350 }
351 }
352
353 // for backward compatibility
354 if (!found) {
355 savedData.forget(aData);
356 *aDataLen = 0;
357 }
358
359 return found ? NS_OK : NS_ERROR_FAILURE;
360 }
361
362
363 //
364 // GetAnyTransferData
365 //
366 // Returns the data of the first flavor found. Caller is responsible for deleting the
367 // flavor string.
368 //
369 NS_IMETHODIMP
370 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, uint32_t *aDataLen)
371 {
372 MOZ_ASSERT(mInitialized);
373
374 NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
375
376 for ( uint32_t i=0; i < mDataArray.Length(); ++i ) {
377 DataStruct& data = mDataArray.ElementAt(i);
378 if (data.IsDataAvailable()) {
379 *aFlavor = ToNewCString(data.GetFlavor());
380 data.GetData(aData, aDataLen);
381 return NS_OK;
382 }
383 }
384
385 return NS_ERROR_FAILURE;
386 }
387
388
389 //
390 // SetTransferData
391 //
392 //
393 //
394 NS_IMETHODIMP
395 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, uint32_t aDataLen)
396 {
397 MOZ_ASSERT(mInitialized);
398
399 NS_ENSURE_ARG(aFlavor);
400
401 // first check our intrinsic flavors to see if one has been registered.
402 uint32_t i = 0;
403 for (i = 0; i < mDataArray.Length(); ++i) {
404 DataStruct& data = mDataArray.ElementAt(i);
405 if ( data.GetFlavor().Equals(aFlavor) ) {
406 data.SetData ( aData, aDataLen );
407 return NS_OK;
408 }
409 }
410
411 // if not, try using a format converter to find a flavor to put the data in
412 if ( mFormatConv ) {
413 for (i = 0; i < mDataArray.Length(); ++i) {
414 DataStruct& data = mDataArray.ElementAt(i);
415 bool canConvert = false;
416 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
417
418 if ( canConvert ) {
419 nsCOMPtr<nsISupports> ConvertedData;
420 uint32_t ConvertedLen;
421 mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
422 data.SetData(ConvertedData, ConvertedLen);
423 return NS_OK;
424 }
425 }
426 }
427
428 // Can't set data neither directly nor through converter. Just add this flavor and try again
429 nsresult result = NS_ERROR_FAILURE;
430 if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
431 result = SetTransferData (aFlavor, aData, aDataLen);
432
433 return result;
434 }
435
436
437 //
438 // AddDataFlavor
439 //
440 // Adds a data flavor to our list with no data. Error if it already exists.
441 //
442 NS_IMETHODIMP
443 nsTransferable::AddDataFlavor(const char *aDataFlavor)
444 {
445 MOZ_ASSERT(mInitialized);
446
447 if (GetDataForFlavor (mDataArray, aDataFlavor) != mDataArray.NoIndex)
448 return NS_ERROR_FAILURE;
449
450 // Create a new "slot" for the data
451 mDataArray.AppendElement(DataStruct ( aDataFlavor ));
452
453 return NS_OK;
454 }
455
456
457 //
458 // RemoveDataFlavor
459 //
460 // Removes a data flavor (and causes the data to be destroyed). Error if
461 // the requested flavor is not present.
462 //
463 NS_IMETHODIMP
464 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
465 {
466 MOZ_ASSERT(mInitialized);
467
468 uint32_t idx;
469 if ((idx = GetDataForFlavor(mDataArray, aDataFlavor)) != mDataArray.NoIndex) {
470 mDataArray.RemoveElementAt (idx);
471 return NS_OK;
472 }
473 return NS_ERROR_FAILURE;
474 }
475
476
477 /**
478 *
479 *
480 */
481 NS_IMETHODIMP
482 nsTransferable::IsLargeDataSet(bool *_retval)
483 {
484 MOZ_ASSERT(mInitialized);
485
486 NS_ENSURE_ARG_POINTER(_retval);
487 *_retval = false;
488 return NS_OK;
489 }
490
491
492 /**
493 *
494 *
495 */
496 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
497 {
498 MOZ_ASSERT(mInitialized);
499
500 mFormatConv = aConverter;
501 return NS_OK;
502 }
503
504
505 /**
506 *
507 *
508 */
509 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
510 {
511 MOZ_ASSERT(mInitialized);
512
513 NS_ENSURE_ARG_POINTER(aConverter);
514 *aConverter = mFormatConv;
515 NS_IF_ADDREF(*aConverter);
516 return NS_OK;
517 }
518
519
520 //
521 // FlavorsTransferableCanImport
522 //
523 // Computes a list of flavors that the transferable can accept into it, either through
524 // intrinsic knowledge or input data converters.
525 //
526 NS_IMETHODIMP
527 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
528 {
529 MOZ_ASSERT(mInitialized);
530
531 NS_ENSURE_ARG_POINTER(_retval);
532
533 // Get the flavor list, and on to the end of it, append the list of flavors we
534 // can also get to through a converter. This is so that we can just walk the list
535 // in one go, looking for the desired flavor.
536 GetTransferDataFlavors(_retval); // addrefs
537 nsCOMPtr<nsIFormatConverter> converter;
538 GetConverter(getter_AddRefs(converter));
539 if ( converter ) {
540 nsCOMPtr<nsISupportsArray> convertedList;
541 converter->GetInputDataFlavors(getter_AddRefs(convertedList));
542
543 if ( convertedList ) {
544 uint32_t importListLen;
545 convertedList->Count(&importListLen);
546
547 for ( uint32_t i=0; i < importListLen; ++i ) {
548 nsCOMPtr<nsISupports> genericFlavor;
549 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
550
551 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
552 nsAutoCString flavorStr;
553 flavorWrapper->GetData( flavorStr );
554
555 if (GetDataForFlavor (mDataArray, flavorStr.get())
556 == mDataArray.NoIndex) // Don't append if already in intrinsic list
557 (*_retval)->AppendElement (genericFlavor);
558 } // foreach flavor that can be converted to
559 }
560 } // if a converter exists
561
562 return NS_OK;
563 } // FlavorsTransferableCanImport
564
565
566 //
567 // FlavorsTransferableCanExport
568 //
569 // Computes a list of flavors that the transferable can export, either through
570 // intrinsic knowledge or output data converters.
571 //
572 NS_IMETHODIMP
573 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
574 {
575 MOZ_ASSERT(mInitialized);
576
577 NS_ENSURE_ARG_POINTER(_retval);
578
579 // Get the flavor list, and on to the end of it, append the list of flavors we
580 // can also get to through a converter. This is so that we can just walk the list
581 // in one go, looking for the desired flavor.
582 GetTransferDataFlavors(_retval); // addrefs
583 nsCOMPtr<nsIFormatConverter> converter;
584 GetConverter(getter_AddRefs(converter));
585 if ( converter ) {
586 nsCOMPtr<nsISupportsArray> convertedList;
587 converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
588
589 if ( convertedList ) {
590 uint32_t importListLen;
591 convertedList->Count(&importListLen);
592
593 for ( uint32_t i=0; i < importListLen; ++i ) {
594 nsCOMPtr<nsISupports> genericFlavor;
595 convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
596
597 nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
598 nsAutoCString flavorStr;
599 flavorWrapper->GetData( flavorStr );
600
601 if (GetDataForFlavor (mDataArray, flavorStr.get())
602 == mDataArray.NoIndex) // Don't append if already in intrinsic list
603 (*_retval)->AppendElement (genericFlavor);
604 } // foreach flavor that can be converted to
605 }
606 } // if a converter exists
607
608 return NS_OK;
609 } // FlavorsTransferableCanExport
610
611 NS_IMETHODIMP
612 nsTransferable::GetIsPrivateData(bool *aIsPrivateData)
613 {
614 MOZ_ASSERT(mInitialized);
615
616 NS_ENSURE_ARG_POINTER(aIsPrivateData);
617
618 *aIsPrivateData = mPrivateData;
619
620 return NS_OK;
621 }
622
623 NS_IMETHODIMP
624 nsTransferable::SetIsPrivateData(bool aIsPrivateData)
625 {
626 MOZ_ASSERT(mInitialized);
627
628 mPrivateData = aIsPrivateData;
629
630 return NS_OK;
631 }
632

mercurial