~mmach/netext73/webkit2gtk

« back to all changes in this revision

Viewing changes to Source/WebCore/Modules/async-clipboard/ClipboardItemBindingsDataSource.cpp

  • Committer: mmach
  • Date: 2023-06-16 17:21:37 UTC
  • Revision ID: netbit73@gmail.com-20230616172137-2rqx6yr96ga9g3kp
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2019 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. AND ITS CONTRIBUTORS ``AS IS''
 
14
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
15
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
16
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 
17
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
18
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
19
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
20
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
21
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
22
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 
23
 * THE POSSIBILITY OF SUCH DAMAGE.
 
24
 */
 
25
 
 
26
#include "config.h"
 
27
#include "ClipboardItemBindingsDataSource.h"
 
28
 
 
29
#include "BitmapImage.h"
 
30
#include "Blob.h"
 
31
#include "Clipboard.h"
 
32
#include "ClipboardItem.h"
 
33
#include "Document.h"
 
34
#include "FileReaderLoader.h"
 
35
#include "Frame.h"
 
36
#include "GraphicsContext.h"
 
37
#include "ImageBuffer.h"
 
38
#include "JSBlob.h"
 
39
#include "JSDOMPromise.h"
 
40
#include "JSDOMPromiseDeferred.h"
 
41
#include "PasteboardCustomData.h"
 
42
#include "SharedBuffer.h"
 
43
#include "markup.h"
 
44
#include <wtf/Function.h>
 
