Bug 1641886 [wpt PR 23851] - Support interpolating contain-intrinsic-size, a=testonly
[gecko.git] / widget / nsTransferable.cpp
blob9ccfc8639350cecc268c787428b51447f099bef2
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 nsIArrays.
15 #include "nsTransferable.h"
16 #include "nsAnonymousTemporaryFile.h"
17 #include "nsArray.h"
18 #include "nsArrayUtils.h"
19 #include "nsString.h"
20 #include "nsReadableUtils.h"
21 #include "nsTArray.h"
22 #include "nsIFormatConverter.h"
23 #include "nsIContentPolicy.h"
24 #include "nsCOMPtr.h"
25 #include "nsXPCOM.h"
26 #include "nsISupportsPrimitives.h"
27 #include "nsMemory.h"
28 #include "nsPrimitiveHelpers.h"
29 #include "nsDirectoryServiceDefs.h"
30 #include "nsDirectoryService.h"
31 #include "nsCRT.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() {
50 if (mCacheFD) {
51 PR_Close(mCacheFD);
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()) {
62 void* data = nullptr;
63 uint32_t dataLen = 0;
64 nsPrimitiveHelpers::CreateDataFromPrimitive(mFlavor, aData, &data,
65 &dataLen);
67 if (dataLen > kLargeDatasetSize) {
68 // Too large, cache it to disk instead of memory.
69 if (NS_SUCCEEDED(WriteCache(data, dataLen))) {
70 free(data);
71 // Clear previously set small data.
72 mData = nullptr;
73 return;
76 NS_WARNING("Oh no, couldn't write data to the cache file");
79 free(data);
82 if (mCacheFD) {
83 // Clear previously set big data.
84 PR_Close(mCacheFD);
85 mCacheFD = nullptr;
88 mData = aData;
91 //-------------------------------------------------------------------------
92 void DataStruct::GetData(nsISupports** aData) {
93 // check here to see if the data is cached on disk
94 if (mCacheFD) {
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))) {
98 return;
101 // oh shit, something went horribly wrong here.
102 NS_WARNING("Oh no, couldn't read data in from the cache file");
103 *aData = nullptr;
104 PR_Close(mCacheFD);
105 mCacheFD = nullptr;
106 return;
109 nsCOMPtr<nsISupports> data = mData;
110 data.forget(aData);
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");
119 nsresult rv;
120 if (!mCacheFD) {
121 rv = NS_OpenAnonymousTemporaryFile(&mCacheFD);
122 if (NS_FAILED(rv)) {
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)) {
132 return NS_OK;
135 PR_Close(mCacheFD);
136 mCacheFD = nullptr;
137 return NS_ERROR_FAILURE;
140 //-------------------------------------------------------------------------
141 nsresult DataStruct::ReadCache(nsISupports** aData) {
142 if (!mCacheFD) {
143 return NS_ERROR_FAILURE;
146 PRFileInfo fileInfo;
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);
156 if (!data) {
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,
166 aData);
167 return NS_OK;
170 //-------------------------------------------------------------------------
172 // Transferable constructor
174 //-------------------------------------------------------------------------
175 nsTransferable::nsTransferable()
176 : mPrivateData(false),
177 mContentPolicyType(nsIContentPolicy::TYPE_OTHER)
178 #ifdef DEBUG
180 mInitialized(false)
181 #endif
185 //-------------------------------------------------------------------------
187 // Transferable destructor
189 //-------------------------------------------------------------------------
190 nsTransferable::~nsTransferable() = default;
192 NS_IMETHODIMP
193 nsTransferable::Init(nsILoadContext* aContext) {
194 MOZ_ASSERT(!mInitialized);
196 if (aContext) {
197 mPrivateData = aContext->UsePrivateBrowsing();
199 #ifdef DEBUG
200 mInitialized = true;
201 #endif
202 return NS_OK;
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)) {
225 return Some(i);
229 return Nothing();
233 // GetTransferData
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.
239 NS_IMETHODIMP
240 nsTransferable::GetTransferData(const char* aFlavor, nsISupports** aData) {
241 MOZ_ASSERT(mInitialized);
243 *aData = nullptr;
245 nsresult rv = NS_OK;
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)) {
255 rv =
256 dataProvider->GetFlavorData(this, aFlavor, getter_AddRefs(dataBytes));
257 if (NS_FAILED(rv)) {
258 dataBytes = nullptr;
259 // The provider failed, fall into the converter code below.
263 if (dataBytes) {
264 dataBytes.forget(aData);
265 return NS_OK;
268 // Empty data
271 // If not, try using a format converter to get the requested flavor.
272 if (mFormatConv) {
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);
277 if (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));
286 if (NS_FAILED(rv)) {
287 // Give up.
288 return rv;
292 return mFormatConv->Convert(data.GetFlavor().get(), dataBytes, aFlavor,
293 aData);
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.
307 NS_IMETHODIMP
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());
315 data.GetData(aData);
316 return NS_OK;
320 return NS_ERROR_FAILURE;
324 // SetTransferData
328 NS_IMETHODIMP
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);
336 return NS_OK;
339 // if not, try using a format converter to find a flavor to put the data in
340 if (mFormatConv) {
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);
346 if (canConvert) {
347 nsCOMPtr<nsISupports> ConvertedData;
348 mFormatConv->Convert(aFlavor, aData, data.GetFlavor().get(),
349 getter_AddRefs(ConvertedData));
350 data.SetData(ConvertedData, mPrivateData);
351 return NS_OK;
356 // Can't set data neither directly nor through converter. Just add this flavor
357 // and try again
358 if (NS_SUCCEEDED(AddDataFlavor(aFlavor))) {
359 return SetTransferData(aFlavor, aData);
362 return NS_ERROR_FAILURE;
366 // AddDataFlavor
368 // Adds a data flavor to our list with no data. Error if it already exists.
370 NS_IMETHODIMP
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));
380 return NS_OK;
384 // RemoveDataFlavor
386 // Removes a data flavor (and causes the data to be destroyed). Error if
387 // the requested flavor is not present.
389 NS_IMETHODIMP
390 nsTransferable::RemoveDataFlavor(const char* aDataFlavor) {
391 MOZ_ASSERT(mInitialized);
393 if (Maybe<size_t> index = FindDataFlavor(aDataFlavor)) {
394 mDataArray.RemoveElementAt(index.value());
395 return NS_OK;
398 return NS_ERROR_FAILURE;
401 NS_IMETHODIMP
402 nsTransferable::SetConverter(nsIFormatConverter* aConverter) {
403 MOZ_ASSERT(mInitialized);
405 mFormatConv = aConverter;
406 return NS_OK;
409 NS_IMETHODIMP
410 nsTransferable::GetConverter(nsIFormatConverter** aConverter) {
411 MOZ_ASSERT(mInitialized);
413 nsCOMPtr<nsIFormatConverter> converter = mFormatConv;
414 converter.forget(aConverter);
415 return NS_OK;
419 // FlavorsTransferableCanImport
421 // Computes a list of flavors that the transferable can accept into it, either
422 // through intrinsic knowledge or input data converters.
424 NS_IMETHODIMP
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);
433 if (mFormatConv) {
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);
447 return NS_OK;
451 // FlavorsTransferableCanExport
453 // Computes a list of flavors that the transferable can export, either through
454 // intrinsic knowledge or output data converters.
456 NS_IMETHODIMP
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);
465 if (mFormatConv) {
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);
479 return NS_OK;
482 bool nsTransferable::GetIsPrivateData() {
483 MOZ_ASSERT(mInitialized);
485 return mPrivateData;
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;