1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/BasicEvents.h"
9 
10 #include "DataTransfer.h"
11 
12 #include "nsIDOMDocument.h"
13 #include "nsISupportsPrimitives.h"
14 #include "nsIScriptSecurityManager.h"
15 #include "mozilla/dom/DOMStringList.h"
16 #include "nsArray.h"
17 #include "nsError.h"
18 #include "nsIDragService.h"
19 #include "nsIClipboard.h"
20 #include "nsContentUtils.h"
21 #include "nsIContent.h"
22 #include "nsIObjectInputStream.h"
23 #include "nsIObjectOutputStream.h"
24 #include "nsIStorageStream.h"
25 #include "nsStringStream.h"
26 #include "nsCRT.h"
27 #include "nsIScriptObjectPrincipal.h"
28 #include "nsIScriptContext.h"
29 #include "nsIDocument.h"
30 #include "nsIScriptGlobalObject.h"
31 #include "nsVariant.h"
32 #include "mozilla/dom/ContentChild.h"
33 #include "mozilla/dom/DataTransferBinding.h"
34 #include "mozilla/dom/DataTransferItemList.h"
35 #include "mozilla/dom/Directory.h"
36 #include "mozilla/dom/Element.h"
37 #include "mozilla/dom/FileList.h"
38 #include "mozilla/dom/BindingUtils.h"
39 #include "mozilla/dom/OSFileSystem.h"
40 #include "mozilla/dom/Promise.h"
41 #include "nsNetUtil.h"
42 #include "nsReadableUtils.h"
43 
44 namespace mozilla {
45 namespace dom {
46 
47 NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
48 
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
50   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
51   NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
52   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
53   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
54   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
55 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
57   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
58   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
59   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
60   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
62 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer)
63 
64 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
65 NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
66 
67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
68   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
69   NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
70   NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
71   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
72 NS_INTERFACE_MAP_END
73 
74 // the size of the array
75 const char DataTransfer::sEffects[8][9] = {
76     "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"};
77 
78 // Used for custom clipboard types.
79 enum CustomClipboardTypeId {
80   eCustomClipboardTypeId_None,
81   eCustomClipboardTypeId_String
82 };
83 
84 // The dom.events.dataTransfer.protected.enabled preference controls whether or
85 // not the `protected` dataTransfer state is enabled. If the `protected`
86 // dataTransfer stae is disabled, then the DataTransfer will be read-only
87 // whenever it should be protected, and will not be disconnected after a drag
88 // event is completed.
PrefProtected()89 static bool PrefProtected() {
90   static bool sInitialized = false;
91   static bool sValue = false;
92   if (!sInitialized) {
93     sInitialized = true;
94     Preferences::AddBoolVarCache(&sValue,
95                                  "dom.events.dataTransfer.protected.enabled");
96   }
97   return sValue;
98 }
99 
ModeForEvent(EventMessage aEventMessage)100 static DataTransfer::Mode ModeForEvent(EventMessage aEventMessage) {
101   switch (aEventMessage) {
102     case eCut:
103     case eCopy:
104     case eDragStart:
105       // For these events, we want to be able to add data to the data transfer,
106       // Otherwise, the data is already present.
107       return DataTransfer::Mode::ReadWrite;
108     case eDrop:
109     case ePaste:
110     case ePasteNoFormatting:
111       // For these events we want to be able to read the data which is stored in
112       // the DataTransfer, rather than just the type information.
113       return DataTransfer::Mode::ReadOnly;
114     default:
115       return PrefProtected() ? DataTransfer::Mode::Protected
116                              : DataTransfer::Mode::ReadOnly;
117   }
118 }
119 
DataTransfer(nsISupports * aParent,EventMessage aEventMessage,bool aIsExternal,int32_t aClipboardType)120 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
121                            bool aIsExternal, int32_t aClipboardType)
122     : mParent(aParent),
123       mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
124       mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
125       mEventMessage(aEventMessage),
126       mCursorState(false),
127       mMode(ModeForEvent(aEventMessage)),
128       mIsExternal(aIsExternal),
129       mUserCancelled(false),
130       mIsCrossDomainSubFrameDrop(false),
131       mClipboardType(aClipboardType),
132       mDragImageX(0),
133       mDragImageY(0) {
134   mItems = new DataTransferItemList(this, aIsExternal);
135 
136   // For external usage, cache the data from the native clipboard or drag.
137   if (mIsExternal && mMode != Mode::ReadWrite) {
138     if (aEventMessage == ePasteNoFormatting) {
139       mEventMessage = ePaste;
140       CacheExternalClipboardFormats(true);
141     } else if (aEventMessage == ePaste) {
142       CacheExternalClipboardFormats(false);
143     } else if (aEventMessage >= eDragDropEventFirst &&
144                aEventMessage <= eDragDropEventLast) {
145       CacheExternalDragFormats();
146     }
147   }
148 }
149 
DataTransfer(nsISupports * aParent,EventMessage aEventMessage,const uint32_t aEffectAllowed,bool aCursorState,bool aIsExternal,bool aUserCancelled,bool aIsCrossDomainSubFrameDrop,int32_t aClipboardType,DataTransferItemList * aItems,Element * aDragImage,uint32_t aDragImageX,uint32_t aDragImageY)150 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
151                            const uint32_t aEffectAllowed, bool aCursorState,
152                            bool aIsExternal, bool aUserCancelled,
153                            bool aIsCrossDomainSubFrameDrop,
154                            int32_t aClipboardType, DataTransferItemList* aItems,
155                            Element* aDragImage, uint32_t aDragImageX,
156                            uint32_t aDragImageY)
157     : mParent(aParent),
158       mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
159       mEffectAllowed(aEffectAllowed),
160       mEventMessage(aEventMessage),
161       mCursorState(aCursorState),
162       mMode(ModeForEvent(aEventMessage)),
163       mIsExternal(aIsExternal),
164       mUserCancelled(aUserCancelled),
165       mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop),
166       mClipboardType(aClipboardType),
167       mDragImage(aDragImage),
168       mDragImageX(aDragImageX),
169       mDragImageY(aDragImageY) {
170   MOZ_ASSERT(mParent);
171   MOZ_ASSERT(aItems);
172 
173   // We clone the items array after everything else, so that it has a valid
174   // mParent value
175   mItems = aItems->Clone(this);
176   // The items are copied from aItems into mItems. There is no need to copy
177   // the actual data in the items as the data transfer will be read only. The
178   // dragstart event is the only time when items are
179   // modifiable, but those events should have been using the first constructor
180   // above.
181   NS_ASSERTION(aEventMessage != eDragStart,
182                "invalid event type for DataTransfer constructor");
183 }
184 
~DataTransfer()185 DataTransfer::~DataTransfer() {}
186 
187 // static
Constructor(const GlobalObject & aGlobal,const nsAString & aEventType,bool aIsExternal,ErrorResult & aRv)188 already_AddRefed<DataTransfer> DataTransfer::Constructor(
189     const GlobalObject& aGlobal, const nsAString& aEventType, bool aIsExternal,
190     ErrorResult& aRv) {
191   nsAutoCString onEventType("on");
192   AppendUTF16toUTF8(aEventType, onEventType);
193   RefPtr<nsAtom> eventTypeAtom = NS_Atomize(onEventType);
194   if (!eventTypeAtom) {
195     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
196     return nullptr;
197   }
198 
199   EventMessage eventMessage = nsContentUtils::GetEventMessage(eventTypeAtom);
200   RefPtr<DataTransfer> transfer =
201       new DataTransfer(aGlobal.GetAsSupports(), eventMessage, aIsExternal, -1);
202   return transfer.forget();
203 }
204 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)205 JSObject* DataTransfer::WrapObject(JSContext* aCx,
206                                    JS::Handle<JSObject*> aGivenProto) {
207   return DataTransferBinding::Wrap(aCx, this, aGivenProto);
208 }
209 
210 NS_IMETHODIMP
GetDropEffect(nsAString & aDropEffect)211 DataTransfer::GetDropEffect(nsAString& aDropEffect) {
212   nsString dropEffect;
213   GetDropEffect(dropEffect);
214   aDropEffect = dropEffect;
215   return NS_OK;
216 }
217 
218 NS_IMETHODIMP
SetDropEffect(const nsAString & aDropEffect)219 DataTransfer::SetDropEffect(const nsAString& aDropEffect) {
220   // the drop effect can only be 'none', 'copy', 'move' or 'link'.
221   for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
222     if (aDropEffect.EqualsASCII(sEffects[e])) {
223       // don't allow copyMove
224       if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
225                 nsIDragService::DRAGDROP_ACTION_MOVE)) {
226         mDropEffect = e;
227       }
228       break;
229     }
230   }
231 
232   return NS_OK;
233 }
234 
235 NS_IMETHODIMP
GetEffectAllowed(nsAString & aEffectAllowed)236 DataTransfer::GetEffectAllowed(nsAString& aEffectAllowed) {
237   nsString effectAllowed;
238   GetEffectAllowed(effectAllowed);
239   aEffectAllowed = effectAllowed;
240   return NS_OK;
241 }
242 
243 NS_IMETHODIMP
SetEffectAllowed(const nsAString & aEffectAllowed)244 DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed) {
245   if (aEffectAllowed.EqualsLiteral("uninitialized")) {
246     mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
247     return NS_OK;
248   }
249 
250   static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0,
251                 "DRAGDROP_ACTION_NONE constant is wrong");
252   static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1,
253                 "DRAGDROP_ACTION_COPY constant is wrong");
254   static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2,
255                 "DRAGDROP_ACTION_MOVE constant is wrong");
256   static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4,
257                 "DRAGDROP_ACTION_LINK constant is wrong");
258 
259   for (uint32_t e = 0; e < ArrayLength(sEffects); e++) {
260     if (aEffectAllowed.EqualsASCII(sEffects[e])) {
261       mEffectAllowed = e;
262       break;
263     }
264   }
265 
266   return NS_OK;
267 }
268 
269 NS_IMETHODIMP
GetDropEffectInt(uint32_t * aDropEffect)270 DataTransfer::GetDropEffectInt(uint32_t* aDropEffect) {
271   *aDropEffect = mDropEffect;
272   return NS_OK;
273 }
274 
275 NS_IMETHODIMP
SetDropEffectInt(uint32_t aDropEffect)276 DataTransfer::SetDropEffectInt(uint32_t aDropEffect) {
277   mDropEffect = aDropEffect;
278   return NS_OK;
279 }
280 
281 NS_IMETHODIMP
GetEffectAllowedInt(uint32_t * aEffectAllowed)282 DataTransfer::GetEffectAllowedInt(uint32_t* aEffectAllowed) {
283   *aEffectAllowed = mEffectAllowed;
284   return NS_OK;
285 }
286 
GetMozTriggeringPrincipalURISpec(nsAString & aPrincipalURISpec)287 void DataTransfer::GetMozTriggeringPrincipalURISpec(
288     nsAString& aPrincipalURISpec) {
289   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
290   if (!dragSession) {
291     aPrincipalURISpec.Truncate(0);
292     return;
293   }
294 
295   nsCString principalURISpec;
296   dragSession->GetTriggeringPrincipalURISpec(principalURISpec);
297   CopyUTF8toUTF16(principalURISpec, aPrincipalURISpec);
298 }
299 
300 NS_IMETHODIMP
SetEffectAllowedInt(uint32_t aEffectAllowed)301 DataTransfer::SetEffectAllowedInt(uint32_t aEffectAllowed) {
302   mEffectAllowed = aEffectAllowed;
303   return NS_OK;
304 }
305 
306 NS_IMETHODIMP
GetMozUserCancelled(bool * aUserCancelled)307 DataTransfer::GetMozUserCancelled(bool* aUserCancelled) {
308   *aUserCancelled = MozUserCancelled();
309   return NS_OK;
310 }
311 
GetFiles(nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)312 already_AddRefed<FileList> DataTransfer::GetFiles(
313     nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
314   return mItems->Files(&aSubjectPrincipal);
315 }
316 
317 NS_IMETHODIMP
GetFiles(nsIDOMFileList ** aFileList)318 DataTransfer::GetFiles(nsIDOMFileList** aFileList) {
319   if (!aFileList) {
320     return NS_ERROR_FAILURE;
321   }
322 
323   // The XPCOM interface is only avaliable to system code, and thus we can
324   // assume the system principal. This is consistent with the previous behavour
325   // of this function, which also assumed the system principal.
326   //
327   // This code is also called from C++ code, which expects it to have a System
328   // Principal, and thus the SubjectPrincipal cannot be used.
329   RefPtr<FileList> files = mItems->Files(nsContentUtils::GetSystemPrincipal());
330 
331   files.forget(aFileList);
332   return NS_OK;
333 }
334 
GetTypes(nsTArray<nsString> & aTypes,CallerType aCallerType) const335 void DataTransfer::GetTypes(nsTArray<nsString>& aTypes,
336                             CallerType aCallerType) const {
337   // When called from bindings, aTypes will be empty, but since we might have
338   // Gecko-internal callers too, clear it to be safe.
339   aTypes.Clear();
340 
341   const nsTArray<RefPtr<DataTransferItem>>* items = mItems->MozItemsAt(0);
342   if (NS_WARN_IF(!items)) {
343     return;
344   }
345 
346   for (uint32_t i = 0; i < items->Length(); i++) {
347     DataTransferItem* item = items->ElementAt(i);
348     MOZ_ASSERT(item);
349 
350     if (item->ChromeOnly() && aCallerType != CallerType::System) {
351       continue;
352     }
353 
354     // NOTE: The reason why we get the internal type here is because we want
355     // kFileMime to appear in the types list for backwards compatibility
356     // reasons.
357     nsAutoString type;
358     item->GetInternalType(type);
359     if (item->Kind() != DataTransferItem::KIND_FILE ||
360         type.EqualsASCII(kFileMime)) {
361       // If the entry has kind KIND_STRING or KIND_OTHER we want to add it to
362       // the list.
363       aTypes.AppendElement(type);
364     }
365   }
366 
367   for (uint32_t i = 0; i < mItems->Length(); ++i) {
368     bool found = false;
369     DataTransferItem* item = mItems->IndexedGetter(i, found);
370     MOZ_ASSERT(found);
371     if (item->Kind() != DataTransferItem::KIND_FILE) {
372       continue;
373     }
374     aTypes.AppendElement(NS_LITERAL_STRING("Files"));
375     break;
376   }
377 }
378 
GetData(const nsAString & aFormat,nsAString & aData,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)379 void DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
380                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
381   // return an empty string if data for the format was not found
382   aData.Truncate();
383 
384   nsCOMPtr<nsIVariant> data;
385   nsresult rv =
386       GetDataAtInternal(aFormat, 0, &aSubjectPrincipal, getter_AddRefs(data));
387   if (NS_FAILED(rv)) {
388     if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
389       aRv.Throw(rv);
390     }
391     return;
392   }
393 
394   if (data) {
395     nsAutoString stringdata;
396     data->GetAsAString(stringdata);
397 
398     // for the URL type, parse out the first URI from the list. The URIs are
399     // separated by newlines
400     nsAutoString lowercaseFormat;
401     nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
402 
403     if (lowercaseFormat.EqualsLiteral("url")) {
404       int32_t lastidx = 0, idx;
405       int32_t length = stringdata.Length();
406       while (lastidx < length) {
407         idx = stringdata.FindChar('\n', lastidx);
408         // lines beginning with # are comments
409         if (stringdata[lastidx] == '#') {
410           if (idx == -1) {
411             break;
412           }
413         } else {
414           if (idx == -1) {
415             aData.Assign(Substring(stringdata, lastidx));
416           } else {
417             aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
418           }
419           aData =
420               nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
421           return;
422         }
423         lastidx = idx + 1;
424       }
425     } else {
426       aData = stringdata;
427     }
428   }
429 }
430 
SetData(const nsAString & aFormat,const nsAString & aData,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)431 void DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
432                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
433   RefPtr<nsVariantCC> variant = new nsVariantCC();
434   variant->SetAsAString(aData);
435 
436   aRv = SetDataAtInternal(aFormat, variant, 0, &aSubjectPrincipal);
437 }
438 
ClearData(const Optional<nsAString> & aFormat,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)439 void DataTransfer::ClearData(const Optional<nsAString>& aFormat,
440                              nsIPrincipal& aSubjectPrincipal,
441                              ErrorResult& aRv) {
442   if (IsReadOnly()) {
443     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
444     return;
445   }
446 
447   if (MozItemCount() == 0) {
448     return;
449   }
450 
451   if (aFormat.WasPassed()) {
452     MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv);
453   } else {
454     MozClearDataAtHelper(EmptyString(), 0, aSubjectPrincipal, aRv);
455   }
456 }
457 
458 NS_IMETHODIMP
GetMozItemCount(uint32_t * aCount)459 DataTransfer::GetMozItemCount(uint32_t* aCount) {
460   *aCount = MozItemCount();
461   return NS_OK;
462 }
463 
464 NS_IMETHODIMP
GetMozCursor(nsAString & aCursorState)465 DataTransfer::GetMozCursor(nsAString& aCursorState) {
466   nsString cursor;
467   GetMozCursor(cursor);
468   aCursorState = cursor;
469   return NS_OK;
470 }
471 
472 NS_IMETHODIMP
SetMozCursor(const nsAString & aCursorState)473 DataTransfer::SetMozCursor(const nsAString& aCursorState) {
474   // Lock the cursor to an arrow during the drag.
475   mCursorState = aCursorState.EqualsLiteral("default");
476 
477   return NS_OK;
478 }
479 
GetMozSourceNode()480 already_AddRefed<nsINode> DataTransfer::GetMozSourceNode() {
481   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
482   if (!dragSession) {
483     return nullptr;
484   }
485 
486   nsCOMPtr<nsIDOMNode> sourceNode;
487   dragSession->GetSourceNode(getter_AddRefs(sourceNode));
488   nsCOMPtr<nsINode> node = do_QueryInterface(sourceNode);
489   if (node && !nsContentUtils::LegacyIsCallerNativeCode() &&
490       !nsContentUtils::CanCallerAccess(node)) {
491     return nullptr;
492   }
493 
494   return node.forget();
495 }
496 
497 NS_IMETHODIMP
GetMozSourceNode(nsIDOMNode ** aSourceNode)498 DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode) {
499   nsCOMPtr<nsINode> sourceNode = GetMozSourceNode();
500   if (!sourceNode) {
501     *aSourceNode = nullptr;
502     return NS_OK;
503   }
504 
505   return CallQueryInterface(sourceNode, aSourceNode);
506 }
507 
MozTypesAt(uint32_t aIndex,CallerType aCallerType,ErrorResult & aRv) const508 already_AddRefed<DOMStringList> DataTransfer::MozTypesAt(
509     uint32_t aIndex, CallerType aCallerType, ErrorResult& aRv) const {
510   // Only the first item is valid for clipboard events
511   if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
512                      mEventMessage == ePaste)) {
513     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
514     return nullptr;
515   }
516 
517   RefPtr<DOMStringList> types = new DOMStringList();
518   if (aIndex < MozItemCount()) {
519     // note that you can retrieve the types regardless of their principal
520     const nsTArray<RefPtr<DataTransferItem>>& items =
521         *mItems->MozItemsAt(aIndex);
522 
523     bool addFile = false;
524     for (uint32_t i = 0; i < items.Length(); i++) {
525       if (items[i]->ChromeOnly() && aCallerType != CallerType::System) {
526         continue;
527       }
528 
529       // NOTE: The reason why we get the internal type here is because we want
530       // kFileMime to appear in the types list for backwards compatibility
531       // reasons.
532       nsAutoString type;
533       items[i]->GetInternalType(type);
534       if (NS_WARN_IF(!types->Add(type))) {
535         aRv.Throw(NS_ERROR_FAILURE);
536         return nullptr;
537       }
538 
539       if (items[i]->Kind() == DataTransferItem::KIND_FILE) {
540         addFile = true;
541       }
542     }
543 
544     if (addFile) {
545       types->Add(NS_LITERAL_STRING("Files"));
546     }
547   }
548 
549   return types.forget();
550 }
551 
GetDataAtNoSecurityCheck(const nsAString & aFormat,uint32_t aIndex,nsIVariant ** aData)552 nsresult DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat,
553                                                 uint32_t aIndex,
554                                                 nsIVariant** aData) {
555   return GetDataAtInternal(aFormat, aIndex,
556                            nsContentUtils::GetSystemPrincipal(), aData);
557 }
558 
GetDataAtInternal(const nsAString & aFormat,uint32_t aIndex,nsIPrincipal * aSubjectPrincipal,nsIVariant ** aData)559 nsresult DataTransfer::GetDataAtInternal(const nsAString& aFormat,
560                                          uint32_t aIndex,
561                                          nsIPrincipal* aSubjectPrincipal,
562                                          nsIVariant** aData) {
563   *aData = nullptr;
564 
565   if (aFormat.IsEmpty()) {
566     return NS_OK;
567   }
568 
569   if (aIndex >= MozItemCount()) {
570     return NS_ERROR_DOM_INDEX_SIZE_ERR;
571   }
572 
573   // Only the first item is valid for clipboard events
574   if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
575                      mEventMessage == ePaste)) {
576     return NS_ERROR_DOM_INDEX_SIZE_ERR;
577   }
578 
579   nsAutoString format;
580   GetRealFormat(aFormat, format);
581 
582   MOZ_ASSERT(aSubjectPrincipal);
583 
584   RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex);
585   if (!item) {
586     // The index exists but there's no data for the specified format, in this
587     // case we just return undefined
588     return NS_OK;
589   }
590 
591   // If we have chrome only content, and we aren't chrome, don't allow access
592   if (!nsContentUtils::IsSystemPrincipal(aSubjectPrincipal) &&
593       item->ChromeOnly()) {
594     return NS_OK;
595   }
596 
597   // DataTransferItem::Data() handles the principal checks
598   ErrorResult result;
599   nsCOMPtr<nsIVariant> data = item->Data(aSubjectPrincipal, result);
600   if (NS_WARN_IF(!data || result.Failed())) {
601     return result.StealNSResult();
602   }
603 
604   data.forget(aData);
605   return NS_OK;
606 }
607 
MozGetDataAt(JSContext * aCx,const nsAString & aFormat,uint32_t aIndex,JS::MutableHandle<JS::Value> aRetval,nsIPrincipal & aSubjectPrincipal,mozilla::ErrorResult & aRv)608 void DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
609                                 uint32_t aIndex,
610                                 JS::MutableHandle<JS::Value> aRetval,
611                                 nsIPrincipal& aSubjectPrincipal,
612                                 mozilla::ErrorResult& aRv) {
613   nsCOMPtr<nsIVariant> data;
614   aRv = GetDataAtInternal(aFormat, aIndex, &aSubjectPrincipal,
615                           getter_AddRefs(data));
616   if (aRv.Failed()) {
617     return;
618   }
619 
620   if (!data) {
621     aRetval.setNull();
622     return;
623   }
624 
625   JS::Rooted<JS::Value> result(aCx);
626   if (!VariantToJsval(aCx, data, aRetval)) {
627     aRv = NS_ERROR_FAILURE;
628     return;
629   }
630 }
631 
PrincipalMaySetData(const nsAString & aType,nsIVariant * aData,nsIPrincipal * aPrincipal)632 /* static */ bool DataTransfer::PrincipalMaySetData(const nsAString& aType,
633                                                     nsIVariant* aData,
634                                                     nsIPrincipal* aPrincipal) {
635   if (!nsContentUtils::IsSystemPrincipal(aPrincipal)) {
636     DataTransferItem::eKind kind = DataTransferItem::KindFromData(aData);
637     if (kind == DataTransferItem::KIND_OTHER) {
638       NS_WARNING("Disallowing adding non string/file types to DataTransfer");
639       return false;
640     }
641 
642     if (aType.EqualsASCII(kFileMime) || aType.EqualsASCII(kFilePromiseMime)) {
643       NS_WARNING(
644           "Disallowing adding x-moz-file or x-moz-file-promize types to "
645           "DataTransfer");
646       return false;
647     }
648 
649     // Disallow content from creating x-moz-place flavors, so that it cannot
650     // create fake Places smart queries exposing user data, but give a free
651     // pass to WebExtensions.
652     auto principal = BasePrincipal::Cast(aPrincipal);
653     if (!principal->AddonPolicy() &&
654         StringBeginsWith(aType, NS_LITERAL_STRING("text/x-moz-place"))) {
655       NS_WARNING("Disallowing adding moz-place types to DataTransfer");
656       return false;
657     }
658   }
659 
660   return true;
661 }
662 
TypesListMayHaveChanged()663 void DataTransfer::TypesListMayHaveChanged() {
664   DataTransferBinding::ClearCachedTypesValue(this);
665 }
666 
MozCloneForEvent(const nsAString & aEvent,ErrorResult & aRv)667 already_AddRefed<DataTransfer> DataTransfer::MozCloneForEvent(
668     const nsAString& aEvent, ErrorResult& aRv) {
669   RefPtr<nsAtom> atomEvt = NS_Atomize(aEvent);
670   if (!atomEvt) {
671     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
672     return nullptr;
673   }
674   EventMessage eventMessage = nsContentUtils::GetEventMessage(atomEvt);
675 
676   RefPtr<DataTransfer> dt;
677   nsresult rv = Clone(mParent, eventMessage, false, false, getter_AddRefs(dt));
678   if (NS_FAILED(rv)) {
679     aRv.Throw(rv);
680     return nullptr;
681   }
682   return dt.forget();
683 }
684 
SetDataAtInternal(const nsAString & aFormat,nsIVariant * aData,uint32_t aIndex,nsIPrincipal * aSubjectPrincipal)685 nsresult DataTransfer::SetDataAtInternal(const nsAString& aFormat,
686                                          nsIVariant* aData, uint32_t aIndex,
687                                          nsIPrincipal* aSubjectPrincipal) {
688   if (aFormat.IsEmpty()) {
689     return NS_OK;
690   }
691 
692   if (IsReadOnly()) {
693     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
694   }
695 
696   // Specifying an index less than the current length will replace an existing
697   // item. Specifying an index equal to the current length will add a new item.
698   if (aIndex > MozItemCount()) {
699     return NS_ERROR_DOM_INDEX_SIZE_ERR;
700   }
701 
702   // Only the first item is valid for clipboard events
703   if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
704                      mEventMessage == ePaste)) {
705     return NS_ERROR_DOM_INDEX_SIZE_ERR;
706   }
707 
708   // Don't allow the custom type to be assigned.
709   if (aFormat.EqualsLiteral(kCustomTypesMime)) {
710     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
711   }
712 
713   if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) {
714     return NS_ERROR_DOM_SECURITY_ERR;
715   }
716 
717   return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
718 }
719 
MozSetDataAt(JSContext * aCx,const nsAString & aFormat,JS::Handle<JS::Value> aData,uint32_t aIndex,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)720 void DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
721                                 JS::Handle<JS::Value> aData, uint32_t aIndex,
722                                 nsIPrincipal& aSubjectPrincipal,
723                                 ErrorResult& aRv) {
724   nsCOMPtr<nsIVariant> data;
725   aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
726                                                     getter_AddRefs(data));
727   if (!aRv.Failed()) {
728     aRv = SetDataAtInternal(aFormat, data, aIndex, &aSubjectPrincipal);
729   }
730 }
731 
MozClearDataAt(const nsAString & aFormat,uint32_t aIndex,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)732 void DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
733                                   nsIPrincipal& aSubjectPrincipal,
734                                   ErrorResult& aRv) {
735   if (IsReadOnly()) {
736     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
737     return;
738   }
739 
740   if (aIndex >= MozItemCount()) {
741     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
742     return;
743   }
744 
745   // Only the first item is valid for clipboard events
746   if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
747                      mEventMessage == ePaste)) {
748     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
749     return;
750   }
751 
752   MozClearDataAtHelper(aFormat, aIndex, aSubjectPrincipal, aRv);
753 
754   // If we just cleared the 0-th index, and there are still more than 1 indexes
755   // remaining, MozClearDataAt should cause the 1st index to become the 0th
756   // index. This should _only_ happen when the MozClearDataAt function is
757   // explicitly called by script, as this behavior is inconsistent with spec.
758   // (however, so is the MozClearDataAt API)
759 
760   if (aIndex == 0 && mItems->MozItemCount() > 1 &&
761       mItems->MozItemsAt(0)->Length() == 0) {
762     mItems->PopIndexZero();
763   }
764 }
765 
MozClearDataAtHelper(const nsAString & aFormat,uint32_t aIndex,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)766 void DataTransfer::MozClearDataAtHelper(const nsAString& aFormat,
767                                         uint32_t aIndex,
768                                         nsIPrincipal& aSubjectPrincipal,
769                                         ErrorResult& aRv) {
770   MOZ_ASSERT(!IsReadOnly());
771   MOZ_ASSERT(aIndex < MozItemCount());
772   MOZ_ASSERT(aIndex == 0 || (mEventMessage != eCut && mEventMessage != eCopy &&
773                              mEventMessage != ePaste));
774 
775   nsAutoString format;
776   GetRealFormat(aFormat, format);
777 
778   mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
779 }
780 
SetDragImage(Element & aImage,int32_t aX,int32_t aY)781 void DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY) {
782   if (!IsReadOnly()) {
783     mDragImage = &aImage;
784     mDragImageX = aX;
785     mDragImageY = aY;
786   }
787 }
788 
789 NS_IMETHODIMP
SetDragImage(nsIDOMElement * aImage,int32_t aX,int32_t aY)790 DataTransfer::SetDragImage(nsIDOMElement* aImage, int32_t aX, int32_t aY) {
791   nsCOMPtr<Element> image = do_QueryInterface(aImage);
792   if (image) {
793     SetDragImage(*image, aX, aY);
794   }
795   return NS_OK;
796 }
797 
UpdateDragImage(Element & aImage,int32_t aX,int32_t aY)798 void DataTransfer::UpdateDragImage(Element& aImage, int32_t aX, int32_t aY) {
799   if (mEventMessage < eDragDropEventFirst ||
800       mEventMessage > eDragDropEventLast) {
801     return;
802   }
803 
804   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
805   if (dragSession) {
806     dragSession->UpdateDragImage(aImage.AsDOMNode(), aX, aY);
807   }
808 }
809 
GetFilesAndDirectories(nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)810 already_AddRefed<Promise> DataTransfer::GetFilesAndDirectories(
811     nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
812   nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
813   if (!parentNode) {
814     aRv.Throw(NS_ERROR_FAILURE);
815     return nullptr;
816   }
817 
818   nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
819   MOZ_ASSERT(global);
820   if (!global) {
821     aRv.Throw(NS_ERROR_FAILURE);
822     return nullptr;
823   }
824 
825   RefPtr<Promise> p = Promise::Create(global, aRv);
826   if (NS_WARN_IF(aRv.Failed())) {
827     return nullptr;
828   }
829 
830   RefPtr<FileList> files = mItems->Files(&aSubjectPrincipal);
831   if (NS_WARN_IF(!files)) {
832     return nullptr;
833   }
834 
835   Sequence<RefPtr<File>> filesSeq;
836   files->ToSequence(filesSeq, aRv);
837   if (NS_WARN_IF(aRv.Failed())) {
838     return nullptr;
839   }
840 
841   p->MaybeResolve(filesSeq);
842 
843   return p.forget();
844 }
845 
GetFiles(bool aRecursiveFlag,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)846 already_AddRefed<Promise> DataTransfer::GetFiles(
847     bool aRecursiveFlag, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
848   // Currently we don't support directories.
849   return GetFilesAndDirectories(aSubjectPrincipal, aRv);
850 }
851 
AddElement(Element & aElement,ErrorResult & aRv)852 void DataTransfer::AddElement(Element& aElement, ErrorResult& aRv) {
853   if (IsReadOnly()) {
854     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
855     return;
856   }
857 
858   mDragTarget = &aElement;
859 }
860 
861 NS_IMETHODIMP
AddElement(nsIDOMElement * aElement)862 DataTransfer::AddElement(nsIDOMElement* aElement) {
863   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
864 
865   nsCOMPtr<Element> element = do_QueryInterface(aElement);
866   NS_ENSURE_TRUE(element, NS_ERROR_INVALID_ARG);
867 
868   ErrorResult rv;
869   AddElement(*element, rv);
870   return rv.StealNSResult();
871 }
872 
Clone(nsISupports * aParent,EventMessage aEventMessage,bool aUserCancelled,bool aIsCrossDomainSubFrameDrop,DataTransfer ** aNewDataTransfer)873 nsresult DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
874                              bool aUserCancelled,
875                              bool aIsCrossDomainSubFrameDrop,
876                              DataTransfer** aNewDataTransfer) {
877   RefPtr<DataTransfer> newDataTransfer = new DataTransfer(
878       aParent, aEventMessage, mEffectAllowed, mCursorState, mIsExternal,
879       aUserCancelled, aIsCrossDomainSubFrameDrop, mClipboardType, mItems,
880       mDragImage, mDragImageX, mDragImageY);
881 
882   newDataTransfer.forget(aNewDataTransfer);
883   return NS_OK;
884 }
885 
GetTransferables(nsIDOMNode * aDragTarget)886 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
887     nsIDOMNode* aDragTarget) {
888   MOZ_ASSERT(aDragTarget);
889 
890   nsCOMPtr<nsINode> dragNode = do_QueryInterface(aDragTarget);
891   if (!dragNode) {
892     return nullptr;
893   }
894 
895   nsIDocument* doc = dragNode->GetComposedDoc();
896   if (!doc) {
897     return nullptr;
898   }
899 
900   return GetTransferables(doc->GetLoadContext());
901 }
902 
GetTransferables(nsILoadContext * aLoadContext)903 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
904     nsILoadContext* aLoadContext) {
905   nsCOMPtr<nsIMutableArray> transArray = nsArray::Create();
906   if (!transArray) {
907     return nullptr;
908   }
909 
910   uint32_t count = MozItemCount();
911   for (uint32_t i = 0; i < count; i++) {
912     nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
913     if (transferable) {
914       transArray->AppendElement(transferable);
915     }
916   }
917 
918   return transArray.forget();
919 }
920 
GetTransferable(uint32_t aIndex,nsILoadContext * aLoadContext)921 already_AddRefed<nsITransferable> DataTransfer::GetTransferable(
922     uint32_t aIndex, nsILoadContext* aLoadContext) {
923   if (aIndex >= MozItemCount()) {
924     return nullptr;
925   }
926 
927   const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
928   uint32_t count = item.Length();
929   if (!count) {
930     return nullptr;
931   }
932 
933   nsCOMPtr<nsITransferable> transferable =
934       do_CreateInstance("@mozilla.org/widget/transferable;1");
935   if (!transferable) {
936     return nullptr;
937   }
938   transferable->Init(aLoadContext);
939 
940   nsCOMPtr<nsIStorageStream> storageStream;
941   nsCOMPtr<nsIObjectOutputStream> stream;
942 
943   bool added = false;
944   bool handlingCustomFormats = true;
945 
946   // When writing the custom data, we need to ensure that there is sufficient
947   // space for a (uint32_t) data ending type, and the null byte character at
948   // the end of the nsCString. We claim that space upfront and store it in
949   // baseLength. This value will be set to zero if a write error occurs
950   // indicating that the data and length are no longer valid.
951   const uint32_t baseLength = sizeof(uint32_t) + 1;
952   uint32_t totalCustomLength = baseLength;
953 
954   const char* knownFormats[] = {kTextMime,
955                                 kHTMLMime,
956                                 kNativeHTMLMime,
957                                 kRTFMime,
958                                 kURLMime,
959                                 kURLDataMime,
960                                 kURLDescriptionMime,
961                                 kURLPrivateMime,
962                                 kPNGImageMime,
963                                 kJPEGImageMime,
964                                 kGIFImageMime,
965                                 kNativeImageMime,
966                                 kFileMime,
967                                 kFilePromiseMime,
968                                 kFilePromiseURLMime,
969                                 kFilePromiseDestFilename,
970                                 kFilePromiseDirectoryMime,
971                                 kMozTextInternal,
972                                 kHTMLContext,
973                                 kHTMLInfo,
974                                 kImageRequestMime};
975 
976   /*
977    * Two passes are made here to iterate over all of the types. First, look for
978    * any types that are not in the list of known types. For this pass,
979    * handlingCustomFormats will be true. Data that corresponds to unknown types
980    * will be pulled out and inserted into a single type (kCustomTypesMime) by
981    * writing the data into a stream.
982    *
983    * The second pass will iterate over the formats looking for known types.
984    * These are added as is. The unknown types are all then inserted as a single
985    * type (kCustomTypesMime) in the same position of the first custom type. This
986    * model is used to maintain the format order as best as possible.
987    *
988    * The format of the kCustomTypesMime type is one or more of the following
989    * stored sequentially:
990    *   <32-bit> type (only none or string is supported)
991    *   <32-bit> length of format
992    *   <wide string> format
993    *   <32-bit> length of data
994    *   <wide string> data
995    * A type of eCustomClipboardTypeId_None ends the list, without any following
996    * data.
997    */
998   do {
999     for (uint32_t f = 0; f < count; f++) {
1000       RefPtr<DataTransferItem> formatitem = item[f];
1001       nsCOMPtr<nsIVariant> variant = formatitem->DataNoSecurityCheck();
1002       if (!variant) {  // skip empty items
1003         continue;
1004       }
1005 
1006       nsAutoString type;
1007       formatitem->GetInternalType(type);
1008 
1009       // If the data is of one of the well-known formats, use it directly.
1010       bool isCustomFormat = true;
1011       for (uint32_t f = 0; f < ArrayLength(knownFormats); f++) {
1012         if (type.EqualsASCII(knownFormats[f])) {
1013           isCustomFormat = false;
1014           break;
1015         }
1016       }
1017 
1018       uint32_t lengthInBytes;
1019       nsCOMPtr<nsISupports> convertedData;
1020 
1021       if (handlingCustomFormats) {
1022         if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
1023                                 &lengthInBytes)) {
1024           continue;
1025         }
1026 
1027         // When handling custom types, add the data to the stream if this is a
1028         // custom type. If totalCustomLength is 0, then a write error occurred
1029         // on a previous item, so ignore any others.
1030         if (isCustomFormat && totalCustomLength > 0) {
1031           // If it isn't a string, just ignore it. The dataTransfer is cached in
1032           // the drag sesion during drag-and-drop, so non-strings will be
1033           // available when dragging locally.
1034           nsCOMPtr<nsISupportsString> str(do_QueryInterface(convertedData));
1035           if (str) {
1036             nsAutoString data;
1037             str->GetData(data);
1038 
1039             if (!stream) {
1040               // Create a storage stream to write to.
1041               NS_NewStorageStream(1024, UINT32_MAX,
1042                                   getter_AddRefs(storageStream));
1043 
1044               nsCOMPtr<nsIOutputStream> outputStream;
1045               storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
1046 
1047               stream = NS_NewObjectOutputStream(outputStream);
1048             }
1049 
1050             CheckedInt<uint32_t> formatLength =
1051                 CheckedInt<uint32_t>(type.Length()) *
1052                 sizeof(nsString::char_type);
1053 
1054             // The total size of the stream is the format length, the data
1055             // length, two integers to hold the lengths and one integer for
1056             // the string flag. Guard against large data by ignoring any that
1057             // don't fit.
1058             CheckedInt<uint32_t> newSize = formatLength + totalCustomLength +
1059                                            lengthInBytes +
1060                                            (sizeof(uint32_t) * 3);
1061             if (newSize.isValid()) {
1062               // If a write error occurs, set totalCustomLength to 0 so that
1063               // further processing gets ignored.
1064               nsresult rv = stream->Write32(eCustomClipboardTypeId_String);
1065               if (NS_WARN_IF(NS_FAILED(rv))) {
1066                 totalCustomLength = 0;
1067                 continue;
1068               }
1069               rv = stream->Write32(formatLength.value());
1070               if (NS_WARN_IF(NS_FAILED(rv))) {
1071                 totalCustomLength = 0;
1072                 continue;
1073               }
1074               rv = stream->WriteBytes((const char*)type.get(),
1075                                       formatLength.value());
1076               if (NS_WARN_IF(NS_FAILED(rv))) {
1077                 totalCustomLength = 0;
1078                 continue;
1079               }
1080               rv = stream->Write32(lengthInBytes);
1081               if (NS_WARN_IF(NS_FAILED(rv))) {
1082                 totalCustomLength = 0;
1083                 continue;
1084               }
1085               rv = stream->WriteBytes((const char*)data.get(), lengthInBytes);
1086               if (NS_WARN_IF(NS_FAILED(rv))) {
1087                 totalCustomLength = 0;
1088                 continue;
1089               }
1090 
1091               totalCustomLength = newSize.value();
1092             }
1093           }
1094         }
1095       } else if (isCustomFormat && stream) {
1096         // This is the second pass of the loop (handlingCustomFormats is false).
1097         // When encountering the first custom format, append all of the stream
1098         // at this position. If totalCustomLength is 0 indicating a write error
1099         // occurred, or no data has been added to it, don't output anything,
1100         if (totalCustomLength > baseLength) {
1101           // Write out an end of data terminator.
1102           nsresult rv = stream->Write32(eCustomClipboardTypeId_None);
1103           if (NS_SUCCEEDED(rv)) {
1104             nsCOMPtr<nsIInputStream> inputStream;
1105             storageStream->NewInputStream(0, getter_AddRefs(inputStream));
1106 
1107             RefPtr<nsStringBuffer> stringBuffer =
1108                 nsStringBuffer::Alloc(totalCustomLength);
1109 
1110             // Subtract off the null terminator when reading.
1111             totalCustomLength--;
1112 
1113             // Read the data from the stream and add a null-terminator as
1114             // ToString needs it.
1115             uint32_t amountRead;
1116             rv = inputStream->Read(static_cast<char*>(stringBuffer->Data()),
1117                                    totalCustomLength, &amountRead);
1118             if (NS_SUCCEEDED(rv)) {
1119               static_cast<char*>(stringBuffer->Data())[amountRead] = 0;
1120 
1121               nsCString str;
1122               stringBuffer->ToString(totalCustomLength, str);
1123               nsCOMPtr<nsISupportsCString> strSupports(
1124                   do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1125               strSupports->SetData(str);
1126 
1127               nsresult rv = transferable->SetTransferData(
1128                   kCustomTypesMime, strSupports, totalCustomLength);
1129               if (NS_FAILED(rv)) {
1130                 return nullptr;
1131               }
1132 
1133               added = true;
1134             }
1135           }
1136         }
1137 
1138         // Clear the stream so it doesn't get used again.
1139         stream = nullptr;
1140       } else {
1141         // This is the second pass of the loop and a known type is encountered.
1142         // Add it as is.
1143         if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
1144                                 &lengthInBytes)) {
1145           continue;
1146         }
1147 
1148         // The underlying drag code uses text/unicode, so use that instead of
1149         // text/plain
1150         const char* format;
1151         NS_ConvertUTF16toUTF8 utf8format(type);
1152         if (utf8format.EqualsLiteral(kTextMime)) {
1153           format = kUnicodeMime;
1154         } else {
1155           format = utf8format.get();
1156         }
1157 
1158         // If a converter is set for a format, set the converter for the
1159         // transferable and don't add the item
1160         nsCOMPtr<nsIFormatConverter> converter =
1161             do_QueryInterface(convertedData);
1162         if (converter) {
1163           transferable->AddDataFlavor(format);
1164           transferable->SetConverter(converter);
1165           continue;
1166         }
1167 
1168         nsresult rv =
1169             transferable->SetTransferData(format, convertedData, lengthInBytes);
1170         if (NS_FAILED(rv)) {
1171           return nullptr;
1172         }
1173 
1174         added = true;
1175       }
1176     }
1177 
1178     handlingCustomFormats = !handlingCustomFormats;
1179   } while (!handlingCustomFormats);
1180 
1181   // only return the transferable if data was successfully added to it
1182   if (added) {
1183     return transferable.forget();
1184   }
1185 
1186   return nullptr;
1187 }
1188 
ConvertFromVariant(nsIVariant * aVariant,nsISupports ** aSupports,uint32_t * aLength) const1189 bool DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
1190                                       nsISupports** aSupports,
1191                                       uint32_t* aLength) const {
1192   *aSupports = nullptr;
1193   *aLength = 0;
1194 
1195   uint16_t type;
1196   aVariant->GetDataType(&type);
1197   if (type == nsIDataType::VTYPE_INTERFACE ||
1198       type == nsIDataType::VTYPE_INTERFACE_IS) {
1199     nsCOMPtr<nsISupports> data;
1200     if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) {
1201       return false;
1202     }
1203 
1204     nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data);
1205     if (fdp) {
1206       // for flavour data providers, use kFlavorHasDataProvider (which has the
1207       // value 0) as the length.
1208       fdp.forget(aSupports);
1209       *aLength = nsITransferable::kFlavorHasDataProvider;
1210     } else {
1211       // wrap the item in an nsISupportsInterfacePointer
1212       nsCOMPtr<nsISupportsInterfacePointer> ptrSupports =
1213           do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
1214       if (!ptrSupports) {
1215         return false;
1216       }
1217 
1218       ptrSupports->SetData(data);
1219       ptrSupports.forget(aSupports);
1220 
1221       *aLength = sizeof(nsISupportsInterfacePointer*);
1222     }
1223 
1224     return true;
1225   }
1226 
1227   char16_t* chrs;
1228   uint32_t len = 0;
1229   nsresult rv = aVariant->GetAsWStringWithSize(&len, &chrs);
1230   if (NS_FAILED(rv)) {
1231     return false;
1232   }
1233 
1234   nsAutoString str;
1235   str.Adopt(chrs, len);
1236 
1237   nsCOMPtr<nsISupportsString> strSupports(
1238       do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1239   if (!strSupports) {
1240     return false;
1241   }
1242 
1243   strSupports->SetData(str);
1244 
1245   strSupports.forget(aSupports);
1246 
1247   // each character is two bytes
1248   *aLength = str.Length() << 1;
1249 
1250   return true;
1251 }
1252 
Disconnect()1253 void DataTransfer::Disconnect() {
1254   SetMode(Mode::Protected);
1255   if (PrefProtected()) {
1256     ClearAll();
1257   }
1258 }
1259 
ClearAll()1260 void DataTransfer::ClearAll() { mItems->ClearAllItems(); }
1261 
MozItemCount() const1262 uint32_t DataTransfer::MozItemCount() const { return mItems->MozItemCount(); }
1263 
SetDataWithPrincipal(const nsAString & aFormat,nsIVariant * aData,uint32_t aIndex,nsIPrincipal * aPrincipal,bool aHidden)1264 nsresult DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
1265                                             nsIVariant* aData, uint32_t aIndex,
1266                                             nsIPrincipal* aPrincipal,
1267                                             bool aHidden) {
1268   nsAutoString format;
1269   GetRealFormat(aFormat, format);
1270 
1271   ErrorResult rv;
1272   RefPtr<DataTransferItem> item =
1273       mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1274                                    /* aInsertOnly = */ false, aHidden, rv);
1275   return rv.StealNSResult();
1276 }
1277 
SetDataWithPrincipalFromOtherProcess(const nsAString & aFormat,nsIVariant * aData,uint32_t aIndex,nsIPrincipal * aPrincipal,bool aHidden)1278 void DataTransfer::SetDataWithPrincipalFromOtherProcess(
1279     const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex,
1280     nsIPrincipal* aPrincipal, bool aHidden) {
1281   if (aFormat.EqualsLiteral(kCustomTypesMime)) {
1282     FillInExternalCustomTypes(aData, aIndex, aPrincipal);
1283   } else {
1284     nsAutoString format;
1285     GetRealFormat(aFormat, format);
1286 
1287     ErrorResult rv;
1288     RefPtr<DataTransferItem> item =
1289         mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1290                                      /* aInsertOnly = */ false, aHidden, rv);
1291     if (NS_WARN_IF(rv.Failed())) {
1292       rv.SuppressException();
1293     }
1294   }
1295 }
1296 
GetRealFormat(const nsAString & aInFormat,nsAString & aOutFormat) const1297 void DataTransfer::GetRealFormat(const nsAString& aInFormat,
1298                                  nsAString& aOutFormat) const {
1299   // treat text/unicode as equivalent to text/plain
1300   nsAutoString lowercaseFormat;
1301   nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
1302   if (lowercaseFormat.EqualsLiteral("text") ||
1303       lowercaseFormat.EqualsLiteral("text/unicode")) {
1304     aOutFormat.AssignLiteral("text/plain");
1305     return;
1306   }
1307 
1308   if (lowercaseFormat.EqualsLiteral("url")) {
1309     aOutFormat.AssignLiteral("text/uri-list");
1310     return;
1311   }
1312 
1313   aOutFormat.Assign(lowercaseFormat);
1314 }
1315 
CacheExternalData(const char * aFormat,uint32_t aIndex,nsIPrincipal * aPrincipal,bool aHidden)1316 nsresult DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
1317                                          nsIPrincipal* aPrincipal,
1318                                          bool aHidden) {
1319   ErrorResult rv;
1320   RefPtr<DataTransferItem> item;
1321 
1322   if (strcmp(aFormat, kUnicodeMime) == 0) {
1323     item =
1324         mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr,
1325                                      aIndex, aPrincipal, false, aHidden, rv);
1326     if (NS_WARN_IF(rv.Failed())) {
1327       return rv.StealNSResult();
1328     }
1329     return NS_OK;
1330   }
1331 
1332   if (strcmp(aFormat, kURLDataMime) == 0) {
1333     item = mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"),
1334                                         nullptr, aIndex, aPrincipal, false,
1335                                         aHidden, rv);
1336     if (NS_WARN_IF(rv.Failed())) {
1337       return rv.StealNSResult();
1338     }
1339     return NS_OK;
1340   }
1341 
1342   nsAutoString format;
1343   GetRealFormat(NS_ConvertUTF8toUTF16(aFormat), format);
1344   item = mItems->SetDataWithPrincipal(format, nullptr, aIndex, aPrincipal,
1345                                       false, aHidden, rv);
1346   if (NS_WARN_IF(rv.Failed())) {
1347     return rv.StealNSResult();
1348   }
1349   return NS_OK;
1350 }
1351 
CacheExternalDragFormats()1352 void DataTransfer::CacheExternalDragFormats() {
1353   // Called during the constructor to cache the formats available from an
1354   // external drag. The data associated with each format will be set to null.
1355   // This data will instead only be retrieved in FillInExternalDragData when
1356   // asked for, as it may be time consuming for the source application to
1357   // generate it.
1358 
1359   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
1360   if (!dragSession) {
1361     return;
1362   }
1363 
1364   // make sure that the system principal is used for external drags
1365   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1366   nsCOMPtr<nsIPrincipal> sysPrincipal;
1367   ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
1368 
1369   // there isn't a way to get a list of the formats that might be available on
1370   // all platforms, so just check for the types that can actually be imported
1371   // XXXndeakin there are some other formats but those are platform specific.
1372   // NOTE: kFileMime must have index 0
1373   const char* formats[] = {kFileMime,    kHTMLMime,    kURLMime,
1374                            kURLDataMime, kUnicodeMime, kPNGImageMime};
1375 
1376   uint32_t count;
1377   dragSession->GetNumDropItems(&count);
1378   for (uint32_t c = 0; c < count; c++) {
1379     bool hasFileData = false;
1380     dragSession->IsDataFlavorSupported(kFileMime, &hasFileData);
1381 
1382     // First, check for the special format that holds custom types.
1383     bool supported;
1384     dragSession->IsDataFlavorSupported(kCustomTypesMime, &supported);
1385     if (supported) {
1386       FillInExternalCustomTypes(c, sysPrincipal);
1387     }
1388 
1389     for (uint32_t f = 0; f < ArrayLength(formats); f++) {
1390       // IsDataFlavorSupported doesn't take an index as an argument and just
1391       // checks if any of the items support a particular flavor, even though
1392       // the GetData method does take an index. Here, we just assume that
1393       // every item being dragged has the same set of flavors.
1394       bool supported;
1395       dragSession->IsDataFlavorSupported(formats[f], &supported);
1396       // if the format is supported, add an item to the array with null as
1397       // the data. When retrieved, GetRealData will read the data.
1398       if (supported) {
1399         CacheExternalData(formats[f], c, sysPrincipal,
1400                           /* hidden = */ f && hasFileData);
1401       }
1402     }
1403   }
1404 }
1405 
CacheExternalClipboardFormats(bool aPlainTextOnly)1406 void DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly) {
1407   NS_ASSERTION(mEventMessage == ePaste,
1408                "caching clipboard data for invalid event");
1409 
1410   // Called during the constructor for paste events to cache the formats
1411   // available on the clipboard. As with CacheExternalDragFormats, the
1412   // data will only be retrieved when needed.
1413 
1414   nsCOMPtr<nsIClipboard> clipboard =
1415       do_GetService("@mozilla.org/widget/clipboard;1");
1416   if (!clipboard || mClipboardType < 0) {
1417     return;
1418   }
1419 
1420   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1421   nsCOMPtr<nsIPrincipal> sysPrincipal;
1422   ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
1423 
1424   if (aPlainTextOnly) {
1425     bool supported;
1426     const char* unicodeMime[] = {kUnicodeMime};
1427     clipboard->HasDataMatchingFlavors(unicodeMime, 1, mClipboardType,
1428                                       &supported);
1429     if (supported) {
1430       CacheExternalData(kUnicodeMime, 0, sysPrincipal, false);
1431     }
1432     return;
1433   }
1434 
1435   // Check if the clipboard has any files
1436   bool hasFileData = false;
1437   const char* fileMime[] = {kFileMime};
1438   clipboard->HasDataMatchingFlavors(fileMime, 1, mClipboardType, &hasFileData);
1439 
1440   // We will be ignoring any application/x-moz-file files found in the paste
1441   // datatransfer within e10s, as they will fail to be sent over IPC. Because of
1442   // that, we will unset hasFileData, whether or not it would have been set.
1443   // (bug 1308007)
1444   if (XRE_IsContentProcess()) {
1445     hasFileData = false;
1446   }
1447 
1448   // there isn't a way to get a list of the formats that might be available on
1449   // all platforms, so just check for the types that can actually be imported.
1450   // NOTE: kCustomTypesMime must have index 0, kFileMime index 1
1451   const char* formats[] = {kCustomTypesMime, kFileMime,    kHTMLMime,
1452                            kRTFMime,         kURLMime,     kURLDataMime,
1453                            kUnicodeMime,     kPNGImageMime};
1454 
1455   for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
1456     // check each format one at a time
1457     bool supported;
1458     clipboard->HasDataMatchingFlavors(&(formats[f]), 1, mClipboardType,
1459                                       &supported);
1460     // if the format is supported, add an item to the array with null as
1461     // the data. When retrieved, GetRealData will read the data.
1462     if (supported) {
1463       if (f == 0) {
1464         FillInExternalCustomTypes(0, sysPrincipal);
1465       } else {
1466         // In non-e10s we support pasting files from explorer.exe.
1467         // Unfortunately, we fail to send that data over IPC in e10s, so we
1468         // don't want to add the item to the DataTransfer and end up producing a
1469         // null `application/x-moz-file`. (bug 1308007)
1470         if (XRE_IsContentProcess() && f == 1) {
1471           continue;
1472         }
1473 
1474         // If we aren't the file data, and we have file data, we want to be
1475         // hidden
1476         CacheExternalData(formats[f], 0, sysPrincipal,
1477                           /* hidden = */ f != 1 && hasFileData);
1478       }
1479     }
1480   }
1481 }
1482 
FillAllExternalData()1483 void DataTransfer::FillAllExternalData() {
1484   if (mIsExternal) {
1485     for (uint32_t i = 0; i < MozItemCount(); ++i) {
1486       const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(i);
1487       for (uint32_t j = 0; j < items.Length(); ++j) {
1488         MOZ_ASSERT(items[j]->Index() == i);
1489 
1490         items[j]->FillInExternalData();
1491       }
1492     }
1493   }
1494 }
1495 
FillInExternalCustomTypes(uint32_t aIndex,nsIPrincipal * aPrincipal)1496 void DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
1497                                              nsIPrincipal* aPrincipal) {
1498   RefPtr<DataTransferItem> item = new DataTransferItem(
1499       this, NS_LITERAL_STRING(kCustomTypesMime), DataTransferItem::KIND_STRING);
1500   item->SetIndex(aIndex);
1501 
1502   nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
1503   if (!variant) {
1504     return;
1505   }
1506 
1507   FillInExternalCustomTypes(variant, aIndex, aPrincipal);
1508 }
1509 
FillInExternalCustomTypes(nsIVariant * aData,uint32_t aIndex,nsIPrincipal * aPrincipal)1510 void DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
1511                                              nsIPrincipal* aPrincipal) {
1512   char* chrs;
1513   uint32_t len = 0;
1514   nsresult rv = aData->GetAsStringWithSize(&len, &chrs);
1515   if (NS_FAILED(rv)) {
1516     return;
1517   }
1518 
1519   nsAutoCString str;
1520   str.Adopt(chrs, len);
1521 
1522   nsCOMPtr<nsIInputStream> stringStream;
1523   NS_NewCStringInputStream(getter_AddRefs(stringStream), str);
1524 
1525   nsCOMPtr<nsIObjectInputStream> stream = NS_NewObjectInputStream(stringStream);
1526 
1527   uint32_t type;
1528   do {
1529     rv = stream->Read32(&type);
1530     NS_ENSURE_SUCCESS_VOID(rv);
1531     if (type == eCustomClipboardTypeId_String) {
1532       uint32_t formatLength;
1533       rv = stream->Read32(&formatLength);
1534       NS_ENSURE_SUCCESS_VOID(rv);
1535       char* formatBytes;
1536       rv = stream->ReadBytes(formatLength, &formatBytes);
1537       NS_ENSURE_SUCCESS_VOID(rv);
1538       nsAutoString format;
1539       format.Adopt(reinterpret_cast<char16_t*>(formatBytes),
1540                    formatLength / sizeof(char16_t));
1541 
1542       uint32_t dataLength;
1543       rv = stream->Read32(&dataLength);
1544       NS_ENSURE_SUCCESS_VOID(rv);
1545       char* dataBytes;
1546       rv = stream->ReadBytes(dataLength, &dataBytes);
1547       NS_ENSURE_SUCCESS_VOID(rv);
1548       nsAutoString data;
1549       data.Adopt(reinterpret_cast<char16_t*>(dataBytes),
1550                  dataLength / sizeof(char16_t));
1551 
1552       RefPtr<nsVariantCC> variant = new nsVariantCC();
1553       rv = variant->SetAsAString(data);
1554       NS_ENSURE_SUCCESS_VOID(rv);
1555 
1556       SetDataWithPrincipal(format, variant, aIndex, aPrincipal);
1557     }
1558   } while (type != eCustomClipboardTypeId_None);
1559 }
1560 
SetMode(DataTransfer::Mode aMode)1561 void DataTransfer::SetMode(DataTransfer::Mode aMode) {
1562   if (!PrefProtected() && aMode == Mode::Protected) {
1563     mMode = Mode::ReadOnly;
1564   } else {
1565     mMode = aMode;
1566   }
1567 }
1568 
1569 }  // namespace dom
1570 }  // namespace mozilla
1571