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"
26 #include "nsISupportsPrimitives.h"
28 #include "nsPrimitiveHelpers.h"
29 #include "nsDirectoryServiceDefs.h"
30 #include "nsDirectoryService.h"
32 #include "nsNetUtil.h"
33 #include "nsILoadContext.h"
34 #include "nsXULAppAPI.h"
35 #include "mozilla/UniquePtr.h"
37 using namespace mozilla
;
39 NS_IMPL_ISUPPORTS(nsTransferable
, nsITransferable
)
41 DataStruct::DataStruct(DataStruct
&& aRHS
)
42 : mData(aRHS
.mData
.forget()),
43 mCacheFD(aRHS
.mCacheFD
),
44 mFlavor(aRHS
.mFlavor
) {
45 aRHS
.mCacheFD
= nullptr;
48 //-------------------------------------------------------------------------
49 DataStruct::~DataStruct() {
55 //-------------------------------------------------------------------------
57 void DataStruct::SetData(nsISupports
* aData
, bool aIsPrivateData
) {
58 // Now, check to see if we consider the data to be "too large"
59 // as well as ensuring that private browsing mode is disabled.
60 // File IO is not allowed in content processes.
61 if (!aIsPrivateData
&& XRE_IsParentProcess()) {
64 nsPrimitiveHelpers::CreateDataFromPrimitive(mFlavor
, aData
, &data
,
67 if (dataLen
> kLargeDatasetSize
) {
68 // Too large, cache it to disk instead of memory.
69 if (NS_SUCCEEDED(WriteCache(data
, dataLen
))) {
71 // Clear previously set small data.
76 NS_WARNING("Oh no, couldn't write data to the cache file");
83 // Clear previously set big data.
91 //-------------------------------------------------------------------------
92 void DataStruct::GetData(nsISupports
** aData
) {
93 // check here to see if the data is cached on disk
95 // if so, read it in and pass it back
96 // ReadCache creates memory and copies the data into it.
97 if (NS_SUCCEEDED(ReadCache(aData
))) {
101 // oh shit, something went horribly wrong here.
102 NS_WARNING("Oh no, couldn't read data in from the cache file");
109 nsCOMPtr
<nsISupports
> data
= mData
;
113 //-------------------------------------------------------------------------
114 nsresult
DataStruct::WriteCache(void* aData
, uint32_t aDataLen
) {
115 MOZ_ASSERT(aData
&& aDataLen
);
116 MOZ_ASSERT(aDataLen
<= uint32_t(std::numeric_limits
<int32_t>::max()),
117 "too large size for PR_Write");
121 rv
= NS_OpenAnonymousTemporaryFile(&mCacheFD
);
123 return NS_ERROR_FAILURE
;
125 } else if (PR_Seek64(mCacheFD
, 0, PR_SEEK_SET
) == -1) {
126 return NS_ERROR_FAILURE
;
129 // Write out the contents of the clipboard to the file.
130 int32_t written
= PR_Write(mCacheFD
, aData
, aDataLen
);
131 if (written
== int32_t(aDataLen
)) {
137 return NS_ERROR_FAILURE
;
140 //-------------------------------------------------------------------------
141 nsresult
DataStruct::ReadCache(nsISupports
** aData
) {
143 return NS_ERROR_FAILURE
;
147 if (PR_GetOpenFileInfo(mCacheFD
, &fileInfo
) != PR_SUCCESS
) {
148 return NS_ERROR_FAILURE
;
150 if (PR_Seek64(mCacheFD
, 0, PR_SEEK_SET
) == -1) {
151 return NS_ERROR_FAILURE
;
153 uint32_t fileSize
= fileInfo
.size
;
155 auto data
= MakeUnique
<char[]>(fileSize
);
157 return NS_ERROR_OUT_OF_MEMORY
;
160 uint32_t actual
= PR_Read(mCacheFD
, data
.get(), fileSize
);
161 if (actual
!= fileSize
) {
162 return NS_ERROR_FAILURE
;
165 nsPrimitiveHelpers::CreatePrimitiveForData(mFlavor
, data
.get(), fileSize
,
170 //-------------------------------------------------------------------------
172 // Transferable constructor
174 //-------------------------------------------------------------------------
175 nsTransferable::nsTransferable()
176 : mPrivateData(false),
177 mContentPolicyType(nsIContentPolicy::TYPE_OTHER
)
185 //-------------------------------------------------------------------------
187 // Transferable destructor
189 //-------------------------------------------------------------------------
190 nsTransferable::~nsTransferable() {}
193 nsTransferable::Init(nsILoadContext
* aContext
) {
194 MOZ_ASSERT(!mInitialized
);
197 mPrivateData
= aContext
->UsePrivateBrowsing();
206 // GetTransferDataFlavors
208 // Returns a copy of the internal list of flavors. This does NOT take into
209 // account any converter that may be registered.
211 void nsTransferable::GetTransferDataFlavors(nsTArray
<nsCString
>& aFlavors
) {
212 MOZ_ASSERT(mInitialized
);
214 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
215 DataStruct
& data
= mDataArray
.ElementAt(i
);
216 aFlavors
.AppendElement(data
.GetFlavor());
220 Maybe
<size_t> nsTransferable::FindDataFlavor(const char* aFlavor
) {
221 nsDependentCString
flavor(aFlavor
);
223 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
224 if (mDataArray
[i
].GetFlavor().Equals(flavor
)) {
235 // Returns the data of the requested flavor, obtained from either having the
236 // data on hand or using a converter to get it. The data is wrapped in a
237 // nsISupports primitive so that it is accessible from JS.
240 nsTransferable::GetTransferData(const char* aFlavor
, nsISupports
** aData
) {
241 MOZ_ASSERT(mInitialized
);
247 // First look and see if the data is present in one of the intrinsic flavors.
248 if (Maybe
<size_t> index
= FindDataFlavor(aFlavor
)) {
249 nsCOMPtr
<nsISupports
> dataBytes
;
250 mDataArray
[index
.value()].GetData(getter_AddRefs(dataBytes
));
252 // Do we have a (lazy) data provider?
253 if (nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
=
254 do_QueryInterface(dataBytes
)) {
256 dataProvider
->GetFlavorData(this, aFlavor
, getter_AddRefs(dataBytes
));
259 // The provider failed, fall into the converter code below.
264 dataBytes
.forget(aData
);
271 // If not, try using a format converter to get the requested flavor.
273 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
274 DataStruct
& data
= mDataArray
.ElementAt(i
);
275 bool canConvert
= false;
276 mFormatConv
->CanConvert(data
.GetFlavor().get(), aFlavor
, &canConvert
);
278 nsCOMPtr
<nsISupports
> dataBytes
;
279 data
.GetData(getter_AddRefs(dataBytes
));
281 // Do we have a (lazy) data provider?
282 if (nsCOMPtr
<nsIFlavorDataProvider
> dataProvider
=
283 do_QueryInterface(dataBytes
)) {
284 rv
= dataProvider
->GetFlavorData(this, aFlavor
,
285 getter_AddRefs(dataBytes
));
292 return mFormatConv
->Convert(data
.GetFlavor().get(), dataBytes
, aFlavor
,
298 return NS_ERROR_FAILURE
;
302 // GetAnyTransferData
304 // Returns the data of the first flavor found. Caller is responsible for
305 // deleting the flavor string.
308 nsTransferable::GetAnyTransferData(nsACString
& aFlavor
, nsISupports
** aData
) {
309 MOZ_ASSERT(mInitialized
);
311 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
312 DataStruct
& data
= mDataArray
.ElementAt(i
);
313 if (data
.IsDataAvailable()) {
314 aFlavor
.Assign(data
.GetFlavor());
320 return NS_ERROR_FAILURE
;
329 nsTransferable::SetTransferData(const char* aFlavor
, nsISupports
* aData
) {
330 MOZ_ASSERT(mInitialized
);
332 // first check our intrinsic flavors to see if one has been registered.
333 if (Maybe
<size_t> index
= FindDataFlavor(aFlavor
)) {
334 DataStruct
& data
= mDataArray
.ElementAt(index
.value());
335 data
.SetData(aData
, mPrivateData
);
339 // if not, try using a format converter to find a flavor to put the data in
341 for (size_t i
= 0; i
< mDataArray
.Length(); ++i
) {
342 DataStruct
& data
= mDataArray
.ElementAt(i
);
343 bool canConvert
= false;
344 mFormatConv
->CanConvert(aFlavor
, data
.GetFlavor().get(), &canConvert
);
347 nsCOMPtr
<nsISupports
> ConvertedData
;
348 mFormatConv
->Convert(aFlavor
, aData
, data
.GetFlavor().get(),
349 getter_AddRefs(ConvertedData
));
350 data
.SetData(ConvertedData
, mPrivateData
);
356 // Can't set data neither directly nor through converter. Just add this flavor
358 if (NS_SUCCEEDED(AddDataFlavor(aFlavor
))) {
359 return SetTransferData(aFlavor
, aData
);
362 return NS_ERROR_FAILURE
;
368 // Adds a data flavor to our list with no data. Error if it already exists.
371 nsTransferable::AddDataFlavor(const char* aDataFlavor
) {
372 MOZ_ASSERT(mInitialized
);
374 if (FindDataFlavor(aDataFlavor
).isSome()) {
375 return NS_ERROR_FAILURE
;
378 // Create a new "slot" for the data
379 mDataArray
.AppendElement(DataStruct(aDataFlavor
));
386 // Removes a data flavor (and causes the data to be destroyed). Error if
387 // the requested flavor is not present.
390 nsTransferable::RemoveDataFlavor(const char* aDataFlavor
) {
391 MOZ_ASSERT(mInitialized
);
393 if (Maybe
<size_t> index
= FindDataFlavor(aDataFlavor
)) {
394 mDataArray
.RemoveElementAt(index
.value());
398 return NS_ERROR_FAILURE
;
402 nsTransferable::SetConverter(nsIFormatConverter
* aConverter
) {
403 MOZ_ASSERT(mInitialized
);
405 mFormatConv
= aConverter
;
410 nsTransferable::GetConverter(nsIFormatConverter
** aConverter
) {
411 MOZ_ASSERT(mInitialized
);
413 nsCOMPtr
<nsIFormatConverter
> converter
= mFormatConv
;
414 converter
.forget(aConverter
);
419 // FlavorsTransferableCanImport
421 // Computes a list of flavors that the transferable can accept into it, either
422 // through intrinsic knowledge or input data converters.
425 nsTransferable::FlavorsTransferableCanImport(nsTArray
<nsCString
>& aFlavors
) {
426 MOZ_ASSERT(mInitialized
);
428 // Get the flavor list, and on to the end of it, append the list of flavors we
429 // can also get to through a converter. This is so that we can just walk the
430 // list in one go, looking for the desired flavor.
431 GetTransferDataFlavors(aFlavors
);
434 nsTArray
<nsCString
> convertedList
;
435 mFormatConv
->GetInputDataFlavors(convertedList
);
437 for (uint32_t i
= 0; i
< convertedList
.Length(); ++i
) {
438 nsCString
& flavorStr
= convertedList
[i
];
440 // Don't append if already in intrinsic list
441 if (!aFlavors
.Contains(flavorStr
)) {
442 aFlavors
.AppendElement(flavorStr
);
451 // FlavorsTransferableCanExport
453 // Computes a list of flavors that the transferable can export, either through
454 // intrinsic knowledge or output data converters.
457 nsTransferable::FlavorsTransferableCanExport(nsTArray
<nsCString
>& aFlavors
) {
458 MOZ_ASSERT(mInitialized
);
460 // Get the flavor list, and on to the end of it, append the list of flavors we
461 // can also get to through a converter. This is so that we can just walk the
462 // list in one go, looking for the desired flavor.
463 GetTransferDataFlavors(aFlavors
);
466 nsTArray
<nsCString
> convertedList
;
467 mFormatConv
->GetOutputDataFlavors(convertedList
);
469 for (uint32_t i
= 0; i
< convertedList
.Length(); ++i
) {
470 nsCString
& flavorStr
= convertedList
[i
];
472 // Don't append if already in intrinsic list
473 if (!aFlavors
.Contains(flavorStr
)) {
474 aFlavors
.AppendElement(flavorStr
);
482 bool nsTransferable::GetIsPrivateData() {
483 MOZ_ASSERT(mInitialized
);
488 void nsTransferable::SetIsPrivateData(bool aIsPrivateData
) {
489 MOZ_ASSERT(mInitialized
);
491 mPrivateData
= aIsPrivateData
;
494 nsIPrincipal
* nsTransferable::GetRequestingPrincipal() {
495 MOZ_ASSERT(mInitialized
);
497 return mRequestingPrincipal
;
500 void nsTransferable::SetRequestingPrincipal(
501 nsIPrincipal
* aRequestingPrincipal
) {
502 MOZ_ASSERT(mInitialized
);
504 mRequestingPrincipal
= aRequestingPrincipal
;
507 nsContentPolicyType
nsTransferable::GetContentPolicyType() {
508 MOZ_ASSERT(mInitialized
);
510 return mContentPolicyType
;
513 void nsTransferable::SetContentPolicyType(
514 nsContentPolicyType aContentPolicyType
) {
515 MOZ_ASSERT(mInitialized
);
517 mContentPolicyType
= aContentPolicyType
;