Changeset 235647 in webkit


Ignore:
Timestamp:
Sep 4, 2018 4:47:55 PM (6 years ago)
Author:
Wenson Hsieh
Message:

Populate "text/uri-list" with multiple URLs when the pasteboard contains multiple URLs
https://bugs.webkit.org/show_bug.cgi?id=188890
<rdar://problem/43648605>

Reviewed by Tim Horton.

Source/WebCore:

Adds support for exposing a newline separated list of URLs via DataTransfer's "text/uri-list" type when pasting
or dropping multiple items on the pasteboard that can be represented as URLs. Currently on iOS, only the URL of
the first item (if present) is exposed, and on macOS, only the first out of all the URLs in the pasteboard is
exposed.

To fix this, we introduce Pasteboard::readAllStrings, which reads a list of pasteboard strings collected from
all available items on the platform pasteboard. Currently, this is only used to provide a list of URL strings
when fetching data for the "text/uri-list" type when calling DataTransfer.getData() and
DataTransferItem.getAsString().

Tests: DragAndDropTests.ExposeMultipleURLsInDataTransfer

UIPasteboardTests.DataTransferURIListContainsMultipleURLs
PasteMixedContent.PasteOneOrMoreURLs

  • dom/DataTransfer.cpp:

(WebCore::readURLsFromPasteboardAsString):

Add a helper method that reads all URL strings from the pasteboard (for the MIME type "text/uri-list", which
corresponds to NSURLPboardType and "public.url" on macOS and iOS, respectively) and returns a single string
containing all non-empty URLs joined by newline characters. Also takes a filtering block which may be used to
reject URLs from being included in "text/uri-list" output.

(WebCore::DataTransfer::getDataForItem const):
(WebCore::DataTransfer::readStringFromPasteboard const):

Insteading of reading a single string from the pasteboard for "text/uri-list", call the above helper function to
read all URL strings in the pasteboard. If there are files present in the pasteboard, we also filter out URLs
whose schemes are not in the set of schemes that are safe to expose to the page (i.e. http(s), blob, and data).

  • platform/Pasteboard.cpp:

(WebCore::Pasteboard::readAllStrings):

Add a default non-Cocoa implementation of readAllStrings() that returns a vector, which may contain the result
of readString().

  • platform/Pasteboard.h:
  • platform/PasteboardStrategy.h:
  • platform/PlatformPasteboard.h:

Add plumbing to grab a list of strings from the pasteboard for a given type.

  • platform/cocoa/PasteboardCocoa.mm:

(WebCore::Pasteboard::readAllStrings):
(WebCore::Pasteboard::readString):

Implement these two methods in terms of readPlatformValuesAsStrings. readAllStrings returns the full list of
results, while readString only returns the first result.

  • platform/ios/PasteboardIOS.mm:

(WebCore::Pasteboard::readPlatformValuesAsStrings):
(WebCore::Pasteboard::readPlatformValueAsString): Deleted.

Refactor this Cocoa helper function to return a list of pasteboard string values for the given type, rather than
a single string.

  • platform/ios/PlatformPasteboardIOS.mm:

(WebCore::PlatformPasteboard::allStringsForType const):

Grab a string for each item (represented by an NSItemProvider) in the pasteboard that has data for the given
type identifier.

(WebCore::PlatformPasteboard::readString const):

Return the absolute string of the NSURL, instead of WebCore::URL::string(). This is needed to handle the case
where the NSURL is constructed from absolute and relative parts using a Plist. While -absoluteString gets us the
full URL string, URL::string() only returns the relative portion.

  • platform/mac/PasteboardMac.mm:

(WebCore::Pasteboard::readPlatformValuesAsStrings):
(WebCore::Pasteboard::readPlatformValueAsString): Deleted.

Also refactor this to retrieve a list of pasteboard strings, rather than a single result.

  • platform/mac/PlatformPasteboardMac.mm:

(WebCore::typeIdentifierForPasteboardType):
(WebCore::PlatformPasteboard::allStringsForType const):

Add an implementation for allStringsForType on macOS. Unlike iOS, it's much trickier to get this right since
we need to maintain compatibility with legacy "NS*Pboard" types, and NSPasteboardItem can only provide data
for NSPasteboardTypes (i.e. UTIs), so there's no way to just iterate through each pasteboard item and ask it
for data that matches the given type, if the types are not UTIs. However, in the case where we have multiple
items, the client must have used NSPasteboardWriting-conformant objects and/or NSPasteboardItem itself to write
data to the pasteboard. Since NSPasteboardWriting-conformant objects register modern pasteboard types when
writing to the pasteboard, we can simply iterate over these pasteboard items and ask for property lists using
type identifiers instead of having to worry about legacy pasteboard types. Furthermore, in the case where there
is only a single item in the pasteboard and we do need to handle legacy pasteboard types, using `-[NSPasteboard
stringForType:]` in the same way we do currently should yield the correct result.

As such, in the case where there is a single pasteboard item, we use -[NSPasteboard stringForType:] with the
original legacy type, and in the case where there are multiple items on the pasteboard, we iterate over each of
the pasteboard items and call -[NSPasteboardItem propertyListForType:] with the modern pasteboard type
corresponding to the given legacy pasteboard type.

