2 * Copyright (C) 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "DataTransfer.h"
29 #include "CachedImage.h"
30 #include "CachedImageClient.h"
31 #include "DataTransferItemList.h"
36 #include "FrameLoader.h"
37 #include "HTMLImageElement.h"
39 #include "Pasteboard.h"
40 #include "StaticPasteboard.h"
44 #if ENABLE(DRAG_SUPPORT)
46 class DragImageLoader final : private CachedImageClient {
47 WTF_MAKE_NONCOPYABLE(DragImageLoader); WTF_MAKE_FAST_ALLOCATED;
49 explicit DragImageLoader(DataTransfer*);
50 void startLoading(CachedResourceHandle<CachedImage>&);
51 void stopLoading(CachedResourceHandle<CachedImage>&);
54 void imageChanged(CachedImage*, const IntRect*) override;
55 DataTransfer* m_dataTransfer;
60 DataTransfer::DataTransfer(StoreMode mode, std::unique_ptr<Pasteboard> pasteboard, Type type)
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)
71 #if !ENABLE(DRAG_SUPPORT)
72 ASSERT_UNUSED(type, type != Type::DragAndDropData && type != Type::DragAndDropFiles);
76 Ref<DataTransfer> DataTransfer::createForCopyAndPaste(StoreMode mode)
78 return adoptRef(*new DataTransfer(mode, mode == StoreMode::ReadWrite ? Pasteboard::createPrivate() : Pasteboard::createForCopyAndPaste()));
81 DataTransfer::~DataTransfer()
83 #if ENABLE(DRAG_SUPPORT)
84 if (m_dragImageLoader && m_dragImage)
85 m_dragImageLoader->stopLoading(m_dragImage);
89 bool DataTransfer::canReadTypes() const
91 return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::Protected || m_storeMode == StoreMode::ReadWrite;
94 bool DataTransfer::canReadData() const
96 return m_storeMode == StoreMode::Readonly || m_storeMode == StoreMode::ReadWrite;
99 bool DataTransfer::canWriteData() const
101 return m_storeMode == StoreMode::ReadWrite;
104 void DataTransfer::clearData(const String& type)
110 m_pasteboard->clear();
112 m_pasteboard->clear(type);
114 m_itemList->didClearStringData(type);
117 String DataTransfer::getData(const String& type) const
122 #if ENABLE(DRAG_SUPPORT)
127 return m_pasteboard->readString(type);
130 void DataTransfer::setData(const String& type, const String& data)
135 #if ENABLE(DRAG_SUPPORT)
140 m_pasteboard->writeString(type, data);
142 m_itemList->didSetStringData(type);
145 DataTransferItemList& DataTransfer::items()
148 m_itemList = std::make_unique<DataTransferItemList>(*this);
152 Vector<String> DataTransfer::types() const
157 return m_pasteboard->types();
160 FileList& DataTransfer::files() const
162 bool newlyCreatedFileList = !m_fileList;
164 m_fileList = FileList::create();
166 if (!canReadData()) {
171 #if ENABLE(DRAG_SUPPORT)
172 if (m_forDrag && !m_forFileDrag) {
173 ASSERT(m_fileList->isEmpty());
178 if (newlyCreatedFileList) {
179 for (const String& filename : m_pasteboard->readFilenames())
180 m_fileList->append(File::create(filename));
185 bool DataTransfer::hasFileOfType(const String& type)
187 ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
189 for (const String& path : m_pasteboard->readFilenames()) {
190 if (equalIgnoringASCIICase(File::contentTypeForFile(path), type))
197 bool DataTransfer::hasStringOfType(const String& type)
199 ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
201 return !type.isNull() && types().contains(type);
204 Ref<DataTransfer> DataTransfer::createForInputEvent(const String& plainText, const String& htmlText)
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));
212 #if !ENABLE(DRAG_SUPPORT)
214 String DataTransfer::dropEffect() const
216 return ASCIILiteral("none");
219 void DataTransfer::setDropEffect(const String&)
223 String DataTransfer::effectAllowed() const
225 return ASCIILiteral("uninitialized");
228 void DataTransfer::setEffectAllowed(const String&)
232 void DataTransfer::setDragImage(Element*, int, int)
238 Ref<DataTransfer> DataTransfer::createForDrag()
240 return adoptRef(*new DataTransfer(StoreMode::ReadWrite, Pasteboard::createForDragAndDrop(), Type::DragAndDropData));
243 Ref<DataTransfer> DataTransfer::createForDrop(StoreMode accessMode, const DragData& dragData)
245 auto type = dragData.containsFiles() ? Type::DragAndDropFiles : Type::DragAndDropData;
246 return adoptRef(*new DataTransfer(accessMode, Pasteboard::createForDragAndDrop(dragData), type));
249 bool DataTransfer::canSetDragImage() const
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);
258 void DataTransfer::setDragImage(Element* element, int x, int y)
260 if (!canSetDragImage())
263 CachedImage* image = nullptr;
264 if (is<HTMLImageElement>(element) && !element->isConnected())
265 image = downcast<HTMLImageElement>(*element).cachedImage();
267 m_dragLocation = IntPoint(x, y);
269 if (m_dragImageLoader && m_dragImage)
270 m_dragImageLoader->stopLoading(m_dragImage);
273 if (!m_dragImageLoader)
274 m_dragImageLoader = std::make_unique<DragImageLoader>(this);
275 m_dragImageLoader->startLoading(m_dragImage);
278 m_dragImageElement = image ? nullptr : element;
283 void DataTransfer::updateDragImage()
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)
290 IntPoint computedHotSpot;
291 auto computedImage = DragImage { createDragImage(computedHotSpot) };
295 m_pasteboard->setDragImage(WTFMove(computedImage), computedHotSpot);
300 DragImageRef DataTransfer::createDragImage(IntPoint& location) const
302 location = m_dragLocation;
305 return createDragImageFromImage(m_dragImage->image(), ImageOrientationDescription());
307 if (m_dragImageElement) {
308 if (Frame* frame = m_dragImageElement->document().frame())
309 return createDragImageForNode(*frame, *m_dragImageElement);
312 // We do not have enough information to create a drag image, use the default icon.
318 DragImageLoader::DragImageLoader(DataTransfer* dataTransfer)
319 : m_dataTransfer(dataTransfer)
323 void DragImageLoader::startLoading(CachedResourceHandle<WebCore::CachedImage>& image)
325 // FIXME: Does this really trigger a load? Does it need to?
326 image->addClient(*this);
329 void DragImageLoader::stopLoading(CachedResourceHandle<WebCore::CachedImage>& image)
331 image->removeClient(*this);
334 void DragImageLoader::imageChanged(CachedImage*, const IntRect*)
336 m_dataTransfer->updateDragImage();
339 static DragOperation dragOpFromIEOp(const String& operation)
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"
362 static const char* IEOpFromDragOp(DragOperation operation)
364 bool isGenericMove = operation & (DragOperationGeneric | DragOperationMove);
366 if ((isGenericMove && (operation & DragOperationCopy) && (operation & DragOperationLink)) || operation == DragOperationEvery)
368 if (isGenericMove && (operation & DragOperationCopy))
370 if (isGenericMove && (operation & DragOperationLink))
372 if ((operation & DragOperationCopy) && (operation & DragOperationLink))
376 if (operation & DragOperationCopy)
378 if (operation & DragOperationLink)
383 DragOperation DataTransfer::sourceOperation() const
385 DragOperation operation = dragOpFromIEOp(m_effectAllowed);
386 ASSERT(operation != DragOperationPrivate);
390 DragOperation DataTransfer::destinationOperation() const
392 DragOperation operation = dragOpFromIEOp(m_dropEffect);
393 ASSERT(operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == (DragOperation)(DragOperationGeneric | DragOperationMove) || operation == DragOperationEvery);
397 void DataTransfer::setSourceOperation(DragOperation operation)
399 ASSERT_ARG(operation, operation != DragOperationPrivate);
400 m_effectAllowed = IEOpFromDragOp(operation);
403 void DataTransfer::setDestinationOperation(DragOperation operation)
405 ASSERT_ARG(operation, operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == DragOperationGeneric || operation == DragOperationMove || operation == (DragOperation)(DragOperationGeneric | DragOperationMove));
406 m_dropEffect = IEOpFromDragOp(operation);
409 String DataTransfer::dropEffect() const
411 return m_dropEffect == "uninitialized" ? ASCIILiteral("none") : m_dropEffect;
414 void DataTransfer::setDropEffect(const String& effect)
419 if (effect != "none" && effect != "copy" && effect != "link" && effect != "move")
422 // FIXME: The spec allows this in all circumstances. There is probably no value
423 // in ignoring attempts to change it.
427 m_dropEffect = effect;
430 String DataTransfer::effectAllowed() const
432 return m_effectAllowed;
435 void DataTransfer::setEffectAllowed(const String& effect)
440 // Ignore any attempts to set it to an unknown value.
441 if (dragOpFromIEOp(effect) == DragOperationPrivate)
447 m_effectAllowed = effect;
450 #endif // ENABLE(DRAG_SUPPORT)
452 } // namespace WebCore