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