5c6b227fc63caebcf5fa2e44da2bd708852d96c5
[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 "DragData.h"
34 #include "Editor.h"
35 #include "FileList.h"
36 #include "Frame.h"
37 #include "FrameLoader.h"
38 #include "HTMLImageElement.h"
39 #include "HTMLParserIdioms.h"
40 #include "Image.h"
41 #include "Pasteboard.h"
42 #include "Settings.h"
43 #include "StaticPasteboard.h"
44 #include "WebCorePasteboardFileReader.h"
45
46 namespace WebCore {
47
48 #if ENABLE(DRAG_SUPPORT)
49
50 class DragImageLoader final : private CachedImageClient {
51     WTF_MAKE_NONCOPYABLE(DragImageLoader); WTF_MAKE_FAST_ALLOCATED;
52 public:
53     explicit DragImageLoader(DataTransfer*);
54     void startLoading(CachedResourceHandle<CachedImage>&);
55     void stopLoading(CachedResourceHandle<CachedImage>&);
56     void moveToDataTransfer(DataTransfer&);
57
58 private:
59     void imageChanged(CachedImage*, const IntRect*) override;
60     DataTransfer* m_dataTransfer;
61 };
62
63 #endif
64
65 DataTransfer::DataTransfer(StoreMode mode, std::unique_ptr<Pasteboard> pasteboard, Type type)
66     : m_storeMode(mode)
67     , m_pasteboard(WTFMove(pasteboard))
68 #if ENABLE(DRAG_SUPPORT)
69     , m_type(type)
70     , m_dropEffect(ASCIILiteral("uninitialized"))
71     , m_effectAllowed(ASCIILiteral("uninitialized"))
72     , m_shouldUpdateDragImage(false)
73 #endif
74 {
75 #if !ENABLE(DRAG_SUPPORT)
76     ASSERT_UNUSED(type, type != Type::DragAndDropData && type != Type::DragAndDropFiles);
77 #endif
78 }
79
80 Ref<DataTransfer> DataTransfer::createForCopyAndPaste(StoreMode storeMode, std::unique_ptr<Pasteboard>&& pasteboard)
81 {
82     return adoptRef(*new DataTransfer(storeMode, WTFMove(pasteboard)));
83 }
84
85 DataTransfer::~DataTransfer()
86 {
87 #if ENABLE(DRAG_SUPPORT)
88     if (m_dragImageLoader && m_dragImage)
89         m_dragImageLoader->stopLoading(m_dragImage);
90 #endif
91 }
92
93 bool DataTransfer::canReadTypes() const
94 {
95     return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::Protected || m_storeMode == StoreMode::ReadWrite;
96 }
97
98 bool DataTransfer::canReadData() const
99 {
100     return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::ReadWrite;
101 }
102
103 bool DataTransfer::canWriteData() const
104 {
105     return m_storeMode == StoreMode::ReadWrite;
106 }
107
108 static String normalizeType(const String& type)
109 {
110     if (type.isNull())
111         return type;
112
113     String lowercaseType = stripLeadingAndTrailingHTMLSpaces(type).convertToASCIILowercase();
114     if (lowercaseType == "text" || lowercaseType.startsWithIgnoringASCIICase("text/plain;"))
115         return "text/plain";
116     if (lowercaseType == "url" || lowercaseType.startsWithIgnoringASCIICase("text/uri-list;"))
117         return "text/uri-list";
118     if (lowercaseType.startsWithIgnoringASCIICase("text/html;"))
119         return "text/html";
120
121     return lowercaseType;
122 }
123
124 static bool shouldReadOrWriteTypeAsCustomData(const String& type)
125 {
126     return Settings::customPasteboardDataEnabled() && !Pasteboard::isSafeTypeForDOMToReadAndWrite(type);
127 }
128
129 void DataTransfer::clearData(const String& type)
130 {
131     if (!canWriteData())
132         return;
133
134     String normalizedType = normalizeType(type);
135     if (normalizedType.isNull())
136         m_pasteboard->clear();
137     else
138         m_pasteboard->clear(normalizedType);
139     if (m_itemList)
140         m_itemList->didClearStringData(normalizedType);
141 }
142
143 String DataTransfer::getDataForItem(const String& type) const
144 {
145     if (!canReadData())
146         return String();
147
148     if ((forFileDrag() || Settings::customPasteboardDataEnabled()) && m_pasteboard->containsFiles())
149         return { };
150
151     auto lowercaseType = stripLeadingAndTrailingHTMLSpaces(type).convertToASCIILowercase();
152     if (shouldReadOrWriteTypeAsCustomData(lowercaseType))
153         return m_pasteboard->readStringInCustomData(lowercaseType);
154     return m_pasteboard->readString(lowercaseType);
155 }
156
157 String DataTransfer::getData(const String& type) const
158 {
159     return getDataForItem(normalizeType(type));
160 }
161
162 void DataTransfer::setData(const String& type, const String& data)
163 {
164     if (!canWriteData())
165         return;
166
167     if ((forFileDrag() || Settings::customPasteboardDataEnabled()) && m_pasteboard->containsFiles())
168         return;
169
170     auto normalizedType = normalizeType(type);
171     setDataFromItemList(normalizedType, data);
172     if (m_itemList)
173         m_itemList->didSetStringData(normalizedType);
174 }
175
176 void DataTransfer::setDataFromItemList(const String& type, const String& data)
177 {
178     ASSERT(canWriteData());
179     RELEASE_ASSERT(is<StaticPasteboard>(*m_pasteboard));
180
181     if (shouldReadOrWriteTypeAsCustomData(type))
182         downcast<StaticPasteboard>(*m_pasteboard).writeStringInCustomData(type, data);
183     else
184         m_pasteboard->writeString(type, data);
185 }
186
187 void DataTransfer::updateFileList()
188 {
189     ASSERT(canWriteData());
190
191     m_fileList->m_files = filesFromPasteboardAndItemList();
192 }
193
194 void DataTransfer::didAddFileToItemList()
195 {
196     ASSERT(canWriteData());
197     if (!m_fileList)
198         return;
199
200     auto& newItem = m_itemList->items().last();
201     ASSERT(newItem->isFile());
202     m_fileList->append(*newItem->file());
203 }
204
205 DataTransferItemList& DataTransfer::items()
206 {
207     if (!m_itemList)
208         m_itemList = std::make_unique<DataTransferItemList>(*this);
209     return *m_itemList;
210 }
211
212 Vector<String> DataTransfer::types() const
213 {
214     return types(AddFilesType::Yes);
215 }
216
217 Vector<String> DataTransfer::typesForItemList() const
218 {
219     return types(AddFilesType::No);
220 }
221
222 Vector<String> DataTransfer::types(AddFilesType addFilesType) const
223 {
224     if (!canReadTypes())
225         return { };
226     
227     if (!Settings::customPasteboardDataEnabled()) {
228         auto types = m_pasteboard->typesForLegacyUnsafeBindings();
229         ASSERT(!types.contains("Files"));
230         if (m_pasteboard->containsFiles() && addFilesType == AddFilesType::Yes)
231             types.append("Files");
232         return types;
233     }
234
235     if (m_itemList && m_itemList->hasItems() && m_itemList->items().findMatching([] (const auto& item) { return item->isFile(); }) != notFound)
236         return addFilesType == AddFilesType::Yes ? Vector<String> { ASCIILiteral("Files") } : Vector<String> { };
237
238     if (m_pasteboard->containsFiles()) {
239         ASSERT(!m_pasteboard->typesSafeForBindings().contains("Files"));
240         return addFilesType == AddFilesType::Yes ? Vector<String> { ASCIILiteral("Files") } : Vector<String> { };
241     }
242
243     auto types = m_pasteboard->typesSafeForBindings();
244     ASSERT(!types.contains("Files"));
245     return types;
246 }
247
248 Vector<Ref<File>> DataTransfer::filesFromPasteboardAndItemList() const
249 {
250     bool addedFilesFromPasteboard = false;
251     Vector<Ref<File>> files;
252     if (!forDrag() || forFileDrag()) {
253         WebCorePasteboardFileReader reader;
254         m_pasteboard->read(reader);
255         files = WTFMove(reader.files);
256         addedFilesFromPasteboard = !files.isEmpty();
257     }
258
259     bool itemListContainsItems = false;
260     if (m_itemList && m_itemList->hasItems()) {
261         for (auto& item : m_itemList->items()) {
262             if (auto file = item->file())
263                 files.append(file.releaseNonNull());
264         }
265         itemListContainsItems = true;
266     }
267
268     ASSERT(!itemListContainsItems || !addedFilesFromPasteboard);
269     return files;
270 }
271
272 FileList& DataTransfer::files() const
273 {
274     if (!canReadData()) {
275         if (m_fileList)
276             m_fileList->clear();
277         else
278             m_fileList = FileList::create();
279         return *m_fileList;
280     }
281
282     if (!m_fileList)
283         m_fileList = FileList::create(filesFromPasteboardAndItemList());
284
285     return *m_fileList;
286 }
287
288 struct PasteboardFileTypeReader final : PasteboardFileReader {
289     void readFilename(const String& filename)
290     {
291         types.add(File::contentTypeForFile(filename));
292     }
293
294     void readBuffer(const String&, const String& type, Ref<SharedBuffer>&&)
295     {
296         types.add(type);
297     }
298
299     HashSet<String, ASCIICaseInsensitiveHash> types;
300 };
301
302 bool DataTransfer::hasFileOfType(const String& type)
303 {
304     ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
305     PasteboardFileTypeReader reader;
306     m_pasteboard->read(reader);
307     return reader.types.contains(type);
308 }
309
310 bool DataTransfer::hasStringOfType(const String& type)
311 {
312     ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
313
314     return !type.isNull() && types().contains(type);
315 }
316
317 Ref<DataTransfer> DataTransfer::createForInputEvent(const String& plainText, const String& htmlText)
318 {
319     auto pasteboard = std::make_unique<StaticPasteboard>();
320     pasteboard->writeString(ASCIILiteral("text/plain"), plainText);
321     pasteboard->writeString(ASCIILiteral("text/html"), htmlText);
322     return adoptRef(*new DataTransfer(StoreMode::Readonly, WTFMove(pasteboard), Type::InputEvent));
323 }
324
325 #if !ENABLE(DRAG_SUPPORT)
326
327 String DataTransfer::dropEffect() const
328 {
329     return ASCIILiteral("none");
330 }
331
332 void DataTransfer::setDropEffect(const String&)
333 {
334 }
335
336 String DataTransfer::effectAllowed() const
337 {
338     return ASCIILiteral("uninitialized");
339 }
340
341 void DataTransfer::setEffectAllowed(const String&)
342 {
343 }
344
345 void DataTransfer::setDragImage(Element*, int, int)
346 {
347 }
348
349 #else
350
351 Ref<DataTransfer> DataTransfer::createForDrag()
352 {
353     return adoptRef(*new DataTransfer(StoreMode::ReadWrite, Pasteboard::createForDragAndDrop(), Type::DragAndDropData));
354 }
355
356 Ref<DataTransfer> DataTransfer::createForDragStartEvent()
357 {
358     return adoptRef(*new DataTransfer(StoreMode::ReadWrite, std::make_unique<StaticPasteboard>(), Type::DragAndDropData));
359 }
360
361 Ref<DataTransfer> DataTransfer::createForDrop(std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
362 {
363     auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Readonly, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
364     dataTransfer->setSourceOperation(sourceOperation);
365     return dataTransfer;
366 }
367
368 Ref<DataTransfer> DataTransfer::createForUpdatingDropTarget(Document& document, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles)
369 {
370     auto mode = DataTransfer::StoreMode::Protected;
371 #if ENABLE(DASHBOARD_SUPPORT)
372     if (document.settings().usesDashboardBackwardCompatibilityMode() && document.securityOrigin().isLocal())
373         mode = DataTransfer::StoreMode::Readonly;
374 #else
375     UNUSED_PARAM(document);
376 #endif
377     auto dataTransfer = adoptRef(*new DataTransfer(mode, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
378     dataTransfer->setSourceOperation(sourceOperation);
379     return dataTransfer;
380 }
381
382 void DataTransfer::setDragImage(Element* element, int x, int y)
383 {
384     if (!forDrag() || !canWriteData())
385         return;
386
387     CachedImage* image = nullptr;
388     if (is<HTMLImageElement>(element) && !element->isConnected())
389         image = downcast<HTMLImageElement>(*element).cachedImage();
390
391     m_dragLocation = IntPoint(x, y);
392
393     if (m_dragImageLoader && m_dragImage)
394         m_dragImageLoader->stopLoading(m_dragImage);
395     m_dragImage = image;
396     if (m_dragImage) {
397         if (!m_dragImageLoader)
398             m_dragImageLoader = std::make_unique<DragImageLoader>(this);
399         m_dragImageLoader->startLoading(m_dragImage);
400     }
401
402     m_dragImageElement = image ? nullptr : element;
403
404     updateDragImage();
405 }
406
407 void DataTransfer::updateDragImage()
408 {
409     // Don't allow setting the image if we haven't started dragging yet; we'll rely on the dragging code
410     // to install this drag image as part of getting the drag kicked off.
411     if (!m_shouldUpdateDragImage)
412         return;
413
414     IntPoint computedHotSpot;
415     auto computedImage = DragImage { createDragImage(computedHotSpot) };
416     if (!computedImage)
417         return;
418
419     m_pasteboard->setDragImage(WTFMove(computedImage), computedHotSpot);
420 }
421
422 RefPtr<Element> DataTransfer::dragImageElement() const
423 {
424     return m_dragImageElement;
425 }
426
427 #if !PLATFORM(MAC)
428
429 DragImageRef DataTransfer::createDragImage(IntPoint& location) const
430 {
431     location = m_dragLocation;
432
433     if (m_dragImage)
434         return createDragImageFromImage(m_dragImage->image(), ImageOrientationDescription());
435
436     if (m_dragImageElement) {
437         if (Frame* frame = m_dragImageElement->document().frame())
438             return createDragImageForNode(*frame, *m_dragImageElement);
439     }
440
441     // We do not have enough information to create a drag image, use the default icon.
442     return nullptr;
443 }
444
445 #endif
446
447 DragImageLoader::DragImageLoader(DataTransfer* dataTransfer)
448     : m_dataTransfer(dataTransfer)
449 {
450 }
451
452 void DragImageLoader::moveToDataTransfer(DataTransfer& newDataTransfer)
453 {
454     m_dataTransfer = &newDataTransfer;
455 }
456
457 void DragImageLoader::startLoading(CachedResourceHandle<WebCore::CachedImage>& image)
458 {
459     // FIXME: Does this really trigger a load? Does it need to?
460     image->addClient(*this);
461 }
462
463 void DragImageLoader::stopLoading(CachedResourceHandle<WebCore::CachedImage>& image)
464 {
465     image->removeClient(*this);
466 }
467
468 void DragImageLoader::imageChanged(CachedImage*, const IntRect*)
469 {
470     m_dataTransfer->updateDragImage();
471 }
472
473 static DragOperation dragOpFromIEOp(const String& operation)
474 {
475     if (operation == "uninitialized")
476         return DragOperationEvery;
477     if (operation == "none")
478         return DragOperationNone;
479     if (operation == "copy")
480         return DragOperationCopy;
481     if (operation == "link")
482         return DragOperationLink;
483     if (operation == "move")
484         return (DragOperation)(DragOperationGeneric | DragOperationMove);
485     if (operation == "copyLink")
486         return (DragOperation)(DragOperationCopy | DragOperationLink);
487     if (operation == "copyMove")
488         return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove);
489     if (operation == "linkMove")
490         return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove);
491     if (operation == "all")
492         return DragOperationEvery;
493     return DragOperationPrivate; // really a marker for "no conversion"
494 }
495
496 static const char* IEOpFromDragOp(DragOperation operation)
497 {
498     bool isGenericMove = operation & (DragOperationGeneric | DragOperationMove);
499
500     if ((isGenericMove && (operation & DragOperationCopy) && (operation & DragOperationLink)) || operation == DragOperationEvery)
501         return "all";
502     if (isGenericMove && (operation & DragOperationCopy))
503         return "copyMove";
504     if (isGenericMove && (operation & DragOperationLink))
505         return "linkMove";
506     if ((operation & DragOperationCopy) && (operation & DragOperationLink))
507         return "copyLink";
508     if (isGenericMove)
509         return "move";
510     if (operation & DragOperationCopy)
511         return "copy";
512     if (operation & DragOperationLink)
513         return "link";
514     return "none";
515 }
516
517 DragOperation DataTransfer::sourceOperation() const
518 {
519     DragOperation operation = dragOpFromIEOp(m_effectAllowed);
520     ASSERT(operation != DragOperationPrivate);
521     return operation;
522 }
523
524 DragOperation DataTransfer::destinationOperation() const
525 {
526     DragOperation operation = dragOpFromIEOp(m_dropEffect);
527     ASSERT(operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == (DragOperation)(DragOperationGeneric | DragOperationMove) || operation == DragOperationEvery);
528     return operation;
529 }
530
531 void DataTransfer::setSourceOperation(DragOperation operation)
532 {
533     ASSERT_ARG(operation, operation != DragOperationPrivate);
534     m_effectAllowed = IEOpFromDragOp(operation);
535 }
536
537 void DataTransfer::setDestinationOperation(DragOperation operation)
538 {
539     ASSERT_ARG(operation, operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == DragOperationGeneric || operation == DragOperationMove || operation == (DragOperation)(DragOperationGeneric | DragOperationMove));
540     m_dropEffect = IEOpFromDragOp(operation);
541 }
542
543 String DataTransfer::dropEffect() const
544 {
545     return m_dropEffect == "uninitialized" ? ASCIILiteral("none") : m_dropEffect;
546 }
547
548 void DataTransfer::setDropEffect(const String& effect)
549 {
550     if (!forDrag())
551         return;
552
553     if (effect != "none" && effect != "copy" && effect != "link" && effect != "move")
554         return;
555
556     // FIXME: The spec allows this in all circumstances. There is probably no value
557     // in ignoring attempts to change it.
558     if (!canReadTypes())
559         return;
560
561     m_dropEffect = effect;
562 }
563
564 String DataTransfer::effectAllowed() const
565 {
566     return m_effectAllowed;
567 }
568
569 void DataTransfer::setEffectAllowed(const String& effect)
570 {
571     if (!forDrag())
572         return;
573
574     // Ignore any attempts to set it to an unknown value.
575     if (dragOpFromIEOp(effect) == DragOperationPrivate)
576         return;
577
578     if (!canWriteData())
579         return;
580
581     m_effectAllowed = effect;
582 }
583
584 void DataTransfer::moveDragState(Ref<DataTransfer>&& other)
585 {
586     RELEASE_ASSERT(is<StaticPasteboard>(other->pasteboard()));
587     // We clear the platform pasteboard here to ensure that the pasteboard doesn't contain any data
588     // that may have been written before starting the drag, and after ending the last drag session.
589     // After pushing the static pasteboard's contents to the platform, the pasteboard should only
590     // contain data that was in the static pasteboard.
591     m_pasteboard->clear();
592     downcast<StaticPasteboard>(other->pasteboard()).commitToPasteboard(*m_pasteboard);
593
594     m_dropEffect = other->m_dropEffect;
595     m_effectAllowed = other->m_effectAllowed;
596     m_dragLocation = other->m_dragLocation;
597     m_dragImage = other->m_dragImage;
598     m_dragImageElement = WTFMove(other->m_dragImageElement);
599     m_dragImageLoader = WTFMove(other->m_dragImageLoader);
600     if (m_dragImageLoader)
601         m_dragImageLoader->moveToDataTransfer(*this);
602     m_fileList = WTFMove(other->m_fileList);
603 }
604
605 bool DataTransfer::hasDragImage() const
606 {
607     return m_dragImage || m_dragImageElement;
608 }
609
610 #endif // ENABLE(DRAG_SUPPORT)
611
612 } // namespace WebCore