Bug 1882714 [wpt PR 44850] - Update wpt metadata, a=testonly
[gecko.git] / widget / nsTransferable.cpp
blob58a8630801cc7fe929c902cc9d8fc873e0bcf9f2
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 "nsPrimitiveHelpers.h"
28 #include "nsDirectoryServiceDefs.h"
29 #include "nsDirectoryService.h"
30 #include "nsCRT.h"
31 #include "nsNetUtil.h"
32 #include "nsILoadContext.h"
33 #include "nsXULAppAPI.h"
34 #include "mozilla/StaticPrefs_browser.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 void DataStruct::ClearData() {
115 if (mCacheFD) {
116 PR_Close(mCacheFD);
118 mData = nullptr;
121 //-------------------------------------------------------------------------
122 nsresult DataStruct::WriteCache(void* aData, uint32_t aDataLen) {
123 MOZ_ASSERT(aData && aDataLen);
124 MOZ_ASSERT(aDataLen <= uint32_t(std::numeric_limits<int32_t>::max()),
125 "too large size for PR_Write");
127 nsresult rv;
128 if (!mCacheFD) {
129 rv = NS_OpenAnonymousTemporaryFile(&mCacheFD);
130 if (NS_FAILED(rv)) {
131 return NS_ERROR_FAILURE;
133 } else if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
134 return NS_ERROR_FAILURE;
137 // Write out the contents of the clipboard to the file.
138 int32_t written = PR_Write(mCacheFD, aData, aDataLen);
139 if (written == int32_t(aDataLen)) {
140 return NS_OK;
143 PR_Close(mCacheFD);
144 mCacheFD = nullptr;
145 return NS_ERROR_FAILURE;
148 //-------------------------------------------------------------------------
149 nsresult DataStruct::ReadCache(nsISupports** aData) {
150 if (!mCacheFD) {
151 return NS_ERROR_FAILURE;
154 PRFileInfo fileInfo;
155 if (PR_GetOpenFileInfo(mCacheFD, &fileInfo) != PR_SUCCESS) {
156 return NS_ERROR_FAILURE;
158 if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
159 return NS_ERROR_FAILURE;
161 uint32_t fileSize = fileInfo.size;
163 auto data = MakeUnique<char[]>(fileSize);
164 if (!data) {
165 return NS_ERROR_OUT_OF_MEMORY;
168 uint32_t actual = PR_Read(mCacheFD, data.get(), fileSize);
169 if (actual != fileSize) {
170 return NS_ERROR_FAILURE;
173 nsPrimitiveHelpers::CreatePrimitiveForData(mFlavor, data.get(), fileSize,
174 aData);
175 return NS_OK;
178 //-------------------------------------------------------------------------
180 // Transferable constructor
182 //-------------------------------------------------------------------------
183 nsTransferable::nsTransferable()
184 : mPrivateData(false),
185 mContentPolicyType(nsIContentPolicy::TYPE_OTHER)
186 #ifdef DEBUG
188 mInitialized(false)
189 #endif
193 //-------------------------------------------------------------------------
195 // Transferable destructor
197 //-------------------------------------------------------------------------
198 nsTransferable::~nsTransferable() = default;
200 NS_IMETHODIMP
201 nsTransferable::Init(nsILoadContext* aContext) {
202 MOZ_ASSERT(!mInitialized);
204 if (aContext) {
205 mPrivateData = aContext->UsePrivateBrowsing();
206 } else {
207 // without aContext here to provide PrivateBrowsing information, we defer to
208 // the active configured setting
209 mPrivateData = StaticPrefs::browser_privatebrowsing_autostart();
211 #ifdef DEBUG
212 mInitialized = true;
213 #endif
214 return NS_OK;
218 // GetTransferDataFlavors
220 // Returns a copy of the internal list of flavors. This does NOT take into
221 // account any converter that may be registered.
223 void nsTransferable::GetTransferDataFlavors(nsTArray<nsCString>& aFlavors) {
224 MOZ_ASSERT(mInitialized);
226 for (size_t i = 0; i < mDataArray.Length(); ++i) {
227 DataStruct& data = mDataArray.ElementAt(i);
228 aFlavors.AppendElement(data.GetFlavor());
232 Maybe<size_t> nsTransferable::FindDataFlavor(const char* aFlavor) {
233 nsDependentCString flavor(aFlavor);
235 for (size_t i = 0; i < mDataArray.Length(); ++i) {
236 if (mDataArray[i].GetFlavor().Equals(flavor)) {
237 return Some(i);
241 return Nothing();
245 // GetTransferData
247 // Returns the data of the requested flavor, obtained from either having the
248 // data on hand or using a converter to get it. The data is wrapped in a
249 // nsISupports primitive so that it is accessible from JS.
251 NS_IMETHODIMP
252 nsTransferable::GetTransferData(const char* aFlavor, nsISupports** aData) {
253 MOZ_ASSERT(mInitialized);
255 *aData = nullptr;
257 nsresult rv = NS_OK;
259 // First look and see if the data is present in one of the intrinsic flavors.
260 if (Maybe<size_t> index = FindDataFlavor(aFlavor)) {
261 nsCOMPtr<nsISupports> dataBytes;
262 mDataArray[index.value()].GetData(getter_AddRefs(dataBytes));
264 // Do we have a (lazy) data provider?
265 if (nsCOMPtr<nsIFlavorDataProvider> dataProvider =
266 do_QueryInterface(dataBytes)) {
267 rv =
268 dataProvider->GetFlavorData(this, aFlavor, getter_AddRefs(dataBytes));
269 if (NS_FAILED(rv)) {
270 dataBytes = nullptr;
271 // The provider failed, fall into the converter code below.
275 if (dataBytes) {
276 dataBytes.forget(aData);
277 return NS_OK;
280 // Empty data
283 // If not, try using a format converter to get the requested flavor.
284 if (mFormatConv) {
285 for (size_t i = 0; i < mDataArray.Length(); ++i) {
286 DataStruct& data = mDataArray.ElementAt(i);
287 bool canConvert = false;
288 mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
289 if (canConvert) {
290 nsCOMPtr<nsISupports> dataBytes;
291 data.GetData(getter_AddRefs(dataBytes));
293 // Do we have a (lazy) data provider?
294 if (nsCOMPtr<nsIFlavorDataProvider> dataProvider =
295 do_QueryInterface(dataBytes)) {
296 rv = dataProvider->GetFlavorData(this, aFlavor,
297 getter_AddRefs(dataBytes));
298 if (NS_FAILED(rv)) {
299 // Give up.
300 return rv;
304 return mFormatConv->Convert(data.GetFlavor().get(), dataBytes, aFlavor,
305 aData);
310 return NS_ERROR_FAILURE;
314 // GetAnyTransferData
316 // Returns the data of the first flavor found. Caller is responsible for
317 // deleting the flavor string.
319 NS_IMETHODIMP
320 nsTransferable::GetAnyTransferData(nsACString& aFlavor, nsISupports** aData) {
321 MOZ_ASSERT(mInitialized);
323 for (size_t i = 0; i < mDataArray.Length(); ++i) {
324 DataStruct& data = mDataArray.ElementAt(i);
325 if (data.IsDataAvailable()) {
326 aFlavor.Assign(data.GetFlavor());
327 data.GetData(aData);
328 return NS_OK;
332 return NS_ERROR_FAILURE;
336 // SetTransferData
340 NS_IMETHODIMP
341 nsTransferable::SetTransferData(const char* aFlavor, nsISupports* aData) {
342 MOZ_ASSERT(mInitialized);
344 // first check our intrinsic flavors to see if one has been registered.
345 if (Maybe<size_t> index = FindDataFlavor(aFlavor)) {
346 DataStruct& data = mDataArray.ElementAt(index.value());
347 data.SetData(aData, mPrivateData);
348 return NS_OK;
351 // if not, try using a format converter to find a flavor to put the data in
352 if (mFormatConv) {
353 for (size_t i = 0; i < mDataArray.Length(); ++i) {
354 DataStruct& data = mDataArray.ElementAt(i);
355 bool canConvert = false;
356 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
358 if (canConvert) {
359 nsCOMPtr<nsISupports> ConvertedData;
360 mFormatConv->Convert(aFlavor, aData, data.GetFlavor().get(),
361 getter_AddRefs(ConvertedData));
362 data.SetData(ConvertedData, mPrivateData);
363 return NS_OK;
368 // Can't set data neither directly nor through converter. Just add this flavor
369 // and try again
370 if (NS_SUCCEEDED(AddDataFlavor(aFlavor))) {
371 return SetTransferData(aFlavor, aData);
374 return NS_ERROR_FAILURE;
377 NS_IMETHODIMP
378 nsTransferable::ClearAllData() {
379 for (auto& entry : mDataArray) {
380 entry.ClearData();
382 return NS_OK;
386 // AddDataFlavor
388 // Adds a data flavor to our list with no data. Error if it already exists.
390 NS_IMETHODIMP
391 nsTransferable::AddDataFlavor(const char* aDataFlavor) {
392 MOZ_ASSERT(mInitialized);
394 if (FindDataFlavor(aDataFlavor).isSome()) {
395 return NS_ERROR_FAILURE;
398 // Create a new "slot" for the data
399 mDataArray.AppendElement(DataStruct(aDataFlavor));
400 return NS_OK;
404 // RemoveDataFlavor
406 // Removes a data flavor (and causes the data to be destroyed). Error if
407 // the requested flavor is not present.
409 NS_IMETHODIMP
410 nsTransferable::RemoveDataFlavor(const char* aDataFlavor) {
411 MOZ_ASSERT(mInitialized);
413 if (Maybe<size_t> index = FindDataFlavor(aDataFlavor)) {
414 mDataArray.RemoveElementAt(index.value());
415 return NS_OK;
418 return NS_ERROR_FAILURE;
421 NS_IMETHODIMP
422 nsTransferable::SetConverter(nsIFormatConverter* aConverter) {
423 MOZ_ASSERT(mInitialized);
425 mFormatConv = aConverter;
426 return NS_OK;
429 NS_IMETHODIMP
430 nsTransferable::GetConverter(nsIFormatConverter** aConverter) {
431 MOZ_ASSERT(mInitialized);
433 nsCOMPtr<nsIFormatConverter> converter = mFormatConv;
434 converter.forget(aConverter);
435 return NS_OK;
439 // FlavorsTransferableCanImport
441 // Computes a list of flavors that the transferable can accept into it, either
442 // through intrinsic knowledge or input data converters.
444 NS_IMETHODIMP
445 nsTransferable::FlavorsTransferableCanImport(nsTArray<nsCString>& aFlavors) {
446 MOZ_ASSERT(mInitialized);
448 // Get the flavor list, and on to the end of it, append the list of flavors we
449 // can also get to through a converter. This is so that we can just walk the
450 // list in one go, looking for the desired flavor.
451 GetTransferDataFlavors(aFlavors);
453 if (mFormatConv) {
454 nsTArray<nsCString> convertedList;
455 mFormatConv->GetInputDataFlavors(convertedList);
457 for (uint32_t i = 0; i < convertedList.Length(); ++i) {
458 nsCString& flavorStr = convertedList[i];
460 // Don't append if already in intrinsic list
461 if (!aFlavors.Contains(flavorStr)) {
462 aFlavors.AppendElement(flavorStr);
467 return NS_OK;
471 // FlavorsTransferableCanExport
473 // Computes a list of flavors that the transferable can export, either through
474 // intrinsic knowledge or output data converters.
476 NS_IMETHODIMP
477 nsTransferable::FlavorsTransferableCanExport(nsTArray<nsCString>& aFlavors) {
478 MOZ_ASSERT(mInitialized);
480 // Get the flavor list, and on to the end of it, append the list of flavors we
481 // can also get to through a converter. This is so that we can just walk the
482 // list in one go, looking for the desired flavor.
483 GetTransferDataFlavors(aFlavors);
485 if (mFormatConv) {
486 nsTArray<nsCString> convertedList;
487 mFormatConv->GetOutputDataFlavors(convertedList);
489 for (uint32_t i = 0; i < convertedList.Length(); ++i) {
490 nsCString& flavorStr = convertedList[i];
492 // Don't append if already in intrinsic list
493 if (!aFlavors.Contains(flavorStr)) {
494 aFlavors.AppendElement(flavorStr);
499 return NS_OK;
502 bool nsTransferable::GetIsPrivateData() {
503 MOZ_ASSERT(mInitialized);
505 return mPrivateData;
508 void nsTransferable::SetIsPrivateData(bool aIsPrivateData) {
509 MOZ_ASSERT(mInitialized);
511 mPrivateData = aIsPrivateData;
514 nsIPrincipal* nsTransferable::GetRequestingPrincipal() {
515 MOZ_ASSERT(mInitialized);
517 return mRequestingPrincipal;
520 void nsTransferable::SetRequestingPrincipal(
521 nsIPrincipal* aRequestingPrincipal) {
522 MOZ_ASSERT(mInitialized);
524 mRequestingPrincipal = aRequestingPrincipal;
527 nsContentPolicyType nsTransferable::GetContentPolicyType() {
528 MOZ_ASSERT(mInitialized);
530 return mContentPolicyType;
533 void nsTransferable::SetContentPolicyType(
534 nsContentPolicyType aContentPolicyType) {
535 MOZ_ASSERT(mInitialized);
537 mContentPolicyType = aContentPolicyType;
540 nsICookieJarSettings* nsTransferable::GetCookieJarSettings() {
541 MOZ_ASSERT(mInitialized);
543 return mCookieJarSettings;
546 void nsTransferable::SetCookieJarSettings(
547 nsICookieJarSettings* aCookieJarSettings) {
548 MOZ_ASSERT(mInitialized);
550 mCookieJarSettings = aCookieJarSettings;
553 nsIReferrerInfo* nsTransferable::GetReferrerInfo() {
554 MOZ_ASSERT(mInitialized);
555 return mReferrerInfo;
558 void nsTransferable::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
559 MOZ_ASSERT(mInitialized);
560 mReferrerInfo = aReferrerInfo;