The different corner cases in this logic are tested by the new API test, PasteMixedContent.PasteOneOrMoreURLs,
which exercises several different ways of writing one or more URLs to the pasteboard on macOS, which each result
in different legacy and modern pasteboard types being written to the pasteboard; our implementation of
PlatformPasteboard::allStringsForType on macOS handles all cases correctly.

Source/WebKit:

Add some plumbing through pasteboard classes to support Pasteboard::readAllStrings. See WebCore ChangeLog for
more detail.

  • UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:

(WebKit::WebPasteboardProxy::getPasteboardStringsForType):

  • UIProcess/WebPasteboardProxy.h:
  • UIProcess/WebPasteboardProxy.messages.in:
  • WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:

(WebKit::WebPlatformStrategies::allStringsForType):

  • WebProcess/WebCoreSupport/WebPlatformStrategies.h:

Source/WebKitLegacy/mac:

Add some plumbing through pasteboard classes to support Pasteboard::readAllStrings. See WebCore ChangeLog for
more detail.

  • WebCoreSupport/WebPlatformStrategies.h:
  • WebCoreSupport/WebPlatformStrategies.mm:

(WebPlatformStrategies::allStringsForType):

Tools:

  • DumpRenderTree/mac/DumpRenderTreePasteboard.mm:

(-[LocalPasteboard pasteboardItems]):

Implement this method to avoid crashing when running layout tests that access the pasteboard.

  • TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm:

Add a test to verify that on macOS and iOS, multiple URLs dropped onto the page are accessible via
"text/uri-list".

  • TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm:

Add a test that exercises 5 different ways to write one or more URLs to the pasteboard on macOS; in all cases,
the URLs written to the pasteboard should be exposed to the page via "text/uri-list". In all of these different
cases, the results of using -[NSPasteboardItem stringForType:], -[NSURL URLFromPasteboard:] and
-[NSPasteboard stringForType:] will yield different results, so the purpose of this API test is to ensure that
our logic for grabbing a list of URLs from the pasteboard on macOS is robust enough to handle all of these
different behaviors.

  • TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:

Add a test to verify that on iOS, using -[UIPasteboard setURLs:] to write to multiple URLs to the pasteboard
and then pasting results in "text/uri-list" exposing a list of all the URLs written to the pasteboard.

  • WebKitTestRunner/mac/WebKitTestRunnerPasteboard.mm:

(-[LocalPasteboard pasteboardItems]):

Implement this method to avoid crashing when running layout tests that access the pasteboard.

