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