505403c5244b18164250be8b5c16f28992e0ab54
[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 "DataTransferItemList.h"
32 #include "DragData.h"
33 #include "Editor.h"
34 #include "FileList.h"
35 #include "Frame.h"
36 #include "FrameLoader.h"
37 #include "HTMLImageElement.h"
38 #include "Image.h"
39 #include "Pasteboard.h"
40 #include "StaticPasteboard.h"
41
42 namespace WebCore {
43
44 #if ENABLE(DRAG_SUPPORT)
45
46 class DragImageLoader final : private CachedImageClient {
47     WTF_MAKE_NONCOPYABLE(DragImageLoader); WTF_MAKE_FAST_ALLOCATED;
48 public:
49     explicit DragImageLoader(DataTransfer*);
50     void startLoading(CachedResourceHandle<CachedImage>&);
51     void stopLoading(CachedResourceHandle<CachedImage>&);
52
53 private:
54     void imageChanged(CachedImage*, const IntRect*) override;
55     DataTransfer* m_dataTransfer;
56 };
57
58 #endif
59
60 DataTransfer::DataTransfer(StoreMode mode, std::unique_ptr<Pasteboard> pasteboard, Type type)
61     : m_storeMode(mode)
62     , m_pasteboard(WTFMove(pasteboard))
63 #if ENABLE(DRAG_SUPPORT)
64     , m_forDrag(type == Type::DragAndDropData || type == Type::DragAndDropFiles)
65     , m_forFileDrag(type == Type::DragAndDropFiles)
66     , m_dropEffect(ASCIILiteral("uninitialized"))
67     , m_effectAllowed(ASCIILiteral("uninitialized"))
68     , m_shouldUpdateDragImage(false)
69 #endif
70 {
71 #if !ENABLE(DRAG_SUPPORT)
72     ASSERT_UNUSED(type, type != DragAndDropData && type != DragAndDropFiles);
73 #endif
74 }
75
76 Ref<DataTransfer> DataTransfer::createForCopyAndPaste(StoreMode mode)
77 {
78     return adoptRef(*new DataTransfer(mode, mode == StoreMode::ReadWrite ? Pasteboard::createPrivate() : Pasteboard::createForCopyAndPaste()));
79 }
80
81 DataTransfer::~DataTransfer()
82 {
83 #if ENABLE(DRAG_SUPPORT)
84     if (m_dragImageLoader && m_dragImage)
85         m_dragImageLoader->stopLoading(m_dragImage);
86 #endif
87 }
88
89 bool DataTransfer::canReadTypes() const
90 {
91     return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::Protected || m_storeMode == StoreMode::ReadWrite;
92 }
93
94 bool DataTransfer::canReadData() const
95 {
96     return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::ReadWrite;
97 }
98
99 bool DataTransfer::canWriteData() const
100 {
101     return m_storeMode == StoreMode::ReadWrite;
102 }
103
104 void DataTransfer::clearData(const String& type)
105 {
106     if (!canWriteData())
107         return;
108
109     if (type.isNull())
110         m_pasteboard->clear();
111     else
112         m_pasteboard->clear(type);
113     if (m_itemList)
114         m_itemList->didClearStringData(type);
115 }
116
117 String DataTransfer::getData(const String& type) const
118 {
119     if (!canReadData())
120         return String();
121
122 #if ENABLE(DRAG_SUPPORT)
123     if (m_forFileDrag)
124         return String();
125 #endif
126
127     return m_pasteboard->readString(type);
128 }
129
130 void DataTransfer::setData(const String& type, const String& data)
131 {
132     if (!canWriteData())
133         return;
134
135 #if ENABLE(DRAG_SUPPORT)
136     if (m_forFileDrag)
137         return;
138 #endif
139
140     m_pasteboard->writeString(type, data);
141     if (m_itemList)
142         m_itemList->didSetStringData(type);
143 }
144
145 DataTransferItemList& DataTransfer::items()
146 {
147     if (!m_itemList)
148         m_itemList = std::make_unique<DataTransferItemList>(*this);
149     return *m_itemList;
150 }
151
152 Vector<String> DataTransfer::types() const
153 {
154     if (!canReadTypes())
155         return { };
156
157     return m_pasteboard->types();
158 }
159
160 FileList& DataTransfer::files() const
161 {
162     bool newlyCreatedFileList = !m_fileList;
163     if (!m_fileList)
164         m_fileList = FileList::create();
165
166     if (!canReadData()) {
167         m_fileList->clear();
168         return *m_fileList;
169     }
170
171 #if ENABLE(DRAG_SUPPORT)
172     if (m_forDrag && !m_forFileDrag) {
173         ASSERT(m_fileList->isEmpty());
174         return *m_fileList;
175     }
176 #endif
177
178     if (newlyCreatedFileList) {
179         for (const String& filename : m_pasteboard->readFilenames())
180             m_fileList->append(File::create(filename));
181     }
182     return *m_fileList;
183 }
184
185 bool DataTransfer::hasFileOfType(const String& type)
186 {
187     ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
188
189     for (const String& path : m_pasteboard->readFilenames()) {
190         if (equalIgnoringASCIICase(File::contentTypeForFile(path), type))
191             return true;
192     }
193
194     return false;
195 }
196
197 bool DataTransfer::hasStringOfType(const String& type)
198 {
199     ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
200
201     return !type.isNull() && types().contains(type);
202 }
203
204 Ref<DataTransfer> DataTransfer::createForInputEvent(const String& plainText, const String& htmlText)
205 {
206     TypeToStringMap typeToStringMap;
207     typeToStringMap.set(ASCIILiteral("text/plain"), plainText);
208     typeToStringMap.set(ASCIILiteral("text/html"), htmlText);
209     return adoptRef(*new DataTransfer(StoreMode::Readonly, StaticPasteboard::create(WTFMove(typeToStringMap)), Type::InputEvent));
210 }
211
212 #if !ENABLE(DRAG_SUPPORT)
213
214 String DataTransfer::dropEffect() const
215 {
216     return ASCIILiteral("none");
217 }
218
219 void DataTransfer::setDropEffect(const String&)
220 {
221 }
222
223 String DataTransfer::effectAllowed() const
224 {
225     return ASCIILiteral("uninitialized");
226 }
227
228 void DataTransfer::setEffectAllowed(const String&)
229 {
230 }
231
232 void DataTransfer::setDragImage(Element*, int, int)
233 {
234 }
235
236 #else
237
238 Ref<DataTransfer> DataTransfer::createForDrag()
239 {
240     return adoptRef(*new DataTransfer(StoreMode::ReadWrite, Pasteboard::createForDragAndDrop(), Type::DragAndDropData));
241 }
242
243 Ref<DataTransfer> DataTransfer::createForDrop(StoreMode accessMode, const DragData& dragData)
244 {
245     auto type = dragData.containsFiles() ? Type::DragAndDropFiles : Type::DragAndDropData;
246     return adoptRef(*new DataTransfer(accessMode, Pasteboard::createForDragAndDrop(dragData), type));
247 }
248
249 bool DataTransfer::canSetDragImage() const
250 {
251     // Note that the spec doesn't actually allow drag image modification outside the dragstart
252     // event. This capability is maintained for backwards compatiblity for ports that have
253     // supported this in the past. On many ports, attempting to set a drag image outside the
254     // dragstart operation is a no-op anyway.
255     return m_forDrag && (m_storeMode == StoreMode::DragImageWritable || m_storeMode == StoreMode::ReadWrite);
256 }
257
258 void DataTransfer::setDragImage(Element* element, int x, int y)
259 {
260     if (!canSetDragImage())
261         return;
262
263     CachedImage* image = nullptr;
264     if (is<HTMLImageElement>(element) && !element->isConnected())
265         image = downcast<HTMLImageElement>(*element).cachedImage();
266
267     m_dragLocation = IntPoint(x, y);
268
269     if (m_dragImageLoader && m_dragImage)
270         m_dragImageLoader->stopLoading(m_dragImage);
271     m_dragImage = image;
272     if (m_dragImage) {
273         if (!m_dragImageLoader)
274             m_dragImageLoader = std::make_unique<DragImageLoader>(this);
275         m_dragImageLoader->startLoading(m_dragImage);
276     }
277
278     m_dragImageElement = image ? nullptr : element;
279
280     updateDragImage();
281 }
282
283 void DataTransfer::updateDragImage()
284 {
285     // Don't allow setting the image if we haven't started dragging yet; we'll rely on the dragging code
286     // to install this drag image as part of getting the drag kicked off.
287     if (!m_shouldUpdateDragImage)
288         return;
289
290     IntPoint computedHotSpot;
291     auto computedImage = DragImage { createDragImage(computedHotSpot) };
292     if (!computedImage)
293         return;
294
295     m_pasteboard->setDragImage(WTFMove(computedImage), computedHotSpot);
296 }
297
298 #if !PLATFORM(MAC)
299
300 DragImageRef DataTransfer::createDragImage(IntPoint& location) const
301 {
302     location = m_dragLocation;
303
304     if (m_dragImage)
305         return createDragImageFromImage(m_dragImage->image(), ImageOrientationDescription());
306
307     if (m_dragImageElement) {
308         if (Frame* frame = m_dragImageElement->document().frame())
309             return createDragImageForNode(*frame, *m_dragImageElement);
310     }
311
312     // We do not have enough information to create a drag image, use the default icon.
313     return nullptr;
314 }
315
316 #endif
317
318 DragImageLoader::DragImageLoader(DataTransfer* dataTransfer)
319     : m_dataTransfer(dataTransfer)
320 {
321 }
322
323 void DragImageLoader::startLoading(CachedResourceHandle<WebCore::CachedImage>& image)
324 {
325     // FIXME: Does this really trigger a load? Does it need to?
326     image->addClient(*this);
327 }
328
329 void DragImageLoader::stopLoading(CachedResourceHandle<WebCore::CachedImage>& image)
330 {
331     image->removeClient(*this);
332 }
333
334 void DragImageLoader::imageChanged(CachedImage*, const IntRect*)
335 {
336     m_dataTransfer->updateDragImage();
337 }
338
339 static DragOperation dragOpFromIEOp(const String& operation)
340 {
341     if (operation == "uninitialized")
342         return DragOperationEvery;
343     if (operation == "none")
344         return DragOperationNone;
345     if (operation == "copy")
346         return DragOperationCopy;
347     if (operation == "link")
348         return DragOperationLink;
349     if (operation == "move")
350         return (DragOperation)(DragOperationGeneric | DragOperationMove);
351     if (operation == "copyLink")
352         return (DragOperation)(DragOperationCopy | DragOperationLink);
353     if (operation == "copyMove")
354         return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove);
355     if (operation == "linkMove")
356         return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove);
357     if (operation == "all")
358         return DragOperationEvery;
359     return DragOperationPrivate; // really a marker for "no conversion"
360 }
361
362 static const char* IEOpFromDragOp(DragOperation operation)
363 {
364     bool isGenericMove = operation & (DragOperationGeneric | DragOperationMove);
365
366     if ((isGenericMove && (operation & DragOperationCopy) && (operation & DragOperationLink)) || operation == DragOperationEvery)
367         return "all";
368     if (isGenericMove && (operation & DragOperationCopy))
369         return "copyMove";
370     if (isGenericMove && (operation & DragOperationLink))
371         return "linkMove";
372     if ((operation & DragOperationCopy) && (operation & DragOperationLink))
373         return "copyLink";
374     if (isGenericMove)
375         return "move";
376     if (operation & DragOperationCopy)
377         return "copy";
378     if (operation & DragOperationLink)
379         return "link";
380     return "none";
381 }
382
383 DragOperation DataTransfer::sourceOperation() const
384 {
385     DragOperation operation = dragOpFromIEOp(m_effectAllowed);
386     ASSERT(operation != DragOperationPrivate);
387     return operation;
388 }
389
390 DragOperation DataTransfer::destinationOperation() const
391 {
392     DragOperation operation = dragOpFromIEOp(m_dropEffect);
393     ASSERT(operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == (DragOperation)(DragOperationGeneric | DragOperationMove) || operation == DragOperationEvery);
394     return operation;
395 }
396
397 void DataTransfer::setSourceOperation(DragOperation operation)
398 {
399     ASSERT_ARG(operation, operation != DragOperationPrivate);
400     m_effectAllowed = IEOpFromDragOp(operation);
401 }
402
403 void DataTransfer::setDestinationOperation(DragOperation operation)
404 {
405     ASSERT_ARG(operation, operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == DragOperationGeneric || operation == DragOperationMove || operation == (DragOperation)(DragOperationGeneric | DragOperationMove));
406     m_dropEffect = IEOpFromDragOp(operation);
407 }
408
409 String DataTransfer::dropEffect() const
410 {
411     return m_dropEffect == "uninitialized" ? ASCIILiteral("none") : m_dropEffect;
412 }
413
414 void DataTransfer::setDropEffect(const String& effect)
415 {
416     if (!m_forDrag)
417         return;
418
419     if (effect != "none" && effect != "copy" && effect != "link" && effect != "move")
420         return;
421
422     // FIXME: The spec allows this in all circumstances. There is probably no value
423     // in ignoring attempts to change it.
424     if (!canReadTypes())
425         return;
426
427     m_dropEffect = effect;
428 }
429
430 String DataTransfer::effectAllowed() const
431 {
432     return m_effectAllowed;
433 }
434
435 void DataTransfer::setEffectAllowed(const String& effect)
436 {
437     if (!m_forDrag)
438         return;
439
440     // Ignore any attempts to set it to an unknown value.
441     if (dragOpFromIEOp(effect) == DragOperationPrivate)
442         return;
443
444     if (!canWriteData())
445         return;
446
447     m_effectAllowed = effect;
448 }
449
450 #endif // ENABLE(DRAG_SUPPORT)
451
452 } // namespace WebCore