fdfda272de78245c3bd398a2ef9b7f25f6fedf85
[WebKit-https.git] / Source / WebCore / dom / DataTransferItemList.cpp
1 /*
2  * Copyright (C) 2017 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 "DataTransferItemList.h"
28
29 #include "DataTransferItem.h"
30 #include "FileList.h"
31 #include "Pasteboard.h"
32
33 namespace WebCore {
34
35 // FIXME: DataTransfer should filter types itself.
36 static bool isSupportedType(const String& type)
37 {
38     return type == "text/plain";
39 }
40
41 DataTransferItemList::DataTransferItemList(DataTransfer& dataTransfer)
42     : m_weakPtrFactory(this)
43     , m_dataTransfer(dataTransfer)
44 {
45 }
46
47 DataTransferItemList::~DataTransferItemList()
48 {
49 }
50
51 unsigned DataTransferItemList::length() const
52 {
53     return ensureItems().size();
54 }
55
56 RefPtr<DataTransferItem> DataTransferItemList::item(unsigned index)
57 {
58     auto& items = ensureItems();
59     if (items.size() <= index)
60         return nullptr;
61     return items[index].copyRef();
62 }
63
64 ExceptionOr<RefPtr<DataTransferItem>> DataTransferItemList::add(const String& data, const String& type)
65 {
66     if (!m_dataTransfer.canWriteData())
67         return nullptr;
68
69     for (auto& item : ensureItems()) {
70         if (!item->isFile() && equalIgnoringASCIICase(item->type(), type))
71             return Exception { NotSupportedError };
72     }
73
74     String lowercasedType = type.convertToASCIILowercase();
75
76     // FIXME: Allow writing & reading of any types to clipboard / drag data store.
77     if (!isSupportedType(lowercasedType))
78         return nullptr;
79
80     m_dataTransfer.pasteboard().writeString(lowercasedType, data);
81     ASSERT(m_items);
82     m_items->append(DataTransferItem::create(m_weakPtrFactory.createWeakPtr(), type));
83     return RefPtr<DataTransferItem> { m_items->last().copyRef() };
84 }
85
86 RefPtr<DataTransferItem> DataTransferItemList::add(Ref<File>&&)
87 {
88     return nullptr;
89 }
90
91 ExceptionOr<void> DataTransferItemList::remove(unsigned index)
92 {
93     if (!m_dataTransfer.canWriteData())
94         return Exception { InvalidStateError };
95
96     auto& items = ensureItems();
97     if (items.size() <= index)
98         return Exception { IndexSizeError }; // Matches Gecko. See https://github.com/whatwg/html/issues/2925
99
100     // FIXME: Handle the removal of files once we added the support for writing a File.
101     ASSERT(!items[index]->isFile());
102
103     auto& removedItem = items[index].get();
104     m_dataTransfer.pasteboard().clear(removedItem.type());
105     removedItem.clearListAndPutIntoDisabledMode();
106     items.remove(index);
107
108     return { };
109 }
110
111 void DataTransferItemList::clear()
112 {
113     m_dataTransfer.pasteboard().clear();
114     if (m_items) {
115         for (auto& item : *m_items)
116             item->clearListAndPutIntoDisabledMode();
117         m_items->clear();
118     }
119 }
120
121 Vector<Ref<DataTransferItem>>& DataTransferItemList::ensureItems() const
122 {
123     if (m_items)
124         return *m_items;
125
126     Vector<Ref<DataTransferItem>> items;
127     for (String& type : m_dataTransfer.types()) {
128         String lowercasedType = type.convertToASCIILowercase();
129         if (isSupportedType(lowercasedType))
130             items.append(DataTransferItem::create(m_weakPtrFactory.createWeakPtr(), lowercasedType));
131     }
132
133     FileList& files = m_dataTransfer.files();
134     for (unsigned i = 0, length = files.length(); i < length; ++i) {
135         File& file = *files.item(i);
136         String type = File::contentTypeForFile(file.path()).convertToASCIILowercase();
137         if (isSupportedType(type) || file.isDirectory())
138             items.append(DataTransferItem::create(m_weakPtrFactory.createWeakPtr(), type, file));
139     }
140
141     m_items = WTFMove(items);
142
143     return *m_items;
144 }
145
146 static void removeStringItemOfLowercasedType(Vector<Ref<DataTransferItem>>& items, const String& lowercasedType)
147 {
148     auto index = items.findMatching([lowercasedType](auto& item) {
149         return !item->isFile() && item->type() == lowercasedType;
150     });
151     if (index == notFound)
152         return;
153     items[index]->clearListAndPutIntoDisabledMode();
154     items.remove(index);
155 }
156
157 void DataTransferItemList::didClearStringData(const String& type)
158 {
159     if (!m_items)
160         return;
161
162     auto& items = *m_items;
163     if (!type.isNull())
164         return removeStringItemOfLowercasedType(items, type.convertToASCIILowercase());
165
166     for (auto& item : items) {
167         if (!item->isFile())
168             item->clearListAndPutIntoDisabledMode();
169     }
170     items.removeAllMatching([](auto& item) {
171         return !item->isFile();
172     });
173 }
174
175 // https://html.spec.whatwg.org/multipage/dnd.html#dom-datatransfer-setdata
176 void DataTransferItemList::didSetStringData(const String& type)
177 {
178     if (!m_items)
179         return;
180
181     String lowercasedType = type.convertToASCIILowercase();
182     removeStringItemOfLowercasedType(*m_items, type.convertToASCIILowercase());
183
184     m_items->append(DataTransferItem::create(m_weakPtrFactory.createWeakPtr(), lowercasedType));
185 }
186
187 }
188