Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / events / DataTransfer.cpp
blobba5657574999e0ca1ec60ae1fa8821dfdcd1423a
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/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)
78 NS_INTERFACE_MAP_END
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) {
92 case eCut:
93 case eCopy:
94 case eDragStart:
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;
98 case eDrop:
99 case ePaste:
100 case ePasteNoFormatting:
101 case eEditorInput:
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;
105 default:
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)
114 : mParent(aParent),
115 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
116 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
117 mEventMessage(aEventMessage),
118 mCursorState(false),
119 mMode(ModeForEvent(aEventMessage)),
120 mIsExternal(aIsExternal),
121 mUserCancelled(false),
122 mIsCrossDomainSubFrameDrop(false),
123 mClipboardType(aClipboardType),
124 mDragImageX(0),
125 mDragImageY(0) {
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)
144 : mParent(aParent),
145 mTransferable(aTransferable),
146 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
147 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
148 mEventMessage(aEventMessage),
149 mCursorState(false),
150 mMode(ModeForEvent(aEventMessage)),
151 mIsExternal(true),
152 mUserCancelled(false),
153 mIsCrossDomainSubFrameDrop(false),
154 mClipboardType(-1),
155 mDragImageX(0),
156 mDragImageY(0) {
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
171 // internal mode.
172 mIsExternal = false;
173 // Release mTransferable because it won't be referred anymore.
174 mTransferable = nullptr;
177 DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
178 const nsAString& aString)
179 : mParent(aParent),
180 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
181 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
182 mEventMessage(aEventMessage),
183 mCursorState(false),
184 mMode(ModeForEvent(aEventMessage)),
185 mIsExternal(false),
186 mUserCancelled(false),
187 mIsCrossDomainSubFrameDrop(false),
188 mClipboardType(-1),
189 mDragImageX(0),
190 mDragImageY(0) {
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)
210 : mParent(aParent),
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) {
224 MOZ_ASSERT(mParent);
225 MOZ_ASSERT(aItems);
227 // We clone the items array after everything else, so that it has a valid
228 // mParent value
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
234 // above.
235 NS_ASSERTION(aEventMessage != eDragStart,
236 "invalid event type for DataTransfer constructor");
239 DataTransfer::~DataTransfer() = default;
241 // static
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)) {
263 mDropEffect = e;
265 break;
270 void DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed) {
271 if (aEffectAllowed.EqualsLiteral("uninitialized")) {
272 mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
273 return;
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])) {
287 mEffectAllowed = e;
288 break;
293 void DataTransfer::GetMozTriggeringPrincipalURISpec(
294 nsAString& aPrincipalURISpec) {
295 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
296 if (!dragSession) {
297 aPrincipalURISpec.Truncate(0);
298 return;
301 nsCOMPtr<nsIPrincipal> principal;
302 dragSession->GetTriggeringPrincipal(getter_AddRefs(principal));
303 if (!principal) {
304 aPrincipalURISpec.Truncate(0);
305 return;
308 nsAutoCString spec;
309 principal->GetAsciiSpec(spec);
310 CopyUTF8toUTF16(spec, aPrincipalURISpec);
313 nsIContentSecurityPolicy* DataTransfer::GetMozCSP() {
314 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
315 if (!dragSession) {
316 return nullptr;
318 nsCOMPtr<nsIContentSecurityPolicy> csp;
319 dragSession->GetCsp(getter_AddRefs(csp));
320 return 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.
332 aTypes.Clear();
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
347 aData.Truncate();
349 nsCOMPtr<nsIVariant> data;
350 nsresult rv =
351 GetDataAtInternal(aFormat, 0, &aSubjectPrincipal, getter_AddRefs(data));
352 if (NS_FAILED(rv)) {
353 if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
354 aRv.Throw(rv);
356 return;
359 if (data) {
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] == '#') {
375 if (idx == -1) {
376 break;
378 } else {
379 if (idx == -1) {
380 aData.Assign(Substring(stringdata, lastidx));
381 } else {
382 aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
384 aData =
385 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
386 return;
388 lastidx = idx + 1;
390 } else {
391 aData = stringdata;
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,
406 ErrorResult& aRv) {
407 if (IsReadOnly()) {
408 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
409 return;
412 if (MozItemCount() == 0) {
413 return;
416 if (aFormat.WasPassed()) {
417 MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv);
418 } else {
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();
430 if (!dragSession) {
431 return nullptr;
434 nsCOMPtr<nsINode> sourceNode;
435 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
436 if (sourceNode && !nsContentUtils::LegacyIsCallerNativeCode() &&
437 !nsContentUtils::CanCallerAccess(sourceNode)) {
438 return nullptr;
441 return sourceNode.forget();
444 already_AddRefed<WindowContext> DataTransfer::GetSourceTopWindowContext() {
445 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
446 if (!dragSession) {
447 return nullptr;
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);
462 return nullptr;
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
475 // reasons.
476 nsAutoString type;
477 items[i]->GetInternalType(type);
478 if (NS_WARN_IF(!types->Add(type))) {
479 aRv.Throw(NS_ERROR_FAILURE);
480 return nullptr;
483 if (items[i]->Kind() == DataTransferItem::KIND_FILE) {
484 addFile = true;
488 if (addFile) {
489 types->Add(u"Files"_ns);
493 return types.forget();
496 nsresult DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat,
497 uint32_t aIndex,
498 nsIVariant** aData) const {
499 return GetDataAtInternal(aFormat, aIndex,
500 nsContentUtils::GetSystemPrincipal(), aData);
503 nsresult DataTransfer::GetDataAtInternal(const nsAString& aFormat,
504 uint32_t aIndex,
505 nsIPrincipal* aSubjectPrincipal,
506 nsIVariant** aData) const {
507 *aData = nullptr;
509 if (aFormat.IsEmpty()) {
510 return NS_OK;
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;
523 nsAutoString format;
524 GetRealFormat(aFormat, format);
526 MOZ_ASSERT(aSubjectPrincipal);
528 RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex);
529 if (!item) {
530 // The index exists but there's no data for the specified format, in this
531 // case we just return undefined
532 return NS_OK;
535 // If we have chrome only content, and we aren't chrome, don't allow access
536 if (!aSubjectPrincipal->IsSystemPrincipal() && item->ChromeOnly()) {
537 return NS_OK;
540 // DataTransferItem::Data() handles the principal checks
541 ErrorResult result;
542 nsCOMPtr<nsIVariant> data = item->Data(aSubjectPrincipal, result);
543 if (NS_WARN_IF(!data || result.Failed())) {
544 return result.StealNSResult();
547 data.forget(aData);
548 return NS_OK;
551 void DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
552 uint32_t aIndex,
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));
558 if (aRv.Failed()) {
559 return;
562 if (!data) {
563 aRetval.setNull();
564 return;
567 JS::Rooted<JS::Value> result(aCx);
568 if (!VariantToJsval(aCx, data, aRetval)) {
569 aRv = NS_ERROR_FAILURE;
570 return;
574 /* static */
575 bool DataTransfer::PrincipalMaySetData(const nsAString& aType,
576 nsIVariant* aData,
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");
582 return false;
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");
594 return false;
599 return true;
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);
609 if (!atomEvt) {
610 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
611 return nullptr;
613 EventMessage eventMessage = nsContentUtils::GetEventMessage(atomEvt);
615 RefPtr<DataTransfer> dt;
616 nsresult rv = Clone(mParent, eventMessage, false, false, getter_AddRefs(dt));
617 if (NS_FAILED(rv)) {
618 aRv.Throw(rv);
619 return nullptr;
621 return dt.forget();
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
638 // work similarly.
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?");
646 return;
649 nsCOMPtr<nsIClipboard> clipboard =
650 do_GetService("@mozilla.org/widget/clipboard;1");
651 if (!clipboard || mClipboardType < 0) {
652 return;
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));
661 } else {
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) {
669 return;
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;
685 /* static */
686 void DataTransfer::GetExternalTransferableFormats(
687 nsITransferable* aTransferable, bool aPlainTextOnly,
688 nsTArray<nsCString>* aResult) {
689 MOZ_ASSERT(aTransferable);
690 MOZ_ASSERT(aResult);
692 aResult->Clear();
694 // NOTE: When you change this method, you may need to change
695 // GetExternalClipboardFormats() too since those methods should
696 // work similarly.
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));
706 return;
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()) {
722 return NS_OK;
725 if (IsReadOnly()) {
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,
755 ErrorResult& aRv) {
756 nsCOMPtr<nsIVariant> data;
757 aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
758 getter_AddRefs(data));
759 if (!aRv.Failed()) {
760 aRv = SetDataAtInternal(aFormat, data, aIndex,
761 nsContentUtils::GetSystemPrincipal());
765 void DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
766 ErrorResult& aRv) {
767 if (IsReadOnly()) {
768 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
769 return;
772 if (aIndex >= MozItemCount()) {
773 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
774 return;
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);
781 return;
784 MozClearDataAtHelper(aFormat, aIndex, *nsContentUtils::GetSystemPrincipal(),
785 aRv);
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,
800 uint32_t aIndex,
801 nsIPrincipal& aSubjectPrincipal,
802 ErrorResult& aRv) {
803 MOZ_ASSERT(!IsReadOnly());
804 MOZ_ASSERT(aIndex < MozItemCount());
805 MOZ_ASSERT(aIndex == 0 || (mEventMessage != eCut && mEventMessage != eCopy &&
806 mEventMessage != ePaste));
808 nsAutoString format;
809 GetRealFormat(aFormat, format);
811 mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
814 void DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY) {
815 if (!IsReadOnly()) {
816 mDragImage = &aImage;
817 mDragImageX = aX;
818 mDragImageY = aY;
822 void DataTransfer::UpdateDragImage(Element& aImage, int32_t aX, int32_t aY) {
823 if (mEventMessage < eDragDropEventFirst ||
824 mEventMessage > eDragDropEventLast) {
825 return;
828 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
829 if (dragSession) {
830 dragSession->UpdateDragImage(&aImage, aX, aY);
834 void DataTransfer::AddElement(Element& aElement, ErrorResult& aRv) {
835 if (IsReadOnly()) {
836 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
837 return;
840 mDragTarget = &aElement;
843 nsresult DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
844 bool aUserCancelled,
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);
853 return NS_OK;
856 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
857 nsINode* aDragTarget) {
858 MOZ_ASSERT(aDragTarget);
860 Document* doc = aDragTarget->GetComposedDoc();
861 if (!doc) {
862 return nullptr;
865 return GetTransferables(doc->GetLoadContext());
868 already_AddRefed<nsIArray> DataTransfer::GetTransferables(
869 nsILoadContext* aLoadContext) {
870 nsCOMPtr<nsIMutableArray> transArray = nsArray::Create();
871 if (!transArray) {
872 return nullptr;
875 uint32_t count = MozItemCount();
876 for (uint32_t i = 0; i < count; i++) {
877 nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
878 if (transferable) {
879 transArray->AppendElement(transferable);
883 return transArray.forget();
886 already_AddRefed<nsITransferable> DataTransfer::GetTransferable(
887 uint32_t aIndex, nsILoadContext* aLoadContext) {
888 if (aIndex >= MozItemCount()) {
889 return nullptr;
892 const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
893 uint32_t count = item.Length();
894 if (!count) {
895 return nullptr;
898 nsCOMPtr<nsITransferable> transferable =
899 do_CreateInstance("@mozilla.org/widget/transferable;1");
900 if (!transferable) {
901 return nullptr;
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;
919 bool added = false;
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
948 * <wide string> data
949 * A type of eCustomClipboardTypeId_None ends the list, without any following
950 * data.
952 do {
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
957 continue;
960 nsAutoString type;
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;
968 break;
972 uint32_t lengthInBytes;
973 nsCOMPtr<nsISupports> convertedData;
975 if (handlingCustomFormats) {
976 if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
977 &lengthInBytes)) {
978 continue;
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));
989 if (str) {
990 nsAutoString data;
991 str->GetData(data);
993 if (!stream) {
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
1011 // don't fit.
1012 CheckedInt<uint32_t> newSize = formatLength + totalCustomLength +
1013 lengthInBytes +
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;
1021 continue;
1023 rv = stream->Write32(formatLength.value());
1024 if (NS_WARN_IF(NS_FAILED(rv))) {
1025 totalCustomLength = 0;
1026 continue;
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;
1036 continue;
1038 rv = stream->Write32(lengthInBytes);
1039 if (NS_WARN_IF(NS_FAILED(rv))) {
1040 totalCustomLength = 0;
1041 continue;
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()),
1049 lengthInBytes));
1050 if (NS_WARN_IF(NS_FAILED(rv))) {
1051 totalCustomLength = 0;
1052 continue;
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;
1085 nsCString str;
1086 stringBuffer->ToString(totalCustomLength, str);
1087 nsCOMPtr<nsISupportsCString> strSupports(
1088 do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1089 strSupports->SetData(str);
1091 nsresult rv =
1092 transferable->SetTransferData(kCustomTypesMime, strSupports);
1093 if (NS_FAILED(rv)) {
1094 return nullptr;
1097 added = true;
1102 // Clear the stream so it doesn't get used again.
1103 stream = nullptr;
1104 } else {
1105 // This is the second pass of the loop and a known type is encountered.
1106 // Add it as is.
1107 if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
1108 &lengthInBytes)) {
1109 continue;
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);
1118 if (converter) {
1119 transferable->AddDataFlavor(format.get());
1120 transferable->SetConverter(converter);
1121 continue;
1124 nsresult rv =
1125 transferable->SetTransferData(format.get(), convertedData);
1126 if (NS_FAILED(rv)) {
1127 return nullptr;
1130 added = true;
1134 handlingCustomFormats = !handlingCustomFormats;
1135 } while (!handlingCustomFormats);
1137 // only return the transferable if data was successfully added to it
1138 if (added) {
1139 return transferable.forget();
1142 return nullptr;
1145 bool DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
1146 nsISupports** aSupports,
1147 uint32_t* aLength) const {
1148 *aSupports = nullptr;
1149 *aLength = 0;
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)))) {
1156 return false;
1159 // For flavour data providers, use 0 as the length.
1160 if (nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data)) {
1161 fdp.forget(aSupports);
1162 *aLength = 0;
1163 return true;
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);
1170 } else {
1171 data.forget(aSupports);
1174 *aLength = sizeof(nsISupports*);
1175 return true;
1178 nsAutoString str;
1179 nsresult rv = aVariant->GetAsAString(str);
1180 if (NS_FAILED(rv)) {
1181 return false;
1184 nsCOMPtr<nsISupportsString> strSupports(
1185 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1186 if (!strSupports) {
1187 return false;
1190 strSupports->SetData(str);
1192 strSupports.forget(aSupports);
1194 // each character is two bytes
1195 *aLength = str.Length() * 2;
1197 return true;
1200 void DataTransfer::Disconnect() {
1201 SetMode(Mode::Protected);
1202 if (StaticPrefs::dom_events_dataTransfer_protected_enabled()) {
1203 ClearAll();
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,
1217 bool aHidden) {
1218 nsAutoString format;
1219 GetRealFormat(aFormat, format);
1221 ErrorResult rv;
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);
1233 } else {
1234 nsAutoString format;
1235 GetRealFormat(aFormat, format);
1237 ErrorResult rv;
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");
1255 return;
1258 if (lowercaseFormat.EqualsLiteral("url")) {
1259 aOutFormat.AssignLiteral("text/uri-list");
1260 return;
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();
1280 if (!global) {
1281 return nullptr;
1284 const auto* innerWindow = global->GetAsInnerWindow();
1285 if (!innerWindow) {
1286 return nullptr;
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,
1298 bool aHidden) {
1299 ErrorResult rv;
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();
1308 return NS_OK;
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();
1317 return NS_OK;
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();
1327 return NS_OK;
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
1335 // generate it.
1337 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
1338 if (!dragSession) {
1339 return;
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};
1355 uint32_t count;
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.
1362 bool supported;
1363 dragSession->IsDataFlavorSupported(kCustomTypesMime, &supported);
1364 if (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.
1373 bool supported;
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.
1377 if (supported) {
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);
1402 return;
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;
1430 continue;
1431 } else {
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)) {
1436 hasFileData = true;
1439 // If we aren't the file data, and we have file data, we want to be hidden
1440 CacheExternalData(
1441 type.get(), 0, aPrincipal,
1442 /* hidden = */ !type.EqualsLiteral(kFileMime) && hasFileData);
1447 void DataTransfer::FillAllExternalData() {
1448 if (mIsExternal) {
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();
1468 if (!variant) {
1469 return;
1472 FillInExternalCustomTypes(variant, aIndex, aPrincipal);
1475 void DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
1476 nsIPrincipal* aPrincipal) {
1477 char* chrs;
1478 uint32_t len = 0;
1479 nsresult rv = aData->GetAsStringWithSize(&len, &chrs);
1480 if (NS_FAILED(rv)) {
1481 return;
1484 CheckedInt<int32_t> checkedLen(len);
1485 if (!checkedLen.isValid()) {
1486 return;
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);
1495 uint32_t type;
1496 do {
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);
1503 char* formatBytes;
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);
1513 char* dataBytes;
1514 rv = stream->ReadBytes(dataLength, &dataBytes);
1515 NS_ENSURE_SUCCESS_VOID(rv);
1516 nsAutoString data;
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;
1533 } else {
1534 mMode = aMode;
1538 } // namespace mozilla::dom