Location:
trunk
Files:
26 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r235644 r235647  
     12018-09-04  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Populate "text/uri-list" with multiple URLs when the pasteboard contains multiple URLs
     4        https://bugs.webkit.org/show_bug.cgi?id=188890
     5        <rdar://problem/43648605>
     6
     7        Reviewed by Tim Horton.
     8
     9        Adds support for exposing a newline separated list of URLs via DataTransfer's "text/uri-list" type when pasting
     10        or dropping multiple items on the pasteboard that can be represented as URLs. Currently on iOS, only the URL of
     11        the first item (if present) is exposed, and on macOS, only the first out of all the URLs in the pasteboard is
     12        exposed.
     13
     14        To fix this, we introduce `Pasteboard::readAllStrings`, which reads a list of pasteboard strings collected from
     15        all available items on the platform pasteboard. Currently, this is only used to provide a list of URL strings
     16        when fetching data for the "text/uri-list" type when calling `DataTransfer.getData()` and
     17        `DataTransferItem.getAsString()`.
     18
     19        Tests:  DragAndDropTests.ExposeMultipleURLsInDataTransfer
     20                UIPasteboardTests.DataTransferURIListContainsMultipleURLs
     21                PasteMixedContent.PasteOneOrMoreURLs
     22
     23        * dom/DataTransfer.cpp:
     24        (WebCore::readURLsFromPasteboardAsString):
     25
     26        Add a helper method that reads all URL strings from the pasteboard (for the MIME type "text/uri-list", which
     27        corresponds to NSURLPboardType and "public.url" on macOS and iOS, respectively) and returns a single string
     28        containing all non-empty URLs joined by newline characters. Also takes a filtering block which may be used to
     29        reject URLs from being included in "text/uri-list" output.
     30
     31        (WebCore::DataTransfer::getDataForItem const):
     32        (WebCore::DataTransfer::readStringFromPasteboard const):
     33
     34        Insteading of reading a single string from the pasteboard for "text/uri-list", call the above helper function to
     35        read all URL strings in the pasteboard. If there are files present in the pasteboard, we also filter out URLs
     36        whose schemes are not in the set of schemes that are safe to expose to the page (i.e. http(s), blob, and data).
     37
     38        * platform/Pasteboard.cpp:
     39        (WebCore::Pasteboard::readAllStrings):
     40
     41        Add a default non-Cocoa implementation of readAllStrings() that returns a vector, which may contain the result
     42        of readString().
     43
     44        * platform/Pasteboard.h:
     45        * platform/PasteboardStrategy.h:
     46        * platform/PlatformPasteboard.h:
     47
     48        Add plumbing to grab a list of strings from the pasteboard for a given type.
     49
     50        * platform/cocoa/PasteboardCocoa.mm:
     51        (WebCore::Pasteboard::readAllStrings):
     52        (WebCore::Pasteboard::readString):
     53
     54        Implement these two methods in terms of `readPlatformValuesAsStrings`. `readAllStrings` returns the full list of
     55        results, while `readString` only returns the first result.
     56
     57        * platform/ios/PasteboardIOS.mm:
     58        (WebCore::Pasteboard::readPlatformValuesAsStrings):
     59        (WebCore::Pasteboard::readPlatformValueAsString): Deleted.
     60
     61        Refactor this Cocoa helper function to return a list of pasteboard string values for the given type, rather than
     62        a single string.
     63
     64        * platform/ios/PlatformPasteboardIOS.mm:
     65        (WebCore::PlatformPasteboard::allStringsForType const):
     66
     67        Grab a string for each item (represented by an NSItemProvider) in the pasteboard that has data for the given
     68        type identifier.
     69
     70        (WebCore::PlatformPasteboard::readString const):
     71
     72        Return the absolute string of the NSURL, instead of WebCore::URL::string(). This is needed to handle the case
     73        where the NSURL is constructed from absolute and relative parts using a Plist. While -absoluteString gets us the
     74        full URL string, URL::string() only returns the relative portion.
     75
     76        * platform/mac/PasteboardMac.mm:
     77        (WebCore::Pasteboard::readPlatformValuesAsStrings):
     78        (WebCore::Pasteboard::readPlatformValueAsString): Deleted.
     79
     80        Also refactor this to retrieve a list of pasteboard strings, rather than a single result.
     81
     82        * platform/mac/PlatformPasteboardMac.mm:
     83        (WebCore::typeIdentifierForPasteboardType):
     84        (WebCore::PlatformPasteboard::allStringsForType const):
     85
     86        Add an implementation for `allStringsForType` on macOS. Unlike iOS, it's much trickier to get this right since
     87        we need to maintain compatibility with legacy "NS*Pboard" types, and `NSPasteboardItem` can only provide data
     88        for `NSPasteboardType`s (i.e. UTIs), so there's no way to just iterate through each pasteboard item and ask it
     89        for data that matches the given type, if the types are not UTIs. However, in the case where we have multiple
     90        items, the client must have used NSPasteboardWriting-conformant objects and/or NSPasteboardItem itself to write
     91        data to the pasteboard. Since NSPasteboardWriting-conformant objects register modern pasteboard types when
     92        writing to the pasteboard, we can simply iterate over these pasteboard items and ask for property lists using
     93        type identifiers instead of having to worry about legacy pasteboard types. Furthermore, in the case where there
     94        is only a single item in the pasteboard and we do need to handle legacy pasteboard types, using `-[NSPasteboard
     95        stringForType:]` in the same way we do currently should yield the correct result.
     96
     97        As such, in the case where there is a single pasteboard item, we use `-[NSPasteboard stringForType:]` with the
     98        original legacy type, and in the case where there are multiple items on the pasteboard, we iterate over each of
     99        the pasteboard items and call `-[NSPasteboardItem propertyListForType:]` with the modern pasteboard type
     100        corresponding to the given legacy pasteboard type.
     101
     102        The different corner cases in this logic are tested by the new API test, PasteMixedContent.PasteOneOrMoreURLs,
     103        which exercises several different ways of writing one or more URLs to the pasteboard on macOS, which each result
     104        in different legacy and modern pasteboard types being written to the pasteboard; our implementation of
     105        `PlatformPasteboard::allStringsForType` on macOS handles all cases correctly.
     106
    11072018-09-04  Simon Fraser  <simon.fraser@apple.com>
    2108
  • trunk/Source/WebCore/dom/DataTransfer.cpp

    r233339 r235647  
    4848#include "WebCorePasteboardFileReader.h"
    4949#include "markup.h"
     50#include <wtf/unicode/CharacterNames.h>
    5051
    5152namespace WebCore {
     
    143144}
    144145
     146static String readURLsFromPasteboardAsString(Pasteboard& pasteboard, Function<bool(const String&)>&& shouldIncludeURL)
     147{
     148    StringBuilder urlList;
     149    for (auto urlString : pasteboard.readAllStrings("text/uri-list"_s)) {
     150        if (!shouldIncludeURL(urlString))
     151            continue;
     152        if (!urlList.isEmpty())
     153            urlList.append(newlineCharacter);
     154        urlList.append(urlString);
     155    }
     156    return urlList.toString();
     157}
     158
    145159String DataTransfer::getDataForItem(Document& document, const String& type) const
    146160{
     
    151165    if (shouldSuppressGetAndSetDataToAvoidExposingFilePaths()) {
    152166        if (lowercaseType == "text/uri-list") {
    153             auto urlString = m_pasteboard->readString(lowercaseType);
    154             if (Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(urlString))
    155                 return urlString;
     167            return readURLsFromPasteboardAsString(*m_pasteboard, [] (auto& urlString) {
     168                return Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(urlString);
     169            });
    156170        }
    157171
     
    191205        m_pasteboard->read(reader, policy);
    192206        return reader.markup;
     207    }
     208
     209    if (!is<StaticPasteboard>(*m_pasteboard) && lowercaseType == "text/uri-list") {
     210        return readURLsFromPasteboardAsString(*m_pasteboard, [] (auto&) {
     211            return true;
     212        });
    193213    }
    194214
  • trunk/Source/WebCore/platform/Pasteboard.cpp

    r223471 r235647  
    8686}
    8787
     88#if !PLATFORM(COCOA)
     89
     90Vector<String> Pasteboard::readAllStrings(const String& type)
     91{
     92    auto result = readString(type);
     93    if (result.isEmpty())
     94        return { };
     95
     96    return { result };
     97}
     98
     99#endif
     100
    88101};
  • trunk/Source/WebCore/platform/Pasteboard.h

    r235521 r235647  
    205205    virtual WEBCORE_EXPORT String readString(const String& type);
    206206    virtual WEBCORE_EXPORT String readStringInCustomData(const String& type);
     207    virtual WEBCORE_EXPORT Vector<String> readAllStrings(const String& type);
    207208
    208209    virtual WEBCORE_EXPORT void writeString(const String& type, const String& data);
     
    299300#if PLATFORM(COCOA)
    300301    Vector<String> readFilePaths();
    301     String readPlatformValueAsString(const String& domType, long changeCount, const String& pasteboardName);
     302    Vector<String> readPlatformValuesAsStrings(const String& domType, long changeCount, const String& pasteboardName);
    302303    static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, const String& cocoaType);
    303304    String readStringForPlatformType(const String&);
  • trunk/Source/WebCore/platform/PasteboardStrategy.h

    r235448 r235647  
    6262    virtual void getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType, const String& pasteboardName) = 0;
    6363    virtual String stringForType(const String& pasteboardType, const String& pasteboardName) = 0;
     64    virtual Vector<String> allStringsForType(const String& pasteboardType, const String& pasteboardName) = 0;
    6465    virtual long changeCount(const String& pasteboardName) = 0;
    6566    virtual String uniqueName() = 0;
  • trunk/Source/WebCore/platform/PlatformPasteboard.h

    r235574 r235647  
    7474    WEBCORE_EXPORT void getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType) const;
    7575    WEBCORE_EXPORT String stringForType(const String& pasteboardType) const;
     76    WEBCORE_EXPORT Vector<String> allStringsForType(const String& pasteboardType) const;
    7677    WEBCORE_EXPORT long changeCount() const;
    7778    WEBCORE_EXPORT Color color();
  • trunk/Source/WebCore/platform/cocoa/PasteboardCocoa.mm

    r233339 r235647  
    260260}
    261261
     262Vector<String> Pasteboard::readAllStrings(const String& type)
     263{
     264    return readPlatformValuesAsStrings(type, m_changeCount, m_pasteboardName);
     265}
     266
    262267String Pasteboard::readString(const String& type)
    263268{
    264     return readPlatformValueAsString(type, m_changeCount, m_pasteboardName);
     269    auto values = readPlatformValuesAsStrings(type, m_changeCount, m_pasteboardName);
     270    return values.isEmpty() ? String() : values.first();
    265271}
    266272
  • trunk/Source/WebCore/platform/ios/PasteboardIOS.mm

    r235448 r235647  
    374374}
    375375
    376 String Pasteboard::readPlatformValueAsString(const String& domType, long changeCount, const String& pasteboardName)
    377 {
    378     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
    379 
    380     int numberOfItems = strategy.getPasteboardItemsCount(pasteboardName);
    381 
    382     if (!numberOfItems)
    383         return String();
     376Vector<String> Pasteboard::readPlatformValuesAsStrings(const String& domType, long changeCount, const String& pasteboardName)
     377{
     378    auto& strategy = *platformStrategies()->pasteboardStrategy();
    384379
    385380    // Grab the value off the pasteboard corresponding to the cocoaType.
    386     RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(domType);
    387 
    388     NSString *cocoaValue = nil;
    389 
    390     if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) {
    391         String title;
    392         URL url = strategy.readURLFromPasteboard(0, pasteboardName, title);
    393         if (!url.isNull())
    394             cocoaValue = [(NSURL *)url absoluteString];
    395     } else if ([cocoaType isEqualToString:(NSString *)kUTTypePlainText]) {
    396         String value = strategy.readStringFromPasteboard(0, kUTTypePlainText, pasteboardName);
    397         if (!value.isNull())
    398             cocoaValue = [(NSString *)value precomposedStringWithCanonicalMapping];
    399     } else if (cocoaType)
    400         cocoaValue = (NSString *)strategy.readStringFromPasteboard(0, cocoaType.get(), pasteboardName);
     381    auto cocoaType = cocoaTypeFromHTMLClipboardType(domType);
     382    if (!cocoaType)
     383        return { };
     384
     385    auto values = strategy.allStringsForType(cocoaType.get(), pasteboardName);
     386    if ([cocoaType isEqualToString:(__bridge NSString *)kUTTypePlainText]) {
     387        values = values.map([&] (auto& value) -> String {
     388            return [value precomposedStringWithCanonicalMapping];
     389        });
     390    }
    401391
    402392    // Enforce changeCount ourselves for security. We check after reading instead of before to be
    403393    // sure it doesn't change between our testing the change count and accessing the data.
    404     if (cocoaValue && changeCount == changeCountForPasteboard(pasteboardName))
    405         return cocoaValue;
    406 
    407     return String();
     394    if (changeCount != changeCountForPasteboard(pasteboardName))
     395        return { };
     396
     397    return values;
    408398}
    409399
  • trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm

    r235574 r235647  
    604604}
    605605
     606Vector<String> PlatformPasteboard::allStringsForType(const String& type) const
     607{
     608    auto numberOfItems = count();
     609    Vector<String> strings;
     610    strings.reserveInitialCapacity(numberOfItems);
     611    for (int index = 0; index < numberOfItems; ++index) {
     612        String value = readString(index, type);
     613        if (!value.isEmpty())
     614            strings.uncheckedAppend(WTFMove(value));
     615    }
     616    return strings;
     617}
     618
    606619RefPtr<SharedBuffer> PlatformPasteboard::readBuffer(int index, const String& type) const
    607620{
     
    619632    if (type == String(kUTTypeURL)) {
    620633        String title;
    621         return readURL(index, title);
     634        return [(NSURL *)readURL(index, title) absoluteString];
    622635    }
    623636
  • trunk/Source/WebCore/platform/mac/PasteboardMac.mm

    r235521 r235647  
    489489}
    490490
    491 String Pasteboard::readPlatformValueAsString(const String& domType, long changeCount, const String& pasteboardName)
    492 {
    493     const String& cocoaType = cocoaTypeFromHTMLClipboardType(domType);
    494     String cocoaValue;
    495 
    496     if (cocoaType == String(legacyStringPasteboardType()))
    497         cocoaValue = [platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, pasteboardName) precomposedStringWithCanonicalMapping];
    498     else if (!cocoaType.isEmpty())
    499         cocoaValue = platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, pasteboardName);
     491Vector<String> Pasteboard::readPlatformValuesAsStrings(const String& domType, long changeCount, const String& pasteboardName)
     492{
     493    auto& strategy = *platformStrategies()->pasteboardStrategy();
     494    auto cocoaType = cocoaTypeFromHTMLClipboardType(domType);
     495    if (cocoaType.isEmpty())
     496        return { };
     497
     498    auto values = strategy.allStringsForType(cocoaType, pasteboardName);
     499    if (cocoaType == String(legacyStringPasteboardType())) {
     500        values = values.map([&] (auto& value) -> String {
     501            return [value precomposedStringWithCanonicalMapping];
     502        });
     503    }
    500504
    501505    // Enforce changeCount ourselves for security.  We check after reading instead of before to be
    502506    // sure it doesn't change between our testing the change count and accessing the data.
    503     if (!cocoaValue.isEmpty() && changeCount == platformStrategies()->pasteboardStrategy()->changeCount(pasteboardName))
    504         return cocoaValue;
    505 
    506     return String();
     507    if (changeCount != platformStrategies()->pasteboardStrategy()->changeCount(pasteboardName))
     508        return { };
     509
     510    return values;
    507511}
    508512
  • trunk/Source/WebCore/platform/mac/PlatformPasteboardMac.mm

    r234930 r235647  
    117117}
    118118
     119static Vector<String> urlStringsFromPasteboard(NSPasteboard *pasteboard)
     120{
     121    NSArray<NSPasteboardItem *> *items = pasteboard.pasteboardItems;
     122    Vector<String> urlStrings;
     123    urlStrings.reserveInitialCapacity(items.count);
     124    if (items.count > 1) {
     125        for (NSPasteboardItem *item in items) {
     126            if (id propertyList = [item propertyListForType:(__bridge NSString *)kUTTypeURL]) {
     127                if (auto urlFromItem = adoptNS([[NSURL alloc] initWithPasteboardPropertyList:propertyList ofType:(__bridge NSString *)kUTTypeURL]))
     128                    urlStrings.uncheckedAppend([urlFromItem absoluteString]);
     129            }
     130        }
     131    } else if (NSURL *urlFromPasteboard = [NSURL URLFromPasteboard:pasteboard])
     132        urlStrings.uncheckedAppend(urlFromPasteboard.absoluteString);
     133    else if (NSString *urlStringFromPasteboard = [pasteboard stringForType:legacyURLPasteboardType()])
     134        urlStrings.uncheckedAppend(urlStringFromPasteboard);
     135
     136    bool mayContainFiles = pasteboardMayContainFilePaths(pasteboard);
     137    urlStrings.removeAllMatching([&] (auto& urlString) {
     138        return urlString.isEmpty() || (mayContainFiles && !Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(urlString));
     139    });
     140
     141    return urlStrings;
     142}
     143
     144static String typeIdentifierForPasteboardType(const String& pasteboardType)
     145{
     146    if (UTTypeIsDeclared(pasteboardType.createCFString().get()))
     147        return pasteboardType;
     148
     149    if (pasteboardType == String(legacyStringPasteboardType()))
     150        return kUTTypeUTF8PlainText;
     151
     152    if (pasteboardType == String(legacyHTMLPasteboardType()))
     153        return kUTTypeHTML;
     154
     155    if (pasteboardType == String(legacyURLPasteboardType()))
     156        return kUTTypeURL;
     157
     158    return { };
     159}
     160
     161Vector<String> PlatformPasteboard::allStringsForType(const String& pasteboardType) const
     162{
     163    auto typeIdentifier = typeIdentifierForPasteboardType(pasteboardType);
     164    if (typeIdentifier == String(kUTTypeURL))
     165        return urlStringsFromPasteboard(m_pasteboard.get());
     166
     167    NSArray<NSPasteboardItem *> *items = [m_pasteboard pasteboardItems];
     168    Vector<String> strings;
     169    strings.reserveInitialCapacity(items.count);
     170    if (items.count > 1 && !typeIdentifier.isNull()) {
     171        for (NSPasteboardItem *item in items) {
     172            if (NSString *stringFromItem = [item stringForType:typeIdentifier])
     173                strings.append(stringFromItem);
     174        }
     175    } else if (NSString *stringFromPasteboard = [m_pasteboard stringForType:pasteboardType])
     176        strings.append(stringFromPasteboard);
     177
     178    return strings;
     179}
     180
    119181static const char* safeTypeForDOMToReadAndWriteForPlatformType(const String& platformType)
    120182{
  • trunk/Source/WebKit/ChangeLog

    r235641 r235647  
     12018-09-04  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Populate "text/uri-list" with multiple URLs when the pasteboard contains multiple URLs
     4        https://bugs.webkit.org/show_bug.cgi?id=188890
     5        <rdar://problem/43648605>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add some plumbing through pasteboard classes to support `Pasteboard::readAllStrings`. See WebCore ChangeLog for
     10        more detail.
     11
     12        * UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:
     13        (WebKit::WebPasteboardProxy::getPasteboardStringsForType):
     14        * UIProcess/WebPasteboardProxy.h:
     15        * UIProcess/WebPasteboardProxy.messages.in:
     16        * WebProcess/WebCoreSupport/WebPlatformStrategies.cpp:
     17        (WebKit::WebPlatformStrategies::allStringsForType):
     18        * WebProcess/WebCoreSupport/WebPlatformStrategies.h:
     19
    1202018-09-04  Youenn Fablet  <youenn@apple.com>
    221
  • trunk/Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm

    r235448 r235647  
    7171}
    7272
     73void WebPasteboardProxy::getPasteboardStringsForType(const String& pasteboardName, const String& pasteboardType, Vector<String>& strings)
     74{
     75    strings = PlatformPasteboard(pasteboardName).allStringsForType(pasteboardType);
     76}
     77
    7378void WebPasteboardProxy::getPasteboardBufferForType(const String& pasteboardName, const String& pasteboardType, SharedMemory::Handle& handle, uint64_t& size)
    7479{
  • trunk/Source/WebKit/UIProcess/WebPasteboardProxy.h

    r235448 r235647  
    8888    void getPasteboardPathnamesForType(IPC::Connection&, const String& pasteboardName, const String& pasteboardType, Vector<String>& pathnames, SandboxExtension::HandleArray&);
    8989    void getPasteboardStringForType(const String& pasteboardName, const String& pasteboardType, String&);
     90    void getPasteboardStringsForType(const String& pasteboardName, const String& pasteboardType, Vector<String>&);
    9091    void getPasteboardBufferForType(const String& pasteboardName, const String& pasteboardType, SharedMemory::Handle&, uint64_t& size);
    9192    void pasteboardCopy(const String& fromPasteboard, const String& toPasteboard, uint64_t& newChangeCount);
  • trunk/Source/WebKit/UIProcess/WebPasteboardProxy.messages.in

    r235448 r235647  
    4646    GetPasteboardPathnamesForType(String pasteboardName, String pasteboardType) -> (Vector<String> pathnames, WebKit::SandboxExtension::HandleArray sandboxExtensions) WantsConnection
    4747    GetPasteboardStringForType(String pasteboardName, String pasteboardType) -> (String string)
     48    GetPasteboardStringsForType(String pasteboardName, String pasteboardType) -> (Vector<String> strings)
    4849    GetPasteboardBufferForType(String pasteboardName, String pasteboardType) -> (WebKit::SharedMemory::Handle handle, uint64_t size)
    4950    PasteboardCopy(String fromPasteboard, String toPasteboard) -> (uint64_t changeCount)
  • trunk/Source/WebKit/WebProcess/WebCoreSupport/WebPlatformStrategies.cpp

    r235448 r235647  
    208208}
    209209
     210Vector<String> WebPlatformStrategies::allStringsForType(const String& pasteboardType, const String& pasteboardName)
     211{
     212    Vector<String> values;
     213    WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::GetPasteboardStringsForType(pasteboardName, pasteboardType), Messages::WebPasteboardProxy::GetPasteboardStringsForType::Reply(values), 0);
     214    return values;
     215}
     216
    210217long WebPlatformStrategies::changeCount(const WTF::String &pasteboardName)
    211218{
  • trunk/Source/WebKit/WebProcess/WebCoreSupport/WebPlatformStrategies.h

    r235448 r235647  
    7777    void getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType, const String& pasteboardName) override;
    7878    String stringForType(const String& pasteboardType, const String& pasteboardName) override;
     79    Vector<String> allStringsForType(const String& pasteboardType, const String& pasteboardName) override;
    7980    long changeCount(const String& pasteboardName) override;
    8081    String uniqueName() override;
  • trunk/Source/WebKitLegacy/mac/ChangeLog

    r235585 r235647  
     12018-09-04  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Populate "text/uri-list" with multiple URLs when the pasteboard contains multiple URLs
     4        https://bugs.webkit.org/show_bug.cgi?id=188890
     5        <rdar://problem/43648605>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add some plumbing through pasteboard classes to support `Pasteboard::readAllStrings`. See WebCore ChangeLog for
     10        more detail.
     11
     12        * WebCoreSupport/WebPlatformStrategies.h:
     13        * WebCoreSupport/WebPlatformStrategies.mm:
     14        (WebPlatformStrategies::allStringsForType):
     15
    1162018-09-01  Darin Adler  <darin@apple.com>
    217
  • trunk/Source/WebKitLegacy/mac/WebCoreSupport/WebPlatformStrategies.h

    r235448 r235647  
    7777    void getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType, const String& pasteboardName) override;
    7878    String stringForType(const String& pasteboardType, const String& pasteboardName) override;
     79    Vector<String> allStringsForType(const String& pasteboardType, const String& pasteboardName) override;
    7980    long changeCount(const String& pasteboardName) override;
    8081    String uniqueName() override;
  • trunk/Source/WebKitLegacy/mac/WebCoreSupport/WebPlatformStrategies.mm

    r235448 r235647  
    124124}
    125125
     126Vector<String> WebPlatformStrategies::allStringsForType(const String& pasteboardType, const String& pasteboardName)
     127{
     128    return PlatformPasteboard(pasteboardName).allStringsForType(pasteboardType);
     129}
     130
    126131String WebPlatformStrategies::stringForType(const String& pasteboardType, const String& pasteboardName)
    127132{
  • trunk/Tools/ChangeLog

    r235646 r235647  
     12018-09-04  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Populate "text/uri-list" with multiple URLs when the pasteboard contains multiple URLs
     4        https://bugs.webkit.org/show_bug.cgi?id=188890
     5        <rdar://problem/43648605>
     6
     7        Reviewed by Tim Horton.
     8
     9        * DumpRenderTree/mac/DumpRenderTreePasteboard.mm:
     10        (-[LocalPasteboard pasteboardItems]):
     11
     12        Implement this method to avoid crashing when running layout tests that access the pasteboard.
     13
     14        * TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm:
     15
     16        Add a test to verify that on macOS and iOS, multiple URLs dropped onto the page are accessible via
     17        "text/uri-list".
     18
     19        * TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm:
     20
     21        Add a test that exercises 5 different ways to write one or more URLs to the pasteboard on macOS; in all cases,
     22        the URLs written to the pasteboard should be exposed to the page via "text/uri-list". In all of these different
     23        cases, the results of using `-[NSPasteboardItem stringForType:]`, `-[NSURL URLFromPasteboard:]` and
     24        `-[NSPasteboard stringForType:]` will yield different results, so the purpose of this API test is to ensure that
     25        our logic for grabbing a list of URLs from the pasteboard on macOS is robust enough to handle all of these
     26        different behaviors.
     27
     28        * TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:
     29
     30        Add a test to verify that on iOS, using `-[UIPasteboard setURLs:]` to write to multiple URLs to the pasteboard
     31        and then pasting results in "text/uri-list" exposing a list of all the URLs written to the pasteboard.
     32
     33        * WebKitTestRunner/mac/WebKitTestRunnerPasteboard.mm:
     34        (-[LocalPasteboard pasteboardItems]):
     35
     36        Implement this method to avoid crashing when running layout tests that access the pasteboard.
     37
    1382018-09-04  Simon Fraser  <simon.fraser@apple.com>
    239
  • trunk/Tools/DumpRenderTree/mac/DumpRenderTreePasteboard.mm

    r234685 r235647  
    238238}
    239239
     240- (NSArray<NSPasteboardItem *> *)pasteboardItems
     241{
     242    auto item = adoptNS([[NSPasteboardItem alloc] init]);
     243    for (auto typeAndData : _data) {
     244        NSData *data = (__bridge NSData *)typeAndData.value.get();
     245        NSString *type = (__bridge NSString *)typeAndData.key.get();
     246        [item setData:data forType:type];
     247    }
     248    return @[ item.get() ];
     249}
     250
    240251@end
    241252
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DragAndDropTests.mm

    r234976 r235647  
    4242    EXPECT_TRUE(NSPointInRect([simulator initialDragImageLocationInView], NSMakeRect(0, 250, 400, 250)));
    4343#endif
     44}
     45
     46TEST(DragAndDropTests, ExposeMultipleURLsInDataTransfer)
     47{
     48    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:CGRectMake(0, 0, 320, 500)]);
     49    auto webView = [simulator webView];
     50    [webView synchronouslyLoadTestPageNamed:@"DataTransfer"];
     51
     52    NSString *stringData = @"Hello world";
     53    NSURL *firstURL = [NSURL URLWithString:@"https://webkit.org/"];
     54    NSURL *secondURL = [NSURL URLWithString:@"https://apple.com/"];
     55
     56#if PLATFORM(MAC)
     57    NSPasteboard *pasteboard = [NSPasteboard pasteboardWithUniqueName];
     58    [pasteboard writeObjects:@[ stringData, firstURL, secondURL ]];
     59    [simulator setExternalDragPasteboard:pasteboard];
     60#else
     61    auto stringItem = adoptNS([[NSItemProvider alloc] initWithObject:stringData]);
     62    auto firstURLItem = adoptNS([[NSItemProvider alloc] initWithObject:firstURL]);
     63    auto secondURLItem = adoptNS([[NSItemProvider alloc] initWithObject:secondURL]);
     64    for (NSItemProvider *item in @[ stringItem.get(), firstURLItem.get(), secondURLItem.get() ])
     65        item.preferredPresentationStyle = UIPreferredPresentationStyleInline;
     66    [simulator setExternalItemProviders:@[ stringItem.get(), firstURLItem.get(), secondURLItem.get() ]];
     67#endif
     68
     69    [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(100, 100)];
     70
     71    EXPECT_WK_STREQ("text/plain, text/uri-list", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
     72    EXPECT_WK_STREQ("(STRING, text/plain), (STRING, text/uri-list)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
     73    EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
     74    EXPECT_WK_STREQ("https://webkit.org/\nhttps://apple.com/", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
    4475}
    4576
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm

    r235079 r235647  
    256256}
    257257
     258TEST(PasteMixedContent, PasteOneOrMoreURLs)
     259{
     260    NSURL *appleURL = [NSURL URLWithString:@"https://www.apple.com/"];
     261    NSURL *webKitURL = [NSURL URLWithString:@"https://webkit.org/"];
     262
     263    auto webView = setUpWebView();
     264    auto runTest = [webView] (NSString *description, NSString *expectedURLString, Function<void(NSPasteboard *)>&& writeURLsToPasteboard) {
     265        NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
     266
     267        [pasteboard clearContents];
     268        writeURLsToPasteboard(pasteboard);
     269        [webView stringByEvaluatingJavaScript:@"reset(); document.body.focus()"];
     270        [webView paste:nil];
     271
     272        EXPECT_WK_STREQ(expectedURLString, [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
     273        EXPECT_WK_STREQ("(STRING, text/uri-list)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
     274        EXPECT_WK_STREQ("text/uri-list", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
     275    };
     276
     277    runTest(@"Write multiple URLs.", @"https://www.apple.com/\nhttps://webkit.org/", ^(NSPasteboard *pasteboard) {
     278        [pasteboard writeObjects:@[appleURL, webKitURL]];
     279    });
     280
     281    runTest(@"Declare legacy URL and write URL to pasteboard.", @"https://www.apple.com/", ^(NSPasteboard *pasteboard) {
     282        [pasteboard declareTypes:@[NSURLPboardType] owner:nil];
     283        [appleURL writeToPasteboard:pasteboard];
     284    });
     285
     286    runTest(@"Declare legacy URL and set a URL string.", @"https://www.apple.com/", ^(NSPasteboard *pasteboard) {
     287        [pasteboard declareTypes:@[NSURLPboardType] owner:nil];
     288        [pasteboard setString:appleURL.absoluteString forType:NSURLPboardType];
     289    });
     290
     291    runTest(@"Declare legacy URL and set a property list.", @"https://www.apple.com/", ^(NSPasteboard *pasteboard) {
     292        [pasteboard declareTypes:@[NSURLPboardType] owner:nil];
     293        [pasteboard setPropertyList:@[@"/", @"https://www.apple.com"] forType:NSURLPboardType];
     294    });
     295
     296    runTest(@"Declare URL UTI and set a URL string.", @"https://www.apple.com/", ^(NSPasteboard *pasteboard) {
     297        [pasteboard declareTypes:@[(__bridge NSString *)kUTTypeURL] owner:nil];
     298        [pasteboard setString:appleURL.absoluteString forType:(__bridge NSString *)kUTTypeURL];
     299    });
     300}
     301
    258302#endif // PLATFORM(MAC)
    259303
  • trunk/Tools/TestWebKitAPI/Tests/ios/UIPasteboardTests.mm

    r229503 r235647  
    268268}
    269269
     270TEST(UIPasteboardTests, DataTransferURIListContainsMultipleURLs)
     271{
     272    auto webView = setUpWebViewForPasteboardTests(@"DataTransfer");
     273
     274    NSURL *firstURL = [NSURL URLWithString:@"https://www.apple.com/"];
     275    NSURL *secondURL = [NSURL URLWithString:@"https://webkit.org/"];
     276    [UIPasteboard generalPasteboard].URLs = @[ firstURL, secondURL ];
     277
     278    [webView paste:nil];
     279
     280    EXPECT_WK_STREQ("text/uri-list, text/plain", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
     281    EXPECT_WK_STREQ("(STRING, text/uri-list), (STRING, text/plain)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
     282    EXPECT_WK_STREQ("https://www.apple.com/\nhttps://webkit.org/", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
     283    EXPECT_WK_STREQ("https://www.apple.com/", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
     284}
     285
    270286#endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
    271287
  • trunk/Tools/WebKitTestRunner/mac/WebKitTestRunnerPasteboard.mm

    r234685 r235647  
    3030
    3131#include <objc/runtime.h>
     32#include <wtf/RetainPtr.h>
    3233
    3334@interface LocalPasteboard : NSPasteboard
     
    198199}
    199200
     201- (NSArray<NSPasteboardItem *> *)pasteboardItems
     202{
     203    auto item = adoptNS([[NSPasteboardItem alloc] init]);
     204    for (NSString *type in dataByType)
     205        [item setData:dataByType[type] forType:type];
     206    return @[ item.get() ];
     207}
     208
    200209@end
Note: See TracChangeset for help on using the changeset viewer.