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/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/BasePrincipal.h"
9 #include "mozilla/BasicEvents.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/Span.h"
12 #include "mozilla/StaticPrefs_dom.h"
13 #include "DataTransfer.h"
15 #include "nsISupportsPrimitives.h"
16 #include "nsIScriptSecurityManager.h"
17 #include "mozilla/dom/DOMStringList.h"
20 #include "nsIDragService.h"
21 #include "nsIClipboard.h"
22 #include "nsIXPConnect.h"
23 #include "nsContentUtils.h"
24 #include "nsIContent.h"
25 #include "nsIObjectInputStream.h"
26 #include "nsIObjectOutputStream.h"
27 #include "nsIStorageStream.h"
28 #include "nsStringStream.h"
30 #include "nsIScriptObjectPrincipal.h"
31 #include "nsIScriptContext.h"
32 #include "mozilla/dom/Document.h"
33 #include "nsIScriptGlobalObject.h"
34 #include "nsVariant.h"
35 #include "mozilla/dom/ContentChild.h"
36 #include "mozilla/dom/DataTransferBinding.h"
37 #include "mozilla/dom/DataTransferItemList.h"
38 #include "mozilla/dom/Directory.h"
39 #include "mozilla/dom/Element.h"
40 #include "mozilla/dom/FileList.h"
41 #include "mozilla/dom/BindingUtils.h"
42 #include "mozilla/dom/OSFileSystem.h"
43 #include "mozilla/dom/Promise.h"
44 #include "nsComponentManagerUtils.h"
45 #include "nsNetUtil.h"
46 #include "nsReadableUtils.h"
48 namespace mozilla::dom
{
50 NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer
)
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer
)
53 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent
)
54 NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems
)
55 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget
)
56 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage
)
57 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
58 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
59 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer
)
60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent
)
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems
)
62 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget
)
63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage
)
64 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
65 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer
)
67 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer
)
68 NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer
)
70 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer
)
71 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
72 NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer
)
73 NS_INTERFACE_MAP_ENTRY(nsISupports
)
76 // the size of the array
77 const char DataTransfer::sEffects
[8][9] = {
78 "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"};
80 // Used for custom clipboard types.
81 enum CustomClipboardTypeId
{
82 eCustomClipboardTypeId_None
,
83 eCustomClipboardTypeId_String
86 static DataTransfer::Mode
ModeForEvent(EventMessage aEventMessage
) {
87 switch (aEventMessage
) {
91 // For these events, we want to be able to add data to the data transfer,
92 // Otherwise, the data is already present.
93 return DataTransfer::Mode::ReadWrite
;
96 case ePasteNoFormatting
:
98 // For these events we want to be able to read the data which is stored in
99 // the DataTransfer, rather than just the type information.
100 return DataTransfer::Mode::ReadOnly
;
102 return StaticPrefs::dom_events_dataTransfer_protected_enabled()
103 ? DataTransfer::Mode::Protected
104 : DataTransfer::Mode::ReadOnly
;
108 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
109 bool aIsExternal
, int32_t aClipboardType
)
111 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
112 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
),
113 mEventMessage(aEventMessage
),
115 mMode(ModeForEvent(aEventMessage
)),
116 mIsExternal(aIsExternal
),
117 mUserCancelled(false),
118 mIsCrossDomainSubFrameDrop(false),
119 mClipboardType(aClipboardType
),
122 mItems
= new DataTransferItemList(this);
124 // For external usage, cache the data from the native clipboard or drag.
125 if (mIsExternal
&& mMode
!= Mode::ReadWrite
) {
126 if (aEventMessage
== ePasteNoFormatting
) {
127 mEventMessage
= ePaste
;
128 CacheExternalClipboardFormats(true);
129 } else if (aEventMessage
== ePaste
) {
130 CacheExternalClipboardFormats(false);
131 } else if (aEventMessage
>= eDragDropEventFirst
&&
132 aEventMessage
<= eDragDropEventLast
) {
133 CacheExternalDragFormats();
138 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
139 nsITransferable
* aTransferable
)
141 mTransferable(aTransferable
),
142 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
143 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
),
144 mEventMessage(aEventMessage
),
146 mMode(ModeForEvent(aEventMessage
)),
148 mUserCancelled(false),
149 mIsCrossDomainSubFrameDrop(false),
153 mItems
= new DataTransferItemList(this);
155 // XXX Currently, we cannot make DataTransfer grabs mTransferable for long
156 // time because nsITransferable is not cycle collectable but this may
157 // be grabbed by JS. Additionally, the data initializing path is too
158 // complicated (too optimized) for D&D and clipboard. They are cached
159 // only formats first, then, data of all items will be filled by the
160 // items later and by themselves. However, we shouldn't duplicate such
161 // path for saving the maintenance cost. Therefore, we need to treat
162 // that DataTransfer and its items are in external mode. Finally,
163 // release mTransferable and make them in internal mode.
164 CacheTransferableFormats();
165 FillAllExternalData();
166 // Now, we have all necessary data of mTransferable. So, we can work as
169 // Release mTransferable because it won't be referred anymore.
170 mTransferable
= nullptr;
173 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
174 const nsAString
& aString
)
176 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
177 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
),
178 mEventMessage(aEventMessage
),
180 mMode(ModeForEvent(aEventMessage
)),
182 mUserCancelled(false),
183 mIsCrossDomainSubFrameDrop(false),
187 mItems
= new DataTransferItemList(this);
189 nsCOMPtr
<nsIPrincipal
> sysPrincipal
= nsContentUtils::GetSystemPrincipal();
191 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
192 variant
->SetAsAString(aString
);
193 DebugOnly
<nsresult
> rvIgnored
=
194 SetDataWithPrincipal(u
"text/plain"_ns
, variant
, 0, sysPrincipal
, false);
195 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
196 "Failed to set given string to the DataTransfer object");
199 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
200 const uint32_t aEffectAllowed
, bool aCursorState
,
201 bool aIsExternal
, bool aUserCancelled
,
202 bool aIsCrossDomainSubFrameDrop
,
203 int32_t aClipboardType
, DataTransferItemList
* aItems
,
204 Element
* aDragImage
, uint32_t aDragImageX
,
205 uint32_t aDragImageY
)
207 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
208 mEffectAllowed(aEffectAllowed
),
209 mEventMessage(aEventMessage
),
210 mCursorState(aCursorState
),
211 mMode(ModeForEvent(aEventMessage
)),
212 mIsExternal(aIsExternal
),
213 mUserCancelled(aUserCancelled
),
214 mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop
),
215 mClipboardType(aClipboardType
),
216 mDragImage(aDragImage
),
217 mDragImageX(aDragImageX
),
218 mDragImageY(aDragImageY
) {
222 // We clone the items array after everything else, so that it has a valid
224 mItems
= aItems
->Clone(this);
225 // The items are copied from aItems into mItems. There is no need to copy
226 // the actual data in the items as the data transfer will be read only. The
227 // dragstart event is the only time when items are
228 // modifiable, but those events should have been using the first constructor
230 NS_ASSERTION(aEventMessage
!= eDragStart
,
231 "invalid event type for DataTransfer constructor");
234 DataTransfer::~DataTransfer() = default;
237 already_AddRefed
<DataTransfer
> DataTransfer::Constructor(
238 const GlobalObject
& aGlobal
) {
239 RefPtr
<DataTransfer
> transfer
=
240 new DataTransfer(aGlobal
.GetAsSupports(), eCopy
, /* is external */ false,
241 /* clipboard type */ -1);
242 transfer
->mEffectAllowed
= nsIDragService::DRAGDROP_ACTION_NONE
;
243 return transfer
.forget();
246 JSObject
* DataTransfer::WrapObject(JSContext
* aCx
,
247 JS::Handle
<JSObject
*> aGivenProto
) {
248 return DataTransfer_Binding::Wrap(aCx
, this, aGivenProto
);
251 void DataTransfer::SetDropEffect(const nsAString
& aDropEffect
) {
252 // the drop effect can only be 'none', 'copy', 'move' or 'link'.
253 for (uint32_t e
= 0; e
<= nsIDragService::DRAGDROP_ACTION_LINK
; e
++) {
254 if (aDropEffect
.EqualsASCII(sEffects
[e
])) {
255 // don't allow copyMove
256 if (e
!= (nsIDragService::DRAGDROP_ACTION_COPY
|
257 nsIDragService::DRAGDROP_ACTION_MOVE
)) {
265 void DataTransfer::SetEffectAllowed(const nsAString
& aEffectAllowed
) {
266 if (aEffectAllowed
.EqualsLiteral("uninitialized")) {
267 mEffectAllowed
= nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
;
271 static_assert(nsIDragService::DRAGDROP_ACTION_NONE
== 0,
272 "DRAGDROP_ACTION_NONE constant is wrong");
273 static_assert(nsIDragService::DRAGDROP_ACTION_COPY
== 1,
274 "DRAGDROP_ACTION_COPY constant is wrong");
275 static_assert(nsIDragService::DRAGDROP_ACTION_MOVE
== 2,
276 "DRAGDROP_ACTION_MOVE constant is wrong");
277 static_assert(nsIDragService::DRAGDROP_ACTION_LINK
== 4,
278 "DRAGDROP_ACTION_LINK constant is wrong");
280 for (uint32_t e
= 0; e
< ArrayLength(sEffects
); e
++) {
281 if (aEffectAllowed
.EqualsASCII(sEffects
[e
])) {
288 void DataTransfer::GetMozTriggeringPrincipalURISpec(
289 nsAString
& aPrincipalURISpec
) {
290 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
292 aPrincipalURISpec
.Truncate(0);
296 nsCOMPtr
<nsIPrincipal
> principal
;
297 dragSession
->GetTriggeringPrincipal(getter_AddRefs(principal
));
299 aPrincipalURISpec
.Truncate(0);
304 principal
->GetAsciiSpec(spec
);
305 CopyUTF8toUTF16(spec
, aPrincipalURISpec
);
308 nsIContentSecurityPolicy
* DataTransfer::GetMozCSP() {
309 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
313 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
314 dragSession
->GetCsp(getter_AddRefs(csp
));
318 already_AddRefed
<FileList
> DataTransfer::GetFiles(
319 nsIPrincipal
& aSubjectPrincipal
) {
320 return mItems
->Files(&aSubjectPrincipal
);
323 void DataTransfer::GetTypes(nsTArray
<nsString
>& aTypes
,
324 CallerType aCallerType
) const {
325 // When called from bindings, aTypes will be empty, but since we might have
326 // Gecko-internal callers too, clear it to be safe.
329 return mItems
->GetTypes(aTypes
, aCallerType
);
332 bool DataTransfer::HasType(const nsAString
& aType
) const {
333 return mItems
->HasType(aType
);
336 bool DataTransfer::HasFile() const { return mItems
->HasFile(); }
338 void DataTransfer::GetData(const nsAString
& aFormat
, nsAString
& aData
,
339 nsIPrincipal
& aSubjectPrincipal
,
340 ErrorResult
& aRv
) const {
341 // return an empty string if data for the format was not found
344 nsCOMPtr
<nsIVariant
> data
;
346 GetDataAtInternal(aFormat
, 0, &aSubjectPrincipal
, getter_AddRefs(data
));
348 if (rv
!= NS_ERROR_DOM_INDEX_SIZE_ERR
) {
355 nsAutoString stringdata
;
356 data
->GetAsAString(stringdata
);
358 // for the URL type, parse out the first URI from the list. The URIs are
359 // separated by newlines
360 nsAutoString lowercaseFormat
;
361 nsContentUtils::ASCIIToLower(aFormat
, lowercaseFormat
);
363 if (lowercaseFormat
.EqualsLiteral("url")) {
364 int32_t lastidx
= 0, idx
;
365 int32_t length
= stringdata
.Length();
366 while (lastidx
< length
) {
367 idx
= stringdata
.FindChar('\n', lastidx
);
368 // lines beginning with # are comments
369 if (stringdata
[lastidx
] == '#') {
375 aData
.Assign(Substring(stringdata
, lastidx
));
377 aData
.Assign(Substring(stringdata
, lastidx
, idx
- lastidx
));
380 nsContentUtils::TrimWhitespace
<nsCRT::IsAsciiSpace
>(aData
, true);
391 void DataTransfer::SetData(const nsAString
& aFormat
, const nsAString
& aData
,
392 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
393 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
394 variant
->SetAsAString(aData
);
396 aRv
= SetDataAtInternal(aFormat
, variant
, 0, &aSubjectPrincipal
);
399 void DataTransfer::ClearData(const Optional
<nsAString
>& aFormat
,
400 nsIPrincipal
& aSubjectPrincipal
,
403 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
407 if (MozItemCount() == 0) {
411 if (aFormat
.WasPassed()) {
412 MozClearDataAtHelper(aFormat
.Value(), 0, aSubjectPrincipal
, aRv
);
414 MozClearDataAtHelper(u
""_ns
, 0, aSubjectPrincipal
, aRv
);
418 void DataTransfer::SetMozCursor(const nsAString
& aCursorState
) {
419 // Lock the cursor to an arrow during the drag.
420 mCursorState
= aCursorState
.EqualsLiteral("default");
423 already_AddRefed
<nsINode
> DataTransfer::GetMozSourceNode() {
424 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
429 nsCOMPtr
<nsINode
> sourceNode
;
430 dragSession
->GetSourceNode(getter_AddRefs(sourceNode
));
431 if (sourceNode
&& !nsContentUtils::LegacyIsCallerNativeCode() &&
432 !nsContentUtils::CanCallerAccess(sourceNode
)) {
436 return sourceNode
.forget();
439 already_AddRefed
<DOMStringList
> DataTransfer::MozTypesAt(
440 uint32_t aIndex
, CallerType aCallerType
, ErrorResult
& aRv
) const {
441 // Only the first item is valid for clipboard events
442 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
443 mEventMessage
== ePaste
)) {
444 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
448 RefPtr
<DOMStringList
> types
= new DOMStringList();
449 if (aIndex
< MozItemCount()) {
450 // note that you can retrieve the types regardless of their principal
451 const nsTArray
<RefPtr
<DataTransferItem
>>& items
=
452 *mItems
->MozItemsAt(aIndex
);
454 bool addFile
= false;
455 for (uint32_t i
= 0; i
< items
.Length(); i
++) {
456 if (items
[i
]->ChromeOnly() && aCallerType
!= CallerType::System
) {
460 // NOTE: The reason why we get the internal type here is because we want
461 // kFileMime to appear in the types list for backwards compatibility
464 items
[i
]->GetInternalType(type
);
465 if (NS_WARN_IF(!types
->Add(type
))) {
466 aRv
.Throw(NS_ERROR_FAILURE
);
470 if (items
[i
]->Kind() == DataTransferItem::KIND_FILE
) {
476 types
->Add(u
"Files"_ns
);
480 return types
.forget();
483 nsresult
DataTransfer::GetDataAtNoSecurityCheck(const nsAString
& aFormat
,
485 nsIVariant
** aData
) const {
486 return GetDataAtInternal(aFormat
, aIndex
,
487 nsContentUtils::GetSystemPrincipal(), aData
);
490 nsresult
DataTransfer::GetDataAtInternal(const nsAString
& aFormat
,
492 nsIPrincipal
* aSubjectPrincipal
,
493 nsIVariant
** aData
) const {
496 if (aFormat
.IsEmpty()) {
500 if (aIndex
>= MozItemCount()) {
501 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
504 // Only the first item is valid for clipboard events
505 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
506 mEventMessage
== ePaste
)) {
507 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
511 GetRealFormat(aFormat
, format
);
513 MOZ_ASSERT(aSubjectPrincipal
);
515 RefPtr
<DataTransferItem
> item
= mItems
->MozItemByTypeAt(format
, aIndex
);
517 // The index exists but there's no data for the specified format, in this
518 // case we just return undefined
522 // If we have chrome only content, and we aren't chrome, don't allow access
523 if (!aSubjectPrincipal
->IsSystemPrincipal() && item
->ChromeOnly()) {
527 // DataTransferItem::Data() handles the principal checks
529 nsCOMPtr
<nsIVariant
> data
= item
->Data(aSubjectPrincipal
, result
);
530 if (NS_WARN_IF(!data
|| result
.Failed())) {
531 return result
.StealNSResult();
538 void DataTransfer::MozGetDataAt(JSContext
* aCx
, const nsAString
& aFormat
,
540 JS::MutableHandle
<JS::Value
> aRetval
,
541 nsIPrincipal
& aSubjectPrincipal
,
542 mozilla::ErrorResult
& aRv
) {
543 nsCOMPtr
<nsIVariant
> data
;
544 aRv
= GetDataAtInternal(aFormat
, aIndex
, &aSubjectPrincipal
,
545 getter_AddRefs(data
));
555 JS::Rooted
<JS::Value
> result(aCx
);
556 if (!VariantToJsval(aCx
, data
, aRetval
)) {
557 aRv
= NS_ERROR_FAILURE
;
563 bool DataTransfer::PrincipalMaySetData(const nsAString
& aType
,
565 nsIPrincipal
* aPrincipal
) {
566 if (!aPrincipal
->IsSystemPrincipal()) {
567 DataTransferItem::eKind kind
= DataTransferItem::KindFromData(aData
);
568 if (kind
== DataTransferItem::KIND_OTHER
) {
569 NS_WARNING("Disallowing adding non string/file types to DataTransfer");
573 if (aType
.EqualsASCII(kFileMime
) || aType
.EqualsASCII(kFilePromiseMime
)) {
575 "Disallowing adding x-moz-file or x-moz-file-promize types to "
580 // Disallow content from creating x-moz-place flavors, so that it cannot
581 // create fake Places smart queries exposing user data, but give a free
582 // pass to WebExtensions.
583 auto principal
= BasePrincipal::Cast(aPrincipal
);
584 if (!principal
->AddonPolicy() &&
585 StringBeginsWith(aType
, u
"text/x-moz-place"_ns
)) {
586 NS_WARNING("Disallowing adding moz-place types to DataTransfer");
594 void DataTransfer::TypesListMayHaveChanged() {
595 DataTransfer_Binding::ClearCachedTypesValue(this);
598 already_AddRefed
<DataTransfer
> DataTransfer::MozCloneForEvent(
599 const nsAString
& aEvent
, ErrorResult
& aRv
) {
600 RefPtr
<nsAtom
> atomEvt
= NS_Atomize(aEvent
);
602 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
605 EventMessage eventMessage
= nsContentUtils::GetEventMessage(atomEvt
);
607 RefPtr
<DataTransfer
> dt
;
608 nsresult rv
= Clone(mParent
, eventMessage
, false, false, getter_AddRefs(dt
));
617 void DataTransfer::GetExternalClipboardFormats(const int32_t& aWhichClipboard
,
618 const bool& aPlainTextOnly
,
619 nsTArray
<nsCString
>* aResult
) {
622 // NOTE: When you change this method, you may need to change
623 // GetExternalTransferableFormats() too since those methods should
626 nsCOMPtr
<nsIClipboard
> clipboard
=
627 do_GetService("@mozilla.org/widget/clipboard;1");
628 if (!clipboard
|| aWhichClipboard
< 0) {
632 if (aPlainTextOnly
) {
634 AutoTArray
<nsCString
, 1> unicodeMime
= {nsDependentCString(kUnicodeMime
)};
635 nsresult rv
= clipboard
->HasDataMatchingFlavors(unicodeMime
,
636 aWhichClipboard
, &hasType
);
639 aResult
->AppendElement(kUnicodeMime
);
644 // If not plain text only, then instead check all the other types
645 static const char* formats
[] = {kCustomTypesMime
, kFileMime
, kHTMLMime
,
646 kRTFMime
, kURLMime
, kURLDataMime
,
647 kUnicodeMime
, kPNGImageMime
};
649 for (uint32_t f
= 0; f
< mozilla::ArrayLength(formats
); ++f
) {
651 AutoTArray
<nsCString
, 1> format
= {nsDependentCString(formats
[f
])};
653 clipboard
->HasDataMatchingFlavors(format
, aWhichClipboard
, &hasType
);
656 aResult
->AppendElement(formats
[f
]);
662 void DataTransfer::GetExternalTransferableFormats(
663 nsITransferable
* aTransferable
, bool aPlainTextOnly
,
664 nsTArray
<nsCString
>* aResult
) {
665 MOZ_ASSERT(aTransferable
);
670 // NOTE: When you change this method, you may need to change
671 // GetExternalClipboardFormats() too since those methods should
674 AutoTArray
<nsCString
, 10> flavors
;
675 aTransferable
->FlavorsTransferableCanExport(flavors
);
677 if (aPlainTextOnly
) {
678 auto index
= flavors
.IndexOf(nsLiteralCString(kUnicodeMime
));
679 if (index
!= flavors
.NoIndex
) {
680 aResult
->AppendElement(nsLiteralCString(kUnicodeMime
));
685 // If not plain text only, then instead check all the other types
686 static const char* formats
[] = {kCustomTypesMime
, kFileMime
, kHTMLMime
,
687 kRTFMime
, kURLMime
, kURLDataMime
,
688 kUnicodeMime
, kPNGImageMime
};
690 for (const char* format
: formats
) {
691 auto index
= flavors
.IndexOf(nsCString(format
));
692 if (index
!= flavors
.NoIndex
) {
693 aResult
->AppendElement(nsCString(format
));
698 nsresult
DataTransfer::SetDataAtInternal(const nsAString
& aFormat
,
699 nsIVariant
* aData
, uint32_t aIndex
,
700 nsIPrincipal
* aSubjectPrincipal
) {
701 if (aFormat
.IsEmpty()) {
706 return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
;
709 // Specifying an index less than the current length will replace an existing
710 // item. Specifying an index equal to the current length will add a new item.
711 if (aIndex
> MozItemCount()) {
712 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
715 // Only the first item is valid for clipboard events
716 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
717 mEventMessage
== ePaste
)) {
718 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
721 // Don't allow the custom type to be assigned.
722 if (aFormat
.EqualsLiteral(kCustomTypesMime
)) {
723 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
726 if (!PrincipalMaySetData(aFormat
, aData
, aSubjectPrincipal
)) {
727 return NS_ERROR_DOM_SECURITY_ERR
;
730 return SetDataWithPrincipal(aFormat
, aData
, aIndex
, aSubjectPrincipal
);
733 void DataTransfer::MozSetDataAt(JSContext
* aCx
, const nsAString
& aFormat
,
734 JS::Handle
<JS::Value
> aData
, uint32_t aIndex
,
735 nsIPrincipal
& aSubjectPrincipal
,
737 nsCOMPtr
<nsIVariant
> data
;
738 aRv
= nsContentUtils::XPConnect()->JSValToVariant(aCx
, aData
,
739 getter_AddRefs(data
));
741 aRv
= SetDataAtInternal(aFormat
, data
, aIndex
, &aSubjectPrincipal
);
745 void DataTransfer::MozClearDataAt(const nsAString
& aFormat
, uint32_t aIndex
,
746 nsIPrincipal
& aSubjectPrincipal
,
749 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
753 if (aIndex
>= MozItemCount()) {
754 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
758 // Only the first item is valid for clipboard events
759 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
760 mEventMessage
== ePaste
)) {
761 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
765 MozClearDataAtHelper(aFormat
, aIndex
, aSubjectPrincipal
, aRv
);
767 // If we just cleared the 0-th index, and there are still more than 1 indexes
768 // remaining, MozClearDataAt should cause the 1st index to become the 0th
769 // index. This should _only_ happen when the MozClearDataAt function is
770 // explicitly called by script, as this behavior is inconsistent with spec.
771 // (however, so is the MozClearDataAt API)
773 if (aIndex
== 0 && mItems
->MozItemCount() > 1 &&
774 mItems
->MozItemsAt(0)->Length() == 0) {
775 mItems
->PopIndexZero();
779 void DataTransfer::MozClearDataAtHelper(const nsAString
& aFormat
,
781 nsIPrincipal
& aSubjectPrincipal
,
783 MOZ_ASSERT(!IsReadOnly());
784 MOZ_ASSERT(aIndex
< MozItemCount());
785 MOZ_ASSERT(aIndex
== 0 || (mEventMessage
!= eCut
&& mEventMessage
!= eCopy
&&
786 mEventMessage
!= ePaste
));
789 GetRealFormat(aFormat
, format
);
791 mItems
->MozRemoveByTypeAt(format
, aIndex
, aSubjectPrincipal
, aRv
);
794 void DataTransfer::SetDragImage(Element
& aImage
, int32_t aX
, int32_t aY
) {
796 mDragImage
= &aImage
;
802 void DataTransfer::UpdateDragImage(Element
& aImage
, int32_t aX
, int32_t aY
) {
803 if (mEventMessage
< eDragDropEventFirst
||
804 mEventMessage
> eDragDropEventLast
) {
808 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
810 dragSession
->UpdateDragImage(&aImage
, aX
, aY
);
814 already_AddRefed
<Promise
> DataTransfer::GetFilesAndDirectories(
815 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
816 nsCOMPtr
<nsINode
> parentNode
= do_QueryInterface(mParent
);
818 aRv
.Throw(NS_ERROR_FAILURE
);
822 nsCOMPtr
<nsIGlobalObject
> global
= parentNode
->OwnerDoc()->GetScopeObject();
825 aRv
.Throw(NS_ERROR_FAILURE
);
829 RefPtr
<Promise
> p
= Promise::Create(global
, aRv
);
830 if (NS_WARN_IF(aRv
.Failed())) {
834 RefPtr
<FileList
> files
= mItems
->Files(&aSubjectPrincipal
);
835 if (NS_WARN_IF(!files
)) {
839 Sequence
<RefPtr
<File
>> filesSeq
;
840 files
->ToSequence(filesSeq
, aRv
);
841 if (NS_WARN_IF(aRv
.Failed())) {
845 p
->MaybeResolve(filesSeq
);
850 already_AddRefed
<Promise
> DataTransfer::GetFiles(
851 bool aRecursiveFlag
, nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
852 // Currently we don't support directories.
853 return GetFilesAndDirectories(aSubjectPrincipal
, aRv
);
856 void DataTransfer::AddElement(Element
& aElement
, ErrorResult
& aRv
) {
858 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
862 mDragTarget
= &aElement
;
865 nsresult
DataTransfer::Clone(nsISupports
* aParent
, EventMessage aEventMessage
,
867 bool aIsCrossDomainSubFrameDrop
,
868 DataTransfer
** aNewDataTransfer
) {
869 RefPtr
<DataTransfer
> newDataTransfer
= new DataTransfer(
870 aParent
, aEventMessage
, mEffectAllowed
, mCursorState
, mIsExternal
,
871 aUserCancelled
, aIsCrossDomainSubFrameDrop
, mClipboardType
, mItems
,
872 mDragImage
, mDragImageX
, mDragImageY
);
874 newDataTransfer
.forget(aNewDataTransfer
);
878 already_AddRefed
<nsIArray
> DataTransfer::GetTransferables(
879 nsINode
* aDragTarget
) {
880 MOZ_ASSERT(aDragTarget
);
882 Document
* doc
= aDragTarget
->GetComposedDoc();
887 return GetTransferables(doc
->GetLoadContext());
890 already_AddRefed
<nsIArray
> DataTransfer::GetTransferables(
891 nsILoadContext
* aLoadContext
) {
892 nsCOMPtr
<nsIMutableArray
> transArray
= nsArray::Create();
897 uint32_t count
= MozItemCount();
898 for (uint32_t i
= 0; i
< count
; i
++) {
899 nsCOMPtr
<nsITransferable
> transferable
= GetTransferable(i
, aLoadContext
);
901 transArray
->AppendElement(transferable
);
905 return transArray
.forget();
908 already_AddRefed
<nsITransferable
> DataTransfer::GetTransferable(
909 uint32_t aIndex
, nsILoadContext
* aLoadContext
) {
910 if (aIndex
>= MozItemCount()) {
914 const nsTArray
<RefPtr
<DataTransferItem
>>& item
= *mItems
->MozItemsAt(aIndex
);
915 uint32_t count
= item
.Length();
920 nsCOMPtr
<nsITransferable
> transferable
=
921 do_CreateInstance("@mozilla.org/widget/transferable;1");
925 transferable
->Init(aLoadContext
);
927 nsCOMPtr
<nsIStorageStream
> storageStream
;
928 nsCOMPtr
<nsIObjectOutputStream
> stream
;
931 bool handlingCustomFormats
= true;
933 // When writing the custom data, we need to ensure that there is sufficient
934 // space for a (uint32_t) data ending type, and the null byte character at
935 // the end of the nsCString. We claim that space upfront and store it in
936 // baseLength. This value will be set to zero if a write error occurs
937 // indicating that the data and length are no longer valid.
938 const uint32_t baseLength
= sizeof(uint32_t) + 1;
939 uint32_t totalCustomLength
= baseLength
;
941 const char* knownFormats
[] = {kTextMime
,
956 kFilePromiseDestFilename
,
957 kFilePromiseDirectoryMime
,
964 * Two passes are made here to iterate over all of the types. First, look for
965 * any types that are not in the list of known types. For this pass,
966 * handlingCustomFormats will be true. Data that corresponds to unknown types
967 * will be pulled out and inserted into a single type (kCustomTypesMime) by
968 * writing the data into a stream.
970 * The second pass will iterate over the formats looking for known types.
971 * These are added as is. The unknown types are all then inserted as a single
972 * type (kCustomTypesMime) in the same position of the first custom type. This
973 * model is used to maintain the format order as best as possible.
975 * The format of the kCustomTypesMime type is one or more of the following
976 * stored sequentially:
977 * <32-bit> type (only none or string is supported)
978 * <32-bit> length of format
979 * <wide string> format
980 * <32-bit> length of data
982 * A type of eCustomClipboardTypeId_None ends the list, without any following
986 for (uint32_t f
= 0; f
< count
; f
++) {
987 RefPtr
<DataTransferItem
> formatitem
= item
[f
];
988 nsCOMPtr
<nsIVariant
> variant
= formatitem
->DataNoSecurityCheck();
989 if (!variant
) { // skip empty items
994 formatitem
->GetInternalType(type
);
996 // If the data is of one of the well-known formats, use it directly.
997 bool isCustomFormat
= true;
998 for (uint32_t f
= 0; f
< ArrayLength(knownFormats
); f
++) {
999 if (type
.EqualsASCII(knownFormats
[f
])) {
1000 isCustomFormat
= false;
1005 uint32_t lengthInBytes
;
1006 nsCOMPtr
<nsISupports
> convertedData
;
1008 if (handlingCustomFormats
) {
1009 if (!ConvertFromVariant(variant
, getter_AddRefs(convertedData
),
1014 // When handling custom types, add the data to the stream if this is a
1015 // custom type. If totalCustomLength is 0, then a write error occurred
1016 // on a previous item, so ignore any others.
1017 if (isCustomFormat
&& totalCustomLength
> 0) {
1018 // If it isn't a string, just ignore it. The dataTransfer is cached in
1019 // the drag sesion during drag-and-drop, so non-strings will be
1020 // available when dragging locally.
1021 nsCOMPtr
<nsISupportsString
> str(do_QueryInterface(convertedData
));
1027 // Create a storage stream to write to.
1028 NS_NewStorageStream(1024, UINT32_MAX
,
1029 getter_AddRefs(storageStream
));
1031 nsCOMPtr
<nsIOutputStream
> outputStream
;
1032 storageStream
->GetOutputStream(0, getter_AddRefs(outputStream
));
1034 stream
= NS_NewObjectOutputStream(outputStream
);
1037 CheckedInt
<uint32_t> formatLength
=
1038 CheckedInt
<uint32_t>(type
.Length()) *
1039 sizeof(nsString::char_type
);
1041 // The total size of the stream is the format length, the data
1042 // length, two integers to hold the lengths and one integer for
1043 // the string flag. Guard against large data by ignoring any that
1045 CheckedInt
<uint32_t> newSize
= formatLength
+ totalCustomLength
+
1047 (sizeof(uint32_t) * 3);
1048 if (newSize
.isValid()) {
1049 // If a write error occurs, set totalCustomLength to 0 so that
1050 // further processing gets ignored.
1051 nsresult rv
= stream
->Write32(eCustomClipboardTypeId_String
);
1052 if (NS_WARN_IF(NS_FAILED(rv
))) {
1053 totalCustomLength
= 0;
1056 rv
= stream
->Write32(formatLength
.value());
1057 if (NS_WARN_IF(NS_FAILED(rv
))) {
1058 totalCustomLength
= 0;
1061 MOZ_ASSERT(formatLength
.isValid() &&
1062 formatLength
.value() ==
1063 type
.Length() * sizeof(nsString::char_type
),
1064 "Why is formatLength off?");
1065 rv
= stream
->WriteBytes(
1066 AsBytes(Span(type
.BeginReading(), type
.Length())));
1067 if (NS_WARN_IF(NS_FAILED(rv
))) {
1068 totalCustomLength
= 0;
1071 rv
= stream
->Write32(lengthInBytes
);
1072 if (NS_WARN_IF(NS_FAILED(rv
))) {
1073 totalCustomLength
= 0;
1076 // XXXbz it's not obvious to me that lengthInBytes is the actual
1077 // length of "data" if the variant contained an nsISupportsString
1078 // as VTYPE_INTERFACE, say. We used lengthInBytes above for
1079 // sizing, so just keep doing that.
1080 rv
= stream
->WriteBytes(
1081 Span(reinterpret_cast<const uint8_t*>(data
.BeginReading()),
1083 if (NS_WARN_IF(NS_FAILED(rv
))) {
1084 totalCustomLength
= 0;
1088 totalCustomLength
= newSize
.value();
1092 } else if (isCustomFormat
&& stream
) {
1093 // This is the second pass of the loop (handlingCustomFormats is false).
1094 // When encountering the first custom format, append all of the stream
1095 // at this position. If totalCustomLength is 0 indicating a write error
1096 // occurred, or no data has been added to it, don't output anything,
1097 if (totalCustomLength
> baseLength
) {
1098 // Write out an end of data terminator.
1099 nsresult rv
= stream
->Write32(eCustomClipboardTypeId_None
);
1100 if (NS_SUCCEEDED(rv
)) {
1101 nsCOMPtr
<nsIInputStream
> inputStream
;
1102 storageStream
->NewInputStream(0, getter_AddRefs(inputStream
));
1104 RefPtr
<nsStringBuffer
> stringBuffer
=
1105 nsStringBuffer::Alloc(totalCustomLength
);
1107 // Subtract off the null terminator when reading.
1108 totalCustomLength
--;
1110 // Read the data from the stream and add a null-terminator as
1111 // ToString needs it.
1112 uint32_t amountRead
;
1113 rv
= inputStream
->Read(static_cast<char*>(stringBuffer
->Data()),
1114 totalCustomLength
, &amountRead
);
1115 if (NS_SUCCEEDED(rv
)) {
1116 static_cast<char*>(stringBuffer
->Data())[amountRead
] = 0;
1119 stringBuffer
->ToString(totalCustomLength
, str
);
1120 nsCOMPtr
<nsISupportsCString
> strSupports(
1121 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID
));
1122 strSupports
->SetData(str
);
1125 transferable
->SetTransferData(kCustomTypesMime
, strSupports
);
1126 if (NS_FAILED(rv
)) {
1135 // Clear the stream so it doesn't get used again.
1138 // This is the second pass of the loop and a known type is encountered.
1140 if (!ConvertFromVariant(variant
, getter_AddRefs(convertedData
),
1145 // The underlying drag code uses text/unicode, so use that instead of
1148 NS_ConvertUTF16toUTF8
utf8format(type
);
1149 if (utf8format
.EqualsLiteral(kTextMime
)) {
1150 format
= kUnicodeMime
;
1152 format
= utf8format
.get();
1155 // If a converter is set for a format, set the converter for the
1156 // transferable and don't add the item
1157 nsCOMPtr
<nsIFormatConverter
> converter
=
1158 do_QueryInterface(convertedData
);
1160 transferable
->AddDataFlavor(format
);
1161 transferable
->SetConverter(converter
);
1165 nsresult rv
= transferable
->SetTransferData(format
, convertedData
);
1166 if (NS_FAILED(rv
)) {
1174 handlingCustomFormats
= !handlingCustomFormats
;
1175 } while (!handlingCustomFormats
);
1177 // only return the transferable if data was successfully added to it
1179 return transferable
.forget();
1185 bool DataTransfer::ConvertFromVariant(nsIVariant
* aVariant
,
1186 nsISupports
** aSupports
,
1187 uint32_t* aLength
) const {
1188 *aSupports
= nullptr;
1191 uint16_t type
= aVariant
->GetDataType();
1192 if (type
== nsIDataType::VTYPE_INTERFACE
||
1193 type
== nsIDataType::VTYPE_INTERFACE_IS
) {
1194 nsCOMPtr
<nsISupports
> data
;
1195 if (NS_FAILED(aVariant
->GetAsISupports(getter_AddRefs(data
)))) {
1199 nsCOMPtr
<nsIFlavorDataProvider
> fdp
= do_QueryInterface(data
);
1201 // For flavour data providers, use 0 as the length.
1202 fdp
.forget(aSupports
);
1205 data
.forget(aSupports
);
1206 *aLength
= sizeof(nsISupports
*);
1213 nsresult rv
= aVariant
->GetAsAString(str
);
1214 if (NS_FAILED(rv
)) {
1218 nsCOMPtr
<nsISupportsString
> strSupports(
1219 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
1224 strSupports
->SetData(str
);
1226 strSupports
.forget(aSupports
);
1228 // each character is two bytes
1229 *aLength
= str
.Length() * 2;
1234 void DataTransfer::Disconnect() {
1235 SetMode(Mode::Protected
);
1236 if (StaticPrefs::dom_events_dataTransfer_protected_enabled()) {
1241 void DataTransfer::ClearAll() { mItems
->ClearAllItems(); }
1243 uint32_t DataTransfer::MozItemCount() const { return mItems
->MozItemCount(); }
1245 nsresult
DataTransfer::SetDataWithPrincipal(const nsAString
& aFormat
,
1246 nsIVariant
* aData
, uint32_t aIndex
,
1247 nsIPrincipal
* aPrincipal
,
1249 nsAutoString format
;
1250 GetRealFormat(aFormat
, format
);
1253 RefPtr
<DataTransferItem
> item
=
1254 mItems
->SetDataWithPrincipal(format
, aData
, aIndex
, aPrincipal
,
1255 /* aInsertOnly = */ false, aHidden
, rv
);
1256 return rv
.StealNSResult();
1259 void DataTransfer::SetDataWithPrincipalFromOtherProcess(
1260 const nsAString
& aFormat
, nsIVariant
* aData
, uint32_t aIndex
,
1261 nsIPrincipal
* aPrincipal
, bool aHidden
) {
1262 if (aFormat
.EqualsLiteral(kCustomTypesMime
)) {
1263 FillInExternalCustomTypes(aData
, aIndex
, aPrincipal
);
1265 nsAutoString format
;
1266 GetRealFormat(aFormat
, format
);
1269 RefPtr
<DataTransferItem
> item
=
1270 mItems
->SetDataWithPrincipal(format
, aData
, aIndex
, aPrincipal
,
1271 /* aInsertOnly = */ false, aHidden
, rv
);
1272 if (NS_WARN_IF(rv
.Failed())) {
1273 rv
.SuppressException();
1278 void DataTransfer::GetRealFormat(const nsAString
& aInFormat
,
1279 nsAString
& aOutFormat
) const {
1280 // treat text/unicode as equivalent to text/plain
1281 nsAutoString lowercaseFormat
;
1282 nsContentUtils::ASCIIToLower(aInFormat
, lowercaseFormat
);
1283 if (lowercaseFormat
.EqualsLiteral("text") ||
1284 lowercaseFormat
.EqualsLiteral("text/unicode")) {
1285 aOutFormat
.AssignLiteral("text/plain");
1289 if (lowercaseFormat
.EqualsLiteral("url")) {
1290 aOutFormat
.AssignLiteral("text/uri-list");
1294 aOutFormat
.Assign(lowercaseFormat
);
1297 nsresult
DataTransfer::CacheExternalData(const char* aFormat
, uint32_t aIndex
,
1298 nsIPrincipal
* aPrincipal
,
1301 RefPtr
<DataTransferItem
> item
;
1303 if (strcmp(aFormat
, kUnicodeMime
) == 0) {
1304 item
= mItems
->SetDataWithPrincipal(u
"text/plain"_ns
, nullptr, aIndex
,
1305 aPrincipal
, false, aHidden
, rv
);
1306 if (NS_WARN_IF(rv
.Failed())) {
1307 return rv
.StealNSResult();
1312 if (strcmp(aFormat
, kURLDataMime
) == 0) {
1313 item
= mItems
->SetDataWithPrincipal(u
"text/uri-list"_ns
, nullptr, aIndex
,
1314 aPrincipal
, false, aHidden
, rv
);
1315 if (NS_WARN_IF(rv
.Failed())) {
1316 return rv
.StealNSResult();
1321 nsAutoString format
;
1322 GetRealFormat(NS_ConvertUTF8toUTF16(aFormat
), format
);
1323 item
= mItems
->SetDataWithPrincipal(format
, nullptr, aIndex
, aPrincipal
,
1324 false, aHidden
, rv
);
1325 if (NS_WARN_IF(rv
.Failed())) {
1326 return rv
.StealNSResult();
1331 void DataTransfer::CacheExternalDragFormats() {
1332 // Called during the constructor to cache the formats available from an
1333 // external drag. The data associated with each format will be set to null.
1334 // This data will instead only be retrieved in FillInExternalDragData when
1335 // asked for, as it may be time consuming for the source application to
1338 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
1343 // make sure that the system principal is used for external drags
1344 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
1345 nsCOMPtr
<nsIPrincipal
> sysPrincipal
;
1346 ssm
->GetSystemPrincipal(getter_AddRefs(sysPrincipal
));
1348 // there isn't a way to get a list of the formats that might be available on
1349 // all platforms, so just check for the types that can actually be imported
1350 // XXXndeakin there are some other formats but those are platform specific.
1351 // NOTE: kFileMime must have index 0
1352 const char* formats
[] = {kFileMime
, kHTMLMime
, kURLMime
,
1353 kURLDataMime
, kUnicodeMime
, kPNGImageMime
};
1356 dragSession
->GetNumDropItems(&count
);
1357 for (uint32_t c
= 0; c
< count
; c
++) {
1358 bool hasFileData
= false;
1359 dragSession
->IsDataFlavorSupported(kFileMime
, &hasFileData
);
1361 // First, check for the special format that holds custom types.
1363 dragSession
->IsDataFlavorSupported(kCustomTypesMime
, &supported
);
1365 FillInExternalCustomTypes(c
, sysPrincipal
);
1368 for (uint32_t f
= 0; f
< ArrayLength(formats
); f
++) {
1369 // IsDataFlavorSupported doesn't take an index as an argument and just
1370 // checks if any of the items support a particular flavor, even though
1371 // the GetData method does take an index. Here, we just assume that
1372 // every item being dragged has the same set of flavors.
1374 dragSession
->IsDataFlavorSupported(formats
[f
], &supported
);
1375 // if the format is supported, add an item to the array with null as
1376 // the data. When retrieved, GetRealData will read the data.
1378 CacheExternalData(formats
[f
], c
, sysPrincipal
,
1379 /* hidden = */ f
&& hasFileData
);
1385 void DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly
) {
1386 // Called during the constructor for paste events to cache the formats
1387 // available on the clipboard. As with CacheExternalDragFormats, the
1388 // data will only be retrieved when needed.
1389 NS_ASSERTION(mEventMessage
== ePaste
,
1390 "caching clipboard data for invalid event");
1392 nsCOMPtr
<nsIPrincipal
> sysPrincipal
= nsContentUtils::GetSystemPrincipal();
1394 nsTArray
<nsCString
> typesArray
;
1396 if (XRE_IsContentProcess()) {
1397 ContentChild::GetSingleton()->SendGetExternalClipboardFormats(
1398 mClipboardType
, aPlainTextOnly
, &typesArray
);
1400 GetExternalClipboardFormats(mClipboardType
, aPlainTextOnly
, &typesArray
);
1403 if (aPlainTextOnly
) {
1404 // The only thing that will be in types is kUnicodeMime
1405 MOZ_ASSERT(typesArray
.IsEmpty() || typesArray
.Length() == 1);
1406 if (typesArray
.Length() == 1) {
1407 CacheExternalData(kUnicodeMime
, 0, sysPrincipal
, false);
1412 CacheExternalData(typesArray
, sysPrincipal
);
1415 void DataTransfer::CacheTransferableFormats() {
1416 nsCOMPtr
<nsIPrincipal
> sysPrincipal
= nsContentUtils::GetSystemPrincipal();
1418 AutoTArray
<nsCString
, 10> typesArray
;
1419 GetExternalTransferableFormats(mTransferable
, false, &typesArray
);
1421 CacheExternalData(typesArray
, sysPrincipal
);
1424 void DataTransfer::CacheExternalData(const nsTArray
<nsCString
>& aTypes
,
1425 nsIPrincipal
* aPrincipal
) {
1426 bool hasFileData
= false;
1427 for (const nsCString
& type
: aTypes
) {
1428 if (type
.EqualsLiteral(kCustomTypesMime
)) {
1429 FillInExternalCustomTypes(0, aPrincipal
);
1430 } else if (type
.EqualsLiteral(kFileMime
) && XRE_IsContentProcess()) {
1431 // We will be ignoring any application/x-moz-file files found in the paste
1432 // datatransfer within e10s, as they will fail top be sent over IPC.
1433 // Because of that, we will unset hasFileData, whether or not it would
1434 // have been set. (bug 1308007)
1435 hasFileData
= false;
1438 // We expect that if kFileMime is supported, then it will be the either at
1439 // index 0 or at index 1 in the aTypes returned by
1440 // GetExternalClipboardFormats
1441 if (type
.EqualsLiteral(kFileMime
) && !XRE_IsContentProcess()) {
1444 // If we aren't the file data, and we have file data, we want to be hidden
1446 type
.get(), 0, aPrincipal
,
1447 /* hidden = */ !type
.EqualsLiteral(kFileMime
) && hasFileData
);
1452 void DataTransfer::FillAllExternalData() {
1454 for (uint32_t i
= 0; i
< MozItemCount(); ++i
) {
1455 const nsTArray
<RefPtr
<DataTransferItem
>>& items
= *mItems
->MozItemsAt(i
);
1456 for (uint32_t j
= 0; j
< items
.Length(); ++j
) {
1457 MOZ_ASSERT(items
[j
]->Index() == i
);
1459 items
[j
]->FillInExternalData();
1465 void DataTransfer::FillInExternalCustomTypes(uint32_t aIndex
,
1466 nsIPrincipal
* aPrincipal
) {
1467 RefPtr
<DataTransferItem
> item
= new DataTransferItem(
1468 this, NS_LITERAL_STRING_FROM_CSTRING(kCustomTypesMime
),
1469 DataTransferItem::KIND_STRING
);
1470 item
->SetIndex(aIndex
);
1472 nsCOMPtr
<nsIVariant
> variant
= item
->DataNoSecurityCheck();
1477 FillInExternalCustomTypes(variant
, aIndex
, aPrincipal
);
1480 void DataTransfer::FillInExternalCustomTypes(nsIVariant
* aData
, uint32_t aIndex
,
1481 nsIPrincipal
* aPrincipal
) {
1484 nsresult rv
= aData
->GetAsStringWithSize(&len
, &chrs
);
1485 if (NS_FAILED(rv
)) {
1489 CheckedInt
<int32_t> checkedLen(len
);
1490 if (!checkedLen
.isValid()) {
1494 nsCOMPtr
<nsIInputStream
> stringStream
;
1495 NS_NewByteInputStream(getter_AddRefs(stringStream
),
1496 Span(chrs
, checkedLen
.value()), NS_ASSIGNMENT_ADOPT
);
1498 nsCOMPtr
<nsIObjectInputStream
> stream
= NS_NewObjectInputStream(stringStream
);
1502 rv
= stream
->Read32(&type
);
1503 NS_ENSURE_SUCCESS_VOID(rv
);
1504 if (type
== eCustomClipboardTypeId_String
) {
1505 uint32_t formatLength
;
1506 rv
= stream
->Read32(&formatLength
);
1507 NS_ENSURE_SUCCESS_VOID(rv
);
1509 rv
= stream
->ReadBytes(formatLength
, &formatBytes
);
1510 NS_ENSURE_SUCCESS_VOID(rv
);
1511 nsAutoString format
;
1512 format
.Adopt(reinterpret_cast<char16_t
*>(formatBytes
),
1513 formatLength
/ sizeof(char16_t
));
1515 uint32_t dataLength
;
1516 rv
= stream
->Read32(&dataLength
);
1517 NS_ENSURE_SUCCESS_VOID(rv
);
1519 rv
= stream
->ReadBytes(dataLength
, &dataBytes
);
1520 NS_ENSURE_SUCCESS_VOID(rv
);
1522 data
.Adopt(reinterpret_cast<char16_t
*>(dataBytes
),
1523 dataLength
/ sizeof(char16_t
));
1525 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
1526 rv
= variant
->SetAsAString(data
);
1527 NS_ENSURE_SUCCESS_VOID(rv
);
1529 SetDataWithPrincipal(format
, variant
, aIndex
, aPrincipal
);
1531 } while (type
!= eCustomClipboardTypeId_None
);
1534 void DataTransfer::SetMode(DataTransfer::Mode aMode
) {
1535 if (!StaticPrefs::dom_events_dataTransfer_protected_enabled() &&
1536 aMode
== Mode::Protected
) {
1537 mMode
= Mode::ReadOnly
;
1543 } // namespace mozilla::dom