Changeset 252315 in webkit


Ignore:
Timestamp:
Nov 10, 2019 4:16:47 PM (4 years ago)
Author:
Wenson Hsieh
Message:

[Clipboard API] Add some infrastructure to resolve ClipboardItems into pasteboard data for writing
https://bugs.webkit.org/show_bug.cgi?id=203707

Reviewed by Ryosuke Niwa.

Implements a new method that will be used in a future patch to aggregate data vended by ClipboardItems when
writing items to the platform pasteboard. See below for more details; no new tests, since there is no change in
behavior yet.

  • Modules/async-clipboard/ClipboardItem.cpp:

(WebCore::ClipboardItem::collectDataForWriting):

Add a new virtual collectDataForWriting method, which is used when writing ClipboardItem data to the
pasteboard. This allows ClipboardItems to asynchronously convert data to a PasteboardCustomData after resolving
promises to strings or blobs; or alternately, cancel all data loading if a promise is rejected.

In order to convert items into a list of PasteboardCustomData, we do the following:

set up a PasteboardCustomData corresponding to each clipboard item
for each clipboard item:

for each type in the clipboard item:

try to resolve the promise
if the promise resolved to a string:

write the string to custom data under the type

if the promise resolved to a blob:

load the blob data as either text or an ArrayBuffer (depending on the type)
write either the loaded string or buffer to custom data under the type

if the promise rejected or resolved to an unsupported value, bail from these steps

  • Modules/async-clipboard/ClipboardItem.h:
  • Modules/async-clipboard/ClipboardItemBindingsDataSource.cpp:

(WebCore::documentFromClipboard):
(WebCore::readTypeForMIMEType):
(WebCore::ClipboardItemBindingsDataSource::collectDataForWriting):
(WebCore::ClipboardItemBindingsDataSource::invokeCompletionHandler):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::ClipboardItemTypeLoader):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::~ClipboardItemTypeLoader):

Add a helper class to hold state associated with loading each clipboard type. This includes the final data
itself (a variant that holds either a String or Blob), as well as a FileReaderLoader which may be present in the
case where the clipboard item type resolves to a blob.

(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFinishLoading):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFail):

Each ClipboardItemType is also the client for its FileReaderLoader, if present; when the FileReaderLoader
finishes loading or fails, we then extract data from the loader and invoke the completion handler.

(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::invokeCompletionHandler):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToBlob):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFailToResolve):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToString):

One of these three methods is called when the promise corresponding to a clipboard type is either resolved or
rejected. If rejected or resolved to an incompatible type, we call the completion handler immediately with no
data; if we resolve to a string, we simply store the string in m_data and invoke the completion handler;
otherwise, if we resolve to a blob, we create a new loader to fetch either the string or data buffer for the
blob, and wait until either didFinishLoading or didFail is called.

  • Modules/async-clipboard/ClipboardItemBindingsDataSource.h:

(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::create):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::type):
(WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::data):

  • Modules/async-clipboard/ClipboardItemDataSource.h:
  • Modules/async-clipboard/ClipboardItemPasteboardDataSource.cpp:

(WebCore::ClipboardItemPasteboardDataSource::collectDataForWriting):

For the time being, leave this unimplemented; a future patch will add support for writing ClipboardItems that
came from the platform pasteboard, as opposed to those created by the page.

  • Modules/async-clipboard/ClipboardItemPasteboardDataSource.h:
