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