| /* |
| * Copyright (C) 2017-2022 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 "DataTransferItemList.h" |
| |
| #include "ContextDestructionObserver.h" |
| #include "DataTransferItem.h" |
| #include "Document.h" |
| #include "FileList.h" |
| #include "Pasteboard.h" |
| #include "RuntimeEnabledFeatures.h" |
| #include "Settings.h" |
| #include <wtf/IsoMallocInlines.h> |
| |
| namespace WebCore { |
| |
| WTF_MAKE_ISO_ALLOCATED_IMPL(DataTransferItemList); |
| |
| DataTransferItemList::DataTransferItemList(Document& document, DataTransfer& dataTransfer) |
| : ContextDestructionObserver(&document) |
| , m_dataTransfer(dataTransfer) |
| { |
| } |
| |
| DataTransferItemList::~DataTransferItemList() = default; |
| |
| unsigned DataTransferItemList::length() const |
| { |
| return ensureItems().size(); |
| } |
| |
| RefPtr<DataTransferItem> DataTransferItemList::item(unsigned index) |
| { |
| auto& items = ensureItems(); |
| if (items.size() <= index) |
| return nullptr; |
| return items[index].copyRef(); |
| } |
| |
| static bool shouldExposeTypeInItemList(const String& type) |
| { |
| return RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled() || Pasteboard::isSafeTypeForDOMToReadAndWrite(type); |
| } |
| |
| ExceptionOr<RefPtr<DataTransferItem>> DataTransferItemList::add(const String& data, const String& type) |
| { |
| if (!m_dataTransfer.canWriteData()) |
| return nullptr; |
| |
| for (auto& item : ensureItems()) { |
| if (!item->isFile() && equalIgnoringASCIICase(item->type(), type)) |
| return Exception { NotSupportedError }; |
| } |
| |
| String lowercasedType = type.convertToASCIILowercase(); |
| |
| if (!shouldExposeTypeInItemList(lowercasedType)) |
| return nullptr; |
| |
| m_dataTransfer.setDataFromItemList(lowercasedType, data); |
| ASSERT(m_items); |
| m_items->append(DataTransferItem::create(*this, lowercasedType)); |
| return m_items->last().ptr(); |
| } |
| |
| RefPtr<DataTransferItem> DataTransferItemList::add(Ref<File>&& file) |
| { |
| if (!m_dataTransfer.canWriteData()) |
| return nullptr; |
| |
| ensureItems().append(DataTransferItem::create(*this, file->type(), file.copyRef())); |
| m_dataTransfer.didAddFileToItemList(); |
| return m_items->last().ptr(); |
| } |
| |
| ExceptionOr<void> DataTransferItemList::remove(unsigned index) |
| { |
| if (!m_dataTransfer.canWriteData()) |
| return Exception { InvalidStateError }; |
| |
| auto& items = ensureItems(); |
| if (items.size() <= index) |
| return { }; |
| |
| // FIXME: Remove the file from the pasteboard object once we add support for it. |
| Ref<DataTransferItem> removedItem = items[index].copyRef(); |
| if (!removedItem->isFile()) |
| m_dataTransfer.pasteboard().clear(removedItem->type()); |
| removedItem->clearListAndPutIntoDisabledMode(); |
| items.remove(index); |
| if (removedItem->isFile()) |
| m_dataTransfer.updateFileList(scriptExecutionContext()); |
| |
| return { }; |
| } |
| |
| void DataTransferItemList::clear() |
| { |
| m_dataTransfer.pasteboard().clear(); |
| bool removedItemContainingFile = false; |
| if (m_items) { |
| for (auto& item : *m_items) { |
| removedItemContainingFile |= item->isFile(); |
| item->clearListAndPutIntoDisabledMode(); |
| } |
| m_items->clear(); |
| } |
| |
| if (removedItemContainingFile) |
| m_dataTransfer.updateFileList(scriptExecutionContext()); |
| } |
| |
| Vector<Ref<DataTransferItem>>& DataTransferItemList::ensureItems() const |
| { |
| if (m_items) |
| return *m_items; |
| |
| Vector<Ref<DataTransferItem>> items; |
| for (auto& type : m_dataTransfer.typesForItemList()) { |
| auto lowercasedType = type.convertToASCIILowercase(); |
| if (shouldExposeTypeInItemList(lowercasedType)) |
| items.append(DataTransferItem::create(*this, lowercasedType)); |
| } |
| |
| for (auto& file : m_dataTransfer.files(document()).files()) |
| items.append(DataTransferItem::create(*this, file->type(), file.copyRef())); |
| |
| m_items = WTFMove(items); |
| |
| return *m_items; |
| } |
| |
| static void removeStringItemOfLowercasedType(Vector<Ref<DataTransferItem>>& items, const String& lowercasedType) |
| { |
| auto index = items.findIf([lowercasedType](auto& item) { |
| return !item->isFile() && item->type() == lowercasedType; |
| }); |
| if (index == notFound) |
| return; |
| items[index]->clearListAndPutIntoDisabledMode(); |
| items.remove(index); |
| } |
| |
| void DataTransferItemList::didClearStringData(const String& type) |
| { |
| if (!m_items) |
| return; |
| |
| auto& items = *m_items; |
| if (!type.isNull()) |
| return removeStringItemOfLowercasedType(items, type.convertToASCIILowercase()); |
| |
| for (auto& item : items) { |
| if (!item->isFile()) |
| item->clearListAndPutIntoDisabledMode(); |
| } |
| items.removeAllMatching([](auto& item) { |
| return !item->isFile(); |
| }); |
| } |
| |
| // https://html.spec.whatwg.org/multipage/dnd.html#dom-datatransfer-setdata |
| void DataTransferItemList::didSetStringData(const String& type) |
| { |
| if (!m_items) |
| return; |
| |
| String lowercasedType = type.convertToASCIILowercase(); |
| removeStringItemOfLowercasedType(*m_items, type.convertToASCIILowercase()); |
| |
| m_items->append(DataTransferItem::create(*this, lowercasedType)); |
| } |
| |
| Document* DataTransferItemList::document() const |
| { |
| return downcast<Document>(scriptExecutionContext()); |
| } |
| |
| } |
| |