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