45
 
 
46
namespace WebCore {
 
47
 
 
48
static RefPtr<Document> documentFromClipboard(const Clipboard* clipboard)
 
49
{
 
50
    if (!clipboard)
 
51
        return nullptr;
 
52
 
 
53
    auto* frame = clipboard->frame();
 
54
    if (!frame)
 
55
        return nullptr;
 
56
 
 
57
    return frame->document();
 
58
}
 
59
 
 
60
static FileReaderLoader::ReadType readTypeForMIMEType(const String& type)
 
61
{
 
62
    if (type == "text/uri-list"_s || type == "text/plain"_s || type == "text/html"_s)
 
63
        return FileReaderLoader::ReadAsText;
 
64
    return FileReaderLoader::ReadAsArrayBuffer;
 
65
}
 
66
 
 
67
ClipboardItemBindingsDataSource::ClipboardItemBindingsDataSource(ClipboardItem& item, Vector<KeyValuePair<String, RefPtr<DOMPromise>>>&& itemPromises)
 
68
    : ClipboardItemDataSource(item)
 
69
    , m_itemPromises(WTFMove(itemPromises))
 
70
{
 
71
}
 
72
 
 
73
ClipboardItemBindingsDataSource::~ClipboardItemBindingsDataSource() = default;
 
74
 
 
75
Vector<String> ClipboardItemBindingsDataSource::types() const
 
76
{
 
77
    return m_itemPromises.map([&] (auto& typeAndItem) {
 
78
        return typeAndItem.key;
 
79
    });
 
80
}
 
81
 
 
82
void ClipboardItemBindingsDataSource::getType(const String& type, Ref<DeferredPromise>&& promise)
 
83
{
 
84
    auto matchIndex = m_itemPromises.findMatching([&] (auto& item) {
 
85
        return type == item.key;
 
86
    });
 
87
 
 
88
    if (matchIndex == notFound) {
 
89
        promise->reject(NotFoundError);
 
90
        return;
 
91
    }
 
92
 
 
93
    auto itemPromise = m_itemPromises[matchIndex].value;
 
94
    itemPromise->whenSettled([itemPromise, promise = makeRefPtr(promise.get()), type] () mutable {
 
95
        if (itemPromise->status() != DOMPromise::Status::Fulfilled) {
 
96
            promise->reject(AbortError);
 
97
            return;
 
98
        }
 
99
 
 
100
        auto result = itemPromise->result();
 
101
        if (!result) {
 
102
            promise->reject(TypeError);
 
103
            return;
 
104
        }
 
105
 
 
106
        String string;
 
107
        result.getString(itemPromise->globalObject(), string);
 
108
        if (!string.isNull()) {
 
109
            promise->resolve<IDLInterface<Blob>>(ClipboardItem::blobFromString(string, type));
 
110
            return;
 
111
        }
 
112
 
 
113
        if (!result.isObject()) {
 
114
            promise->reject(TypeError);
 
115
            return;
 
116
        }
 
117
 
 
118
        if (auto blob = JSBlob::toWrapped(result.getObject()->vm(), result.getObject()))
 
119
            promise->resolve<IDLInterface<Blob>>(*blob);
 
120
        else
 
121
            promise->reject(TypeError);
 
122
    });
 
123
}
 
124
 
 
125
void ClipboardItemBindingsDataSource::collectDataForWriting(Clipboard& destination, CompletionHandler<void(Optional<PasteboardCustomData>)>&& completion)
 
126
{
 
127
    m_itemTypeLoaders.clear();
 
128
    ASSERT(!m_completionHandler);
 
129
    m_completionHandler = WTFMove(completion);
 
130
    m_writingDestination = makeWeakPtr(destination);
 
131
    m_numberOfPendingClipboardTypes = m_itemPromises.size();
 
132
    m_itemTypeLoaders = m_itemPromises.map([&] (auto& typeAndItem) {
 
133
        auto type = typeAndItem.key;
 
134
        auto itemTypeLoader = ClipboardItemTypeLoader::create(type, [this, protectedItem = makeRef(m_item)] {
 
135
            ASSERT(m_numberOfPendingClipboardTypes);
 
136
            if (!--m_numberOfPendingClipboardTypes)
 
137
                invokeCompletionHandler();
 
138
        });
 
139
 
 
140
        auto promise = typeAndItem.value;
 
141
        promise->whenSettled([this, protectedItem = makeRefPtr(m_item), destination = m_writingDestination, promise, type, weakItemTypeLoader = makeWeakPtr(itemTypeLoader.ptr())] () mutable {
 
142
            if (!weakItemTypeLoader)
 
143
                return;
 
144
 
 
145
            auto itemTypeLoader = makeRef(*weakItemTypeLoader);
 
146
            ASSERT_UNUSED(this, notFound != m_itemTypeLoaders.findMatching([&] (auto& loader) { return loader.ptr() == itemTypeLoader.ptr(); }));
 
147
 
 
148
            auto result = promise->result();
 
149
            if (!result) {
 
150
                itemTypeLoader->didFailToResolve();
 
151
                return;
 
152
            }
 
153
 
 
154
            auto clipboard = makeRefPtr(destination.get());
 
155
            if (!clipboard) {
 
156
                itemTypeLoader->didFailToResolve();
 
157
                return;
 
158
            }
 
159
 
 
160
            if (!clipboard->scriptExecutionContext()) {
 
161
                itemTypeLoader->didFailToResolve();
 
162
                return;
 
163
            }
 
164
 
 
165
            String text;
 
166
            result.getString(promise->globalObject(), text);
 
167
            if (!text.isNull()) {
 
168
                itemTypeLoader->didResolveToString(text);
 
169
                return;
 
170
            }
 
171
 
 
172
            if (!result.isObject()) {
 
173
                itemTypeLoader->didFailToResolve();
 
174
                return;
 
175
            }
 
176
 
 
177
            if (auto blob = makeRefPtr(JSBlob::toWrapped(result.getObject()->vm(), result.getObject())))
 
178
                itemTypeLoader->didResolveToBlob(*clipboard->scriptExecutionContext(), blob.releaseNonNull());
 
179
            else
 
180
                itemTypeLoader->didFailToResolve();
 
181
        });
 
182
 
 
183
        return itemTypeLoader;
 
184
    });
 
185
 
 
186
    if (!m_numberOfPendingClipboardTypes)
 
187
        invokeCompletionHandler();
 
188
}
 
189
 
 
190
void ClipboardItemBindingsDataSource::invokeCompletionHandler()
 
191
{
 
192
    if (!m_completionHandler) {
 
193
        ASSERT_NOT_REACHED();
 
194
        return;
 
195
    }
 
196
 
 
197
    auto completionHandler = std::exchange(m_completionHandler, { });
 
198
    auto itemTypeLoaders = std::exchange(m_itemTypeLoaders, { });
 
199
    auto clipboard = makeRefPtr(m_writingDestination.get());
 
200
    m_writingDestination = nullptr;
 
201
 
 
202
    auto document = documentFromClipboard(clipboard.get());
 
203
    if (!document) {
 
204
        completionHandler(WTF::nullopt);
 
205
        return;
 
206
    }
 
207
 
 
208
    PasteboardCustomData customData;
 
209
    for (auto& itemTypeLoader : itemTypeLoaders) {
 
210
        auto type = itemTypeLoader->type();
 
211
        auto& data = itemTypeLoader->data();
 
212
        if (WTF::holds_alternative<String>(data) && !!WTF::get<String>(data))
 
213
            customData.writeString(type, WTF::get<String>(data));
 
214
        else if (WTF::holds_alternative<Ref<SharedBuffer>>(data))
 
215
            customData.writeData(type, WTF::get<Ref<SharedBuffer>>(data).copyRef());
 
216
        else {
 
217
            completionHandler(WTF::nullopt);
 
218
            return;
 
219
        }
 
220
    }
 
221
 
 
222
    customData.setOrigin(document->originIdentifierForPasteboard());
 
223
    completionHandler(WTFMove(customData));
 
224
}
 
225
 
 
226
ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::ClipboardItemTypeLoader(const String& type, CompletionHandler<void()>&& completionHandler)
 
227
    : m_type(type)
 
228
    , m_completionHandler(WTFMove(completionHandler))
 
229
{
 
230
}
 
231
 
 
232
ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::~ClipboardItemTypeLoader()
 
233
{
 
234
    if (m_blobLoader)
 
235
        m_blobLoader->cancel();
 
236
 
 
237
    invokeCompletionHandler();
 
238
}
 
239
 
 
240
void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFinishLoading()
 
241
{
 
242
    ASSERT(m_blobLoader);
 
243
    auto stringResult = readTypeForMIMEType(m_type) == FileReaderLoader::ReadAsText ? m_blobLoader->stringResult() : nullString();
 
244
    if (!stringResult.isNull())
 
245
        m_data = { stringResult };
 
246
    else if (auto arrayBuffer = m_blobLoader->arrayBufferResult())
 
247
        m_data = { SharedBuffer::create(static_cast<const char*>(arrayBuffer->data()), arrayBuffer->byteLength()) };
 
248
    m_blobLoader = nullptr;
 
249
    invokeCompletionHandler();
 
250
}
 
251
 
 
252
void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFail(int)
 
253
{
 
254
    ASSERT(m_blobLoader);
 
255
    m_blobLoader = nullptr;
 
256
    invokeCompletionHandler();
 
257
}
 
258
 
 
259
void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::sanitizeDataIfNeeded()
 
260
{
 
261
    if (m_type == "text/html"_s) {
 
262
        String markupToSanitize;
 
263
        if (WTF::holds_alternative<Ref<SharedBuffer>>(m_data)) {
 
264
            auto& buffer = WTF::get<Ref<SharedBuffer>>(m_data);
 
265
            markupToSanitize = String::fromUTF8(buffer->data(), buffer->size());
 
266
        } else if (WTF::holds_alternative<String>(m_data))
 
267
            markupToSanitize = WTF::get<String>(m_data);
 
268
 
 
269
        if (markupToSanitize.isEmpty())
 
270
            return;
 
271
 
 
272
        m_data = { sanitizeMarkup(markupToSanitize) };
 
273
    }
 
274
 
 
275
    if (m_type == "image/png"_s) {
 
276
        RefPtr<SharedBuffer> bufferToSanitize;
 
277
        if (WTF::holds_alternative<Ref<SharedBuffer>>(m_data))
 
278
            bufferToSanitize = WTF::get<Ref<SharedBuffer>>(m_data).ptr();
 
279
        else if (WTF::holds_alternative<String>(m_data))
 
280
            bufferToSanitize = utf8Buffer(WTF::get<String>(m_data));
 
281
 
 
282
        if (!bufferToSanitize || bufferToSanitize->isEmpty())
 
283
            return;
 
284
 
 
285
        auto bitmapImage = BitmapImage::create();
 
286
        bitmapImage->setData(WTFMove(bufferToSanitize), true);
 
287
        auto imageBuffer = ImageBuffer::create(bitmapImage->size(), RenderingMode::Unaccelerated);
 
288
        if (!imageBuffer) {
 
289
            m_data = { nullString() };
 
290
            return;
 
291
        }
 
292
 
 
293
        imageBuffer->context().drawImage(bitmapImage.get(), FloatPoint::zero());
 
294
        m_data = { SharedBuffer::create(imageBuffer->toData("image/png"_s)) };
 
295
    }
 
296
}
 
297
 
 
298
void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::invokeCompletionHandler()
 
299
{
 
300
    if (auto completion = WTFMove(m_completionHandler)) {
 
301
        sanitizeDataIfNeeded();
 
302
        completion();
 
303
    }
 
304
}
 
305
 
 
306
void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToBlob(ScriptExecutionContext& context, Ref<Blob>&& blob)
 
307
{
 
308
    ASSERT(!m_blobLoader);
 
309
    m_blobLoader = makeUnique<FileReaderLoader>(readTypeForMIMEType(m_type), this);
 
310
    m_blobLoader->start(&context, WTFMove(blob));
 
311
}
 
312
 
 
313
void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFailToResolve()
 
314
{
 
315
    ASSERT(!m_blobLoader);
 
316
    invokeCompletionHandler();
 
317
}
 
318
 
 
319
void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToString(const String& text)
 
320
{
 
321
    ASSERT(!m_blobLoader);
 
322
    m_data = { text };
 
323
    invokeCompletionHandler();
 
324
}
 
325
 
 
326
} // namespace WebCore