blob: ae5c681f62d66dacdcdb5633c184cea26350c2ae [file] [log] [blame]
/*
* 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());
}
}