Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / events / DataTransfer.cpp
blob42edbdf33cc1a2535845ac61c331c62f577dfd9d
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"
18 #include "nsArray.h"
19 #include "nsError.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"
29 #include "nsCRT.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/FileList.h"
42 #include "mozilla/dom/IPCBlobUtils.h"
43 #include "mozilla/dom/BindingUtils.h"
44 #include "mozilla/dom/OSFileSystem.h"
45 #include "mozilla/dom/Promise.h"
46 #include "mozilla/dom/WindowContext.h"
47 #include "mozilla/Unused.h"
48 #include "nsComponentManagerUtils.h"
49 #include "nsNetUtil.h"
50 #include "nsReadableUtils.h"
52 namespace mozilla::dom {
54 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(DataTransfer)
56 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
57 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
58 NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
59 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
60 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
61 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
62 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
64 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
65 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
70 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
71 NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
73 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
74 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
75 NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
76 NS_INTERFACE_MAP_ENTRY(nsISupports)
77 NS_INTERFACE_MAP_END
79 // the size of the array
80 const char DataTransfer::sEffects[8][9] = {
81 "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"};
83 // Used for custom clipboard types.
84 enum CustomClipboardTypeId {
85 eCustomClipboardTypeId_None,
86 eCustomClipboardTypeId_String
89 static DataTransfer::Mode ModeForEvent(EventMessage aEventMessage) {
90 switch (aEventMessage) {
91 case eCut:
92 case eCopy:
93 case eDragStart:
94 // For these events, we want to be able to add data to the data transfer,
95 // Otherwise, the data is already present.
96 return DataTransfer::Mode::ReadWrite;
97 case eDrop:
98 case ePaste:
99 case ePasteNoFormatting:
100 case eEditorInput:
101 // For these events we want to be able to read the data which is stored in
102 // the DataTransfer, rather than just the type information.
103 return DataTransfer::Mode::ReadOnly;
104 default:
105 return StaticPrefs::dom_events_dataTransfer_protected_enabled()
106 ? DataTransfer::Mode::Protected
107 : DataTransfer::Mode::ReadOnly;
111 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
112 bool aIsExternal, int32_t aClipboardType)
113 : mParent(aParent),
114 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
115 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
116 mEventMessage(aEventMessage),
117 mCursorState(false),
118 mMode(ModeForEvent(aEventMessage)),
119 mIsExternal(aIsExternal),
120 mUserCancelled(false),
121 mIsCrossDomainSubFrameDrop(false),
122 mClipboardType(aClipboardType),
123 mDragImageX(0),
124 mDragImageY(0) {
125 mItems = new DataTransferItemList(this);
127 // For external usage, cache the data from the native clipboard or drag.
128 if (mIsExternal && mMode != Mode::ReadWrite) {
129 if (aEventMessage == ePasteNoFormatting) {
130 mEventMessage = ePaste;
131 CacheExternalClipboardFormats(true);
132 } else if (aEventMessage == ePaste) {
133 CacheExternalClipboardFormats(false);
134 } else if (aEventMessage >= eDragDropEventFirst &&
135 aEventMessage <= eDragDropEventLast) {
136 CacheExternalDragFormats();
141 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
142 nsITransferable* aTransferable)
143 : mParent(aParent),
144 mTransferable(aTransferable),
145 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
146 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
147 mEventMessage(aEventMessage),
148 mCursorState(false),
149 mMode(ModeForEvent(aEventMessage)),
150 mIsExternal(true),
151 mUserCancelled(false),
152 mIsCrossDomainSubFrameDrop(false),
153 mClipboardType(-1),
154 mDragImageX(0),
155 mDragImageY(0) {
156 mItems = new DataTransferItemList(this);
158 // XXX Currently, we cannot make DataTransfer grabs mTransferable for long
159 // time because nsITransferable is not cycle collectable but this may
160 // be grabbed by JS. Additionally, the data initializing path is too
161 // complicated (too optimized) for D&D and clipboard. They are cached
162 // only formats first, then, data of all items will be filled by the
163 // items later and by themselves. However, we shouldn't duplicate such
164 // path for saving the maintenance cost. Therefore, we need to treat
165 // that DataTransfer and its items are in external mode. Finally,
166 // release mTransferable and make them in internal mode.
167 CacheTransferableFormats();
168 FillAllExternalData();
169 // Now, we have all necessary data of mTransferable. So, we can work as
170 // internal mode.
171 mIsExternal = false;
172 // Release mTransferable because it won't be referred anymore.
173 mTransferable = nullptr;
176 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
177 const nsAString& aString)
178 : mParent(aParent),
179 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
180 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
181 mEventMessage(aEventMessage),
182 mCursorState(false),
183 mMode(ModeForEvent(aEventMessage)),
184 mIsExternal(false),
185 mUserCancelled(false),
186 mIsCrossDomainSubFrameDrop(false),
187 mClipboardType(-1),
188 mDragImageX(0),
189 mDragImageY(0) {
190 mItems = new DataTransferItemList(this);
192 nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal();
194 RefPtr<nsVariantCC> variant = new nsVariantCC();
195 variant->SetAsAString(aString);
196 DebugOnly<nsresult> rvIgnored =
197 SetDataWithPrincipal(u"text/plain"_ns, variant, 0, sysPrincipal, false);
198 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
199 "Failed to set given string to the DataTransfer object");
202 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
203 const uint32_t aEffectAllowed, bool aCursorState,
204 bool aIsExternal, bool aUserCancelled,
205 bool aIsCrossDomainSubFrameDrop,
206 int32_t aClipboardType, DataTransferItemList* aItems,
207 Element* aDragImage, uint32_t aDragImageX,
208 uint32_t aDragImageY, bool aShowFailAnimation)
209 : mParent(aParent),
210 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
211 mEffectAllowed(aEffectAllowed),
212 mEventMessage(aEventMessage),
213 mCursorState(aCursorState),
214 mMode(ModeForEvent(aEventMessage)),
215 mIsExternal(aIsExternal),
216 mUserCancelled(aUserCancelled),
217 mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop),
218 mClipboardType(aClipboardType),
219 mDragImage(aDragImage),
220 mDragImageX(aDragImageX),
221 mDragImageY(aDragImageY),
222 mShowFailAnimation(aShowFailAnimation) {
223 MOZ_ASSERT(mParent);
224 MOZ_ASSERT(aItems);
226 // We clone the items array after everything else, so that it has a valid
227 // mParent value
228 mItems = aItems->Clone(this);
229 // The items are copied from aItems into mItems. There is no need to copy
230 // the actual data in the items as the data transfer will be read only. The
231 // dragstart event is the only time when items are
232 // modifiable, but those events should have been using the first constructor
233 // above.
234 NS_ASSERTION(aEventMessage != eDragStart,
235 "invalid event type for DataTransfer constructor");
238 DataTransfer::~DataTransfer() = default;
240 // static
241 already_AddRefed<DataTransfer> DataTransfer::Constructor(
242 const GlobalObject& aGlobal) {
243 RefPtr<DataTransfer> transfer =
244 new DataTransfer(aGlobal.GetAsSupports(), eCopy, /* is external */ false,
245 /* clipboard type */ -1);
246 transfer->mEffectAllowed = nsIDragService::DRAGDROP_ACTION_NONE;
247 return transfer.forget();
250 JSObject* DataTransfer::WrapObject(JSContext* aCx,
251 JS::Handle<JSObject*> aGivenProto) {
252 return DataTransfer_Binding::Wrap(aCx, this, aGivenProto);
255 void DataTransfer::SetDropEffect(const nsAString& aDropEffect) {
256 // the drop effect can only be 'none', 'copy', 'move' or 'link'.
257 for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
258 if (aDropEffect.EqualsASCII(sEffects[e])) {
259 // don't allow copyMove
260 if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
261 nsIDragService::DRAGDROP_ACTION_MOVE)) {
262 mDropEffect = e;
264 break;
269 void DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed) {
270 if (aEffectAllowed.EqualsLiteral("uninitialized")) {
271 mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
272 return;
275 static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0,
276 "DRAGDROP_ACTION_NONE constant is wrong");
277 static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1,
278 "DRAGDROP_ACTION_COPY constant is wrong");
279 static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2,
280 "DRAGDROP_ACTION_MOVE constant is wrong");
281 static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4,
282 "DRAGDROP_ACTION_LINK constant is wrong");
284 for (uint32_t e = 0; e < ArrayLength(sEffects); e++) {
285 if (aEffectAllowed.EqualsASCII(sEffects[e])) {
286 mEffectAllowed = e;
287 break;
292 void DataTransfer::GetMozTriggeringPrincipalURISpec(
293 nsAString& aPrincipalURISpec) {
294 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
295 if (!dragSession) {
296 aPrincipalURISpec.Truncate(0);
297 return;
300 nsCOMPtr<nsIPrincipal> principal;
301 dragSession->GetTriggeringPrincipal(getter_AddRefs(principal));
302 if (!principal) {
303 aPrincipalURISpec.Truncate(0);
304 return;
307 nsAutoCString spec;
308 principal->GetAsciiSpec(spec);
309 CopyUTF8toUTF16(spec, aPrincipalURISpec);
312 nsIContentSecurityPolicy* DataTransfer::GetMozCSP() {
313 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
314 if (!dragSession) {
315 return nullptr;
317 nsCOMPtr<nsIContentSecurityPolicy> csp;
318 dragSession->GetCsp(getter_AddRefs(csp));
319 return csp;
322 already_AddRefed<FileList> DataTransfer::GetFiles(
323 nsIPrincipal& aSubjectPrincipal) {
324 return mItems->Files(&aSubjectPrincipal);
327 void DataTransfer::GetTypes(nsTArray<nsString>& aTypes,
328 CallerType aCallerType) const {
329 // When called from bindings, aTypes will be empty, but since we might have
330 // Gecko-internal callers too, clear it to be safe.
331 aTypes.Clear();
333 return mItems->GetTypes(aTypes, aCallerType);
336 bool DataTransfer::HasType(const nsAString& aType) const {
337 return mItems->HasType(aType);
340 bool DataTransfer::HasFile() const { return mItems->HasFile(); }
342 void DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
343 nsIPrincipal& aSubjectPrincipal,
344 ErrorResult& aRv) const {
345 // return an empty string if data for the format was not found
346 aData.Truncate();
348 nsCOMPtr<nsIVariant> data;
349 nsresult rv =
350 GetDataAtInternal(aFormat, 0, &aSubjectPrincipal, getter_AddRefs(data));
351 if (NS_FAILED(rv)) {
352 if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
353 aRv.Throw(rv);
355 return;
358 if (data) {
359 nsAutoString stringdata;
360 data->GetAsAString(stringdata);
362 // for the URL type, parse out the first URI from the list. The URIs are
363 // separated by newlines
364 nsAutoString lowercaseFormat;
365 nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
367 if (lowercaseFormat.EqualsLiteral("url")) {
368 int32_t lastidx = 0, idx;
369 int32_t length = stringdata.Length();
370 while (lastidx < length) {
371 idx = stringdata.FindChar('\n', lastidx);
372 // lines beginning with # are comments
373 if (stringdata[lastidx] == '#') {
374 if (idx == -1) {
375 break;
377 } else {
378 if (idx == -1) {
379 aData.Assign(Substring(stringdata, lastidx));
380 } else {
381 aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
383 aData =
384 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
385 return;
387 lastidx = idx + 1;
389 } else {
390 aData = stringdata;
395 void DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
396 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
397 RefPtr<nsVariantCC> variant = new nsVariantCC();
398 variant->SetAsAString(aData);
400 aRv = SetDataAtInternal(aFormat, variant, 0, &aSubjectPrincipal);
403 void DataTransfer::ClearData(const Optional<nsAString>& aFormat,
404 nsIPrincipal& aSubjectPrincipal,
405 ErrorResult& aRv) {
406 if (IsReadOnly()) {
407 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
408 return;
411 if (MozItemCount() == 0) {
412 return;
415 if (aFormat.WasPassed()) {
416 MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv);
417 } else {
418 MozClearDataAtHelper(u""_ns, 0, aSubjectPrincipal, aRv);
422 void DataTransfer::SetMozCursor(const nsAString& aCursorState) {
423 // Lock the cursor to an arrow during the drag.
424 mCursorState = aCursorState.EqualsLiteral("default");
427 already_AddRefed<nsINode> DataTransfer::GetMozSourceNode() {
428 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
429 if (!dragSession) {
430 return nullptr;
433 nsCOMPtr<nsINode> sourceNode;
434 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
435 if (sourceNode && !nsContentUtils::LegacyIsCallerNativeCode() &&
436 !nsContentUtils::CanCallerAccess(sourceNode)) {
437 return nullptr;
440 return sourceNode.forget();
443 already_AddRefed<WindowContext> DataTransfer::GetSourceTopWindowContext() {
444 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
445 if (!dragSession) {
446 return nullptr;
449 RefPtr<WindowContext> sourceTopWindowContext;
450 dragSession->GetSourceTopWindowContext(
451 getter_AddRefs(sourceTopWindowContext));
452 return sourceTopWindowContext.forget();
455 already_AddRefed<DOMStringList> DataTransfer::MozTypesAt(
456 uint32_t aIndex, ErrorResult& aRv) const {
457 // Only the first item is valid for clipboard events
458 if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
459 mEventMessage == ePaste)) {
460 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
461 return nullptr;
464 RefPtr<DOMStringList> types = new DOMStringList();
465 if (aIndex < MozItemCount()) {
466 // note that you can retrieve the types regardless of their principal
467 const nsTArray<RefPtr<DataTransferItem>>& items =
468 *mItems->MozItemsAt(aIndex);
470 bool addFile = false;
471 for (uint32_t i = 0; i < items.Length(); i++) {
472 // NOTE: The reason why we get the internal type here is because we want
473 // kFileMime to appear in the types list for backwards compatibility
474 // reasons.
475 nsAutoString type;
476 items[i]->GetInternalType(type);
477 if (NS_WARN_IF(!types->Add(type))) {
478 aRv.Throw(NS_ERROR_FAILURE);
479 return nullptr;
482 if (items[i]->Kind() == DataTransferItem::KIND_FILE) {
483 addFile = true;
487 if (addFile) {
488 types->Add(u"Files"_ns);
492 return types.forget();
495 nsresult DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat,
496 uint32_t aIndex,
497 nsIVariant** aData) const {
498 return GetDataAtInternal(aFormat, aIndex,
499 nsContentUtils::GetSystemPrincipal(), aData);
502 nsresult DataTransfer::GetDataAtInternal(const nsAString& aFormat,
503 uint32_t aIndex,
504 nsIPrincipal* aSubjectPrincipal,
505 nsIVariant** aData) const {
506 *aData = nullptr;
508 if (aFormat.IsEmpty()) {
509 return NS_OK;
512 if (aIndex >= MozItemCount()) {
513 return NS_ERROR_DOM_INDEX_SIZE_ERR;
516 // Only the first item is valid for clipboard events
517 if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
518 mEventMessage == ePaste)) {
519 return NS_ERROR_DOM_INDEX_SIZE_ERR;
522 nsAutoString format;
523 GetRealFormat(aFormat, format);
525 MOZ_ASSERT(aSubjectPrincipal);
527 RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex);
528 if (!item) {
529 // The index exists but there's no data for the specified format, in this
530 // case we just return undefined
531 return NS_OK;
534 // If we have chrome only content, and we aren't chrome, don't allow access
535 if (!aSubjectPrincipal->IsSystemPrincipal() && item->ChromeOnly()) {
536 return NS_OK;
539 // DataTransferItem::Data() handles the principal checks
540 ErrorResult result;
541 nsCOMPtr<nsIVariant> data = item->Data(aSubjectPrincipal, result);
542 if (NS_WARN_IF(!data || result.Failed())) {
543 return result.StealNSResult();
546 data.forget(aData);
547 return NS_OK;
550 void DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
551 uint32_t aIndex,
552 JS::MutableHandle<JS::Value> aRetval,
553 mozilla::ErrorResult& aRv) {
554 nsCOMPtr<nsIVariant> data;
555 aRv = GetDataAtInternal(aFormat, aIndex, nsContentUtils::GetSystemPrincipal(),
556 getter_AddRefs(data));
557 if (aRv.Failed()) {
558 return;
561 if (!data) {
562 aRetval.setNull();
563 return;
566 JS::Rooted<JS::Value> result(aCx);
567 if (!VariantToJsval(aCx, data, aRetval)) {
568 aRv = NS_ERROR_FAILURE;
569 return;
573 /* static */
574 bool DataTransfer::PrincipalMaySetData(const nsAString& aType,
575 nsIVariant* aData,
576 nsIPrincipal* aPrincipal) {
577 if (!aPrincipal->IsSystemPrincipal()) {
578 DataTransferItem::eKind kind = DataTransferItem::KindFromData(aData);
579 if (kind == DataTransferItem::KIND_OTHER) {
580 NS_WARNING("Disallowing adding non string/file types to DataTransfer");
581 return false;
584 // Don't allow adding internal types of the form */x-moz-*, but
585 // special-case the url types as they are simple variations of urls.
586 // In addition, allow x-moz-place flavors to be added by WebExtensions.
587 if (FindInReadable(kInternal_Mimetype_Prefix, aType) &&
588 !StringBeginsWith(aType, u"text/x-moz-url"_ns)) {
589 auto principal = BasePrincipal::Cast(aPrincipal);
590 if (!principal->AddonPolicy() ||
591 !StringBeginsWith(aType, u"text/x-moz-place"_ns)) {
592 NS_WARNING("Disallowing adding this type to DataTransfer");
593 return false;
598 return true;
601 void DataTransfer::TypesListMayHaveChanged() {
602 DataTransfer_Binding::ClearCachedTypesValue(this);
605 already_AddRefed<DataTransfer> DataTransfer::MozCloneForEvent(
606 const nsAString& aEvent, ErrorResult& aRv) {
607 RefPtr<nsAtom> atomEvt = NS_Atomize(aEvent);
608 if (!atomEvt) {
609 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
610 return nullptr;
612 EventMessage eventMessage = nsContentUtils::GetEventMessage(atomEvt);
614 RefPtr<DataTransfer> dt;
615 nsresult rv = Clone(mParent, eventMessage, false, false, getter_AddRefs(dt));
616 if (NS_FAILED(rv)) {
617 aRv.Throw(rv);
618 return nullptr;
620 return dt.forget();
623 // The order of the types matters. `kFileMime` needs to be one of the first two
624 // types.
625 static const char* kNonPlainTextExternalFormats[] = {
626 kCustomTypesMime, kFileMime, kHTMLMime, kRTFMime, kURLMime,
627 kURLDataMime, kTextMime, kPNGImageMime, kPDFJSMime};
629 /* static */
630 void DataTransfer::GetExternalClipboardFormats(const int32_t& aWhichClipboard,
631 const bool& aPlainTextOnly,
632 nsTArray<nsCString>* aResult) {
633 MOZ_ASSERT(aResult);
635 // NOTE: When you change this method, you may need to change
636 // GetExternalTransferableFormats() too since those methods should
637 // work similarly.
639 nsCOMPtr<nsIClipboard> clipboard =
640 do_GetService("@mozilla.org/widget/clipboard;1");
641 if (!clipboard || aWhichClipboard < 0) {
642 return;
645 if (aPlainTextOnly) {
646 bool hasType;
647 AutoTArray<nsCString, 1> textMime = {nsDependentCString(kTextMime)};
648 nsresult rv =
649 clipboard->HasDataMatchingFlavors(textMime, aWhichClipboard, &hasType);
650 NS_SUCCEEDED(rv);
651 if (hasType) {
652 aResult->AppendElement(kTextMime);
654 return;
657 // If not plain text only, then instead check all the other types
658 for (uint32_t f = 0; f < mozilla::ArrayLength(kNonPlainTextExternalFormats);
659 ++f) {
660 bool hasType;
661 AutoTArray<nsCString, 1> format = {
662 nsDependentCString(kNonPlainTextExternalFormats[f])};
663 nsresult rv =
664 clipboard->HasDataMatchingFlavors(format, aWhichClipboard, &hasType);
665 NS_SUCCEEDED(rv);
666 if (hasType) {
667 aResult->AppendElement(kNonPlainTextExternalFormats[f]);
672 /* static */
673 void DataTransfer::GetExternalTransferableFormats(
674 nsITransferable* aTransferable, bool aPlainTextOnly,
675 nsTArray<nsCString>* aResult) {
676 MOZ_ASSERT(aTransferable);
677 MOZ_ASSERT(aResult);
679 aResult->Clear();
681 // NOTE: When you change this method, you may need to change
682 // GetExternalClipboardFormats() too since those methods should
683 // work similarly.
685 AutoTArray<nsCString, 10> flavors;
686 aTransferable->FlavorsTransferableCanExport(flavors);
688 if (aPlainTextOnly) {
689 auto index = flavors.IndexOf(nsLiteralCString(kTextMime));
690 if (index != flavors.NoIndex) {
691 aResult->AppendElement(nsLiteralCString(kTextMime));
693 return;
696 // If not plain text only, then instead check all the other types
697 for (const char* format : kNonPlainTextExternalFormats) {
698 auto index = flavors.IndexOf(nsCString(format));
699 if (index != flavors.NoIndex) {
700 aResult->AppendElement(nsCString(format));
705 nsresult DataTransfer::SetDataAtInternal(const nsAString& aFormat,
706 nsIVariant* aData, uint32_t aIndex,
707 nsIPrincipal* aSubjectPrincipal) {
708 if (aFormat.IsEmpty()) {
709 return NS_OK;
712 if (IsReadOnly()) {
713 return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
716 // Specifying an index less than the current length will replace an existing
717 // item. Specifying an index equal to the current length will add a new item.
718 if (aIndex > MozItemCount()) {
719 return NS_ERROR_DOM_INDEX_SIZE_ERR;
722 // Only the first item is valid for clipboard events
723 if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
724 mEventMessage == ePaste)) {
725 return NS_ERROR_DOM_INDEX_SIZE_ERR;
728 // Don't allow the custom type to be assigned.
729 if (aFormat.EqualsLiteral(kCustomTypesMime)) {
730 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
733 if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) {
734 return NS_ERROR_DOM_SECURITY_ERR;
737 return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
740 void DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
741 JS::Handle<JS::Value> aData, uint32_t aIndex,
742 ErrorResult& aRv) {
743 nsCOMPtr<nsIVariant> data;
744 aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
745 getter_AddRefs(data));
746 if (!aRv.Failed()) {
747 aRv = SetDataAtInternal(aFormat, data, aIndex,
748 nsContentUtils::GetSystemPrincipal());
752 void DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
753 ErrorResult& aRv) {
754 if (IsReadOnly()) {
755 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
756 return;
759 if (aIndex >= MozItemCount()) {
760 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
761 return;
764 // Only the first item is valid for clipboard events
765 if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy ||
766 mEventMessage == ePaste)) {
767 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
768 return;
771 MozClearDataAtHelper(aFormat, aIndex, *nsContentUtils::GetSystemPrincipal(),
772 aRv);
774 // If we just cleared the 0-th index, and there are still more than 1 indexes
775 // remaining, MozClearDataAt should cause the 1st index to become the 0th
776 // index. This should _only_ happen when the MozClearDataAt function is
777 // explicitly called by script, as this behavior is inconsistent with spec.
778 // (however, so is the MozClearDataAt API)
780 if (aIndex == 0 && mItems->MozItemCount() > 1 &&
781 mItems->MozItemsAt(0)->Length() == 0) {
782 mItems->PopIndexZero();
786 void DataTransfer::MozClearDataAtHelper(const nsAString& aFormat,
787 uint32_t aIndex,
788 nsIPrincipal& aSubjectPrincipal,
789 ErrorResult& aRv) {
790 MOZ_ASSERT(!IsReadOnly());
791 MOZ_ASSERT(aIndex < MozItemCount());
792 MOZ_ASSERT(aIndex == 0 || (mEventMessage != eCut && mEventMessage != eCopy &&
793 mEventMessage != ePaste));
795 nsAutoString format;
796 GetRealFormat(aFormat, format);
798 mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
801 void DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY) {
802 if (!IsReadOnly()) {
803 mDragImage = &aImage;
804 mDragImageX = aX;
805 mDragImageY = aY;
809 void DataTransfer::UpdateDragImage(Element& aImage, int32_t aX, int32_t aY) {
810 if (mEventMessage < eDragDropEventFirst ||
811 mEventMessage > eDragDropEventLast) {
812 return;
815 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
816 if (dragSession) {
817 dragSession->UpdateDragImage(&aImage, aX, aY);
821 void DataTransfer::AddElement(Element& aElement, ErrorResult& aRv) {
822 if (IsReadOnly()) {
823 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
824 return;
827 mDragTarget = &aElement;
830 nsresult DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
831 bool aUserCancelled,
832 bool aIsCrossDomainSubFrameDrop,
833 DataTransfer** aNewDataTransfer) {
834 RefPtr<DataTransfer> newDataTransfer = new DataTransfer(
835 aParent, aEventMessage, mEffectAllowed, mCursorState, mIsExternal,
836 aUserCancelled, aIsCrossDomainSubFrameDrop, mClipboardType, mItems,
837 mDragImage, mDragImageX, mDragImageY, mShowFailAnimation);
839 newDataTransfer.forget(aNewDataTransfer);
840 return NS_OK;
843 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
844 nsINode* aDragTarget) {
845 MOZ_ASSERT(aDragTarget);
847 Document* doc = aDragTarget->GetComposedDoc();
848 if (!doc) {
849 return nullptr;
852 return GetTransferables(doc->GetLoadContext());
855 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
856 nsILoadContext* aLoadContext) {
857 nsCOMPtr<nsIMutableArray> transArray = nsArray::Create();
858 if (!transArray) {
859 return nullptr;
862 uint32_t count = MozItemCount();
863 for (uint32_t i = 0; i < count; i++) {
864 nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
865 if (transferable) {
866 transArray->AppendElement(transferable);
870 return transArray.forget();
873 already_AddRefed<nsITransferable> DataTransfer::GetTransferable(
874 uint32_t aIndex, nsILoadContext* aLoadContext) {
875 if (aIndex >= MozItemCount()) {
876 return nullptr;
879 const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
880 uint32_t count = item.Length();
881 if (!count) {
882 return nullptr;
885 nsCOMPtr<nsITransferable> transferable =
886 do_CreateInstance("@mozilla.org/widget/transferable;1");
887 if (!transferable) {
888 return nullptr;
890 transferable->Init(aLoadContext);
892 nsCOMPtr<nsIStorageStream> storageStream;
893 nsCOMPtr<nsIObjectOutputStream> stream;
895 bool added = false;
896 bool handlingCustomFormats = true;
898 // When writing the custom data, we need to ensure that there is sufficient
899 // space for a (uint32_t) data ending type, and the null byte character at
900 // the end of the nsCString. We claim that space upfront and store it in
901 // baseLength. This value will be set to zero if a write error occurs
902 // indicating that the data and length are no longer valid.
903 const uint32_t baseLength = sizeof(uint32_t) + 1;
904 uint32_t totalCustomLength = baseLength;
907 * Two passes are made here to iterate over all of the types. First, look for
908 * any types that are not in the list of known types. For this pass,
909 * handlingCustomFormats will be true. Data that corresponds to unknown types
910 * will be pulled out and inserted into a single type (kCustomTypesMime) by
911 * writing the data into a stream.
913 * The second pass will iterate over the formats looking for known types.
914 * These are added as is. The unknown types are all then inserted as a single
915 * type (kCustomTypesMime) in the same position of the first custom type. This
916 * model is used to maintain the format order as best as possible.
918 * The format of the kCustomTypesMime type is one or more of the following
919 * stored sequentially:
920 * <32-bit> type (only none or string is supported)
921 * <32-bit> length of format
922 * <wide string> format
923 * <32-bit> length of data
924 * <wide string> data
925 * A type of eCustomClipboardTypeId_None ends the list, without any following
926 * data.
928 do {
929 for (uint32_t f = 0; f < count; f++) {
930 RefPtr<DataTransferItem> formatitem = item[f];
931 nsCOMPtr<nsIVariant> variant = formatitem->DataNoSecurityCheck();
932 if (!variant) { // skip empty items
933 continue;
936 nsAutoString type;
937 formatitem->GetInternalType(type);
939 // If the data is of one of the well-known formats, use it directly.
940 bool isCustomFormat = true;
941 for (const char* format : kKnownFormats) {
942 if (type.EqualsASCII(format)) {
943 isCustomFormat = false;
944 break;
948 uint32_t lengthInBytes;
949 nsCOMPtr<nsISupports> convertedData;
951 if (handlingCustomFormats) {
952 if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
953 &lengthInBytes)) {
954 continue;
957 // When handling custom types, add the data to the stream if this is a
958 // custom type. If totalCustomLength is 0, then a write error occurred
959 // on a previous item, so ignore any others.
960 if (isCustomFormat && totalCustomLength > 0) {
961 // If it isn't a string, just ignore it. The dataTransfer is cached in
962 // the drag sesion during drag-and-drop, so non-strings will be
963 // available when dragging locally.
964 nsCOMPtr<nsISupportsString> str(do_QueryInterface(convertedData));
965 if (str) {
966 nsAutoString data;
967 str->GetData(data);
969 if (!stream) {
970 // Create a storage stream to write to.
971 NS_NewStorageStream(1024, UINT32_MAX,
972 getter_AddRefs(storageStream));
974 nsCOMPtr<nsIOutputStream> outputStream;
975 storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
977 stream = NS_NewObjectOutputStream(outputStream);
980 CheckedInt<uint32_t> formatLength =
981 CheckedInt<uint32_t>(type.Length()) *
982 sizeof(nsString::char_type);
984 // The total size of the stream is the format length, the data
985 // length, two integers to hold the lengths and one integer for
986 // the string flag. Guard against large data by ignoring any that
987 // don't fit.
988 CheckedInt<uint32_t> newSize = formatLength + totalCustomLength +
989 lengthInBytes +
990 (sizeof(uint32_t) * 3);
991 if (newSize.isValid()) {
992 // If a write error occurs, set totalCustomLength to 0 so that
993 // further processing gets ignored.
994 nsresult rv = stream->Write32(eCustomClipboardTypeId_String);
995 if (NS_WARN_IF(NS_FAILED(rv))) {
996 totalCustomLength = 0;
997 continue;
999 rv = stream->Write32(formatLength.value());
1000 if (NS_WARN_IF(NS_FAILED(rv))) {
1001 totalCustomLength = 0;
1002 continue;
1004 MOZ_ASSERT(formatLength.isValid() &&
1005 formatLength.value() ==
1006 type.Length() * sizeof(nsString::char_type),
1007 "Why is formatLength off?");
1008 rv = stream->WriteBytes(
1009 AsBytes(Span(type.BeginReading(), type.Length())));
1010 if (NS_WARN_IF(NS_FAILED(rv))) {
1011 totalCustomLength = 0;
1012 continue;
1014 rv = stream->Write32(lengthInBytes);
1015 if (NS_WARN_IF(NS_FAILED(rv))) {
1016 totalCustomLength = 0;
1017 continue;
1019 // XXXbz it's not obvious to me that lengthInBytes is the actual
1020 // length of "data" if the variant contained an nsISupportsString
1021 // as VTYPE_INTERFACE, say. We used lengthInBytes above for
1022 // sizing, so just keep doing that.
1023 rv = stream->WriteBytes(
1024 Span(reinterpret_cast<const uint8_t*>(data.BeginReading()),
1025 lengthInBytes));
1026 if (NS_WARN_IF(NS_FAILED(rv))) {
1027 totalCustomLength = 0;
1028 continue;
1031 totalCustomLength = newSize.value();
1035 } else if (isCustomFormat && stream) {
1036 // This is the second pass of the loop (handlingCustomFormats is false).
1037 // When encountering the first custom format, append all of the stream
1038 // at this position. If totalCustomLength is 0 indicating a write error
1039 // occurred, or no data has been added to it, don't output anything,
1040 if (totalCustomLength > baseLength) {
1041 // Write out an end of data terminator.
1042 nsresult rv = stream->Write32(eCustomClipboardTypeId_None);
1043 if (NS_SUCCEEDED(rv)) {
1044 nsCOMPtr<nsIInputStream> inputStream;
1045 storageStream->NewInputStream(0, getter_AddRefs(inputStream));
1047 RefPtr<nsStringBuffer> stringBuffer =
1048 nsStringBuffer::Alloc(totalCustomLength);
1050 // Subtract off the null terminator when reading.
1051 totalCustomLength--;
1053 // Read the data from the stream and add a null-terminator as
1054 // ToString needs it.
1055 uint32_t amountRead;
1056 rv = inputStream->Read(static_cast<char*>(stringBuffer->Data()),
1057 totalCustomLength, &amountRead);
1058 if (NS_SUCCEEDED(rv)) {
1059 static_cast<char*>(stringBuffer->Data())[amountRead] = 0;
1061 nsCString str;
1062 stringBuffer->ToString(totalCustomLength, str);
1063 nsCOMPtr<nsISupportsCString> strSupports(
1064 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1065 strSupports->SetData(str);
1067 nsresult rv =
1068 transferable->SetTransferData(kCustomTypesMime, strSupports);
1069 if (NS_FAILED(rv)) {
1070 return nullptr;
1073 added = true;
1078 // Clear the stream so it doesn't get used again.
1079 stream = nullptr;
1080 } else {
1081 // This is the second pass of the loop and a known type is encountered.
1082 // Add it as is.
1083 if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
1084 &lengthInBytes)) {
1085 continue;
1088 NS_ConvertUTF16toUTF8 format(type);
1090 // If a converter is set for a format, set the converter for the
1091 // transferable and don't add the item
1092 nsCOMPtr<nsIFormatConverter> converter =
1093 do_QueryInterface(convertedData);
1094 if (converter) {
1095 transferable->AddDataFlavor(format.get());
1096 transferable->SetConverter(converter);
1097 continue;
1100 nsresult rv =
1101 transferable->SetTransferData(format.get(), convertedData);
1102 if (NS_FAILED(rv)) {
1103 return nullptr;
1106 added = true;
1110 handlingCustomFormats = !handlingCustomFormats;
1111 } while (!handlingCustomFormats);
1113 // only return the transferable if data was successfully added to it
1114 if (added) {
1115 return transferable.forget();
1118 return nullptr;
1121 bool DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
1122 nsISupports** aSupports,
1123 uint32_t* aLength) const {
1124 *aSupports = nullptr;
1125 *aLength = 0;
1127 uint16_t type = aVariant->GetDataType();
1128 if (type == nsIDataType::VTYPE_INTERFACE ||
1129 type == nsIDataType::VTYPE_INTERFACE_IS) {
1130 nsCOMPtr<nsISupports> data;
1131 if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) {
1132 return false;
1135 // For flavour data providers, use 0 as the length.
1136 if (nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data)) {
1137 fdp.forget(aSupports);
1138 *aLength = 0;
1139 return true;
1142 // Only use the underlying BlobImpl for transferables.
1143 if (RefPtr<Blob> blob = do_QueryObject(data)) {
1144 RefPtr<BlobImpl> blobImpl = blob->Impl();
1145 blobImpl.forget(aSupports);
1146 } else {
1147 data.forget(aSupports);
1150 *aLength = sizeof(nsISupports*);
1151 return true;
1154 nsAutoString str;
1155 nsresult rv = aVariant->GetAsAString(str);
1156 if (NS_FAILED(rv)) {
1157 return false;
1160 nsCOMPtr<nsISupportsString> strSupports(
1161 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1162 if (!strSupports) {
1163 return false;
1166 strSupports->SetData(str);
1168 strSupports.forget(aSupports);
1170 // each character is two bytes
1171 *aLength = str.Length() * 2;
1173 return true;
1176 void DataTransfer::Disconnect() {
1177 SetMode(Mode::Protected);
1178 if (StaticPrefs::dom_events_dataTransfer_protected_enabled()) {
1179 ClearAll();
1183 void DataTransfer::ClearAll() { mItems->ClearAllItems(); }
1185 uint32_t DataTransfer::MozItemCount() const { return mItems->MozItemCount(); }
1187 nsresult DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
1188 nsIVariant* aData, uint32_t aIndex,
1189 nsIPrincipal* aPrincipal,
1190 bool aHidden) {
1191 nsAutoString format;
1192 GetRealFormat(aFormat, format);
1194 ErrorResult rv;
1195 RefPtr<DataTransferItem> item =
1196 mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1197 /* aInsertOnly = */ false, aHidden, rv);
1198 return rv.StealNSResult();
1201 void DataTransfer::SetDataWithPrincipalFromOtherProcess(
1202 const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex,
1203 nsIPrincipal* aPrincipal, bool aHidden) {
1204 if (aFormat.EqualsLiteral(kCustomTypesMime)) {
1205 FillInExternalCustomTypes(aData, aIndex, aPrincipal);
1206 } else {
1207 nsAutoString format;
1208 GetRealFormat(aFormat, format);
1210 ErrorResult rv;
1211 RefPtr<DataTransferItem> item =
1212 mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1213 /* aInsertOnly = */ false, aHidden, rv);
1214 if (NS_WARN_IF(rv.Failed())) {
1215 rv.SuppressException();
1220 void DataTransfer::GetRealFormat(const nsAString& aInFormat,
1221 nsAString& aOutFormat) const {
1222 // For compatibility, treat text/unicode as equivalent to text/plain
1223 nsAutoString lowercaseFormat;
1224 nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
1225 if (lowercaseFormat.EqualsLiteral("text") ||
1226 lowercaseFormat.EqualsLiteral("text/unicode")) {
1227 aOutFormat.AssignLiteral("text/plain");
1228 return;
1231 if (lowercaseFormat.EqualsLiteral("url")) {
1232 aOutFormat.AssignLiteral("text/uri-list");
1233 return;
1236 aOutFormat.Assign(lowercaseFormat);
1239 nsresult DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
1240 nsIPrincipal* aPrincipal,
1241 bool aHidden) {
1242 ErrorResult rv;
1243 RefPtr<DataTransferItem> item;
1245 if (strcmp(aFormat, kTextMime) == 0) {
1246 item = mItems->SetDataWithPrincipal(u"text/plain"_ns, nullptr, aIndex,
1247 aPrincipal, false, aHidden, rv);
1248 if (NS_WARN_IF(rv.Failed())) {
1249 return rv.StealNSResult();
1251 return NS_OK;
1254 if (strcmp(aFormat, kURLDataMime) == 0) {
1255 item = mItems->SetDataWithPrincipal(u"text/uri-list"_ns, nullptr, aIndex,
1256 aPrincipal, false, aHidden, rv);
1257 if (NS_WARN_IF(rv.Failed())) {
1258 return rv.StealNSResult();
1260 return NS_OK;
1263 nsAutoString format;
1264 GetRealFormat(NS_ConvertUTF8toUTF16(aFormat), format);
1265 item = mItems->SetDataWithPrincipal(format, nullptr, aIndex, aPrincipal,
1266 false, aHidden, rv);
1267 if (NS_WARN_IF(rv.Failed())) {
1268 return rv.StealNSResult();
1270 return NS_OK;
1273 void DataTransfer::CacheExternalDragFormats() {
1274 // Called during the constructor to cache the formats available from an
1275 // external drag. The data associated with each format will be set to null.
1276 // This data will instead only be retrieved in FillInExternalDragData when
1277 // asked for, as it may be time consuming for the source application to
1278 // generate it.
1280 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
1281 if (!dragSession) {
1282 return;
1285 // make sure that the system principal is used for external drags
1286 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1287 nsCOMPtr<nsIPrincipal> sysPrincipal;
1288 ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
1290 // there isn't a way to get a list of the formats that might be available on
1291 // all platforms, so just check for the types that can actually be imported
1292 // XXXndeakin there are some other formats but those are platform specific.
1293 // NOTE: kFileMime must have index 0
1294 // TODO: should this be `kNonPlainTextExternalFormats` instead?
1295 static const char* formats[] = {kFileMime, kHTMLMime, kURLMime,
1296 kURLDataMime, kTextMime, kPNGImageMime};
1298 uint32_t count;
1299 dragSession->GetNumDropItems(&count);
1300 for (uint32_t c = 0; c < count; c++) {
1301 bool hasFileData = false;
1302 dragSession->IsDataFlavorSupported(kFileMime, &hasFileData);
1304 // First, check for the special format that holds custom types.
1305 bool supported;
1306 dragSession->IsDataFlavorSupported(kCustomTypesMime, &supported);
1307 if (supported) {
1308 FillInExternalCustomTypes(c, sysPrincipal);
1311 for (uint32_t f = 0; f < ArrayLength(formats); f++) {
1312 // IsDataFlavorSupported doesn't take an index as an argument and just
1313 // checks if any of the items support a particular flavor, even though
1314 // the GetData method does take an index. Here, we just assume that
1315 // every item being dragged has the same set of flavors.
1316 bool supported;
1317 dragSession->IsDataFlavorSupported(formats[f], &supported);
1318 // if the format is supported, add an item to the array with null as
1319 // the data. When retrieved, GetRealData will read the data.
1320 if (supported) {
1321 CacheExternalData(formats[f], c, sysPrincipal,
1322 /* hidden = */ f && hasFileData);
1328 void DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly) {
1329 // Called during the constructor for paste events to cache the formats
1330 // available on the clipboard. As with CacheExternalDragFormats, the
1331 // data will only be retrieved when needed.
1332 NS_ASSERTION(mEventMessage == ePaste,
1333 "caching clipboard data for invalid event");
1335 nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal();
1337 nsTArray<nsCString> typesArray;
1339 if (XRE_IsContentProcess()) {
1340 ContentChild::GetSingleton()->SendGetExternalClipboardFormats(
1341 mClipboardType, aPlainTextOnly, &typesArray);
1342 } else {
1343 GetExternalClipboardFormats(mClipboardType, aPlainTextOnly, &typesArray);
1346 if (aPlainTextOnly) {
1347 // The only thing that will be in types is kTextMime
1348 MOZ_ASSERT(typesArray.IsEmpty() || typesArray.Length() == 1);
1349 if (typesArray.Length() == 1) {
1350 CacheExternalData(kTextMime, 0, sysPrincipal, false);
1352 return;
1355 CacheExternalData(typesArray, sysPrincipal);
1358 void DataTransfer::CacheTransferableFormats() {
1359 nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal();
1361 AutoTArray<nsCString, 10> typesArray;
1362 GetExternalTransferableFormats(mTransferable, false, &typesArray);
1364 CacheExternalData(typesArray, sysPrincipal);
1367 void DataTransfer::CacheExternalData(const nsTArray<nsCString>& aTypes,
1368 nsIPrincipal* aPrincipal) {
1369 bool hasFileData = false;
1370 for (const nsCString& type : aTypes) {
1371 if (type.EqualsLiteral(kCustomTypesMime)) {
1372 FillInExternalCustomTypes(0, aPrincipal);
1373 } else if (type.EqualsLiteral(kFileMime) && XRE_IsContentProcess() &&
1374 !StaticPrefs::dom_events_dataTransfer_mozFile_enabled()) {
1375 // We will be ignoring any application/x-moz-file files found in the paste
1376 // datatransfer within e10s, as they will fail top be sent over IPC.
1377 // Because of that, we will unset hasFileData, whether or not it would
1378 // have been set. (bug 1308007)
1379 hasFileData = false;
1380 continue;
1381 } else {
1382 // We expect that if kFileMime is supported, then it will be the either at
1383 // index 0 or at index 1 in the aTypes returned by
1384 // GetExternalClipboardFormats
1385 if (type.EqualsLiteral(kFileMime)) {
1386 hasFileData = true;
1389 // If we aren't the file data, and we have file data, we want to be hidden
1390 CacheExternalData(
1391 type.get(), 0, aPrincipal,
1392 /* hidden = */ !type.EqualsLiteral(kFileMime) && hasFileData);
1397 void DataTransfer::FillAllExternalData() {
1398 if (mIsExternal) {
1399 for (uint32_t i = 0; i < MozItemCount(); ++i) {
1400 const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(i);
1401 for (uint32_t j = 0; j < items.Length(); ++j) {
1402 MOZ_ASSERT(items[j]->Index() == i);
1404 items[j]->FillInExternalData();
1410 void DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
1411 nsIPrincipal* aPrincipal) {
1412 RefPtr<DataTransferItem> item = new DataTransferItem(
1413 this, NS_LITERAL_STRING_FROM_CSTRING(kCustomTypesMime),
1414 DataTransferItem::KIND_STRING);
1415 item->SetIndex(aIndex);
1417 nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
1418 if (!variant) {
1419 return;
1422 FillInExternalCustomTypes(variant, aIndex, aPrincipal);
1425 void DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
1426 nsIPrincipal* aPrincipal) {
1427 char* chrs;
1428 uint32_t len = 0;
1429 nsresult rv = aData->GetAsStringWithSize(&len, &chrs);
1430 if (NS_FAILED(rv)) {
1431 return;
1434 CheckedInt<int32_t> checkedLen(len);
1435 if (!checkedLen.isValid()) {
1436 return;
1439 nsCOMPtr<nsIInputStream> stringStream;
1440 NS_NewByteInputStream(getter_AddRefs(stringStream),
1441 Span(chrs, checkedLen.value()), NS_ASSIGNMENT_ADOPT);
1443 nsCOMPtr<nsIObjectInputStream> stream = NS_NewObjectInputStream(stringStream);
1445 uint32_t type;
1446 do {
1447 rv = stream->Read32(&type);
1448 NS_ENSURE_SUCCESS_VOID(rv);
1449 if (type == eCustomClipboardTypeId_String) {
1450 uint32_t formatLength;
1451 rv = stream->Read32(&formatLength);
1452 NS_ENSURE_SUCCESS_VOID(rv);
1453 char* formatBytes;
1454 rv = stream->ReadBytes(formatLength, &formatBytes);
1455 NS_ENSURE_SUCCESS_VOID(rv);
1456 nsAutoString format;
1457 format.Adopt(reinterpret_cast<char16_t*>(formatBytes),
1458 formatLength / sizeof(char16_t));
1460 uint32_t dataLength;
1461 rv = stream->Read32(&dataLength);
1462 NS_ENSURE_SUCCESS_VOID(rv);
1463 char* dataBytes;
1464 rv = stream->ReadBytes(dataLength, &dataBytes);
1465 NS_ENSURE_SUCCESS_VOID(rv);
1466 nsAutoString data;
1467 data.Adopt(reinterpret_cast<char16_t*>(dataBytes),
1468 dataLength / sizeof(char16_t));
1470 RefPtr<nsVariantCC> variant = new nsVariantCC();
1471 rv = variant->SetAsAString(data);
1472 NS_ENSURE_SUCCESS_VOID(rv);
1474 SetDataWithPrincipal(format, variant, aIndex, aPrincipal);
1476 } while (type != eCustomClipboardTypeId_None);
1479 void DataTransfer::SetMode(DataTransfer::Mode aMode) {
1480 if (!StaticPrefs::dom_events_dataTransfer_protected_enabled() &&
1481 aMode == Mode::Protected) {
1482 mMode = Mode::ReadOnly;
1483 } else {
1484 mMode = aMode;
1488 } // namespace mozilla::dom