Bug 1855375: Basic implementation for Yelp Suggestions r=fluent-reviewers,flod,adw
[gecko.git] / widget / nsTransferable.cpp
blobf8a8201233c9644d6e9fc17411da10d6b218849b
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 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();
198 } else {
199 // without aContext here to provide PrivateBrowsing information, we defer to
200 // the active configured setting
201 mPrivateData = StaticPrefs::browser_privatebrowsing_autostart();
203 #ifdef DEBUG
204 mInitialized = true;
205 #endif
206 return NS_OK;
210 // GetTransferDataFlavors
212 // Returns a copy of the internal list of flavors. This does NOT take into
213 // account any converter that may be registered.
215 void nsTransferable::GetTransferDataFlavors(nsTArray<nsCString>& aFlavors) {
216 MOZ_ASSERT(mInitialized);
218 for (size_t i = 0; i < mDataArray.Length(); ++i) {
219 DataStruct& data = mDataArray.ElementAt(i);
220 aFlavors.AppendElement(data.GetFlavor());
224 Maybe<size_t> nsTransferable::FindDataFlavor(const char* aFlavor) {
225 nsDependentCString flavor(aFlavor);
227 for (size_t i = 0; i < mDataArray.Length(); ++i) {
228 if (mDataArray[i].GetFlavor().Equals(flavor)) {
229 return Some(i);
233 return Nothing();
237 // GetTransferData
239 // Returns the data of the requested flavor, obtained from either having the
240 // data on hand or using a converter to get it. The data is wrapped in a
241 // nsISupports primitive so that it is accessible from JS.
243 NS_IMETHODIMP
244 nsTransferable::GetTransferData(const char* aFlavor, nsISupports** aData) {
245 MOZ_ASSERT(mInitialized);
247 *aData = nullptr;
249 nsresult rv = NS_OK;
251 // First look and see if the data is present in one of the intrinsic flavors.
252 if (Maybe<size_t> index = FindDataFlavor(aFlavor)) {
253 nsCOMPtr<nsISupports> dataBytes;
254 mDataArray[index.value()].GetData(getter_AddRefs(dataBytes));
256 // Do we have a (lazy) data provider?
257 if (nsCOMPtr<nsIFlavorDataProvider> dataProvider =
258 do_QueryInterface(dataBytes)) {
259 rv =
260 dataProvider->GetFlavorData(this, aFlavor, getter_AddRefs(dataBytes));
261 if (NS_FAILED(rv)) {
262 dataBytes = nullptr;
263 // The provider failed, fall into the converter code below.
267 if (dataBytes) {
268 dataBytes.forget(aData);
269 return NS_OK;
272 // Empty data
275 // If not, try using a format converter to get the requested flavor.
276 if (mFormatConv) {
277 for (size_t i = 0; i < mDataArray.Length(); ++i) {
278 DataStruct& data = mDataArray.ElementAt(i);
279 bool canConvert = false;
280 mFormatConv->CanConvert(data.GetFlavor().get(), aFlavor, &canConvert);
281 if (canConvert) {
282 nsCOMPtr<nsISupports> dataBytes;
283 data.GetData(getter_AddRefs(dataBytes));
285 // Do we have a (lazy) data provider?
286 if (nsCOMPtr<nsIFlavorDataProvider> dataProvider =
287 do_QueryInterface(dataBytes)) {
288 rv = dataProvider->GetFlavorData(this, aFlavor,
289 getter_AddRefs(dataBytes));
290 if (NS_FAILED(rv)) {
291 // Give up.
292 return rv;
296 return mFormatConv->Convert(data.GetFlavor().get(), dataBytes, aFlavor,
297 aData);
302 return NS_ERROR_FAILURE;
306 // GetAnyTransferData
308 // Returns the data of the first flavor found. Caller is responsible for
309 // deleting the flavor string.
311 NS_IMETHODIMP
312 nsTransferable::GetAnyTransferData(nsACString& aFlavor, nsISupports** aData) {
313 MOZ_ASSERT(mInitialized);
315 for (size_t i = 0; i < mDataArray.Length(); ++i) {
316 DataStruct& data = mDataArray.ElementAt(i);
317 if (data.IsDataAvailable()) {
318 aFlavor.Assign(data.GetFlavor());
319 data.GetData(aData);
320 return NS_OK;
324 return NS_ERROR_FAILURE;
328 // SetTransferData
332 NS_IMETHODIMP
333 nsTransferable::SetTransferData(const char* aFlavor, nsISupports* aData) {
334 MOZ_ASSERT(mInitialized);
336 // first check our intrinsic flavors to see if one has been registered.
337 if (Maybe<size_t> index = FindDataFlavor(aFlavor)) {
338 DataStruct& data = mDataArray.ElementAt(index.value());
339 data.SetData(aData, mPrivateData);
340 return NS_OK;
343 // if not, try using a format converter to find a flavor to put the data in
344 if (mFormatConv) {
345 for (size_t i = 0; i < mDataArray.Length(); ++i) {
346 DataStruct& data = mDataArray.ElementAt(i);
347 bool canConvert = false;
348 mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
350 if (canConvert) {
351 nsCOMPtr<nsISupports> ConvertedData;
352 mFormatConv->Convert(aFlavor, aData, data.GetFlavor().get(),
353 getter_AddRefs(ConvertedData));
354 data.SetData(ConvertedData, mPrivateData);
355 return NS_OK;
360 // Can't set data neither directly nor through converter. Just add this flavor
361 // and try again
362 if (NS_SUCCEEDED(AddDataFlavor(aFlavor))) {
363 return SetTransferData(aFlavor, aData);
366 return NS_ERROR_FAILURE;
370 // AddDataFlavor
372 // Adds a data flavor to our list with no data. Error if it already exists.
374 NS_IMETHODIMP
375 nsTransferable::AddDataFlavor(const char* aDataFlavor) {
376 MOZ_ASSERT(mInitialized);
378 if (FindDataFlavor(aDataFlavor).isSome()) {
379 return NS_ERROR_FAILURE;
382 // Create a new "slot" for the data
383 mDataArray.AppendElement(DataStruct(aDataFlavor));
384 return NS_OK;
388 // RemoveDataFlavor
390 // Removes a data flavor (and causes the data to be destroyed). Error if
391 // the requested flavor is not present.
393 NS_IMETHODIMP
394 nsTransferable::RemoveDataFlavor(const char* aDataFlavor) {
395 MOZ_ASSERT(mInitialized);
397 if (Maybe<size_t> index = FindDataFlavor(aDataFlavor)) {
398 mDataArray.RemoveElementAt(index.value());
399 return NS_OK;
402 return NS_ERROR_FAILURE;
405 NS_IMETHODIMP
406 nsTransferable::SetConverter(nsIFormatConverter* aConverter) {
407 MOZ_ASSERT(mInitialized);
409 mFormatConv = aConverter;
410 return NS_OK;
413 NS_IMETHODIMP
414 nsTransferable::GetConverter(nsIFormatConverter** aConverter) {
415 MOZ_ASSERT(mInitialized);
417 nsCOMPtr<nsIFormatConverter> converter = mFormatConv;
418 converter.forget(aConverter);
419 return NS_OK;
423 // FlavorsTransferableCanImport
425 // Computes a list of flavors that the transferable can accept into it, either
426 // through intrinsic knowledge or input data converters.
428 NS_IMETHODIMP
429 nsTransferable::FlavorsTransferableCanImport(nsTArray<nsCString>& aFlavors) {
430 MOZ_ASSERT(mInitialized);
432 // Get the flavor list, and on to the end of it, append the list of flavors we
433 // can also get to through a converter. This is so that we can just walk the
434 // list in one go, looking for the desired flavor.
435 GetTransferDataFlavors(aFlavors);
437 if (mFormatConv) {
438 nsTArray<nsCString> convertedList;
439 mFormatConv->GetInputDataFlavors(convertedList);
441 for (uint32_t i = 0; i < convertedList.Length(); ++i) {
442 nsCString& flavorStr = convertedList[i];
444 // Don't append if already in intrinsic list
445 if (!aFlavors.Contains(flavorStr)) {
446 aFlavors.AppendElement(flavorStr);
451 return NS_OK;
455 // FlavorsTransferableCanExport
457 // Computes a list of flavors that the transferable can export, either through
458 // intrinsic knowledge or output data converters.
460 NS_IMETHODIMP
461 nsTransferable::FlavorsTransferableCanExport(nsTArray<nsCString>& aFlavors) {
462 MOZ_ASSERT(mInitialized);
464 // Get the flavor list, and on to the end of it, append the list of flavors we
465 // can also get to through a converter. This is so that we can just walk the
466 // list in one go, looking for the desired flavor.
467 GetTransferDataFlavors(aFlavors);
469 if (mFormatConv) {
470 nsTArray<nsCString> convertedList;
471 mFormatConv->GetOutputDataFlavors(convertedList);
473 for (uint32_t i = 0; i < convertedList.Length(); ++i) {
474 nsCString& flavorStr = convertedList[i];
476 // Don't append if already in intrinsic list
477 if (!aFlavors.Contains(flavorStr)) {
478 aFlavors.AppendElement(flavorStr);
483 return NS_OK;
486 bool nsTransferable::GetIsPrivateData() {
487 MOZ_ASSERT(mInitialized);
489 return mPrivateData;
492 void nsTransferable::SetIsPrivateData(bool aIsPrivateData) {
493 MOZ_ASSERT(mInitialized);
495 mPrivateData = aIsPrivateData;
498 nsIPrincipal* nsTransferable::GetRequestingPrincipal() {
499 MOZ_ASSERT(mInitialized);
501 return mRequestingPrincipal;
504 void nsTransferable::SetRequestingPrincipal(
505 nsIPrincipal* aRequestingPrincipal) {
506 MOZ_ASSERT(mInitialized);
508 mRequestingPrincipal = aRequestingPrincipal;
511 nsContentPolicyType nsTransferable::GetContentPolicyType() {
512 MOZ_ASSERT(mInitialized);
514 return mContentPolicyType;
517 void nsTransferable::SetContentPolicyType(
518 nsContentPolicyType aContentPolicyType) {
519 MOZ_ASSERT(mInitialized);
521 mContentPolicyType = aContentPolicyType;
524 nsICookieJarSettings* nsTransferable::GetCookieJarSettings() {
525 MOZ_ASSERT(mInitialized);
527 return mCookieJarSettings;
530 void nsTransferable::SetCookieJarSettings(
531 nsICookieJarSettings* aCookieJarSettings) {
532 MOZ_ASSERT(mInitialized);
534 mCookieJarSettings = aCookieJarSettings;
537 nsIReferrerInfo* nsTransferable::GetReferrerInfo() {
538 MOZ_ASSERT(mInitialized);
539 return mReferrerInfo;
542 void nsTransferable::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
543 MOZ_ASSERT(mInitialized);
544 mReferrerInfo = aReferrerInfo;