Changeset 238795 in webkit


Ignore:
Timestamp:
Dec 3, 2018 7:42:12 AM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOSMac] Unable to upload non-image files using drag and drop in WKWebView
https://bugs.webkit.org/show_bug.cgi?id=192283
<rdar://problem/46399461>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Currently on iOS, file URLs aren't generally written to the pasteboard during drag and drop unless the
application providing the data explicitly registers "public.file-url" to item providers. As such, our current
logic on iOS for handling drops does not attempt to prevent "public.file-url" from being advertised as the
"text/uri-list" MIME type in DataTransfer, though we do currently succeed in suppressing access to the file URL.

However, on iOSMac, the scenario in which file URLs are registered to item providers becomes pertinent when
uploading files from other macOS apps (e.g. Finder) into a WKWebView running in iOSMac. Furthermore, the
preferredPresentationStyle flag on NSItemProvider is unavailable in iOSMac; currently, this flag is our
primary cue on iOS that an item should be treated as an attachment rather than inline data. In order to support
file uploads in iOSMac, we make several adjustments to drop handling logic in iOS to handle the case where the
"public.file-url" type is registered. See below for more details.

Tests: DragAndDropTests.DataTransferExposePlainTextWithFileURLAsFile

DragAndDropTests.DataTransferGetDataWhenDroppingImageWithFileURL

  • platform/PasteboardItemInfo.h:

(WebCore::PasteboardItemInfo::encode const):
(WebCore::PasteboardItemInfo::decode):

Add a new flag that is set if and only if the item provider contains the "public.file-url" type, and also
contains some non-URL data type that conforms to one of the file types supported for file uploads (i.e.
"public.content", zip archives, and folders).

  • platform/cocoa/PasteboardCocoa.mm:

(WebCore::Pasteboard::fileContentState):

Consider the pasteboard to contain files in the case where one or more of the items contains a file URL, along
with some other pasteboard data that can be represented as a file upload.

  • platform/ios/PlatformPasteboardIOS.mm:

(WebCore::PlatformPasteboard::informationForItemAtIndex):
(WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):

If the pasteboard contains "public.file-url", don't consider "text/uri-list" to be one of the data types that's
safe to expose to the page. Our current behavior in this case is that we will advertise "text/uri-list" as a
pasteboard type in the DataTransfer, but if the page attempts to request this information, we simply return the
empty string. Instead, we shouldn't expose "text/uri-list" as a type in the first place.

  • platform/ios/WebItemProviderPasteboard.h:
  • platform/ios/WebItemProviderPasteboard.mm:

Add a few more __bridge-ing casts where appropriate.

(typeConformsToTypes):

Move this further up the file so that it can be used in NSItemProvider (WebCoreExtras).

(-[NSItemProvider web_containsFileURLAndFileUploadContent]):

Add a helper method on NSItemProvider to determine whether an item provider has a file URL, as well as a content
type suitable for file uploads.

(-[WebItemProviderLoadResult canBeRepresentedAsFileUpload]):

This currently always returns NO in iOSMac; instead, return YES on both iOS and iOSMac in the case where the
item provider contains a file URL and content which may be uploaded.

(-[WebItemProviderPasteboard preferredFileUploadURLAtIndex:fileType:]):
(-[WebItemProviderPasteboard typeIdentifiersToLoad:]):

Refactor this to take an NSItemProvider instead of a list of type identifiers, and bail out of loading data
for "public.url" if the item provider contains a file URL.

(-[WebItemProviderPasteboard doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout:]):
(-[WebItemProviderPasteboard typeIdentifiersToLoadForRegisteredTypeIdentifiers:]): Deleted.

Tools:

Add a new API test to check that an item provider which contains plain text data and a file URL (but is not
marked as an attachment) is still treated as an attachment upon drop. Furthermore, verify that "text/uri-list"
does not expose the actual file URL written to the item provider.