Location:
trunk/Source/WebCore
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r252313 r252315  
     12019-11-10  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [Clipboard API] Add some infrastructure to resolve ClipboardItems into pasteboard data for writing
     4        https://bugs.webkit.org/show_bug.cgi?id=203707
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Implements a new method that will be used in a future patch to aggregate data vended by ClipboardItems when
     9        writing items to the platform pasteboard. See below for more details; no new tests, since there is no change in
     10        behavior yet.
     11
     12        * Modules/async-clipboard/ClipboardItem.cpp:
     13        (WebCore::ClipboardItem::collectDataForWriting):
     14
     15        Add a new virtual `collectDataForWriting` method, which is used when writing ClipboardItem data to the
     16        pasteboard. This allows ClipboardItems to asynchronously convert data to a PasteboardCustomData after resolving
     17        promises to strings or blobs; or alternately, cancel all data loading if a promise is rejected.
     18
     19        In order to convert items into a list of PasteboardCustomData, we do the following:
     20
     21        set up a PasteboardCustomData corresponding to each clipboard item
     22        for each clipboard item:
     23            for each type in the clipboard item:
     24                try to resolve the promise
     25                if the promise resolved to a string:
     26                    write the string to custom data under the type
     27                if the promise resolved to a blob:
     28                    load the blob data as either text or an ArrayBuffer (depending on the type)
     29                    write either the loaded string or buffer to custom data under the type
     30                if the promise rejected or resolved to an unsupported value, bail from these steps
     31
     32        * Modules/async-clipboard/ClipboardItem.h:
     33        * Modules/async-clipboard/ClipboardItemBindingsDataSource.cpp:
     34        (WebCore::documentFromClipboard):
     35        (WebCore::readTypeForMIMEType):
     36        (WebCore::ClipboardItemBindingsDataSource::collectDataForWriting):
     37        (WebCore::ClipboardItemBindingsDataSource::invokeCompletionHandler):
     38        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::ClipboardItemTypeLoader):
     39        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::~ClipboardItemTypeLoader):
     40
     41        Add a helper class to hold state associated with loading each clipboard type. This includes the final data
     42        itself (a variant that holds either a String or Blob), as well as a FileReaderLoader which may be present in the
     43        case where the clipboard item type resolves to a blob.
     44
     45        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFinishLoading):
     46        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFail):
     47
     48        Each ClipboardItemType is also the client for its FileReaderLoader, if present; when the FileReaderLoader
     49        finishes loading or fails, we then extract data from the loader and invoke the completion handler.
     50
     51        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::invokeCompletionHandler):
     52        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToBlob):
     53        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFailToResolve):
     54        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToString):
     55
     56        One of these three methods is called when the promise corresponding to a clipboard type is either resolved or
     57        rejected. If rejected or resolved to an incompatible type, we call the completion handler immediately with no
     58        data; if we resolve to a string, we simply store the string in `m_data` and invoke the completion handler;
     59        otherwise, if we resolve to a blob, we create a new loader to fetch either the string or data buffer for the
     60        blob, and wait until either didFinishLoading or didFail is called.
     61
     62        * Modules/async-clipboard/ClipboardItemBindingsDataSource.h:
     63        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::create):
     64        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::type):
     65        (WebCore::ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::data):
     66        * Modules/async-clipboard/ClipboardItemDataSource.h:
     67        * Modules/async-clipboard/ClipboardItemPasteboardDataSource.cpp:
     68        (WebCore::ClipboardItemPasteboardDataSource::collectDataForWriting):
     69
     70        For the time being, leave this unimplemented; a future patch will add support for writing ClipboardItems that
     71        came from the platform pasteboard, as opposed to those created by the page.
     72
     73        * Modules/async-clipboard/ClipboardItemPasteboardDataSource.h:
     74
    1752019-11-10  Antti Koivisto  <antti@apple.com>
    276
  • trunk/Source/WebCore/Modules/async-clipboard/ClipboardItem.cpp

    r251326 r252315  
    9393}
    9494
     95void ClipboardItem::collectDataForWriting(Clipboard& destination, CompletionHandler<void(Optional<PasteboardCustomData>)>&& completion)
     96{
     97    m_dataSource->collectDataForWriting(destination, WTFMove(completion));
     98}
     99
    95100Navigator* ClipboardItem::navigator()
    96101{
  • trunk/Source/WebCore/Modules/async-clipboard/ClipboardItem.h

    r251279 r252315  
    4141class DOMPromise;
    4242class Navigator;
     43class PasteboardCustomData;
    4344struct PasteboardItemInfo;
    4445
     
    6061    void getType(const String&, Ref<DeferredPromise>&&);
    6162
     63    void collectDataForWriting(Clipboard& destination, CompletionHandler<void(Optional<PasteboardCustomData>)>&&);
     64
    6265    PresentationStyle presentationStyle() const { return m_presentationStyle; };
    6366    Navigator* navigator();
  • trunk/Source/WebCore/Modules/async-clipboard/ClipboardItemBindingsDataSource.cpp

    r251425 r252315  
    2828
    2929#include "Blob.h"
     30#include "Clipboard.h"
    3031#include "ClipboardItem.h"
     32#include "Document.h"
     33#include "FileReaderLoader.h"
     34#include "Frame.h"
    3135#include "JSBlob.h"
    3236#include "JSDOMPromise.h"
     
    3741namespace WebCore {
    3842
     43static RefPtr<Document> documentFromClipboard(const Clipboard* clipboard)
     44{
     45    if (!clipboard)
     46        return nullptr;
     47
     48    auto* frame = clipboard->frame();
     49    if (!frame)
     50        return nullptr;
     51
     52    return frame->document();
     53}
     54
     55static FileReaderLoader::ReadType readTypeForMIMEType(const String& type)
     56{
     57    if (type == "text/uri-list"_s || type == "text/plain"_s || type == "text/html"_s)
     58        return FileReaderLoader::ReadAsText;
     59    return FileReaderLoader::ReadAsArrayBuffer;
     60}
     61
    3962ClipboardItemBindingsDataSource::ClipboardItemBindingsDataSource(ClipboardItem& item, Vector<KeyValuePair<String, RefPtr<DOMPromise>>>&& itemPromises)
    4063    : ClipboardItemDataSource(item)
     
    95118}
    96119
     120void ClipboardItemBindingsDataSource::collectDataForWriting(Clipboard& destination, CompletionHandler<void(Optional<PasteboardCustomData>)>&& completion)
     121{
     122    m_itemTypeLoaders.clear();
     123    ASSERT(!m_completionHandler);
     124    m_completionHandler = WTFMove(completion);
     125    m_writingDestination = makeWeakPtr(destination);
     126    m_numberOfPendingClipboardTypes = m_itemPromises.size();
     127    m_itemTypeLoaders = m_itemPromises.map([&] (auto& typeAndItem) {
     128        auto type = typeAndItem.key;
     129        auto itemTypeLoader = ClipboardItemTypeLoader::create(type, [this, protectedItem = makeRef(m_item)] {
     130            ASSERT(m_numberOfPendingClipboardTypes);
     131            if (!--m_numberOfPendingClipboardTypes)
     132                invokeCompletionHandler();
     133        });
     134
     135        auto promise = typeAndItem.value;
     136        promise->whenSettled([this, protectedItem = makeRefPtr(m_item), destination = m_writingDestination, promise, type, weakItemTypeLoader = makeWeakPtr(itemTypeLoader.ptr())] () mutable {
     137            if (!weakItemTypeLoader)
     138                return;
     139
     140            auto itemTypeLoader = makeRef(*weakItemTypeLoader);
     141            ASSERT_UNUSED(this, notFound != m_itemTypeLoaders.findMatching([&] (auto& loader) { return loader.ptr() == itemTypeLoader.ptr(); }));
     142
     143            auto result = promise->result();
     144            if (!result) {
     145                itemTypeLoader->didFailToResolve();
     146                return;
     147            }
     148
     149            auto clipboard = makeRefPtr(destination.get());
     150            if (!clipboard) {
     151                itemTypeLoader->didFailToResolve();
     152                return;
     153            }
     154
     155            if (!clipboard->scriptExecutionContext()) {
     156                itemTypeLoader->didFailToResolve();
     157                return;
     158            }
     159
     160            String text;
     161            result.getString(promise->globalObject(), text);
     162            if (!text.isNull()) {
     163                itemTypeLoader->didResolveToString(text);
     164                return;
     165            }
     166
     167            if (!result.isObject()) {
     168                itemTypeLoader->didFailToResolve();
     169                return;
     170            }
     171
     172            if (auto blob = makeRefPtr(JSBlob::toWrapped(result.getObject()->vm(), result.getObject())))
     173                itemTypeLoader->didResolveToBlob(*clipboard->scriptExecutionContext(), blob.releaseNonNull());
     174            else
     175                itemTypeLoader->didFailToResolve();
     176        });
     177
     178        return itemTypeLoader;
     179    });
     180
     181    if (!m_numberOfPendingClipboardTypes)
     182        invokeCompletionHandler();
     183}
     184
     185void ClipboardItemBindingsDataSource::invokeCompletionHandler()
     186{
     187    if (!m_completionHandler) {
     188        ASSERT_NOT_REACHED();
     189        return;
     190    }
     191
     192    auto completionHandler = std::exchange(m_completionHandler, { });
     193    auto itemTypeLoaders = std::exchange(m_itemTypeLoaders, { });
     194    auto clipboard = makeRefPtr(m_writingDestination.get());
     195    m_writingDestination = nullptr;
     196
     197    auto document = documentFromClipboard(clipboard.get());
     198    if (!document) {
     199        completionHandler(WTF::nullopt);
     200        return;
     201    }
     202
     203    PasteboardCustomData customData;
     204    for (auto& itemTypeLoader : itemTypeLoaders) {
     205        auto type = itemTypeLoader->type();
     206        auto& data = itemTypeLoader->data();
     207        if (WTF::holds_alternative<String>(data) && !!WTF::get<String>(data))
     208            customData.writeString(type, WTF::get<String>(data));
     209        else if (WTF::holds_alternative<Ref<SharedBuffer>>(data))
     210            customData.writeData(type, WTF::get<Ref<SharedBuffer>>(data).copyRef());
     211        else {
     212            completionHandler(WTF::nullopt);
     213            return;
     214        }
     215    }
     216
     217    customData.setOrigin(document->originIdentifierForPasteboard());
     218    completionHandler(WTFMove(customData));
     219}
     220
     221ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::ClipboardItemTypeLoader(const String& type, CompletionHandler<void()>&& completionHandler)
     222    : m_type(type)
     223    , m_completionHandler(WTFMove(completionHandler))
     224{
     225}
     226
     227ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::~ClipboardItemTypeLoader()
     228{
     229    if (m_blobLoader)
     230        m_blobLoader->cancel();
     231
     232    invokeCompletionHandler();
     233}
     234
     235void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFinishLoading()
     236{
     237    ASSERT(m_blobLoader);
     238    auto stringResult = readTypeForMIMEType(m_type) == FileReaderLoader::ReadAsText ? m_blobLoader->stringResult() : nullString();
     239    if (!stringResult.isNull())
     240        m_data = { stringResult };
     241    else if (auto arrayBuffer = m_blobLoader->arrayBufferResult())
     242        m_data = { SharedBuffer::create(static_cast<const char*>(arrayBuffer->data()), arrayBuffer->byteLength()) };
     243    m_blobLoader = nullptr;
     244    invokeCompletionHandler();
     245}
     246
     247void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFail(int)
     248{
     249    ASSERT(m_blobLoader);
     250    m_blobLoader = nullptr;
     251    invokeCompletionHandler();
     252}
     253
     254void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::invokeCompletionHandler()
     255{
     256    if (auto completion = WTFMove(m_completionHandler))
     257        completion();
     258}
     259
     260void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToBlob(ScriptExecutionContext& context, Ref<Blob>&& blob)
     261{
     262    ASSERT(!m_blobLoader);
     263    m_blobLoader = makeUnique<FileReaderLoader>(readTypeForMIMEType(m_type), this);
     264    m_blobLoader->start(&context, WTFMove(blob));
     265}
     266
     267void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didFailToResolve()
     268{
     269    ASSERT(!m_blobLoader);
     270    invokeCompletionHandler();
     271}
     272
     273void ClipboardItemBindingsDataSource::ClipboardItemTypeLoader::didResolveToString(const String& text)
     274{
     275    ASSERT(!m_blobLoader);
     276    m_data = { text };
     277    invokeCompletionHandler();
     278}
     279
    97280} // namespace WebCore
  • trunk/Source/WebCore/Modules/async-clipboard/ClipboardItemBindingsDataSource.h

    r251134 r252315  
    2727
    2828#include "ClipboardItemDataSource.h"
     29#include "FileReaderLoaderClient.h"
     30#include <wtf/Optional.h>
     31#include <wtf/Variant.h>
     32#include <wtf/WeakPtr.h>
    2933
    3034namespace WebCore {
    3135
    3236class DOMPromise;
     37class FileReaderLoader;
     38class PasteboardCustomData;
    3339
    3440class ClipboardItemBindingsDataSource : public ClipboardItemDataSource {
     
    4147    Vector<String> types() const final;
    4248    void getType(const String&, Ref<DeferredPromise>&&) final;
     49    void collectDataForWriting(Clipboard& destination, CompletionHandler<void(Optional<PasteboardCustomData>)>&&) final;
     50
     51    void invokeCompletionHandler();
     52
     53    using BufferOrString = Variant<String, Ref<SharedBuffer>>;
     54    class ClipboardItemTypeLoader : public FileReaderLoaderClient, public RefCounted<ClipboardItemTypeLoader>, public CanMakeWeakPtr<ClipboardItemTypeLoader> {
     55    public:
     56        static Ref<ClipboardItemTypeLoader> create(const String& type, CompletionHandler<void()>&& completionHandler)
     57        {
     58            return adoptRef(*new ClipboardItemTypeLoader(type, WTFMove(completionHandler)));
     59        }
     60
     61        ~ClipboardItemTypeLoader();
     62
     63        void didResolveToString(const String&);
     64        void didFailToResolve();
     65        void didResolveToBlob(ScriptExecutionContext&, Ref<Blob>&&);
     66
     67        const String& type() { return m_type; }
     68        const BufferOrString& data() { return m_data; }
     69
     70    private:
     71        ClipboardItemTypeLoader(const String& type, CompletionHandler<void()>&&);
     72
     73        void invokeCompletionHandler();
     74
     75        // FileReaderLoaderClient methods.
     76        void didStartLoading() final { }
     77        void didReceiveData() final { }
     78        void didFinishLoading() final;
     79        void didFail(int) final;
     80
     81        String m_type;
     82        BufferOrString m_data;
     83        std::unique_ptr<FileReaderLoader> m_blobLoader;
     84        CompletionHandler<void()> m_completionHandler;
     85    };
     86
     87    unsigned m_numberOfPendingClipboardTypes { 0 };
     88    CompletionHandler<void(Optional<PasteboardCustomData>)> m_completionHandler;
     89    Vector<Ref<ClipboardItemTypeLoader>> m_itemTypeLoaders;
     90    WeakPtr<Clipboard> m_writingDestination;
    4391
    4492    Vector<KeyValuePair<String, RefPtr<DOMPromise>>> m_itemPromises;
  • trunk/Source/WebCore/Modules/async-clipboard/ClipboardItemDataSource.h

    r251134 r252315  
    3333namespace WebCore {
    3434
     35class Clipboard;
     36class ClipboardItem;
    3537class DeferredPromise;
    36 class ClipboardItem;
    3738
    3839class ClipboardItemDataSource {
     
    4748    virtual Vector<String> types() const = 0;
    4849    virtual void getType(const String&, Ref<DeferredPromise>&&) = 0;
     50    virtual void collectDataForWriting(Clipboard& destination, CompletionHandler<void(Optional<PasteboardCustomData>)>&&) = 0;
    4951
    5052protected:
  • trunk/Source/WebCore/Modules/async-clipboard/ClipboardItemPasteboardDataSource.cpp

    r251326 r252315  
    5555}
    5656
     57void ClipboardItemPasteboardDataSource::collectDataForWriting(Clipboard&, CompletionHandler<void(Optional<PasteboardCustomData>)>&& completion)
     58{
     59    // FIXME: Not implemented. This is needed to support writing platform-backed ClipboardItems
     60    // back to the pasteboard using Clipboard.write().
     61    completion(WTF::nullopt);
     62}
     63
    5764} // namespace WebCore
  • trunk/Source/WebCore/Modules/async-clipboard/ClipboardItemPasteboardDataSource.h

    r251279 r252315  
    4141    Vector<String> types() const final;
    4242    void getType(const String&, Ref<DeferredPromise>&&) final;
     43    void collectDataForWriting(Clipboard& destination, CompletionHandler<void(Optional<PasteboardCustomData>)>&&) final;
    4344
    4445    Vector<String> m_types;
Note: See TracChangeset for help on using the changeset viewer.