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/. */
5
6 /*
7 Notes to self:
8
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.
12
13 */
14
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"
36
37 using namespace mozilla;
38
NS_IMPL_ISUPPORTS(nsTransferable,nsITransferable)39 NS_IMPL_ISUPPORTS(nsTransferable, nsITransferable)
40
41 DataStruct::DataStruct(DataStruct&& aRHS)
42 : mData(aRHS.mData.forget()),
43 mCacheFD(aRHS.mCacheFD),
44 mFlavor(aRHS.mFlavor) {
45 aRHS.mCacheFD = nullptr;
46 }
47
48 //-------------------------------------------------------------------------
~DataStruct()49 DataStruct::~DataStruct() {
50 if (mCacheFD) {
51 PR_Close(mCacheFD);
52 }
53 }
54
55 //-------------------------------------------------------------------------
56
SetData(nsISupports * aData,bool aIsPrivateData)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);
66
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;
74 }
75
76 NS_WARNING("Oh no, couldn't write data to the cache file");
77 }
78
79 free(data);
80 }
81
82 if (mCacheFD) {
83 // Clear previously set big data.
84 PR_Close(mCacheFD);
85 mCacheFD = nullptr;
86 }
87
88 mData = aData;
89 }
90
91 //-------------------------------------------------------------------------
GetData(nsISupports ** aData)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;
99 }
100
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;
107 }
108
109 nsCOMPtr<nsISupports> data = mData;
110 data.forget(aData);
111 }
112
113 //-------------------------------------------------------------------------
WriteCache(void * aData,uint32_t aDataLen)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");
118
119 nsresult rv;
120 if (!mCacheFD) {
121 rv = NS_OpenAnonymousTemporaryFile(&mCacheFD);
122 if (NS_FAILED(rv)) {
123 return NS_ERROR_FAILURE;
124 }
125 } else if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
126 return NS_ERROR_FAILURE;
127 }
128
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;
133 }
134
135 PR_Close(mCacheFD);
136 mCacheFD = nullptr;
137 return NS_ERROR_FAILURE;
138 }
139
140 //-------------------------------------------------------------------------
ReadCache(nsISupports ** aData)141 nsresult DataStruct::ReadCache(nsISupports** aData) {
142 if (!mCacheFD) {
143 return NS_ERROR_FAILURE;
144 }
145
146 PRFileInfo fileInfo;
147 if (PR_GetOpenFileInfo(mCacheFD, &fileInfo) != PR_SUCCESS) {
148 return NS_ERROR_FAILURE;
149 }
150 if (PR_Seek64(mCacheFD, 0, PR_SEEK_SET) == -1) {
151 return NS_ERROR_FAILURE;
152 }
153 uint32_t fileSize = fileInfo.size;
154
155 auto data = MakeUnique<char[]>(fileSize);
156 if (!data) {
157 return NS_ERROR_OUT_OF_MEMORY;
158 }
159
160 uint32_t actual = PR_Read(mCacheFD, data.get(), fileSize);
161 if (actual != fileSize) {
162 return NS_ERROR_FAILURE;
163 }
164
165 nsPrimitiveHelpers::CreatePrimitiveForData(mFlavor, data.get(), fileSize,
166 aData);
167 return NS_OK;
168 }
169
170 //-------------------------------------------------------------------------
171 //
172 // Transferable constructor
173 //
174 //-------------------------------------------------------------------------
nsTransferable()175 nsTransferable::nsTransferable()
176 : mPrivateData(false),
177 mContentPolicyType(nsIContentPolicy::TYPE_OTHER)
178 #ifdef DEBUG
179 ,
180 mInitialized(false)
181 #endif
182 {
183 }
184
185 //-------------------------------------------------------------------------
186 //
187 // Transferable destructor
188 //
189 //-------------------------------------------------------------------------
190 nsTransferable::~nsTransferable() = default;
191
192 NS_IMETHODIMP
Init(nsILoadContext * aContext)193 nsTransferable::Init(nsILoadContext* aContext) {
194 MOZ_ASSERT(!mInitialized);
195
196 if (aContext) {
197 mPrivateData = aContext->UsePrivateBrowsing();
198 }
199 #ifdef DEBUG
200 mInitialized = true;
201 #endif
202 return NS_OK;
203 }
204
205 //
206 // GetTransferDataFlavors
207 //
208 // Returns a copy of the internal list of flavors. This does NOT take into
209 // account any converter that may be registered.
210 //
GetTransferDataFlavors(nsTArray<nsCString> & aFlavors)211 void nsTransferable::GetTransferDataFlavors(nsTArray<nsCString>& aFlavors) {
212 MOZ_ASSERT(mInitialized);
213
214 for (size_t i = 0; i < mDataArray.Length(); ++i) {
215 DataStruct& data = mDataArray.ElementAt(i);
216 aFlavors.AppendElement(data.GetFlavor());
217 }
218 }
219
FindDataFlavor(const char * aFlavor)220 Maybe<size_t> nsTransferable::FindDataFlavor(const char* aFlavor) {
221 nsDependentCString flavor(aFlavor);
222
223 for (size_t i = 0; i < mDataArray.Length(); ++i) {
224 if (mDataArray[i].GetFlavor().Equals(flavor)) {
225 return Some(i);
226 }
227 }
228
229 return Nothing();
230 }
231
232 //
233 // GetTransferData
234 //
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.
238 //
239 NS_IMETHODIMP
GetTransferData(const char * aFlavor,nsISupports ** aData)240 nsTransferable::GetTransferData(const char* aFlavor, nsISupports** aData) {
241 MOZ_ASSERT(mInitialized);
242
243 *aData = nullptr;
244
245 nsresult rv = NS_OK;
246
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));
251
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.
260 }
261 }
262
263 if (dataBytes) {
264 dataBytes.forget(aData);
265 return NS_OK;
266 }
267
268 // Empty data
269 }
270
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));
280
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;
289 }
290 }
291
292 return mFormatConv->Convert(data.GetFlavor().get(), dataBytes, aFlavor,
293 aData);
294 }
295 }
296 }
297
298 return NS_ERROR_FAILURE;
299 }
300
301 //
302 // GetAnyTransferData
303 //
304 // Returns the data of the first flavor found. Caller is responsible for
305 // deleting the flavor string.
306 //
307 NS_IMETHODIMP
GetAnyTransferData(nsACString & aFlavor,nsISupports ** aData)308 nsTransferable::GetAnyTransferData(nsACString& aFlavor, nsISupports** aData) {
309 MOZ_ASSERT(mInitialized);
310
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;
317 }
318 }
319
320 return NS_ERROR_FAILURE;
321 }
322
323 //
324 // SetTransferData
325 //
326 //
327 //
328 NS_IMETHODIMP
SetTransferData(const char * aFlavor,nsISupports * aData)329 nsTransferable::SetTransferData(const char* aFlavor, nsISupports* aData) {
330 MOZ_ASSERT(mInitialized);
331
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;
337 }
338
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);
345
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;
352 }
353 }
354 }
355
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);
360 }
361
362 return NS_ERROR_FAILURE;
363 }
364
365 //
366 // AddDataFlavor
367 //
368 // Adds a data flavor to our list with no data. Error if it already exists.
369 //
370 NS_IMETHODIMP
AddDataFlavor(const char * aDataFlavor)371 nsTransferable::AddDataFlavor(const char* aDataFlavor) {
372 MOZ_ASSERT(mInitialized);
373
374 if (FindDataFlavor(aDataFlavor).isSome()) {
375 return NS_ERROR_FAILURE;
376 }
377
378 // Create a new "slot" for the data
379 mDataArray.AppendElement(DataStruct(aDataFlavor));
380 return NS_OK;
381 }
382
383 //
384 // RemoveDataFlavor
385 //
386 // Removes a data flavor (and causes the data to be destroyed). Error if
387 // the requested flavor is not present.
388 //
389 NS_IMETHODIMP
RemoveDataFlavor(const char * aDataFlavor)390 nsTransferable::RemoveDataFlavor(const char* aDataFlavor) {
391 MOZ_ASSERT(mInitialized);
392
393 if (Maybe<size_t> index = FindDataFlavor(aDataFlavor)) {
394 mDataArray.RemoveElementAt(index.value());
395 return NS_OK;
396 }
397
398 return NS_ERROR_FAILURE;
399 }
400
401 NS_IMETHODIMP
SetConverter(nsIFormatConverter * aConverter)402 nsTransferable::SetConverter(nsIFormatConverter* aConverter) {
403 MOZ_ASSERT(mInitialized);
404
405 mFormatConv = aConverter;
406 return NS_OK;
407 }
408
409 NS_IMETHODIMP
GetConverter(nsIFormatConverter ** aConverter)410 nsTransferable::GetConverter(nsIFormatConverter** aConverter) {
411 MOZ_ASSERT(mInitialized);
412
413 nsCOMPtr<nsIFormatConverter> converter = mFormatConv;
414 converter.forget(aConverter);
415 return NS_OK;
416 }
417
418 //
419 // FlavorsTransferableCanImport
420 //
421 // Computes a list of flavors that the transferable can accept into it, either
422 // through intrinsic knowledge or input data converters.
423 //
424 NS_IMETHODIMP
FlavorsTransferableCanImport(nsTArray<nsCString> & aFlavors)425 nsTransferable::FlavorsTransferableCanImport(nsTArray<nsCString>& aFlavors) {
426 MOZ_ASSERT(mInitialized);
427
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);
432
433 if (mFormatConv) {
434 nsTArray<nsCString> convertedList;
435 mFormatConv->GetInputDataFlavors(convertedList);
436
437 for (uint32_t i = 0; i < convertedList.Length(); ++i) {
438 nsCString& flavorStr = convertedList[i];
439
440 // Don't append if already in intrinsic list
441 if (!aFlavors.Contains(flavorStr)) {
442 aFlavors.AppendElement(flavorStr);
443 }
444 }
445 }
446
447 return NS_OK;
448 }
449
450 //
451 // FlavorsTransferableCanExport
452 //
453 // Computes a list of flavors that the transferable can export, either through
454 // intrinsic knowledge or output data converters.
455 //
456 NS_IMETHODIMP
FlavorsTransferableCanExport(nsTArray<nsCString> & aFlavors)457 nsTransferable::FlavorsTransferableCanExport(nsTArray<nsCString>& aFlavors) {
458 MOZ_ASSERT(mInitialized);
459
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);
464
465 if (mFormatConv) {
466 nsTArray<nsCString> convertedList;
467 mFormatConv->GetOutputDataFlavors(convertedList);
468
469 for (uint32_t i = 0; i < convertedList.Length(); ++i) {
470 nsCString& flavorStr = convertedList[i];
471
472 // Don't append if already in intrinsic list
473 if (!aFlavors.Contains(flavorStr)) {
474 aFlavors.AppendElement(flavorStr);
475 }
476 }
477 }
478
479 return NS_OK;
480 }
481
GetIsPrivateData()482 bool nsTransferable::GetIsPrivateData() {
483 MOZ_ASSERT(mInitialized);
484
485 return mPrivateData;
486 }
487
SetIsPrivateData(bool aIsPrivateData)488 void nsTransferable::SetIsPrivateData(bool aIsPrivateData) {
489 MOZ_ASSERT(mInitialized);
490
491 mPrivateData = aIsPrivateData;
492 }
493
GetRequestingPrincipal()494 nsIPrincipal* nsTransferable::GetRequestingPrincipal() {
495 MOZ_ASSERT(mInitialized);
496
497 return mRequestingPrincipal;
498 }
499
SetRequestingPrincipal(nsIPrincipal * aRequestingPrincipal)500 void nsTransferable::SetRequestingPrincipal(
501 nsIPrincipal* aRequestingPrincipal) {
502 MOZ_ASSERT(mInitialized);
503
504 mRequestingPrincipal = aRequestingPrincipal;
505 }
506
GetContentPolicyType()507 nsContentPolicyType nsTransferable::GetContentPolicyType() {
508 MOZ_ASSERT(mInitialized);
509
510 return mContentPolicyType;
511 }
512
SetContentPolicyType(nsContentPolicyType aContentPolicyType)513 void nsTransferable::SetContentPolicyType(
514 nsContentPolicyType aContentPolicyType) {
515 MOZ_ASSERT(mInitialized);
516
517 mContentPolicyType = aContentPolicyType;
518 }
519
GetCookieJarSettings()520 nsICookieJarSettings* nsTransferable::GetCookieJarSettings() {
521 MOZ_ASSERT(mInitialized);
522
523 return mCookieJarSettings;
524 }
525
SetCookieJarSettings(nsICookieJarSettings * aCookieJarSettings)526 void nsTransferable::SetCookieJarSettings(
527 nsICookieJarSettings* aCookieJarSettings) {
528 MOZ_ASSERT(mInitialized);
529
530 mCookieJarSettings = aCookieJarSettings;
531 }
532