summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/DataTransfer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom/DataTransfer.cpp')
-rw-r--r--Source/WebCore/dom/DataTransfer.cpp447
1 files changed, 447 insertions, 0 deletions
diff --git a/Source/WebCore/dom/DataTransfer.cpp b/Source/WebCore/dom/DataTransfer.cpp
new file mode 100644
index 000000000..083d3967b
--- /dev/null
+++ b/Source/WebCore/dom/DataTransfer.cpp
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DataTransfer.h"
+
+#include "CachedImage.h"
+#include "CachedImageClient.h"
+#include "DragData.h"
+#include "Editor.h"
+#include "FileList.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "HTMLImageElement.h"
+#include "Image.h"
+#include "Pasteboard.h"
+#include "StaticPasteboard.h"
+
+namespace WebCore {
+
+#if ENABLE(DRAG_SUPPORT)
+
+class DragImageLoader final : private CachedImageClient {
+ WTF_MAKE_NONCOPYABLE(DragImageLoader); WTF_MAKE_FAST_ALLOCATED;
+public:
+ explicit DragImageLoader(DataTransfer*);
+ void startLoading(CachedResourceHandle<CachedImage>&);
+ void stopLoading(CachedResourceHandle<CachedImage>&);
+
+private:
+ void imageChanged(CachedImage*, const IntRect*) override;
+ DataTransfer* m_dataTransfer;
+};
+
+#endif
+
+DataTransfer::DataTransfer(DataTransferAccessPolicy policy, std::unique_ptr<Pasteboard> pasteboard, Type type, bool forFileDrag)
+ : m_policy(policy)
+ , m_pasteboard(WTFMove(pasteboard))
+#if ENABLE(DRAG_SUPPORT)
+ , m_forDrag(type == DragAndDrop)
+ , m_forFileDrag(forFileDrag)
+ , m_dropEffect(ASCIILiteral("uninitialized"))
+ , m_effectAllowed(ASCIILiteral("uninitialized"))
+ , m_shouldUpdateDragImage(false)
+#endif
+{
+#if !ENABLE(DRAG_SUPPORT)
+ ASSERT_UNUSED(type, type != DragAndDrop);
+ ASSERT_UNUSED(forFileDrag, !forFileDrag);
+#endif
+}
+
+Ref<DataTransfer> DataTransfer::createForCopyAndPaste(DataTransferAccessPolicy policy)
+{
+ return adoptRef(*new DataTransfer(policy, policy == DataTransferAccessPolicy::Writable ? Pasteboard::createPrivate() : Pasteboard::createForCopyAndPaste()));
+}
+
+DataTransfer::~DataTransfer()
+{
+#if ENABLE(DRAG_SUPPORT)
+ if (m_dragImageLoader && m_dragImage)
+ m_dragImageLoader->stopLoading(m_dragImage);
+#endif
+}
+
+void DataTransfer::setAccessPolicy(DataTransferAccessPolicy policy)
+{
+ // Once the dataTransfer goes numb, it can never go back.
+ ASSERT(m_policy != DataTransferAccessPolicy::Numb || policy == DataTransferAccessPolicy::Numb);
+ m_policy = policy;
+}
+
+bool DataTransfer::canReadTypes() const
+{
+ return m_policy == DataTransferAccessPolicy::Readable || m_policy == DataTransferAccessPolicy::TypesReadable || m_policy == DataTransferAccessPolicy::Writable;
+}
+
+bool DataTransfer::canReadData() const
+{
+ return m_policy == DataTransferAccessPolicy::Readable || m_policy == DataTransferAccessPolicy::Writable;
+}
+
+bool DataTransfer::canWriteData() const
+{
+ return m_policy == DataTransferAccessPolicy::Writable;
+}
+
+void DataTransfer::clearData(const String& type)
+{
+ if (!canWriteData())
+ return;
+
+ if (type.isNull())
+ m_pasteboard->clear();
+ else
+ m_pasteboard->clear(type);
+}
+
+String DataTransfer::getData(const String& type) const
+{
+ if (!canReadData())
+ return String();
+
+#if ENABLE(DRAG_SUPPORT)
+ if (m_forFileDrag)
+ return String();
+#endif
+
+ return m_pasteboard->readString(type);
+}
+
+void DataTransfer::setData(const String& type, const String& data)
+{
+ if (!canWriteData())
+ return;
+
+#if ENABLE(DRAG_SUPPORT)
+ if (m_forFileDrag)
+ return;
+#endif
+
+ m_pasteboard->writeString(type, data);
+}
+
+Vector<String> DataTransfer::types() const
+{
+ if (!canReadTypes())
+ return { };
+
+ return m_pasteboard->types();
+}
+
+FileList& DataTransfer::files() const
+{
+ bool newlyCreatedFileList = !m_fileList;
+ if (!m_fileList)
+ m_fileList = FileList::create();
+
+ if (!canReadData()) {
+ m_fileList->clear();
+ return *m_fileList;
+ }
+
+#if ENABLE(DRAG_SUPPORT)
+ if (m_forDrag && !m_forFileDrag) {
+ ASSERT(m_fileList->isEmpty());
+ return *m_fileList;
+ }
+#endif
+
+ if (newlyCreatedFileList) {
+ for (const String& filename : m_pasteboard->readFilenames())
+ m_fileList->append(File::create(filename));
+ }
+ return *m_fileList;
+}
+
+bool DataTransfer::hasFileOfType(const String& type)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
+
+ for (const String& path : m_pasteboard->readFilenames()) {
+ if (equalIgnoringASCIICase(File::contentTypeForFile(path), type))
+ return true;
+ }
+
+ return false;
+}
+
+bool DataTransfer::hasStringOfType(const String& type)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(canReadTypes());
+
+ return !type.isNull() && types().contains(type);
+}
+
+Ref<DataTransfer> DataTransfer::createForInputEvent(const String& plainText, const String& htmlText)
+{
+ TypeToStringMap typeToStringMap;
+ typeToStringMap.set(ASCIILiteral("text/plain"), plainText);
+ typeToStringMap.set(ASCIILiteral("text/html"), htmlText);
+ return adoptRef(*new DataTransfer(DataTransferAccessPolicy::Readable, StaticPasteboard::create(WTFMove(typeToStringMap)), InputEvent));
+}
+
+#if !ENABLE(DRAG_SUPPORT)
+
+String DataTransfer::dropEffect() const
+{
+ return ASCIILiteral("none");
+}
+
+void DataTransfer::setDropEffect(const String&)
+{
+}
+
+String DataTransfer::effectAllowed() const
+{
+ return ASCIILiteral("uninitialized");
+}
+
+void DataTransfer::setEffectAllowed(const String&)
+{
+}
+
+void DataTransfer::setDragImage(Element*, int, int)
+{
+}
+
+#else
+
+Ref<DataTransfer> DataTransfer::createForDrag()
+{
+ return adoptRef(*new DataTransfer(DataTransferAccessPolicy::Writable, Pasteboard::createForDragAndDrop(), DragAndDrop));
+}
+
+Ref<DataTransfer> DataTransfer::createForDrop(DataTransferAccessPolicy policy, const DragData& dragData)
+{
+ return adoptRef(*new DataTransfer(policy, Pasteboard::createForDragAndDrop(dragData), DragAndDrop, dragData.containsFiles()));
+}
+
+bool DataTransfer::canSetDragImage() const
+{
+ // Note that the spec doesn't actually allow drag image modification outside the dragstart
+ // event. This capability is maintained for backwards compatiblity for ports that have
+ // supported this in the past. On many ports, attempting to set a drag image outside the
+ // dragstart operation is a no-op anyway.
+ return m_forDrag && (m_policy == DataTransferAccessPolicy::ImageWritable || m_policy == DataTransferAccessPolicy::Writable);
+}
+
+void DataTransfer::setDragImage(Element* element, int x, int y)
+{
+ if (!canSetDragImage())
+ return;
+
+ CachedImage* image = nullptr;
+ if (is<HTMLImageElement>(element) && !element->isConnected())
+ image = downcast<HTMLImageElement>(*element).cachedImage();
+
+ m_dragLocation = IntPoint(x, y);
+
+ if (m_dragImageLoader && m_dragImage)
+ m_dragImageLoader->stopLoading(m_dragImage);
+ m_dragImage = image;
+ if (m_dragImage) {
+ if (!m_dragImageLoader)
+ m_dragImageLoader = std::make_unique<DragImageLoader>(this);
+ m_dragImageLoader->startLoading(m_dragImage);
+ }
+
+ m_dragImageElement = image ? nullptr : element;
+
+ updateDragImage();
+}
+
+void DataTransfer::updateDragImage()
+{
+ // Don't allow setting the image if we haven't started dragging yet; we'll rely on the dragging code
+ // to install this drag image as part of getting the drag kicked off.
+ if (!m_shouldUpdateDragImage)
+ return;
+
+ IntPoint computedHotSpot;
+ auto computedImage = DragImage { createDragImage(computedHotSpot) };
+ if (!computedImage)
+ return;
+
+ m_pasteboard->setDragImage(WTFMove(computedImage), computedHotSpot);
+}
+
+#if !PLATFORM(MAC)
+
+DragImageRef DataTransfer::createDragImage(IntPoint& location) const
+{
+ location = m_dragLocation;
+
+ if (m_dragImage)
+ return createDragImageFromImage(m_dragImage->image(), ImageOrientationDescription());
+
+ if (m_dragImageElement) {
+ if (Frame* frame = m_dragImageElement->document().frame())
+ return createDragImageForNode(*frame, *m_dragImageElement);
+ }
+
+ // We do not have enough information to create a drag image, use the default icon.
+ return nullptr;
+}
+
+#endif
+
+DragImageLoader::DragImageLoader(DataTransfer* dataTransfer)
+ : m_dataTransfer(dataTransfer)
+{
+}
+
+void DragImageLoader::startLoading(CachedResourceHandle<WebCore::CachedImage>& image)
+{
+ // FIXME: Does this really trigger a load? Does it need to?
+ image->addClient(*this);
+}
+
+void DragImageLoader::stopLoading(CachedResourceHandle<WebCore::CachedImage>& image)
+{
+ image->removeClient(*this);
+}
+
+void DragImageLoader::imageChanged(CachedImage*, const IntRect*)
+{
+ m_dataTransfer->updateDragImage();
+}
+
+static DragOperation dragOpFromIEOp(const String& operation)
+{
+ if (operation == "uninitialized")
+ return DragOperationEvery;
+ if (operation == "none")
+ return DragOperationNone;
+ if (operation == "copy")
+ return DragOperationCopy;
+ if (operation == "link")
+ return DragOperationLink;
+ if (operation == "move")
+ return (DragOperation)(DragOperationGeneric | DragOperationMove);
+ if (operation == "copyLink")
+ return (DragOperation)(DragOperationCopy | DragOperationLink);
+ if (operation == "copyMove")
+ return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove);
+ if (operation == "linkMove")
+ return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove);
+ if (operation == "all")
+ return DragOperationEvery;
+ return DragOperationPrivate; // really a marker for "no conversion"
+}
+
+static const char* IEOpFromDragOp(DragOperation operation)
+{
+ bool isGenericMove = operation & (DragOperationGeneric | DragOperationMove);
+
+ if ((isGenericMove && (operation & DragOperationCopy) && (operation & DragOperationLink)) || operation == DragOperationEvery)
+ return "all";
+ if (isGenericMove && (operation & DragOperationCopy))
+ return "copyMove";
+ if (isGenericMove && (operation & DragOperationLink))
+ return "linkMove";
+ if ((operation & DragOperationCopy) && (operation & DragOperationLink))
+ return "copyLink";
+ if (isGenericMove)
+ return "move";
+ if (operation & DragOperationCopy)
+ return "copy";
+ if (operation & DragOperationLink)
+ return "link";
+ return "none";
+}
+
+DragOperation DataTransfer::sourceOperation() const
+{
+ DragOperation operation = dragOpFromIEOp(m_effectAllowed);
+ ASSERT(operation != DragOperationPrivate);
+ return operation;
+}
+
+DragOperation DataTransfer::destinationOperation() const
+{
+ DragOperation operation = dragOpFromIEOp(m_dropEffect);
+ ASSERT(operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == (DragOperation)(DragOperationGeneric | DragOperationMove) || operation == DragOperationEvery);
+ return operation;
+}
+
+void DataTransfer::setSourceOperation(DragOperation operation)
+{
+ ASSERT_ARG(operation, operation != DragOperationPrivate);
+ m_effectAllowed = IEOpFromDragOp(operation);
+}
+
+void DataTransfer::setDestinationOperation(DragOperation operation)
+{
+ ASSERT_ARG(operation, operation == DragOperationCopy || operation == DragOperationNone || operation == DragOperationLink || operation == DragOperationGeneric || operation == DragOperationMove || operation == (DragOperation)(DragOperationGeneric | DragOperationMove));
+ m_dropEffect = IEOpFromDragOp(operation);
+}
+
+String DataTransfer::dropEffect() const
+{
+ return m_dropEffect == "uninitialized" ? ASCIILiteral("none") : m_dropEffect;
+}
+
+void DataTransfer::setDropEffect(const String& effect)
+{
+ if (!m_forDrag)
+ return;
+
+ if (effect != "none" && effect != "copy" && effect != "link" && effect != "move")
+ return;
+
+ // FIXME: The spec allows this in all circumstances. There is probably no value
+ // in ignoring attempts to change it.
+ if (!canReadTypes())
+ return;
+
+ m_dropEffect = effect;
+}
+
+String DataTransfer::effectAllowed() const
+{
+ return m_effectAllowed;
+}
+
+void DataTransfer::setEffectAllowed(const String& effect)
+{
+ if (!m_forDrag)
+ return;
+
+ // Ignore any attempts to set it to an unknown value.
+ if (dragOpFromIEOp(effect) == DragOperationPrivate)
+ return;
+
+ if (!canWriteData())
+ return;
+
+ m_effectAllowed = effect;
+}
+
+#endif // ENABLE(DRAG_SUPPORT)
+
+} // namespace WebCore