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