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/. */
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 nsIArrays.
15 #include "nsTransferable.h"
16 #include "nsAnonymousTemporaryFile.h"
18 #include "nsArrayUtils.h"
20 #include "nsReadableUtils.h"
22 #include "nsIFormatConverter.h"
23 #include "nsIContentPolicy.h"
24 #include "nsIComponentManager.h"
27 #include "nsISupportsPrimitives.h"
29 #include "nsPrimitiveHelpers.h"
30 #include "nsDirectoryServiceDefs.h"
31 #include "nsDirectoryService.h"
33 #include "nsNetUtil.h"
34 #include "nsIOutputStream.h"
35 #include "nsIInputStream.h"
36 #include "nsILoadContext.h"
37 #include "nsXULAppAPI.h"
38 #include "mozilla/UniquePtr.h"
40 using namespace mozilla
;
42 NS_IMPL_ISUPPORTS(nsTransferable
, nsITransferable
)
44 DataStruct::DataStruct(DataStruct
&& aRHS
)
45 : mData(aRHS
.mData
.forget()),
46 mCacheFD(aRHS
.mCacheFD
),
47 mFlavor(aRHS
.mFlavor
) {
48 aRHS
.mCacheFD
= nullptr;
51 //-------------------------------------------------------------------------
52 DataStruct::~DataStruct() {
58 //-------------------------------------------------------------------------
60 void DataStruct::SetData(nsISupports
* aData
, 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 // File IO is not allowed in content processes.
64 if (!aIsPrivateData
&& XRE_IsParentProcess()) {
67 nsPrimitiveHelpers::CreateDataFromPrimitive(mFlavor
, aData
, &data
,
70 if (dataLen
> kLargeDatasetSize
) {
71 // Too large, cache it to disk instead of memory.
72 if (NS_SUCCEEDED(WriteCache(data
, dataLen
))) {
74 // Clear previously set small data.
79 NS_WARNING("Oh no, couldn't write data to the cache file");
86 // Clear previously set big data.
94 //-------------------------------------------------------------------------
95 void DataStruct::GetData(nsISupports
** aData
) {
96 // check here to see if the data is cached on disk
98 // if so, read it in and pass it back
99 // ReadCache creates memory and copies the data into it.
100 if (NS_SUCCEEDED(ReadCache(aData
))) {
104 // oh shit, something went horribly wrong here.
105 NS_WARNING("Oh no, couldn't read data in from the cache file");
112 nsCOMPtr
<nsISupports
> data
= mData
;
116 //-------------------------------------------------------------------------
117 nsresult
DataStruct::WriteCache(void* aData
, uint32_t aDataLen
) {
118 MOZ_ASSERT(aData
&& aDataLen
);
119 MOZ_ASSERT(aDataLen
<= uint32_t(std::numeric_limits
<int32_t>::max()),
120 "too large size for PR_Write");
124 rv
= NS_OpenAnonymousTemporaryFile(&mCacheFD
);
126 return NS_ERROR_FAILURE
;
128 } else if (PR_Seek64(mCacheFD
, 0, PR_SEEK_SET
) == -1) {
129 return NS_ERROR_FAILURE
;
132 // Write out the contents of the clipboard to the file.
133 int32_t written
= PR_Write(mCacheFD
, aData
, aDataLen
);
134 if (written
== int32_t(aDataLen
)) {
140 return NS_ERROR_FAILURE
;
143 //-------------------------------------------------------------------------
144 nsresult
DataStruct::ReadCache(nsISupports
** aData
) {
146 return NS_ERROR_FAILURE
;
150 if (PR_GetOpenFileInfo(mCacheFD
, &fileInfo
) != PR_SUCCESS
) {
151 return NS_ERROR_FAILURE
;
153 if (PR_Seek64(mCacheFD
, 0, PR_SEEK_SET
) == -1) {
154 return NS_ERROR_FAILURE
;
156 uint32_t fileSize
= fileInfo
.size
;
158 auto data
= MakeUnique
<char[]>(fileSize
);
160 return NS_ERROR_OUT_OF_MEMORY
;
163 uint32_t actual
= PR_Read(mCacheFD
, data
.get(), fileSize
);
164 if (actual
!= fileSize
) {
165 return NS_ERROR_FAILURE
;
168 nsPrimitiveHelpers::CreatePrimitiveForData(mFlavor
, data
.get(), fileSize
,
173 //-------------------------------------------------------------------------
175 // Transferable constructor
177 //-------------------------------------------------------------------------
178 nsTransferable::nsTransferable()
179 : mPrivateData(false),
180 mContentPolicyType(nsIContentPolicy::TYPE_OTHER
)
188 //-------------------------------------------------------------------------
190 // Transferable destructor
192 //-------------------------------------------------------------------------
193 nsTransferable::~nsTransferable() {}
196 nsTransferable::Init(nsILoadContext
* aContext
) {
197 MOZ_ASSERT(!mInitialized
);
200 mPrivateData
= aContext
->UsePrivateBrowsing();
209 // GetTransferDataFlavors
211 // Returns a copy of the internal list of flavors. This does NOT take into
212 // account any converter that may be registered.
214 void nsTransferable::GetTransferDataFlavors(nsTArray
<nsCString
>& aFlavors
) {
215 MOZ_ASSERT(mInitialized
);
217 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
218 DataStruct
& data
= mDataArray
.ElementAt(i
);
219 aFlavors
.AppendElement(data
.GetFlavor());
223 Maybe
<size_t> nsTransferable::FindDataFlavor(const char* aFlavor
) {
224 nsDependentCString
flavor(aFlavor
);
226 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
227 if (mDataArray
[i
].GetFlavor().Equals(flavor
)) {
238 // Returns the data of the requested flavor, obtained from either having the
239 // data on hand or using a converter to get it. The data is wrapped in a
240 // nsISupports primitive so that it is accessible from JS.
243 nsTransferable::GetTransferData(const char* aFlavor
, nsISupports
** aData
) {
244 MOZ_ASSERT(mInitialized
);
250 // First look and see if the data is present in one of the intrinsic flavors.
251 if (Maybe
<size_t> index
= FindDataFlavor(aFlavor
)) {
252 nsCOMPtr
<nsISupports
> dataBytes
;
253 mDataArray
[index
.value()].GetData(getter_AddRefs(dataBytes
));
255 // Do we have a (lazy) data provider?
256 if (nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
=
257 do_QueryInterface(dataBytes
)) {
259 dataProvider
->GetFlavorData(this, aFlavor
, getter_AddRefs(dataBytes
));
262 // The provider failed, fall into the converter code below.
267 dataBytes
.forget(aData
);
274 // If not, try using a format converter to get the requested flavor.
276 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
277 DataStruct
& data
= mDataArray
.ElementAt(i
);
278 bool canConvert
= false;
279 mFormatConv
->CanConvert(data
.GetFlavor().get(), aFlavor
, &canConvert
);
281 nsCOMPtr
<nsISupports
> dataBytes
;
282 data
.GetData(getter_AddRefs(dataBytes
));
284 // Do we have a (lazy) data provider?
285 if (nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
=
286 do_QueryInterface(dataBytes
)) {
287 rv
= dataProvider
->GetFlavorData(this, aFlavor
,
288 getter_AddRefs(dataBytes
));
295 return mFormatConv
->Convert(data
.GetFlavor().get(), dataBytes
, aFlavor
,
301 return NS_ERROR_FAILURE
;
305 // GetAnyTransferData
307 // Returns the data of the first flavor found. Caller is responsible for
308 // deleting the flavor string.
311 nsTransferable::GetAnyTransferData(nsACString
& aFlavor
, nsISupports
** aData
) {
312 MOZ_ASSERT(mInitialized
);
314 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
315 DataStruct
& data
= mDataArray
.ElementAt(i
);
316 if (data
.IsDataAvailable()) {
317 aFlavor
.Assign(data
.GetFlavor());
323 return NS_ERROR_FAILURE
;
332 nsTransferable::SetTransferData(const char* aFlavor
, nsISupports
* aData
) {
333 MOZ_ASSERT(mInitialized
);
335 // first check our intrinsic flavors to see if one has been registered.
336 if (Maybe
<size_t> index
= FindDataFlavor(aFlavor
)) {
337 DataStruct
& data
= mDataArray
.ElementAt(index
.value());
338 data
.SetData(aData
, mPrivateData
);
342 // if not, try using a format converter to find a flavor to put the data in
344 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
345 DataStruct
& data
= mDataArray
.ElementAt(i
);
346 bool canConvert
= false;
347 mFormatConv
->CanConvert(aFlavor
, data
.GetFlavor().get(), &canConvert
);
350 nsCOMPtr
<nsISupports
> ConvertedData
;
351 mFormatConv
->Convert(aFlavor
, aData
, data
.GetFlavor().get(),
352 getter_AddRefs(ConvertedData
));
353 data
.SetData(ConvertedData
, mPrivateData
);
359 // Can't set data neither directly nor through converter. Just add this flavor
361 if (NS_SUCCEEDED(AddDataFlavor(aFlavor
))) {
362 return SetTransferData(aFlavor
, aData
);
365 return NS_ERROR_FAILURE
;
371 // Adds a data flavor to our list with no data. Error if it already exists.
374 nsTransferable::AddDataFlavor(const char* aDataFlavor
) {
375 MOZ_ASSERT(mInitialized
);
377 if (FindDataFlavor(aDataFlavor
).isSome()) {
378 return NS_ERROR_FAILURE
;
381 // Create a new "slot" for the data
382 mDataArray
.AppendElement(DataStruct(aDataFlavor
));
389 // Removes a data flavor (and causes the data to be destroyed). Error if
390 // the requested flavor is not present.
393 nsTransferable::RemoveDataFlavor(const char* aDataFlavor
) {
394 MOZ_ASSERT(mInitialized
);
396 if (Maybe
<size_t> index
= FindDataFlavor(aDataFlavor
)) {
397 mDataArray
.RemoveElementAt(index
.value());
401 return NS_ERROR_FAILURE
;
405 nsTransferable::SetConverter(nsIFormatConverter
* aConverter
) {
406 MOZ_ASSERT(mInitialized
);
408 mFormatConv
= aConverter
;
413 nsTransferable::GetConverter(nsIFormatConverter
** aConverter
) {
414 MOZ_ASSERT(mInitialized
);
416 nsCOMPtr
<nsIFormatConverter
> converter
= mFormatConv
;
417 converter
.forget(aConverter
);
422 // FlavorsTransferableCanImport
424 // Computes a list of flavors that the transferable can accept into it, either
425 // through intrinsic knowledge or input data converters.
428 nsTransferable::FlavorsTransferableCanImport(nsTArray
<nsCString
>& aFlavors
) {
429 MOZ_ASSERT(mInitialized
);
431 // Get the flavor list, and on to the end of it, append the list of flavors we
432 // can also get to through a converter. This is so that we can just walk the
433 // list in one go, looking for the desired flavor.
434 GetTransferDataFlavors(aFlavors
);
437 nsTArray
<nsCString
> convertedList
;
438 mFormatConv
->GetInputDataFlavors(convertedList
);
440 for (uint32_t i
= 0; i
< convertedList
.Length(); ++i
) {
441 nsCString
& flavorStr
= convertedList
[i
];
443 // Don't append if already in intrinsic list
444 if (!aFlavors
.Contains(flavorStr
)) {
445 aFlavors
.AppendElement(flavorStr
);
454 // FlavorsTransferableCanExport
456 // Computes a list of flavors that the transferable can export, either through
457 // intrinsic knowledge or output data converters.
460 nsTransferable::FlavorsTransferableCanExport(nsTArray
<nsCString
>& aFlavors
) {
461 MOZ_ASSERT(mInitialized
);
463 // Get the flavor list, and on to the end of it, append the list of flavors we
464 // can also get to through a converter. This is so that we can just walk the
465 // list in one go, looking for the desired flavor.
466 GetTransferDataFlavors(aFlavors
);
469 nsTArray
<nsCString
> convertedList
;
470 mFormatConv
->GetOutputDataFlavors(convertedList
);
472 for (uint32_t i
= 0; i
< convertedList
.Length(); ++i
) {
473 nsCString
& flavorStr
= convertedList
[i
];
475 // Don't append if already in intrinsic list
476 if (!aFlavors
.Contains(flavorStr
)) {
477 aFlavors
.AppendElement(flavorStr
);
485 bool nsTransferable::GetIsPrivateData() {
486 MOZ_ASSERT(mInitialized
);
491 void nsTransferable::SetIsPrivateData(bool aIsPrivateData
) {
492 MOZ_ASSERT(mInitialized
);
494 mPrivateData
= aIsPrivateData
;
497 nsIPrincipal
* nsTransferable::GetRequestingPrincipal() {
498 MOZ_ASSERT(mInitialized
);
500 return mRequestingPrincipal
;
503 void nsTransferable::SetRequestingPrincipal(
504 nsIPrincipal
* aRequestingPrincipal
) {
505 MOZ_ASSERT(mInitialized
);
507 mRequestingPrincipal
= aRequestingPrincipal
;
510 nsContentPolicyType
nsTransferable::GetContentPolicyType() {
511 MOZ_ASSERT(mInitialized
);
513 return mContentPolicyType
;
516 void nsTransferable::SetContentPolicyType(
517 nsContentPolicyType aContentPolicyType
) {
518 MOZ_ASSERT(mInitialized
);
520 mContentPolicyType
= aContentPolicyType
;