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 "nsQueryObject.h"
35 #include "nsVariant.h"
36 #include "mozilla/dom/ContentChild.h"
37 #include "mozilla/dom/DataTransferBinding.h"
38 #include "mozilla/dom/DataTransferItemList.h"
39 #include "mozilla/dom/Directory.h"
40 #include "mozilla/dom/Element.h"
41 #include "mozilla/dom/Event.h"
42 #include "mozilla/dom/FileList.h"
43 #include "mozilla/dom/IPCBlobUtils.h"
44 #include "mozilla/dom/BindingUtils.h"
45 #include "mozilla/dom/OSFileSystem.h"
46 #include "mozilla/dom/Promise.h"
47 #include "mozilla/dom/WindowContext.h"
48 #include "mozilla/Unused.h"
49 #include "nsComponentManagerUtils.h"
50 #include "nsNetUtil.h"
51 #include "nsReadableUtils.h"
53 namespace mozilla::dom
{
55 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(DataTransfer
)
57 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer
)
58 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent
)
59 NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems
)
60 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget
)
61 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage
)
62 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
63 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
64 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer
)
65 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent
)
66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems
)
67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget
)
68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage
)
69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
71 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer
)
72 NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer
)
74 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer
)
75 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
76 NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer
)
77 NS_INTERFACE_MAP_ENTRY(nsISupports
)
80 // the size of the array
81 const char DataTransfer::sEffects
[8][9] = {
82 "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"};
84 // Used for custom clipboard types.
85 enum CustomClipboardTypeId
{
86 eCustomClipboardTypeId_None
,
87 eCustomClipboardTypeId_String
90 static DataTransfer::Mode
ModeForEvent(EventMessage aEventMessage
) {
91 switch (aEventMessage
) {
95 // For these events, we want to be able to add data to the data transfer,
96 // Otherwise, the data is already present.
97 return DataTransfer::Mode::ReadWrite
;
100 case ePasteNoFormatting
:
102 // For these events we want to be able to read the data which is stored in
103 // the DataTransfer, rather than just the type information.
104 return DataTransfer::Mode::ReadOnly
;
106 return StaticPrefs::dom_events_dataTransfer_protected_enabled()
107 ? DataTransfer::Mode::Protected
108 : DataTransfer::Mode::ReadOnly
;
112 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
113 bool aIsExternal
, int32_t aClipboardType
)
115 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
116 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
),
117 mEventMessage(aEventMessage
),
119 mMode(ModeForEvent(aEventMessage
)),
120 mIsExternal(aIsExternal
),
121 mUserCancelled(false),
122 mIsCrossDomainSubFrameDrop(false),
123 mClipboardType(aClipboardType
),
126 mItems
= new DataTransferItemList(this);
128 // For external usage, cache the data from the native clipboard or drag.
129 if (mIsExternal
&& mMode
!= Mode::ReadWrite
) {
130 if (aEventMessage
== ePasteNoFormatting
) {
131 mEventMessage
= ePaste
;
132 CacheExternalClipboardFormats(true);
133 } else if (aEventMessage
== ePaste
) {
134 CacheExternalClipboardFormats(false);
135 } else if (aEventMessage
>= eDragDropEventFirst
&&
136 aEventMessage
<= eDragDropEventLast
) {
137 CacheExternalDragFormats();
142 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
143 nsITransferable
* aTransferable
)
145 mTransferable(aTransferable
),
146 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
147 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
),
148 mEventMessage(aEventMessage
),
150 mMode(ModeForEvent(aEventMessage
)),
152 mUserCancelled(false),
153 mIsCrossDomainSubFrameDrop(false),
157 mItems
= new DataTransferItemList(this);
159 // XXX Currently, we cannot make DataTransfer grabs mTransferable for long
160 // time because nsITransferable is not cycle collectable but this may
161 // be grabbed by JS. Additionally, the data initializing path is too
162 // complicated (too optimized) for D&D and clipboard. They are cached
163 // only formats first, then, data of all items will be filled by the
164 // items later and by themselves. However, we shouldn't duplicate such
165 // path for saving the maintenance cost. Therefore, we need to treat
166 // that DataTransfer and its items are in external mode. Finally,
167 // release mTransferable and make them in internal mode.
168 CacheTransferableFormats();
169 FillAllExternalData();
170 // Now, we have all necessary data of mTransferable. So, we can work as
173 // Release mTransferable because it won't be referred anymore.
174 mTransferable
= nullptr;
177 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
178 const nsAString
& aString
)
180 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
181 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
),
182 mEventMessage(aEventMessage
),
184 mMode(ModeForEvent(aEventMessage
)),
186 mUserCancelled(false),
187 mIsCrossDomainSubFrameDrop(false),
191 mItems
= new DataTransferItemList(this);
193 nsCOMPtr
<nsIPrincipal
> sysPrincipal
= nsContentUtils::GetSystemPrincipal();
195 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
196 variant
->SetAsAString(aString
);
197 DebugOnly
<nsresult
> rvIgnored
=
198 SetDataWithPrincipal(u
"text/plain"_ns
, variant
, 0, sysPrincipal
, false);
199 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
200 "Failed to set given string to the DataTransfer object");
203 DataTransfer::DataTransfer(nsISupports
* aParent
, EventMessage aEventMessage
,
204 const uint32_t aEffectAllowed
, bool aCursorState
,
205 bool aIsExternal
, bool aUserCancelled
,
206 bool aIsCrossDomainSubFrameDrop
,
207 int32_t aClipboardType
, DataTransferItemList
* aItems
,
208 Element
* aDragImage
, uint32_t aDragImageX
,
209 uint32_t aDragImageY
, bool aShowFailAnimation
)
211 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE
),
212 mEffectAllowed(aEffectAllowed
),
213 mEventMessage(aEventMessage
),
214 mCursorState(aCursorState
),
215 mMode(ModeForEvent(aEventMessage
)),
216 mIsExternal(aIsExternal
),
217 mUserCancelled(aUserCancelled
),
218 mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop
),
219 mClipboardType(aClipboardType
),
220 mDragImage(aDragImage
),
221 mDragImageX(aDragImageX
),
222 mDragImageY(aDragImageY
),
223 mShowFailAnimation(aShowFailAnimation
) {
227 // We clone the items array after everything else, so that it has a valid
229 mItems
= aItems
->Clone(this);
230 // The items are copied from aItems into mItems. There is no need to copy
231 // the actual data in the items as the data transfer will be read only. The
232 // dragstart event is the only time when items are
233 // modifiable, but those events should have been using the first constructor
235 NS_ASSERTION(aEventMessage
!= eDragStart
,
236 "invalid event type for DataTransfer constructor");
239 DataTransfer::~DataTransfer() = default;
242 already_AddRefed
<DataTransfer
> DataTransfer::Constructor(
243 const GlobalObject
& aGlobal
) {
244 RefPtr
<DataTransfer
> transfer
=
245 new DataTransfer(aGlobal
.GetAsSupports(), eCopy
, /* is external */ false,
246 /* clipboard type */ -1);
247 transfer
->mEffectAllowed
= nsIDragService::DRAGDROP_ACTION_NONE
;
248 return transfer
.forget();
251 JSObject
* DataTransfer::WrapObject(JSContext
* aCx
,
252 JS::Handle
<JSObject
*> aGivenProto
) {
253 return DataTransfer_Binding::Wrap(aCx
, this, aGivenProto
);
256 void DataTransfer::SetDropEffect(const nsAString
& aDropEffect
) {
257 // the drop effect can only be 'none', 'copy', 'move' or 'link'.
258 for (uint32_t e
= 0; e
<= nsIDragService::DRAGDROP_ACTION_LINK
; e
++) {
259 if (aDropEffect
.EqualsASCII(sEffects
[e
])) {
260 // don't allow copyMove
261 if (e
!= (nsIDragService::DRAGDROP_ACTION_COPY
|
262 nsIDragService::DRAGDROP_ACTION_MOVE
)) {
270 void DataTransfer::SetEffectAllowed(const nsAString
& aEffectAllowed
) {
271 if (aEffectAllowed
.EqualsLiteral("uninitialized")) {
272 mEffectAllowed
= nsIDragService::DRAGDROP_ACTION_UNINITIALIZED
;
276 static_assert(nsIDragService::DRAGDROP_ACTION_NONE
== 0,
277 "DRAGDROP_ACTION_NONE constant is wrong");
278 static_assert(nsIDragService::DRAGDROP_ACTION_COPY
== 1,
279 "DRAGDROP_ACTION_COPY constant is wrong");
280 static_assert(nsIDragService::DRAGDROP_ACTION_MOVE
== 2,
281 "DRAGDROP_ACTION_MOVE constant is wrong");
282 static_assert(nsIDragService::DRAGDROP_ACTION_LINK
== 4,
283 "DRAGDROP_ACTION_LINK constant is wrong");
285 for (uint32_t e
= 0; e
< ArrayLength(sEffects
); e
++) {
286 if (aEffectAllowed
.EqualsASCII(sEffects
[e
])) {
293 void DataTransfer::GetMozTriggeringPrincipalURISpec(
294 nsAString
& aPrincipalURISpec
) {
295 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
297 aPrincipalURISpec
.Truncate(0);
301 nsCOMPtr
<nsIPrincipal
> principal
;
302 dragSession
->GetTriggeringPrincipal(getter_AddRefs(principal
));
304 aPrincipalURISpec
.Truncate(0);
309 principal
->GetAsciiSpec(spec
);
310 CopyUTF8toUTF16(spec
, aPrincipalURISpec
);
313 nsIContentSecurityPolicy
* DataTransfer::GetMozCSP() {
314 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
318 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
319 dragSession
->GetCsp(getter_AddRefs(csp
));
323 already_AddRefed
<FileList
> DataTransfer::GetFiles(
324 nsIPrincipal
& aSubjectPrincipal
) {
325 return mItems
->Files(&aSubjectPrincipal
);
328 void DataTransfer::GetTypes(nsTArray
<nsString
>& aTypes
,
329 CallerType aCallerType
) const {
330 // When called from bindings, aTypes will be empty, but since we might have
331 // Gecko-internal callers too, clear it to be safe.
334 return mItems
->GetTypes(aTypes
, aCallerType
);
337 bool DataTransfer::HasType(const nsAString
& aType
) const {
338 return mItems
->HasType(aType
);
341 bool DataTransfer::HasFile() const { return mItems
->HasFile(); }
343 void DataTransfer::GetData(const nsAString
& aFormat
, nsAString
& aData
,
344 nsIPrincipal
& aSubjectPrincipal
,
345 ErrorResult
& aRv
) const {
346 // return an empty string if data for the format was not found
349 nsCOMPtr
<nsIVariant
> data
;
351 GetDataAtInternal(aFormat
, 0, &aSubjectPrincipal
, getter_AddRefs(data
));
353 if (rv
!= NS_ERROR_DOM_INDEX_SIZE_ERR
) {
360 nsAutoString stringdata
;
361 data
->GetAsAString(stringdata
);
363 // for the URL type, parse out the first URI from the list. The URIs are
364 // separated by newlines
365 nsAutoString lowercaseFormat
;
366 nsContentUtils::ASCIIToLower(aFormat
, lowercaseFormat
);
368 if (lowercaseFormat
.EqualsLiteral("url")) {
369 int32_t lastidx
= 0, idx
;
370 int32_t length
= stringdata
.Length();
371 while (lastidx
< length
) {
372 idx
= stringdata
.FindChar('\n', lastidx
);
373 // lines beginning with # are comments
374 if (stringdata
[lastidx
] == '#') {
380 aData
.Assign(Substring(stringdata
, lastidx
));
382 aData
.Assign(Substring(stringdata
, lastidx
, idx
- lastidx
));
385 nsContentUtils::TrimWhitespace
<nsCRT::IsAsciiSpace
>(aData
, true);
396 void DataTransfer::SetData(const nsAString
& aFormat
, const nsAString
& aData
,
397 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
398 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
399 variant
->SetAsAString(aData
);
401 aRv
= SetDataAtInternal(aFormat
, variant
, 0, &aSubjectPrincipal
);
404 void DataTransfer::ClearData(const Optional
<nsAString
>& aFormat
,
405 nsIPrincipal
& aSubjectPrincipal
,
408 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
412 if (MozItemCount() == 0) {
416 if (aFormat
.WasPassed()) {
417 MozClearDataAtHelper(aFormat
.Value(), 0, aSubjectPrincipal
, aRv
);
419 MozClearDataAtHelper(u
""_ns
, 0, aSubjectPrincipal
, aRv
);
423 void DataTransfer::SetMozCursor(const nsAString
& aCursorState
) {
424 // Lock the cursor to an arrow during the drag.
425 mCursorState
= aCursorState
.EqualsLiteral("default");
428 already_AddRefed
<nsINode
> DataTransfer::GetMozSourceNode() {
429 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
434 nsCOMPtr
<nsINode
> sourceNode
;
435 dragSession
->GetSourceNode(getter_AddRefs(sourceNode
));
436 if (sourceNode
&& !nsContentUtils::LegacyIsCallerNativeCode() &&
437 !nsContentUtils::CanCallerAccess(sourceNode
)) {
441 return sourceNode
.forget();
444 already_AddRefed
<WindowContext
> DataTransfer::GetSourceTopWindowContext() {
445 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
450 RefPtr
<WindowContext
> sourceTopWindowContext
;
451 dragSession
->GetSourceTopWindowContext(
452 getter_AddRefs(sourceTopWindowContext
));
453 return sourceTopWindowContext
.forget();
456 already_AddRefed
<DOMStringList
> DataTransfer::MozTypesAt(
457 uint32_t aIndex
, ErrorResult
& aRv
) const {
458 // Only the first item is valid for clipboard events
459 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
460 mEventMessage
== ePaste
)) {
461 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
465 RefPtr
<DOMStringList
> types
= new DOMStringList();
466 if (aIndex
< MozItemCount()) {
467 // note that you can retrieve the types regardless of their principal
468 const nsTArray
<RefPtr
<DataTransferItem
>>& items
=
469 *mItems
->MozItemsAt(aIndex
);
471 bool addFile
= false;
472 for (uint32_t i
= 0; i
< items
.Length(); i
++) {
473 // NOTE: The reason why we get the internal type here is because we want
474 // kFileMime to appear in the types list for backwards compatibility
477 items
[i
]->GetInternalType(type
);
478 if (NS_WARN_IF(!types
->Add(type
))) {
479 aRv
.Throw(NS_ERROR_FAILURE
);
483 if (items
[i
]->Kind() == DataTransferItem::KIND_FILE
) {
489 types
->Add(u
"Files"_ns
);
493 return types
.forget();
496 nsresult
DataTransfer::GetDataAtNoSecurityCheck(const nsAString
& aFormat
,
498 nsIVariant
** aData
) const {
499 return GetDataAtInternal(aFormat
, aIndex
,
500 nsContentUtils::GetSystemPrincipal(), aData
);
503 nsresult
DataTransfer::GetDataAtInternal(const nsAString
& aFormat
,
505 nsIPrincipal
* aSubjectPrincipal
,
506 nsIVariant
** aData
) const {
509 if (aFormat
.IsEmpty()) {
513 if (aIndex
>= MozItemCount()) {
514 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
517 // Only the first item is valid for clipboard events
518 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
519 mEventMessage
== ePaste
)) {
520 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
524 GetRealFormat(aFormat
, format
);
526 MOZ_ASSERT(aSubjectPrincipal
);
528 RefPtr
<DataTransferItem
> item
= mItems
->MozItemByTypeAt(format
, aIndex
);
530 // The index exists but there's no data for the specified format, in this
531 // case we just return undefined
535 // If we have chrome only content, and we aren't chrome, don't allow access
536 if (!aSubjectPrincipal
->IsSystemPrincipal() && item
->ChromeOnly()) {
540 // DataTransferItem::Data() handles the principal checks
542 nsCOMPtr
<nsIVariant
> data
= item
->Data(aSubjectPrincipal
, result
);
543 if (NS_WARN_IF(!data
|| result
.Failed())) {
544 return result
.StealNSResult();
551 void DataTransfer::MozGetDataAt(JSContext
* aCx
, const nsAString
& aFormat
,
553 JS::MutableHandle
<JS::Value
> aRetval
,
554 mozilla::ErrorResult
& aRv
) {
555 nsCOMPtr
<nsIVariant
> data
;
556 aRv
= GetDataAtInternal(aFormat
, aIndex
, nsContentUtils::GetSystemPrincipal(),
557 getter_AddRefs(data
));
567 JS::Rooted
<JS::Value
> result(aCx
);
568 if (!VariantToJsval(aCx
, data
, aRetval
)) {
569 aRv
= NS_ERROR_FAILURE
;
575 bool DataTransfer::PrincipalMaySetData(const nsAString
& aType
,
577 nsIPrincipal
* aPrincipal
) {
578 if (!aPrincipal
->IsSystemPrincipal()) {
579 DataTransferItem::eKind kind
= DataTransferItem::KindFromData(aData
);
580 if (kind
== DataTransferItem::KIND_OTHER
) {
581 NS_WARNING("Disallowing adding non string/file types to DataTransfer");
585 // Don't allow adding internal types of the form */x-moz-*, but
586 // special-case the url types as they are simple variations of urls.
587 // In addition, allow x-moz-place flavors to be added by WebExtensions.
588 if (FindInReadable(kInternal_Mimetype_Prefix
, aType
) &&
589 !StringBeginsWith(aType
, u
"text/x-moz-url"_ns
)) {
590 auto principal
= BasePrincipal::Cast(aPrincipal
);
591 if (!principal
->AddonPolicy() ||
592 !StringBeginsWith(aType
, u
"text/x-moz-place"_ns
)) {
593 NS_WARNING("Disallowing adding this type to DataTransfer");
602 void DataTransfer::TypesListMayHaveChanged() {
603 DataTransfer_Binding::ClearCachedTypesValue(this);
606 already_AddRefed
<DataTransfer
> DataTransfer::MozCloneForEvent(
607 const nsAString
& aEvent
, ErrorResult
& aRv
) {
608 RefPtr
<nsAtom
> atomEvt
= NS_Atomize(aEvent
);
610 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
613 EventMessage eventMessage
= nsContentUtils::GetEventMessage(atomEvt
);
615 RefPtr
<DataTransfer
> dt
;
616 nsresult rv
= Clone(mParent
, eventMessage
, false, false, getter_AddRefs(dt
));
624 // The order of the types matters. `kFileMime` needs to be one of the first two
625 // types. And the order should be the same as the types order defined in
626 // MandatoryDataTypesAsCStrings() for Clipboard API.
627 static const nsCString kNonPlainTextExternalFormats
[] = {
628 nsLiteralCString(kCustomTypesMime
), nsLiteralCString(kFileMime
),
629 nsLiteralCString(kHTMLMime
), nsLiteralCString(kRTFMime
),
630 nsLiteralCString(kURLMime
), nsLiteralCString(kURLDataMime
),
631 nsLiteralCString(kTextMime
), nsLiteralCString(kPNGImageMime
),
632 nsLiteralCString(kPDFJSMime
)};
634 void DataTransfer::GetExternalClipboardFormats(const bool& aPlainTextOnly
,
635 nsTArray
<nsCString
>& aResult
) {
636 // NOTE: When you change this method, you may need to change
637 // GetExternalTransferableFormats() too since those methods should
640 MOZ_ASSERT(!mAsyncGetClipboardData
);
642 RefPtr
<WindowContext
> wc
= GetWindowContext();
643 if (NS_WARN_IF(!wc
)) {
644 MOZ_ASSERT_UNREACHABLE(
645 "How could this DataTransfer be created with a non-window global?");
649 nsCOMPtr
<nsIClipboard
> clipboard
=
650 do_GetService("@mozilla.org/widget/clipboard;1");
651 if (!clipboard
|| mClipboardType
< 0) {
655 nsresult rv
= NS_ERROR_FAILURE
;
656 nsCOMPtr
<nsIAsyncGetClipboardData
> asyncGetClipboardData
;
657 if (aPlainTextOnly
) {
658 rv
= clipboard
->GetDataSnapshotSync(
659 AutoTArray
<nsCString
, 1>{nsLiteralCString(kTextMime
)}, mClipboardType
,
660 wc
, getter_AddRefs(asyncGetClipboardData
));
662 AutoTArray
<nsCString
, ArrayLength(kNonPlainTextExternalFormats
)> formats
;
663 formats
.AppendElements(Span
<const nsCString
>(kNonPlainTextExternalFormats
));
664 rv
= clipboard
->GetDataSnapshotSync(formats
, mClipboardType
, wc
,
665 getter_AddRefs(asyncGetClipboardData
));
668 if (NS_FAILED(rv
) || !asyncGetClipboardData
) {
672 // Order is important for DataTransfer; ensure the returned list items follow
673 // the sequence specified in kNonPlainTextExternalFormats.
674 AutoTArray
<nsCString
, ArrayLength(kNonPlainTextExternalFormats
)> flavors
;
675 asyncGetClipboardData
->GetFlavorList(flavors
);
676 for (const auto& format
: kNonPlainTextExternalFormats
) {
677 if (flavors
.Contains(format
)) {
678 aResult
.AppendElement(format
);
682 mAsyncGetClipboardData
= asyncGetClipboardData
;
686 void DataTransfer::GetExternalTransferableFormats(
687 nsITransferable
* aTransferable
, bool aPlainTextOnly
,
688 nsTArray
<nsCString
>* aResult
) {
689 MOZ_ASSERT(aTransferable
);
694 // NOTE: When you change this method, you may need to change
695 // GetExternalClipboardFormats() too since those methods should
698 AutoTArray
<nsCString
, 10> flavors
;
699 aTransferable
->FlavorsTransferableCanExport(flavors
);
701 if (aPlainTextOnly
) {
702 auto index
= flavors
.IndexOf(nsLiteralCString(kTextMime
));
703 if (index
!= flavors
.NoIndex
) {
704 aResult
->AppendElement(nsLiteralCString(kTextMime
));
709 // If not plain text only, then instead check all the other types
710 for (const auto& format
: kNonPlainTextExternalFormats
) {
711 auto index
= flavors
.IndexOf(format
);
712 if (index
!= flavors
.NoIndex
) {
713 aResult
->AppendElement(format
);
718 nsresult
DataTransfer::SetDataAtInternal(const nsAString
& aFormat
,
719 nsIVariant
* aData
, uint32_t aIndex
,
720 nsIPrincipal
* aSubjectPrincipal
) {
721 if (aFormat
.IsEmpty()) {
726 return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
;
729 // Specifying an index less than the current length will replace an existing
730 // item. Specifying an index equal to the current length will add a new item.
731 if (aIndex
> MozItemCount()) {
732 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
735 // Only the first item is valid for clipboard events
736 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
737 mEventMessage
== ePaste
)) {
738 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
741 // Don't allow the custom type to be assigned.
742 if (aFormat
.EqualsLiteral(kCustomTypesMime
)) {
743 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
746 if (!PrincipalMaySetData(aFormat
, aData
, aSubjectPrincipal
)) {
747 return NS_ERROR_DOM_SECURITY_ERR
;
750 return SetDataWithPrincipal(aFormat
, aData
, aIndex
, aSubjectPrincipal
);
753 void DataTransfer::MozSetDataAt(JSContext
* aCx
, const nsAString
& aFormat
,
754 JS::Handle
<JS::Value
> aData
, uint32_t aIndex
,
756 nsCOMPtr
<nsIVariant
> data
;
757 aRv
= nsContentUtils::XPConnect()->JSValToVariant(aCx
, aData
,
758 getter_AddRefs(data
));
760 aRv
= SetDataAtInternal(aFormat
, data
, aIndex
,
761 nsContentUtils::GetSystemPrincipal());
765 void DataTransfer::MozClearDataAt(const nsAString
& aFormat
, uint32_t aIndex
,
768 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
772 if (aIndex
>= MozItemCount()) {
773 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
777 // Only the first item is valid for clipboard events
778 if (aIndex
> 0 && (mEventMessage
== eCut
|| mEventMessage
== eCopy
||
779 mEventMessage
== ePaste
)) {
780 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
784 MozClearDataAtHelper(aFormat
, aIndex
, *nsContentUtils::GetSystemPrincipal(),
787 // If we just cleared the 0-th index, and there are still more than 1 indexes
788 // remaining, MozClearDataAt should cause the 1st index to become the 0th
789 // index. This should _only_ happen when the MozClearDataAt function is
790 // explicitly called by script, as this behavior is inconsistent with spec.
791 // (however, so is the MozClearDataAt API)
793 if (aIndex
== 0 && mItems
->MozItemCount() > 1 &&
794 mItems
->MozItemsAt(0)->Length() == 0) {
795 mItems
->PopIndexZero();
799 void DataTransfer::MozClearDataAtHelper(const nsAString
& aFormat
,
801 nsIPrincipal
& aSubjectPrincipal
,
803 MOZ_ASSERT(!IsReadOnly());
804 MOZ_ASSERT(aIndex
< MozItemCount());
805 MOZ_ASSERT(aIndex
== 0 || (mEventMessage
!= eCut
&& mEventMessage
!= eCopy
&&
806 mEventMessage
!= ePaste
));
809 GetRealFormat(aFormat
, format
);
811 mItems
->MozRemoveByTypeAt(format
, aIndex
, aSubjectPrincipal
, aRv
);
814 void DataTransfer::SetDragImage(Element
& aImage
, int32_t aX
, int32_t aY
) {
816 mDragImage
= &aImage
;
822 void DataTransfer::UpdateDragImage(Element
& aImage
, int32_t aX
, int32_t aY
) {
823 if (mEventMessage
< eDragDropEventFirst
||
824 mEventMessage
> eDragDropEventLast
) {
828 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
830 dragSession
->UpdateDragImage(&aImage
, aX
, aY
);
834 void DataTransfer::AddElement(Element
& aElement
, ErrorResult
& aRv
) {
836 aRv
.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR
);
840 mDragTarget
= &aElement
;
843 nsresult
DataTransfer::Clone(nsISupports
* aParent
, EventMessage aEventMessage
,
845 bool aIsCrossDomainSubFrameDrop
,
846 DataTransfer
** aNewDataTransfer
) {
847 RefPtr
<DataTransfer
> newDataTransfer
= new DataTransfer(
848 aParent
, aEventMessage
, mEffectAllowed
, mCursorState
, mIsExternal
,
849 aUserCancelled
, aIsCrossDomainSubFrameDrop
, mClipboardType
, mItems
,
850 mDragImage
, mDragImageX
, mDragImageY
, mShowFailAnimation
);
852 newDataTransfer
.forget(aNewDataTransfer
);
856 already_AddRefed
<nsIArray
> DataTransfer::GetTransferables(
857 nsINode
* aDragTarget
) {
858 MOZ_ASSERT(aDragTarget
);
860 Document
* doc
= aDragTarget
->GetComposedDoc();
865 return GetTransferables(doc
->GetLoadContext());
868 already_AddRefed
<nsIArray
> DataTransfer::GetTransferables(
869 nsILoadContext
* aLoadContext
) {
870 nsCOMPtr
<nsIMutableArray
> transArray
= nsArray::Create();
875 uint32_t count
= MozItemCount();
876 for (uint32_t i
= 0; i
< count
; i
++) {
877 nsCOMPtr
<nsITransferable
> transferable
= GetTransferable(i
, aLoadContext
);
879 transArray
->AppendElement(transferable
);
883 return transArray
.forget();
886 already_AddRefed
<nsITransferable
> DataTransfer::GetTransferable(
887 uint32_t aIndex
, nsILoadContext
* aLoadContext
) {
888 if (aIndex
>= MozItemCount()) {
892 const nsTArray
<RefPtr
<DataTransferItem
>>& item
= *mItems
->MozItemsAt(aIndex
);
893 uint32_t count
= item
.Length();
898 nsCOMPtr
<nsITransferable
> transferable
=
899 do_CreateInstance("@mozilla.org/widget/transferable;1");
903 transferable
->Init(aLoadContext
);
905 // Set the principal of the global this DataTransfer was created for
906 // on the transferable for ReadWrite events (copy, cut, or dragstart).
908 // For other events, the data inside the transferable may originate
909 // from another origin or from the OS.
910 if (mMode
== Mode::ReadWrite
) {
911 if (nsCOMPtr
<nsIGlobalObject
> global
= GetGlobal()) {
912 transferable
->SetRequestingPrincipal(global
->PrincipalOrNull());
916 nsCOMPtr
<nsIStorageStream
> storageStream
;
917 nsCOMPtr
<nsIObjectOutputStream
> stream
;
920 bool handlingCustomFormats
= true;
922 // When writing the custom data, we need to ensure that there is sufficient
923 // space for a (uint32_t) data ending type, and the null byte character at
924 // the end of the nsCString. We claim that space upfront and store it in
925 // baseLength. This value will be set to zero if a write error occurs
926 // indicating that the data and length are no longer valid.
927 const uint32_t baseLength
= sizeof(uint32_t) + 1;
928 uint32_t totalCustomLength
= baseLength
;
931 * Two passes are made here to iterate over all of the types. First, look for
932 * any types that are not in the list of known types. For this pass,
933 * handlingCustomFormats will be true. Data that corresponds to unknown types
934 * will be pulled out and inserted into a single type (kCustomTypesMime) by
935 * writing the data into a stream.
937 * The second pass will iterate over the formats looking for known types.
938 * These are added as is. The unknown types are all then inserted as a single
939 * type (kCustomTypesMime) in the same position of the first custom type. This
940 * model is used to maintain the format order as best as possible.
942 * The format of the kCustomTypesMime type is one or more of the following
943 * stored sequentially:
944 * <32-bit> type (only none or string is supported)
945 * <32-bit> length of format
946 * <wide string> format
947 * <32-bit> length of data
949 * A type of eCustomClipboardTypeId_None ends the list, without any following
953 for (uint32_t f
= 0; f
< count
; f
++) {
954 RefPtr
<DataTransferItem
> formatitem
= item
[f
];
955 nsCOMPtr
<nsIVariant
> variant
= formatitem
->DataNoSecurityCheck();
956 if (!variant
) { // skip empty items
961 formatitem
->GetInternalType(type
);
963 // If the data is of one of the well-known formats, use it directly.
964 bool isCustomFormat
= true;
965 for (const char* format
: kKnownFormats
) {
966 if (type
.EqualsASCII(format
)) {
967 isCustomFormat
= false;
972 uint32_t lengthInBytes
;
973 nsCOMPtr
<nsISupports
> convertedData
;
975 if (handlingCustomFormats
) {
976 if (!ConvertFromVariant(variant
, getter_AddRefs(convertedData
),
981 // When handling custom types, add the data to the stream if this is a
982 // custom type. If totalCustomLength is 0, then a write error occurred
983 // on a previous item, so ignore any others.
984 if (isCustomFormat
&& totalCustomLength
> 0) {
985 // If it isn't a string, just ignore it. The dataTransfer is cached in
986 // the drag sesion during drag-and-drop, so non-strings will be
987 // available when dragging locally.
988 nsCOMPtr
<nsISupportsString
> str(do_QueryInterface(convertedData
));
994 // Create a storage stream to write to.
995 NS_NewStorageStream(1024, UINT32_MAX
,
996 getter_AddRefs(storageStream
));
998 nsCOMPtr
<nsIOutputStream
> outputStream
;
999 storageStream
->GetOutputStream(0, getter_AddRefs(outputStream
));
1001 stream
= NS_NewObjectOutputStream(outputStream
);
1004 CheckedInt
<uint32_t> formatLength
=
1005 CheckedInt
<uint32_t>(type
.Length()) *
1006 sizeof(nsString::char_type
);
1008 // The total size of the stream is the format length, the data
1009 // length, two integers to hold the lengths and one integer for
1010 // the string flag. Guard against large data by ignoring any that
1012 CheckedInt
<uint32_t> newSize
= formatLength
+ totalCustomLength
+
1014 (sizeof(uint32_t) * 3);
1015 if (newSize
.isValid()) {
1016 // If a write error occurs, set totalCustomLength to 0 so that
1017 // further processing gets ignored.
1018 nsresult rv
= stream
->Write32(eCustomClipboardTypeId_String
);
1019 if (NS_WARN_IF(NS_FAILED(rv
))) {
1020 totalCustomLength
= 0;
1023 rv
= stream
->Write32(formatLength
.value());
1024 if (NS_WARN_IF(NS_FAILED(rv
))) {
1025 totalCustomLength
= 0;
1028 MOZ_ASSERT(formatLength
.isValid() &&
1029 formatLength
.value() ==
1030 type
.Length() * sizeof(nsString::char_type
),
1031 "Why is formatLength off?");
1032 rv
= stream
->WriteBytes(
1033 AsBytes(Span(type
.BeginReading(), type
.Length())));
1034 if (NS_WARN_IF(NS_FAILED(rv
))) {
1035 totalCustomLength
= 0;
1038 rv
= stream
->Write32(lengthInBytes
);
1039 if (NS_WARN_IF(NS_FAILED(rv
))) {
1040 totalCustomLength
= 0;
1043 // XXXbz it's not obvious to me that lengthInBytes is the actual
1044 // length of "data" if the variant contained an nsISupportsString
1045 // as VTYPE_INTERFACE, say. We used lengthInBytes above for
1046 // sizing, so just keep doing that.
1047 rv
= stream
->WriteBytes(
1048 Span(reinterpret_cast<const uint8_t*>(data
.BeginReading()),
1050 if (NS_WARN_IF(NS_FAILED(rv
))) {
1051 totalCustomLength
= 0;
1055 totalCustomLength
= newSize
.value();
1059 } else if (isCustomFormat
&& stream
) {
1060 // This is the second pass of the loop (handlingCustomFormats is false).
1061 // When encountering the first custom format, append all of the stream
1062 // at this position. If totalCustomLength is 0 indicating a write error
1063 // occurred, or no data has been added to it, don't output anything,
1064 if (totalCustomLength
> baseLength
) {
1065 // Write out an end of data terminator.
1066 nsresult rv
= stream
->Write32(eCustomClipboardTypeId_None
);
1067 if (NS_SUCCEEDED(rv
)) {
1068 nsCOMPtr
<nsIInputStream
> inputStream
;
1069 storageStream
->NewInputStream(0, getter_AddRefs(inputStream
));
1071 RefPtr
<nsStringBuffer
> stringBuffer
=
1072 nsStringBuffer::Alloc(totalCustomLength
);
1074 // Subtract off the null terminator when reading.
1075 totalCustomLength
--;
1077 // Read the data from the stream and add a null-terminator as
1078 // ToString needs it.
1079 uint32_t amountRead
;
1080 rv
= inputStream
->Read(static_cast<char*>(stringBuffer
->Data()),
1081 totalCustomLength
, &amountRead
);
1082 if (NS_SUCCEEDED(rv
)) {
1083 static_cast<char*>(stringBuffer
->Data())[amountRead
] = 0;
1086 stringBuffer
->ToString(totalCustomLength
, str
);
1087 nsCOMPtr
<nsISupportsCString
> strSupports(
1088 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID
));
1089 strSupports
->SetData(str
);
1092 transferable
->SetTransferData(kCustomTypesMime
, strSupports
);
1093 if (NS_FAILED(rv
)) {
1102 // Clear the stream so it doesn't get used again.
1105 // This is the second pass of the loop and a known type is encountered.
1107 if (!ConvertFromVariant(variant
, getter_AddRefs(convertedData
),
1112 NS_ConvertUTF16toUTF8
format(type
);
1114 // If a converter is set for a format, set the converter for the
1115 // transferable and don't add the item
1116 nsCOMPtr
<nsIFormatConverter
> converter
=
1117 do_QueryInterface(convertedData
);
1119 transferable
->AddDataFlavor(format
.get());
1120 transferable
->SetConverter(converter
);
1125 transferable
->SetTransferData(format
.get(), convertedData
);
1126 if (NS_FAILED(rv
)) {
1134 handlingCustomFormats
= !handlingCustomFormats
;
1135 } while (!handlingCustomFormats
);
1137 // only return the transferable if data was successfully added to it
1139 return transferable
.forget();
1145 bool DataTransfer::ConvertFromVariant(nsIVariant
* aVariant
,
1146 nsISupports
** aSupports
,
1147 uint32_t* aLength
) const {
1148 *aSupports
= nullptr;
1151 uint16_t type
= aVariant
->GetDataType();
1152 if (type
== nsIDataType::VTYPE_INTERFACE
||
1153 type
== nsIDataType::VTYPE_INTERFACE_IS
) {
1154 nsCOMPtr
<nsISupports
> data
;
1155 if (NS_FAILED(aVariant
->GetAsISupports(getter_AddRefs(data
)))) {
1159 // For flavour data providers, use 0 as the length.
1160 if (nsCOMPtr
<nsIFlavorDataProvider
> fdp
= do_QueryInterface(data
)) {
1161 fdp
.forget(aSupports
);
1166 // Only use the underlying BlobImpl for transferables.
1167 if (RefPtr
<Blob
> blob
= do_QueryObject(data
)) {
1168 RefPtr
<BlobImpl
> blobImpl
= blob
->Impl();
1169 blobImpl
.forget(aSupports
);
1171 data
.forget(aSupports
);
1174 *aLength
= sizeof(nsISupports
*);
1179 nsresult rv
= aVariant
->GetAsAString(str
);
1180 if (NS_FAILED(rv
)) {
1184 nsCOMPtr
<nsISupportsString
> strSupports(
1185 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
));
1190 strSupports
->SetData(str
);
1192 strSupports
.forget(aSupports
);
1194 // each character is two bytes
1195 *aLength
= str
.Length() * 2;
1200 void DataTransfer::Disconnect() {
1201 SetMode(Mode::Protected
);
1202 if (StaticPrefs::dom_events_dataTransfer_protected_enabled()) {
1207 void DataTransfer::ClearAll() {
1208 mItems
->ClearAllItems();
1209 mAsyncGetClipboardData
= nullptr;
1212 uint32_t DataTransfer::MozItemCount() const { return mItems
->MozItemCount(); }
1214 nsresult
DataTransfer::SetDataWithPrincipal(const nsAString
& aFormat
,
1215 nsIVariant
* aData
, uint32_t aIndex
,
1216 nsIPrincipal
* aPrincipal
,
1218 nsAutoString format
;
1219 GetRealFormat(aFormat
, format
);
1222 RefPtr
<DataTransferItem
> item
=
1223 mItems
->SetDataWithPrincipal(format
, aData
, aIndex
, aPrincipal
,
1224 /* aInsertOnly = */ false, aHidden
, rv
);
1225 return rv
.StealNSResult();
1228 void DataTransfer::SetDataWithPrincipalFromOtherProcess(
1229 const nsAString
& aFormat
, nsIVariant
* aData
, uint32_t aIndex
,
1230 nsIPrincipal
* aPrincipal
, bool aHidden
) {
1231 if (aFormat
.EqualsLiteral(kCustomTypesMime
)) {
1232 FillInExternalCustomTypes(aData
, aIndex
, aPrincipal
);
1234 nsAutoString format
;
1235 GetRealFormat(aFormat
, format
);
1238 RefPtr
<DataTransferItem
> item
=
1239 mItems
->SetDataWithPrincipal(format
, aData
, aIndex
, aPrincipal
,
1240 /* aInsertOnly = */ false, aHidden
, rv
);
1241 if (NS_WARN_IF(rv
.Failed())) {
1242 rv
.SuppressException();
1247 void DataTransfer::GetRealFormat(const nsAString
& aInFormat
,
1248 nsAString
& aOutFormat
) const {
1249 // For compatibility, treat text/unicode as equivalent to text/plain
1250 nsAutoString lowercaseFormat
;
1251 nsContentUtils::ASCIIToLower(aInFormat
, lowercaseFormat
);
1252 if (lowercaseFormat
.EqualsLiteral("text") ||
1253 lowercaseFormat
.EqualsLiteral("text/unicode")) {
1254 aOutFormat
.AssignLiteral("text/plain");
1258 if (lowercaseFormat
.EqualsLiteral("url")) {
1259 aOutFormat
.AssignLiteral("text/uri-list");
1263 aOutFormat
.Assign(lowercaseFormat
);
1266 already_AddRefed
<nsIGlobalObject
> DataTransfer::GetGlobal() const {
1267 nsCOMPtr
<nsIGlobalObject
> global
;
1268 // This is annoying, but DataTransfer may have various things as parent.
1269 if (nsCOMPtr
<EventTarget
> target
= do_QueryInterface(mParent
)) {
1270 global
= target
->GetOwnerGlobal();
1271 } else if (RefPtr
<Event
> event
= do_QueryObject(mParent
)) {
1272 global
= event
->GetParentObject();
1275 return global
.forget();
1278 already_AddRefed
<WindowContext
> DataTransfer::GetWindowContext() const {
1279 nsCOMPtr
<nsIGlobalObject
> global
= GetGlobal();
1284 const auto* innerWindow
= global
->GetAsInnerWindow();
1289 return do_AddRef(innerWindow
->GetWindowContext());
1292 nsIAsyncGetClipboardData
* DataTransfer::GetAsyncGetClipboardData() const {
1293 return mAsyncGetClipboardData
;
1296 nsresult
DataTransfer::CacheExternalData(const char* aFormat
, uint32_t aIndex
,
1297 nsIPrincipal
* aPrincipal
,
1300 RefPtr
<DataTransferItem
> item
;
1302 if (strcmp(aFormat
, kTextMime
) == 0) {
1303 item
= mItems
->SetDataWithPrincipal(u
"text/plain"_ns
, nullptr, aIndex
,
1304 aPrincipal
, false, aHidden
, rv
);
1305 if (NS_WARN_IF(rv
.Failed())) {
1306 return rv
.StealNSResult();
1311 if (strcmp(aFormat
, kURLDataMime
) == 0) {
1312 item
= mItems
->SetDataWithPrincipal(u
"text/uri-list"_ns
, nullptr, aIndex
,
1313 aPrincipal
, false, aHidden
, rv
);
1314 if (NS_WARN_IF(rv
.Failed())) {
1315 return rv
.StealNSResult();
1320 nsAutoString format
;
1321 GetRealFormat(NS_ConvertUTF8toUTF16(aFormat
), format
);
1322 item
= mItems
->SetDataWithPrincipal(format
, nullptr, aIndex
, aPrincipal
,
1323 false, aHidden
, rv
);
1324 if (NS_WARN_IF(rv
.Failed())) {
1325 return rv
.StealNSResult();
1330 void DataTransfer::CacheExternalDragFormats() {
1331 // Called during the constructor to cache the formats available from an
1332 // external drag. The data associated with each format will be set to null.
1333 // This data will instead only be retrieved in FillInExternalDragData when
1334 // asked for, as it may be time consuming for the source application to
1337 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
1342 // make sure that the system principal is used for external drags
1343 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
1344 nsCOMPtr
<nsIPrincipal
> sysPrincipal
;
1345 ssm
->GetSystemPrincipal(getter_AddRefs(sysPrincipal
));
1347 // there isn't a way to get a list of the formats that might be available on
1348 // all platforms, so just check for the types that can actually be imported
1349 // XXXndeakin there are some other formats but those are platform specific.
1350 // NOTE: kFileMime must have index 0
1351 // TODO: should this be `kNonPlainTextExternalFormats` instead?
1352 static const char* formats
[] = {kFileMime
, kHTMLMime
, kURLMime
,
1353 kURLDataMime
, kTextMime
, 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();
1393 nsTArray
<nsCString
> typesArray
;
1394 GetExternalClipboardFormats(aPlainTextOnly
, typesArray
);
1395 if (aPlainTextOnly
) {
1396 // The only thing that will be in types is kTextMime
1397 MOZ_ASSERT(typesArray
.IsEmpty() || typesArray
.Length() == 1);
1398 if (typesArray
.Length() == 1) {
1399 MOZ_ASSERT(typesArray
.Contains(kTextMime
));
1400 CacheExternalData(kTextMime
, 0, sysPrincipal
, false);
1405 CacheExternalData(typesArray
, sysPrincipal
);
1408 void DataTransfer::CacheTransferableFormats() {
1409 nsCOMPtr
<nsIPrincipal
> sysPrincipal
= nsContentUtils::GetSystemPrincipal();
1411 AutoTArray
<nsCString
, 10> typesArray
;
1412 GetExternalTransferableFormats(mTransferable
, false, &typesArray
);
1414 CacheExternalData(typesArray
, sysPrincipal
);
1417 void DataTransfer::CacheExternalData(const nsTArray
<nsCString
>& aTypes
,
1418 nsIPrincipal
* aPrincipal
) {
1419 bool hasFileData
= false;
1420 for (const nsCString
& type
: aTypes
) {
1421 if (type
.EqualsLiteral(kCustomTypesMime
)) {
1422 FillInExternalCustomTypes(0, aPrincipal
);
1423 } else if (type
.EqualsLiteral(kFileMime
) && XRE_IsContentProcess() &&
1424 !StaticPrefs::dom_events_dataTransfer_mozFile_enabled()) {
1425 // We will be ignoring any application/x-moz-file files found in the paste
1426 // datatransfer within e10s, as they will fail top be sent over IPC.
1427 // Because of that, we will unset hasFileData, whether or not it would
1428 // have been set. (bug 1308007)
1429 hasFileData
= false;
1432 // We expect that if kFileMime is supported, then it will be the either at
1433 // index 0 or at index 1 in the aTypes returned by
1434 // GetExternalClipboardFormats
1435 if (type
.EqualsLiteral(kFileMime
)) {
1439 // If we aren't the file data, and we have file data, we want to be hidden
1441 type
.get(), 0, aPrincipal
,
1442 /* hidden = */ !type
.EqualsLiteral(kFileMime
) && hasFileData
);
1447 void DataTransfer::FillAllExternalData() {
1449 for (uint32_t i
= 0; i
< MozItemCount(); ++i
) {
1450 const nsTArray
<RefPtr
<DataTransferItem
>>& items
= *mItems
->MozItemsAt(i
);
1451 for (uint32_t j
= 0; j
< items
.Length(); ++j
) {
1452 MOZ_ASSERT(items
[j
]->Index() == i
);
1454 items
[j
]->FillInExternalData();
1460 void DataTransfer::FillInExternalCustomTypes(uint32_t aIndex
,
1461 nsIPrincipal
* aPrincipal
) {
1462 RefPtr
<DataTransferItem
> item
= new DataTransferItem(
1463 this, NS_LITERAL_STRING_FROM_CSTRING(kCustomTypesMime
),
1464 DataTransferItem::KIND_STRING
);
1465 item
->SetIndex(aIndex
);
1467 nsCOMPtr
<nsIVariant
> variant
= item
->DataNoSecurityCheck();
1472 FillInExternalCustomTypes(variant
, aIndex
, aPrincipal
);
1475 void DataTransfer::FillInExternalCustomTypes(nsIVariant
* aData
, uint32_t aIndex
,
1476 nsIPrincipal
* aPrincipal
) {
1479 nsresult rv
= aData
->GetAsStringWithSize(&len
, &chrs
);
1480 if (NS_FAILED(rv
)) {
1484 CheckedInt
<int32_t> checkedLen(len
);
1485 if (!checkedLen
.isValid()) {
1489 nsCOMPtr
<nsIInputStream
> stringStream
;
1490 NS_NewByteInputStream(getter_AddRefs(stringStream
),
1491 Span(chrs
, checkedLen
.value()), NS_ASSIGNMENT_ADOPT
);
1493 nsCOMPtr
<nsIObjectInputStream
> stream
= NS_NewObjectInputStream(stringStream
);
1497 rv
= stream
->Read32(&type
);
1498 NS_ENSURE_SUCCESS_VOID(rv
);
1499 if (type
== eCustomClipboardTypeId_String
) {
1500 uint32_t formatLength
;
1501 rv
= stream
->Read32(&formatLength
);
1502 NS_ENSURE_SUCCESS_VOID(rv
);
1504 rv
= stream
->ReadBytes(formatLength
, &formatBytes
);
1505 NS_ENSURE_SUCCESS_VOID(rv
);
1506 nsAutoString format
;
1507 format
.Adopt(reinterpret_cast<char16_t
*>(formatBytes
),
1508 formatLength
/ sizeof(char16_t
));
1510 uint32_t dataLength
;
1511 rv
= stream
->Read32(&dataLength
);
1512 NS_ENSURE_SUCCESS_VOID(rv
);
1514 rv
= stream
->ReadBytes(dataLength
, &dataBytes
);
1515 NS_ENSURE_SUCCESS_VOID(rv
);
1517 data
.Adopt(reinterpret_cast<char16_t
*>(dataBytes
),
1518 dataLength
/ sizeof(char16_t
));
1520 RefPtr
<nsVariantCC
> variant
= new nsVariantCC();
1521 rv
= variant
->SetAsAString(data
);
1522 NS_ENSURE_SUCCESS_VOID(rv
);
1524 SetDataWithPrincipal(format
, variant
, aIndex
, aPrincipal
);
1526 } while (type
!= eCustomClipboardTypeId_None
);
1529 void DataTransfer::SetMode(DataTransfer::Mode aMode
) {
1530 if (!StaticPrefs::dom_events_dataTransfer_protected_enabled() &&
1531 aMode
== Mode::Protected
) {
1532 mMode
= Mode::ReadOnly
;
1538 } // namespace mozilla::dom