1/*
2 * Copyright (C) 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DataTransfer.h"
28
29#include "CachedImage.h"
30#include "CachedImageClient.h"
31#include "DataTransferItem.h"
32#include "DataTransferItemList.h"
33#include "DocumentFragment.h"
34#include "DragData.h"
35#include "Editor.h"
36#include "FileList.h"
37#include "Frame.h"
38#include "FrameLoader.h"
39#include "HTMLImageElement.h"
40#include "HTMLParserIdioms.h"
41#include "Image.h"
42#include "Pasteboard.h"
43#include "RuntimeEnabledFeatures.h"
44#include "Settings.h"
45#include "StaticPasteboard.h"
46#include "WebContentReader.h"
47#include "WebCorePasteboardFileReader.h"
48#include "markup.h"
49#include <wtf/URLParser.h>
50#include <wtf/unicode/CharacterNames.h>
51
52namespace WebCore {
53
54#if ENABLE(DRAG_SUPPORT)
55
56class DragImageLoader final : private CachedImageClient {
57 WTF_MAKE_NONCOPYABLE(DragImageLoader); WTF_MAKE_FAST_ALLOCATED;
58public:
59 explicit DragImageLoader(DataTransfer*);
60 void startLoading(CachedResourceHandle<CachedImage>&);
61 void stopLoading(CachedResourceHandle<CachedImage>&);
62 void moveToDataTransfer(DataTransfer&);
63
64private:
65 void imageChanged(CachedImage*, const IntRect*) override;
66 DataTransfer* m_dataTransfer;
67};
68
69#endif
70
71DataTransfer::DataTransfer(StoreMode mode, std::unique_ptr<Pasteboard> pasteboard, Type type)
72 : m_storeMode(mode)
73 , m_pasteboard(WTFMove(pasteboard))
74#if ENABLE(DRAG_SUPPORT)
75 , m_type(type)
76 , m_dropEffect("uninitialized"_s)
77 , m_effectAllowed("uninitialized"_s)
78 , m_shouldUpdateDragImage(false)
79#endif
80{
81#if !ENABLE(DRAG_SUPPORT)
82 ASSERT_UNUSED(type, type != Type::DragAndDropData && type != Type::DragAndDropFiles);
83#endif
84}
85
86Ref<DataTransfer> DataTransfer::createForCopyAndPaste(Document& document, StoreMode storeMode, std::unique_ptr<Pasteboard>&& pasteboard)
87{
88 auto dataTransfer = adoptRef(*new DataTransfer(storeMode, WTFMove(pasteboard)));
89 dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
90 return dataTransfer;
91}
92
93DataTransfer::~DataTransfer()
94{
95#if ENABLE(DRAG_SUPPORT)
96 if (m_dragImageLoader && m_dragImage)
97 m_dragImageLoader->stopLoading(m_dragImage);
98#endif
99}
100
101bool DataTransfer::canReadTypes() const
102{
103 return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::Protected || m_storeMode == StoreMode::ReadWrite;
104}
105
106bool DataTransfer::canReadData() const
107{
108 return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::ReadWrite;
109}
110
111bool DataTransfer::canWriteData() const
112{
113 return m_storeMode == StoreMode::ReadWrite;
114}
115
116static String normalizeType(const String& type)
117{
118 if (type.isNull())
119 return type;
120
121 String lowercaseType = stripLeadingAndTrailingHTMLSpaces(type).convertToASCIILowercase();
122 if (lowercaseType == "text" || lowercaseType.startsWith("text/plain;"))
123 return "text/plain";
124 if (lowercaseType == "url" || lowercaseType.startsWith("text/uri-list;"))
125 return "text/uri-list";
126 if (lowercaseType.startsWith("text/html;"))
127 return "text/html";
128
129 return lowercaseType;
130}
131
132void DataTransfer::clearData(const String& type)
133{
134 if (!canWriteData())
135 return;
136
137 String normalizedType = normalizeType(type);
138 if (normalizedType.isNull())
139 m_pasteboard->clear();
140 else
141 m_pasteboard->clear(normalizedType);
142 if (m_itemList)
143 m_itemList->didClearStringData(normalizedType);
144}
145
146static String readURLsFromPasteboardAsString(Pasteboard& pasteboard, Function<bool(const String&)>&& shouldIncludeURL)
147{
148 StringBuilder urlList;
149 for (const auto& urlString : pasteboard.readAllStrings("text/uri-list"_s)) {
150 if (!shouldIncludeURL(urlString))
151 continue;
152 if (!urlList.isEmpty())
153 urlList.append(newlineCharacter);
154 urlList.append(urlString);
155 }
156 return urlList.toString();
157}
158
159String DataTransfer::getDataForItem(Document& document, const String& type) const
160{
161 if (!canReadData())
162 return { };
163
164 auto lowercaseType = stripLeadingAndTrailingHTMLSpaces(type).convertToASCIILowercase();
165 if (shouldSuppressGetAndSetDataToAvoidExposingFilePaths()) {
166 if (lowercaseType == "text/uri-list") {
167 return readURLsFromPasteboardAsString(*m_pasteboard, [] (auto& urlString) {
168 return Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(urlString);
169 });
170 }
171
172 if (lowercaseType == "text/html" && RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) {
173 // If the pasteboard contains files and the page requests 'text/html', we only read from rich text types to prevent file
174 // paths from leaking (e.g. from plain text data on the pasteboard) since we sanitize cross-origin markup. However, if
175 // custom pasteboard data is disabled, then we can't ensure that the markup we deliver is sanitized, so we fall back to
176 // current behavior and return an empty string.
177 return readStringFromPasteboard(document, lowercaseType, WebContentReadingPolicy::OnlyRichTextTypes);
178 }
179
180 return { };
181 }
182
183 return readStringFromPasteboard(document, lowercaseType, WebContentReadingPolicy::AnyType);
184}
185
186String DataTransfer::readStringFromPasteboard(Document& document, const String& lowercaseType, WebContentReadingPolicy policy) const
187{
188 if (!RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled())
189 return m_pasteboard->readString(lowercaseType);
190
191 // StaticPasteboard is only used to stage data written by websites before being committed to the system pasteboard.
192 bool isSameOrigin = is<StaticPasteboard>(*m_pasteboard) || (!m_originIdentifier.isNull() && m_originIdentifier == m_pasteboard->readOrigin());
193 if (isSameOrigin) {
194 String value = m_pasteboard->readStringInCustomData(lowercaseType);
195 if (!value.isNull())
196 return value;
197 }
198 if (!Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType))
199 return { };
200
201 if (!is<StaticPasteboard>(*m_pasteboard) && lowercaseType == "text/html") {
202 if (!document.frame())
203 return { };
204 WebContentMarkupReader reader { *document.frame() };
205 m_pasteboard->read(reader, policy);
206 return reader.markup;
207 }
208
209 if (!is<StaticPasteboard>(*m_pasteboard) && lowercaseType == "text/uri-list") {
210 return readURLsFromPasteboardAsString(*m_pasteboard, [] (auto&) {
211 return true;
212 });
213 }
214
215 return m_pasteboard->readString(lowercaseType);
216}
217
218String DataTransfer::getData(Document& document, const String& type) const
219{
220 return getDataForItem(document, normalizeType(type));
221}
222
223bool DataTransfer::shouldSuppressGetAndSetDataToAvoidExposingFilePaths() const
224{
225 if (!forFileDrag() && !RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled())
226 return false;
227 return m_pasteboard->fileContentState() == Pasteboard::FileContentState::MayContainFilePaths;
228}
229
230void DataTransfer::setData(const String& type, const String& data)
231{
232 if (!canWriteData())
233 return;
234
235 if (shouldSuppressGetAndSetDataToAvoidExposingFilePaths())
236 return;
237
238 auto normalizedType = normalizeType(type);
239 setDataFromItemList(normalizedType, data);
240 if (m_itemList)
241 m_itemList->didSetStringData(normalizedType);
242}
243
244void DataTransfer::setDataFromItemList(const String& type, const String& data)
245{
246 ASSERT(canWriteData());
247 RELEASE_ASSERT(is<StaticPasteboard>(*m_pasteboard));
248
249 if (!RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) {
250 m_pasteboard->writeString(type, data);
251 return;
252 }
253
254 String sanitizedData;
255 if (type == "text/html")
256 sanitizedData = sanitizeMarkup(data);
257 else if (type == "text/uri-list") {
258 auto url = URL({ }, data);
259 if (url.isValid())
260 sanitizedData = url.string();
261 } else if (type == "text/plain")
262 sanitizedData = data; // Nothing to sanitize.
263
264 if (sanitizedData != data)
265 downcast<StaticPasteboard>(*m_pasteboard).writeStringInCustomData(type, data);
266
267 if (Pasteboard::isSafeTypeForDOMToReadAndWrite(type) && !sanitizedData.isNull())
268 m_pasteboard->writeString(type, sanitizedData);
269}
270
271void DataTransfer::updateFileList()
272{
273 ASSERT(canWriteData());
274
275 m_fileList->m_files = filesFromPasteboardAndItemList();
276}
277
278void DataTransfer::didAddFileToItemList()
279{
280 ASSERT(canWriteData());
281 if (!m_fileList)
282 return;
283
284 auto& newItem = m_itemList->items().last();
285 ASSERT(newItem->isFile());
286 m_fileList->append(*newItem->file());
287}
288
289DataTransferItemList& DataTransfer::items()
290{
291 if (!m_itemList)
292 m_itemList = std::make_unique<DataTransferItemList>(*this);
293 return *m_itemList;
294}
295
296Vector<String> DataTransfer::types() const
297{
298 return types(AddFilesType::Yes);
299}
300
301Vector<String> DataTransfer::typesForItemList() const
302{
303 return types(AddFilesType::No);
304}
305
306Vector<String> DataTransfer::types(AddFilesType addFilesType) const
307{
308 if (!canReadTypes())
309 return { };
310
311 if (!RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) {
312 auto types = m_pasteboard->typesForLegacyUnsafeBindings();
313 ASSERT(!types.contains("Files"));
314 if (m_pasteboard->fileContentState() != Pasteboard::FileContentState::NoFileOrImageData && addFilesType == AddFilesType::Yes)
315 types.append("Files");
316 return types;
317 }
318
319 auto safeTypes = m_pasteboard->typesSafeForBindings(m_originIdentifier);
320 bool hasFileBackedItem = m_itemList && m_itemList->hasItems() && notFound != m_itemList->items().findMatching([] (const auto& item) {
321 return item->isFile();
322 });
323
324 auto fileContentState = m_pasteboard->fileContentState();
325 if (hasFileBackedItem || fileContentState != Pasteboard::FileContentState::NoFileOrImageData) {
326 Vector<String> types;
327 if (addFilesType == AddFilesType::Yes)
328 types.append("Files"_s);
329
330 if (fileContentState != Pasteboard::FileContentState::MayContainFilePaths) {
331 types.appendVector(WTFMove(safeTypes));
332 return types;
333 }
334
335 if (safeTypes.contains("text/uri-list"))
336 types.append("text/uri-list"_s);
337 if (safeTypes.contains("text/html") && RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled())
338 types.append("text/html"_s);
339 return types;
340 }
341
342 ASSERT(!safeTypes.contains("Files"));
343 return safeTypes;
344}
345
346Vector<Ref<File>> DataTransfer::filesFromPasteboardAndItemList() const
347{
348 bool addedFilesFromPasteboard = false;
349 Vector<Ref<File>> files;
350 if ((!forDrag() || forFileDrag()) && m_pasteboard->fileContentState() != Pasteboard::FileContentState::NoFileOrImageData) {
351 WebCorePasteboardFileReader reader;
352 m_pasteboard->read(reader);
353 files = WTFMove(reader.files);
354 addedFilesFromPasteboard = !files.isEmpty();
355 }
356
357 bool itemListContainsItems = false;
358 if (m_itemList && m_itemList->hasItems()) {
359 for (auto& item : m_itemList->items()) {
360 if (auto file = item->file())
361 files.append(file.releaseNonNull());
362 }
363 itemListContainsItems = true;
364 }
365
366 bool containsItemsAndFiles = itemListContainsItems && addedFilesFromPasteboard;
367 ASSERT_UNUSED(containsItemsAndFiles, !containsItemsAndFiles);
368 return files;
369}
370
371FileList& DataTransfer::files() const
372{
373 if (!canReadData()) {
374 if (m_fileList)
375 m_fileList->clear();
376 else
377 m_fileList = FileList::create();
378 return *m_fileList;
379 }
380
381 if (!m_fileList)
382 m_fileList = FileList::create(filesFromPasteboardAndItemList());
383
384 return *m_fileList;
385}
386
387struct PasteboardFileTypeReader final : PasteboardFileReader {
388 void readFilename(const String& filename)
389 {
390 types.add(File::contentTypeForFile(filename));
391 }
392
393 void readBuffer(const String&, const String& type, Ref<SharedBuffer>&&)
394 {
395 types.add(type);
396 }
397
398 HashSet<String, ASCIICaseInsensitiveHash> types;
399};
400
401bool DataTransfer::hasFileOfType(const String& type)
402{
403 ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
404 PasteboardFileTypeReader reader;
405 m_pasteboard->read(reader);
406 return reader.types.contains(type);
407}
408
409bool DataTransfer::hasStringOfType(const String& type)
410{
411 ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
412
413 return !type.isNull() && types().contains(type);
414}
415
416Ref<DataTransfer> DataTransfer::createForInputEvent(const String& plainText, const String& htmlText)
417{
418 auto pasteboard = std::make_unique<StaticPasteboard>();
419 pasteboard->writeString("text/plain"_s, plainText);
420 pasteboard->writeString("text/html"_s, htmlText);
421 return adoptRef(*new DataTransfer(StoreMode::Readonly, WTFMove(pasteboard), Type::InputEvent));
422}
423
424void DataTransfer::commitToPasteboard(Pasteboard& nativePasteboard)
425{
426 ASSERT(is<StaticPasteboard>(*m_pasteboard) && !is<StaticPasteboard>(nativePasteboard));
427 PasteboardCustomData customData = downcast<StaticPasteboard>(*m_pasteboard).takeCustomData();
428 if (RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) {
429 customData.origin = m_originIdentifier;
430 nativePasteboard.writeCustomData(customData);
431 return;
432 }
433
434 for (auto& entry : customData.platformData)
435 nativePasteboard.writeString(entry.key, entry.value);
436 for (auto& entry : customData.sameOriginCustomData)
437 nativePasteboard.writeString(entry.key, entry.value);
438}
439
440#if !ENABLE(DRAG_SUPPORT)
441
442String DataTransfer::dropEffect() const
443{
444 return "none"_s;
445}
446
447void DataTransfer::setDropEffect(const String&)
448{
449}
450
451String DataTransfer::effectAllowed() const
452{
453 return "uninitialized"_s;
454}
455
456void DataTransfer::setEffectAllowed(const String&)
457{
458}
459
460void DataTransfer::setDragImage(Element*, int, int)
461{
462}
463
464#else
465
466Ref<DataTransfer> DataTransfer::createForDrag()
467{
468 return adoptRef(*new DataTransfer(StoreMode::ReadWrite, Pasteboard::createForDragAndDrop(), Type::DragAndDropData));
469}
470
471Ref<DataTransfer> DataTransfer::createForDragStartEvent(Document& document)
472{
473 auto dataTransfer = adoptRef(*new DataTransfer(StoreMode::ReadWrite, std::make_unique<StaticPasteboard>(), Type::DragAndDropData));
474 dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
475 return dataTransfer;
476}
477
478Ref<DataTransfer> DataTransfer::createForDrop(Document& document, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
479{
480 auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Readonly, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
481 dataTransfer->setSourceOperation(sourceOperation);
482 dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
483 return dataTransfer;
484}
485
486Ref<DataTransfer> DataTransfer::createForUpdatingDropTarget(Document& document, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
487{
488 auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Protected, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
489 dataTransfer->setSourceOperation(sourceOperation);
490 dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
491 return dataTransfer;
492}
493
494void DataTransfer::setDragImage(Element* element, int x, int y)
495{
496 if (!forDrag() || !canWriteData())
497 return;
498
499 CachedImage* image = nullptr;
500 if (is<HTMLImageElement>(element) && !element->isConnected())
501 image = downcast<HTMLImageElement>(*element).cachedImage();
502
503 m_dragLocation = IntPoint(x, y);
504
505 if (m_dragImageLoader && m_dragImage)
506 m_dragImageLoader->stopLoading(m_dragImage);
507 m_dragImage = image;
508 if (m_dragImage) {
509 if (!m_dragImageLoader)
510 m_dragImageLoader = std::make_unique<DragImageLoader>(this);
511 m_dragImageLoader->startLoading(m_dragImage);
512 }
513
514 m_dragImageElement = image ? nullptr : element;
515
516 updateDragImage();
517}
518
519void DataTransfer::updateDragImage()
520{
521 // Don't allow setting the image if we haven't started dragging yet; we'll rely on the dragging code
522 // to install this drag image as part of getting the drag kicked off.
523 if (!m_shouldUpdateDragImage)
524 return;
525
526 IntPoint computedHotSpot;
527 auto computedImage = DragImage { createDragImage(computedHotSpot) };
528 if (!computedImage)
529 return;
530
531 m_pasteboard->setDragImage(WTFMove(computedImage), computedHotSpot);
532}
533
534RefPtr<Element> DataTransfer::dragImageElement() const
535{
536 return m_dragImageElement;
537}
538
539#if !PLATFORM(MAC)
540
541DragImageRef DataTransfer::createDragImage(IntPoint& location) const
542{
543 location = m_dragLocation;
544
545 if (m_dragImage)
546 return createDragImageFromImage(m_dragImage->image(), ImageOrientationDescription());
547
548 if (m_dragImageElement) {
549 if (Frame* frame = m_dragImageElement->document().frame())
550 return createDragImageForNode(*frame, *m_dragImageElement);
551 }
552
553 // We do not have enough information to create a drag image, use the default icon.
554 return nullptr;
555}
556
557#endif
558
559DragImageLoader::DragImageLoader(DataTransfer* dataTransfer)
560 : m_dataTransfer(dataTransfer)
561{
562}
563
564void DragImageLoader::moveToDataTransfer(DataTransfer& newDataTransfer)
565{
566 m_dataTransfer = &newDataTransfer;
567}
568
569void DragImageLoader::startLoading(CachedResourceHandle<WebCore::CachedImage>& image)
570{
571 // FIXME: Does this really trigger a load? Does it need to?
572 image->addClient(*this);
573}
574
575void DragImageLoader::stopLoading(CachedResourceHandle<WebCore::CachedImage>& image)
576{
577 image->removeClient(*this);
578}
579
580void DragImageLoader::imageChanged(CachedImage*, const IntRect*)
581{
582 m_dataTransfer->updateDragImage();
583}
584
585static DragOperation dragOpFromIEOp(const String& operation)
586{
587 if (operation == "uninitialized")
588 return DragOperationEvery;
589 if (operation == "none")
590 return DragOperationNone;
591 if (operation == "copy")
592 return DragOperationCopy;
593 if (operation == "link")
594 return DragOperationLink;
595 if (operation == "move")
596 return (DragOperation)(DragOperationGeneric | DragOperationMove);
597 if (operation == "copyLink")
598 return (DragOperation)(DragOperationCopy | DragOperationLink);
599 if (operation == "copyMove")
600 return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove);
601 if (operation == "linkMove")
602 return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove);
603 if (operation == "all")
604 return DragOperationEvery;
605 return DragOperationPrivate; // really a marker for "no conversion"
606}
607
608static const char* IEOpFromDragOp(DragOperation operation)
609{
610 bool isGenericMove = operation & (DragOperationGeneric | DragOperationMove);
611
612 if ((isGenericMove && (operation & DragOperationCopy) && (operation & DragOperationLink)) || operation == DragOperationEvery)
613 return "all";
614 if (isGenericMove && (operation & DragOperationCopy))
615 return "copyMove";
616 if (isGenericMove && (operation & DragOperationLink))
617 return "linkMove";
618 if ((operation & DragOperationCopy) && (operation & DragOperationLink))
619 return "copyLink";
620 if (isGenericMove)
621 return "move";
622 if (operation & DragOperationCopy)
623 return "copy";
624 if (operation & DragOperationLink)
625 return "link";
626 return "none";
627}
628
629DragOperation DataTransfer::sourceOperation() const
630{
631 DragOperation operation = dragOpFromIEOp(m_effectAllowed);
632 ASSERT(operation != DragOperationPrivate);
633 return operation;
634}
635
636DragOperation DataTransfer::destinationOperation() const
637{
638 DragOperation operation = dragOpFromIEOp(m_dropEffect);
639 ASSERT(operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == (DragOperation)(DragOperationGeneric | DragOperationMove) || operation == DragOperationEvery);
640 return operation;
641}
642
643void DataTransfer::setSourceOperation(DragOperation operation)
644{
645 ASSERT_ARG(operation, operation != DragOperationPrivate);
646 m_effectAllowed = IEOpFromDragOp(operation);
647}
648
649void DataTransfer::setDestinationOperation(DragOperation operation)
650{
651 ASSERT_ARG(operation, operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == DragOperationGeneric || operation == DragOperationMove || operation == (DragOperation)(DragOperationGeneric | DragOperationMove));
652 m_dropEffect = IEOpFromDragOp(operation);
653}
654
655String DataTransfer::dropEffect() const
656{
657 return m_dropEffect == "uninitialized" ? "none"_s : m_dropEffect;
658}
659
660void DataTransfer::setDropEffect(const String& effect)
661{
662 if (!forDrag())
663 return;
664
665 if (effect != "none" && effect != "copy" && effect != "link" && effect != "move")
666 return;
667
668 // FIXME: The spec allows this in all circumstances. There is probably no value
669 // in ignoring attempts to change it.
670 if (!canReadTypes())
671 return;
672
673 m_dropEffect = effect;
674}
675
676String DataTransfer::effectAllowed() const
677{
678 return m_effectAllowed;
679}
680
681void DataTransfer::setEffectAllowed(const String& effect)
682{
683 if (!forDrag())
684 return;
685
686 // Ignore any attempts to set it to an unknown value.
687 if (dragOpFromIEOp(effect) == DragOperationPrivate)
688 return;
689
690 if (!canWriteData())
691 return;
692
693 m_effectAllowed = effect;
694}
695
696void DataTransfer::moveDragState(Ref<DataTransfer>&& other)
697{
698 RELEASE_ASSERT(is<StaticPasteboard>(other->pasteboard()));
699 // We clear the platform pasteboard here to ensure that the pasteboard doesn't contain any data
700 // that may have been written before starting the drag, and after ending the last drag session.
701 // After pushing the static pasteboard's contents to the platform, the pasteboard should only
702 // contain data that was in the static pasteboard.
703 m_pasteboard->clear();
704 other->commitToPasteboard(*m_pasteboard);
705
706 m_dropEffect = other->m_dropEffect;
707 m_effectAllowed = other->m_effectAllowed;
708 m_dragLocation = other->m_dragLocation;
709 m_dragImage = other->m_dragImage;
710 m_dragImageElement = WTFMove(other->m_dragImageElement);
711 m_dragImageLoader = WTFMove(other->m_dragImageLoader);
712 if (m_dragImageLoader)
713 m_dragImageLoader->moveToDataTransfer(*this);
714 m_fileList = WTFMove(other->m_fileList);
715}
716
717bool DataTransfer::hasDragImage() const
718{
719 return m_dragImage || m_dragImageElement;
720}
721
722#endif // ENABLE(DRAG_SUPPORT)
723
724} // namespace WebCore
725