Bug 1510695 - Fix URL comparisons in reftestWait r=jgraham
[gecko.git] / widget / nsTransferable.cpp
blob4db142c5a370b134f333e28638cd6d70c02d9c6f
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 "nsIComponentManager.h"
25 #include "nsCOMPtr.h"
26 #include "nsXPCOM.h"
27 #include "nsISupportsPrimitives.h"
28 #include "nsMemory.h"
29 #include "nsPrimitiveHelpers.h"
30 #include "nsDirectoryServiceDefs.h"
31 #include "nsDirectoryService.h"
32 #include "nsCRT.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() {
53 if (mCacheFD) {
54 PR_Close(mCacheFD);
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()) {
65 void* data = nullptr;
66 uint32_t dataLen = 0;
67 nsPrimitiveHelpers::CreateDataFromPrimitive(mFlavor, aData, &data,
68 &dataLen);
70 if (dataLen > kLargeDatasetSize) {
71 // Too large, cache it to disk instead of memory.
72 if (NS_SUCCEEDED(WriteCache(data, dataLen))) {
73 free(data);
74 // Clear previously set small data.
75 mData = nullptr;
76 return;
79 NS_WARNING("Oh no, couldn't write data to the cache file");
82 free(data);
85 if (mCacheFD) {
86 // Clear previously set big data.
87 PR_Close(mCacheFD);
88 mCacheFD = nullptr;
91 mData = aData;
94 //-------------------------------------------------------------------------
95 void DataStruct::GetData(nsISupports** aData) {
96 // check here to see if the data is cached on disk
97 if (mCacheFD) {
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))) {
101 return;
104 // oh shit, something went horribly wrong here.
105 NS_WARNING("Oh no, couldn't read data in from the cache file");
106 *aData = nullptr;
107 PR_Close(mCacheFD);
108 mCacheFD = nullptr;
109 return;
112 nsCOMPtr<nsISupports> data = mData;
113 data.forget(aData);
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");
122 nsresult rv;
123 if (!mCacheFD) {
124 rv = NS_OpenAnonymousTemporaryFile(&mCacheFD);
125 if (NS_FAILED(rv)) {
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)) {
135 return NS_OK;
138 PR_Close(mCacheFD);
139 mCacheFD = nullptr;
140 return NS_ERROR_FAILURE;
143 //-------------------------------------------------------------------------
144 nsresult DataStruct::ReadCache(nsISupports** aData) {
145 if (!mCacheFD) {
146 return NS_ERROR_FAILURE;
149 PRFileInfo fileInfo;
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);
159 if (!data) {
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,
169 aData);
170 return NS_OK;
173 //-------------------------------------------------------------------------
175 // Transferable constructor
177 //-------------------------------------------------------------------------
178 nsTransferable::nsTransferable()
179 : mPrivateData(false),
180 mContentPolicyType(nsIContentPolicy::TYPE_OTHER)
181 #ifdef DEBUG
183 mInitialized(false)
184 #endif
188 //-------------------------------------------------------------------------
190 // Transferable destructor
192 //-------------------------------------------------------------------------
193 nsTransferable::~nsTransferable() {}
195 NS_IMETHODIMP
196 nsTransferable::Init(nsILoadContext* aContext) {
197 MOZ_ASSERT(!mInitialized);
199 if (aContext) {
200 mPrivateData = aContext->UsePrivateBrowsing();
202 #ifdef DEBUG
203 mInitialized = true;
204 #endif
205 return NS_OK;
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)) {
228 return Some(i);
232 return Nothing();
236 // GetTransferData
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.
242 NS_IMETHODIMP
243 nsTransferable::GetTransferData(const char* aFlavor, nsISupports** aData) {
244 MOZ_ASSERT(mInitialized);
246 *aData = nullptr;
248 nsresult rv = NS_OK;
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)) {
258 rv =
259 dataProvider->GetFlavorData(this, aFlavor, getter_AddRefs(dataBytes));
260 if (NS_FAILED(rv)) {
261 dataBytes = nullptr;
262 // The provider failed, fall into the converter code below.
266 if (dataBytes) {
267 dataBytes.forget(aData);
268 return NS_OK;
271 // Empty data
274 // If not, try using a format converter to get the requested flavor.
275 if (mFormatConv) {
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);
280 if (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));
289 if (NS_FAILED(rv)) {
290 // Give up.
291 return rv;
295 return mFormatConv->Convert(data.GetFlavor().get(), dataBytes, aFlavor,
296 aData);
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.
310 NS_IMETHODIMP
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());
318 data.GetData(aData);
319 return NS_OK;
323 return NS_ERROR_FAILURE;
327 // SetTransferData
331 NS_IMETHODIMP
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);
339 return NS_OK;
342 // if not, try using a format converter to find a flavor to put the data in
343 if (mFormatConv) {
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);
349 if (canConvert) {
350 nsCOMPtr<nsISupports> ConvertedData;
351 mFormatConv->Convert(aFlavor, aData, data.GetFlavor().get(),
352 getter_AddRefs(ConvertedData));
353 data.SetData(ConvertedData, mPrivateData);
354 return NS_OK;
359 // Can't set data neither directly nor through converter. Just add this flavor
360 // and try again
361 if (NS_SUCCEEDED(AddDataFlavor(aFlavor))) {
362 return SetTransferData(aFlavor, aData);
365 return NS_ERROR_FAILURE;
369 // AddDataFlavor
371 // Adds a data flavor to our list with no data. Error if it already exists.
373 NS_IMETHODIMP
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));
383 return NS_OK;
387 // RemoveDataFlavor
389 // Removes a data flavor (and causes the data to be destroyed). Error if
390 // the requested flavor is not present.
392 NS_IMETHODIMP
393 nsTransferable::RemoveDataFlavor(const char* aDataFlavor) {
394 MOZ_ASSERT(mInitialized);
396 if (Maybe<size_t> index = FindDataFlavor(aDataFlavor)) {
397 mDataArray.RemoveElementAt(index.value());
398 return NS_OK;
401 return NS_ERROR_FAILURE;
404 NS_IMETHODIMP
405 nsTransferable::SetConverter(nsIFormatConverter* aConverter) {
406 MOZ_ASSERT(mInitialized);
408 mFormatConv = aConverter;
409 return NS_OK;
412 NS_IMETHODIMP
413 nsTransferable::GetConverter(nsIFormatConverter** aConverter) {
414 MOZ_ASSERT(mInitialized);
416 nsCOMPtr<nsIFormatConverter> converter = mFormatConv;
417 converter.forget(aConverter);
418 return NS_OK;
422 // FlavorsTransferableCanImport
424 // Computes a list of flavors that the transferable can accept into it, either
425 // through intrinsic knowledge or input data converters.
427 NS_IMETHODIMP
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);
436 if (mFormatConv) {
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);
450 return NS_OK;
454 // FlavorsTransferableCanExport
456 // Computes a list of flavors that the transferable can export, either through
457 // intrinsic knowledge or output data converters.
459 NS_IMETHODIMP
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);
468 if (mFormatConv) {
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);
482 return NS_OK;
485 bool nsTransferable::GetIsPrivateData() {
486 MOZ_ASSERT(mInitialized);
488 return mPrivateData;
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;