Additionally, rebaseline an existing API test to remove an extraneous "text/uri-list" type that appears in
DataTransfer.types, but whose data is inaccessible via getData anyways.

  • TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:
Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r238791 r238795  
     12018-12-03  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOSMac] Unable to upload non-image files using drag and drop in WKWebView
     4        https://bugs.webkit.org/show_bug.cgi?id=192283
     5        <rdar://problem/46399461>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        Currently on iOS, file URLs aren't generally written to the pasteboard during drag and drop unless the
     10        application providing the data explicitly registers "public.file-url" to item providers. As such, our current
     11        logic on iOS for handling drops does not attempt to prevent "public.file-url" from being advertised as the
     12        "text/uri-list" MIME type in DataTransfer, though we do currently succeed in suppressing access to the file URL.
     13
     14        However, on iOSMac, the scenario in which file URLs are registered to item providers becomes pertinent when
     15        uploading files from other macOS apps (e.g. Finder) into a WKWebView running in iOSMac. Furthermore, the
     16        `preferredPresentationStyle` flag on `NSItemProvider` is unavailable in iOSMac; currently, this flag is our
     17        primary cue on iOS that an item should be treated as an attachment rather than inline data. In order to support
     18        file uploads in iOSMac, we make several adjustments to drop handling logic in iOS to handle the case where the
     19        "public.file-url" type is registered. See below for more details.
     20
     21        Tests:  DragAndDropTests.DataTransferExposePlainTextWithFileURLAsFile
     22                DragAndDropTests.DataTransferGetDataWhenDroppingImageWithFileURL
     23
     24        * platform/PasteboardItemInfo.h:
     25        (WebCore::PasteboardItemInfo::encode const):
     26        (WebCore::PasteboardItemInfo::decode):
     27
     28        Add a new flag that is set if and only if the item provider contains the "public.file-url" type, and also
     29        contains some non-URL data type that conforms to one of the file types supported for file uploads (i.e.
     30        "public.content", zip archives, and folders).
     31
     32        * platform/cocoa/PasteboardCocoa.mm:
     33        (WebCore::Pasteboard::fileContentState):
     34
     35        Consider the pasteboard to contain files in the case where one or more of the items contains a file URL, along
     36        with some other pasteboard data that can be represented as a file upload.
     37
     38        * platform/ios/PlatformPasteboardIOS.mm:
     39        (WebCore::PlatformPasteboard::informationForItemAtIndex):
     40        (WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
     41
     42        If the pasteboard contains "public.file-url", don't consider "text/uri-list" to be one of the data types that's
     43        safe to expose to the page. Our current behavior in this case is that we will advertise "text/uri-list" as a
     44        pasteboard type in the DataTransfer, but if the page attempts to request this information, we simply return the
     45        empty string. Instead, we shouldn't expose "text/uri-list" as a type in the first place.
     46
     47        * platform/ios/WebItemProviderPasteboard.h:
     48        * platform/ios/WebItemProviderPasteboard.mm:
     49
     50        Add a few more `__bridge`-ing casts where appropriate.
     51
     52        (typeConformsToTypes):
     53
     54        Move this further up the file so that it can be used in `NSItemProvider (WebCoreExtras)`.
     55
     56        (-[NSItemProvider web_containsFileURLAndFileUploadContent]):
     57
     58        Add a helper method on NSItemProvider to determine whether an item provider has a file URL, as well as a content
     59        type suitable for file uploads.
     60
     61        (-[WebItemProviderLoadResult canBeRepresentedAsFileUpload]):
     62
     63        This currently always returns `NO` in iOSMac; instead, return `YES` on both iOS and iOSMac in the case where the
     64        item provider contains a file URL and content which may be uploaded.
     65
     66        (-[WebItemProviderPasteboard preferredFileUploadURLAtIndex:fileType:]):
     67        (-[WebItemProviderPasteboard typeIdentifiersToLoad:]):
     68
     69        Refactor this to take an `NSItemProvider` instead of a list of type identifiers, and bail out of loading data
     70        for "public.url" if the item provider contains a file URL.
     71
     72        (-[WebItemProviderPasteboard doAfterLoadingProvidedContentIntoFileURLs:synchronousTimeout:]):
     73        (-[WebItemProviderPasteboard typeIdentifiersToLoadForRegisteredTypeIdentifiers:]): Deleted.
     74
    1752018-12-02  Zalan Bujtas  <zalan@apple.com>
    276
  • trunk/Source/WebCore/platform/PasteboardItemInfo.h

    r237074 r238795  
    4242    String suggestedFileName;
    4343    bool isNonTextType { false };
     44    bool containsFileURLAndFileUploadContent { false };
    4445    PasteboardItemPresentationStyle preferredPresentationStyle { PasteboardItemPresentationStyle::Unspecified };
    4546
     
    5152void PasteboardItemInfo::encode(Encoder& encoder) const
    5253{
    53     encoder << pathForFileUpload << contentTypeForFileUpload << suggestedFileName << isNonTextType;
     54    encoder << pathForFileUpload << contentTypeForFileUpload << suggestedFileName << isNonTextType << containsFileURLAndFileUploadContent;
    5455    encoder.encodeEnum(preferredPresentationStyle);
    5556}
     
    6970
    7071    if (!decoder.decode(result.isNonTextType))
     72        return std::nullopt;
     73
     74    if (!decoder.decode(result.containsFileURLAndFileUploadContent))
    7175        return std::nullopt;
    7276
  • trunk/Source/WebCore/platform/cocoa/PasteboardCocoa.mm

    r237266 r238795  
    163163                return item.preferredPresentationStyle == PasteboardItemPresentationStyle::Attachment;
    164164
    165             return !item.suggestedFileName.isEmpty() || item.isNonTextType;
     165            return !item.suggestedFileName.isEmpty() || item.isNonTextType || item.containsFileURLAndFileUploadContent;
    166166        });
    167167    }
  • trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm

    r238771 r238795  
    155155    info.preferredPresentationStyle = pasteboardItemPresentationStyle(itemProvider.preferredPresentationStyle);
    156156#endif
     157    info.containsFileURLAndFileUploadContent = itemProvider.web_containsFileURLAndFileUploadContent;
    157158    info.suggestedFileName = itemProvider.suggestedName;
    158159    for (NSString *typeIdentifier in itemProvider.registeredTypeIdentifiers) {
     
    531532                BOOL ableToDetermineProtocolOfPasteboardURL = ![m_pasteboard isKindOfClass:[WebItemProviderPasteboard class]];
    532533                if (ableToDetermineProtocolOfPasteboardURL && stringForType(kUTTypeURL).isEmpty())
     534                    continue;
     535
     536                if ([[m_pasteboard pasteboardTypes] containsObject:(__bridge NSString *)kUTTypeFileURL])
    533537                    continue;
    534538            }
  • trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.h

    r238360 r238795  
    3737
    3838NS_ASSUME_NONNULL_BEGIN
     39
     40@interface NSItemProvider (WebCoreExtras)
     41@property (nonatomic, readonly) BOOL web_containsFileURLAndFileUploadContent;
     42@end
    3943
    4044/*! A WebItemProviderRegistrar encapsulates a single call to register something to an item provider.
  • trunk/Source/WebCore/platform/ios/WebItemProviderPasteboard.mm

    r238461 r238795  
    5454using WebCore::PasteboardCustomData;
    5555
     56static BOOL typeConformsToTypes(NSString *type, NSArray *conformsToTypes)
     57{
     58    for (NSString *conformsToType in conformsToTypes) {
     59        if (UTTypeConformsTo((__bridge CFStringRef)type, (__bridge CFStringRef)conformsToType))
     60            return YES;
     61    }
     62    return NO;
     63}
     64
     65@implementation NSItemProvider (WebCoreExtras)
     66
     67- (BOOL)web_containsFileURLAndFileUploadContent
     68{
     69    BOOL containsFileURL = NO;
     70    BOOL containsContentForFileUpload = NO;
     71    for (NSString *identifier in self.registeredTypeIdentifiers) {
     72        if (UTTypeConformsTo((__bridge CFStringRef)identifier, kUTTypeFileURL)) {
     73            containsFileURL = YES;
     74            continue;
     75        }
     76
     77        if (UTTypeConformsTo((__bridge CFStringRef)identifier, kUTTypeURL))
     78            continue;
     79
     80        containsContentForFileUpload |= typeConformsToTypes(identifier, Pasteboard::supportedFileUploadPasteboardTypes());
     81        if (containsContentForFileUpload && containsFileURL)
     82            return YES;
     83    }
     84    return NO;
     85}
     86
     87@end
     88
    5689@interface WebItemProviderDataRegistrar : NSObject <WebItemProviderRegistrar>
    5790- (instancetype)initWithData:(NSData *)data type:(NSString *)utiType;
     
    343376- (BOOL)canBeRepresentedAsFileUpload
    344377{
     378    if ([_itemProvider web_containsFileURLAndFileUploadContent])
     379        return YES;
     380
    345381#if PLATFORM(IOSMAC)
    346     return false;
     382    return NO;
    347383#else
    348384    return [_itemProvider preferredPresentationStyle] != UIPreferredPresentationStyleInline;
     
    607643    for (NSString *registeredTypeIdentifier in itemProvider.registeredTypeIdentifiers) {
    608644        // Search for the highest fidelity non-private type identifier we loaded from the item provider.
    609         if (!UTTypeIsDeclared((CFStringRef)registeredTypeIdentifier) && !UTTypeIsDynamic((CFStringRef)registeredTypeIdentifier))
     645        if (!UTTypeIsDeclared((__bridge CFStringRef)registeredTypeIdentifier) && !UTTypeIsDynamic((__bridge CFStringRef)registeredTypeIdentifier))
    610646            continue;
    611647
    612648        for (NSString *loadedTypeIdentifier in [result loadedTypeIdentifiers]) {
    613             if (!UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, (CFStringRef)loadedTypeIdentifier))
     649            if (!UTTypeConformsTo((__bridge CFStringRef)registeredTypeIdentifier, (__bridge CFStringRef)loadedTypeIdentifier))
    614650                continue;
    615651
     
    631667    }
    632668    return fileURLs;
    633 }
    634 
    635 static BOOL typeConformsToTypes(NSString *type, NSArray *conformsToTypes)
    636 {
    637     // A type is considered appropriate to load if it conforms to one or more supported types.
    638     for (NSString *conformsToType in conformsToTypes) {
    639         if (UTTypeConformsTo((CFStringRef)type, (CFStringRef)conformsToType))
    640             return YES;
    641     }
    642     return NO;
    643669}
    644670
     
    689715}
    690716
    691 - (NSArray<NSString *> *)typeIdentifiersToLoadForRegisteredTypeIdentifiers:(NSArray<NSString *> *)registeredTypeIdentifiers
     717- (NSArray<NSString *> *)typeIdentifiersToLoad:(NSItemProvider *)itemProvider
    692718{
    693719    auto typesToLoad = adoptNS([[NSMutableOrderedSet alloc] init]);
     
    695721    NSString *highestFidelityContentType = nil;
    696722
    697     BOOL containsFlatRTFD = [registeredTypeIdentifiers containsObject:(NSString *)kUTTypeFlatRTFD];
     723    NSArray<NSString *> *registeredTypeIdentifiers = itemProvider.registeredTypeIdentifiers;
     724    BOOL containsFile = itemProvider.web_containsFileURLAndFileUploadContent;
     725    BOOL containsFlatRTFD = [registeredTypeIdentifiers containsObject:(__bridge NSString *)kUTTypeFlatRTFD];
    698726    // First, search for the highest fidelity supported type or the highest fidelity generic content type.
    699727    for (NSString *registeredTypeIdentifier in registeredTypeIdentifiers) {
    700         if (containsFlatRTFD && [registeredTypeIdentifier isEqualToString:(NSString *)kUTTypeRTFD]) {
     728        if (containsFlatRTFD && [registeredTypeIdentifier isEqualToString:(__bridge NSString *)kUTTypeRTFD]) {
    701729            // In the case where attachments are enabled and we're accepting all types of content using attachment
    702730            // elements as a fallback representation, if the source writes attributed strings to the pasteboard with
     
    708736        }
    709737
     738        if (containsFile && UTTypeConformsTo((__bridge CFStringRef)registeredTypeIdentifier, kUTTypeURL))
     739            continue;
     740
    710741        if (!highestFidelitySupportedType && typeConformsToTypes(registeredTypeIdentifier, _supportedTypeIdentifiers.get()))
    711742            highestFidelitySupportedType = registeredTypeIdentifier;
     
    722753            || [registeredTypeIdentifier isEqualToString:highestFidelitySupportedType]
    723754            || [registeredTypeIdentifier isEqualToString:customPasteboardDataUTI]
    724             || UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeURL)
    725             || UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypeHTML)
    726             || UTTypeConformsTo((CFStringRef)registeredTypeIdentifier, kUTTypePlainText))
     755            || (!containsFile && UTTypeConformsTo((__bridge CFStringRef)registeredTypeIdentifier, kUTTypeURL))
     756            || UTTypeConformsTo((__bridge CFStringRef)registeredTypeIdentifier, kUTTypeHTML)
     757            || UTTypeConformsTo((__bridge CFStringRef)registeredTypeIdentifier, kUTTypePlainText))
    727758            [typesToLoad addObject:registeredTypeIdentifier];
    728759    }
     
    747778    RetainPtr<WebItemProviderPasteboard> protectedSelf = self;
    748779    for (NSItemProvider *itemProvider in _itemProviders.get()) {
    749         NSArray<NSString *> *typeIdentifiersToLoad = [protectedSelf typeIdentifiersToLoadForRegisteredTypeIdentifiers:itemProvider.registeredTypeIdentifiers];
     780        NSArray<NSString *> *typeIdentifiersToLoad = [protectedSelf typeIdentifiersToLoad:itemProvider];
    750781        foundAnyDataToLoad |= typeIdentifiersToLoad.count;
    751782        [loadResults addObject:[WebItemProviderLoadResult loadResultWithItemProvider:itemProvider typesToLoad:typeIdentifiersToLoad]];
  • trunk/Tools/ChangeLog

    r238789 r238795  
     12018-12-03  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOSMac] Unable to upload non-image files using drag and drop in WKWebView
     4        https://bugs.webkit.org/show_bug.cgi?id=192283
     5        <rdar://problem/46399461>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        Add a new API test to check that an item provider which contains plain text data and a file URL (but is not
     10        marked as an attachment) is still treated as an attachment upon drop. Furthermore, verify that "text/uri-list"
     11        does not expose the actual file URL written to the item provider.
     12
     13        Additionally, rebaseline an existing API test to remove an extraneous "text/uri-list" type that appears in
     14        `DataTransfer.types`, but whose data is inaccessible via `getData` anyways.
     15
     16        * TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:
     17
    1182018-12-01  Don Olmstead  <don.olmstead@sony.com>
    219
  • trunk/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm

    r238728 r238795  
    16471647    // File URLs should never be exposed directly to web content, so DataTransfer.getData should return an empty string here.
    16481648    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
    1649         @"dragover": @{ @"Files": @"", @"text/uri-list": @"" },
    1650         @"drop": @{ @"Files": @"", @"text/uri-list": @"" }
     1649        @"dragover": @{ @"Files": @"" },
     1650        @"drop": @{ @"Files": @"" }
    16511651    });
    16521652}
     
    17711771    EXPECT_WK_STREQ("(FILE, text/plain)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
    17721772    EXPECT_WK_STREQ("('hello.txt', text/plain)", [webView stringByEvaluatingJavaScript:@"files.textContent"]);
     1773}
     1774
     1775TEST(DragAndDropTests, DataTransferExposePlainTextWithFileURLAsFile)
     1776{
     1777    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     1778    [webView synchronouslyLoadTestPageNamed:@"DataTransfer"];
     1779    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
     1780
     1781    auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
     1782    NSData *urlAsData = [@"file:///some/file/path.txt" dataUsingEncoding:NSUTF8StringEncoding];
     1783    [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeFileURL withData:urlAsData];
     1784    [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeURL withData:urlAsData];
     1785    [itemProvider registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll];
     1786
     1787    [simulator setExternalItemProviders:@[ itemProvider.get() ]];
     1788    [simulator runFrom:CGPointZero to:CGPointMake(50, 100)];
     1789
     1790    EXPECT_WK_STREQ("Files", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
     1791    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
     1792    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
     1793    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]);
     1794    EXPECT_WK_STREQ("(FILE, text/plain)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
     1795    EXPECT_WK_STREQ("('text.txt', text/plain)", [webView stringByEvaluatingJavaScript:@"files.textContent"]);
    17731796}
    17741797
Note: See TracChangeset for help on using the changeset viewer.