⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 243713 in webkit


Ignore:
Timestamp:
Apr 1, 2019, 2:21:24 PM (7 years ago)
Author:
Wenson Hsieh
Message:

Unable to copy and paste a PDF from Notes into Mail compose body
https://bugs.webkit.org/show_bug.cgi?id=196442
<rdar://problem/48573098>

Reviewed by Tim Horton.

Source/WebCore:

Refactor some logic for inserting attachment elements upon paste or drop. Currently, we only prefer inserting
content as attachment elements if the items are annotated with UIPreferredPresentationStyleAttachment. However,
many data sources around the system (both first and third party) have not adopted this API, which makes it
difficult to determine whether a given item provider should be treated as a file or not. In this bug in
particular, no preferred presentation style is set, so we fail to handle the paste command by inserting an
attachment element.

However, most apps around the system that write file or attachment-like data to the pasteboard will at least
offer a suggested name for the file, in the form of -[NSItemProvider suggestedName]. To address this, instead of
relying solely on the preferredPresentationStyle, additionally take a suggested name as an indicator that the
item is probably a file.

In fact, Pasteboard::fileContentState already has similar logic to check for either a suggested file name or
explicitly specified presentation style. We pull this out into a separate helper method on PasteboardItemInfo,
and use it for both Pasteboard::fileContentState and prefersAttachmentRepresentation.

Tests: WKAttachmentTestsIOS.InsertPastedContactAsAttachment

WKAttachmentTestsIOS.InsertPastedMapItemAsAttachment

  • editing/cocoa/WebContentReaderCocoa.mm:

(WebCore::mimeTypeFromContentType):

Work around <rdar://problem/49478229> by using the "text/vcard" MIME type to handle "public.vcard". CoreServices
currently maps "public.vcard" to "text/directory" when using UTTypeCopyPreferredTagWithClass, despite the SPI
-[NSURLFileTypeMappings MIMETypeForExtension:] returning "text/vcard" for a ".vcf" file.

  • platform/PasteboardItemInfo.h:

(WebCore::PasteboardItemInfo::canBeTreatedAsAttachmentOrFile const):

Add a helper method to determine whether the PasteboardItemInfo prefers to be represented as inline data, or an
attachment, or neither. This differs slightly from the existing value of preferredPresentationStyle in that we
consider having a suggested file name as a strong indicator that the item should be treated as an attachment,
even if the presentation style is unspecified.

  • platform/cocoa/PasteboardCocoa.mm:

(WebCore::Pasteboard::fileContentState):

Use PasteboardItemInfo::canBeTreatedAsAttachmentOrFile().

  • platform/ios/PasteboardIOS.mm:

(WebCore::prefersAttachmentRepresentation):

Use PasteboardItemInfo::canBeTreatedAsAttachmentOrFile().

Source/WebKit:

Relax the -canPerformAction: logic in the case of pasting an attachment. Consider an NSItemProvider to possibly
paste as an attachment if either it has a preferred presentation style of UIPreferredPresentationStyleAttachment
or has a style of UIPreferredPresentationStyleUnspecified, but has a suggested file name.

This allows for the "Paste" action to be shown in the callout menu when copying and pasting a non-text file.

  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView canPerformActionForWebView:withSender:]):

Tools:

Add new API tests to exercise pasting CNContact and MKMapItem-backed item providers. Additionally, adjust an
existing test that pastes a PDF file as an attachment to not require UIPreferredPresentationStyleAttachment
to be specified on the item providers.

  • TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:

(TestWebKitAPI::mapItemForTesting):
(TestWebKitAPI::contactItemForTesting):

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r243712 r243713  
     12019-04-01  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Unable to copy and paste a PDF from Notes into Mail compose body
     4        https://bugs.webkit.org/show_bug.cgi?id=196442
     5        <rdar://problem/48573098>
     6
     7        Reviewed by Tim Horton.
     8
     9        Refactor some logic for inserting attachment elements upon paste or drop. Currently, we only prefer inserting
     10        content as attachment elements if the items are annotated with UIPreferredPresentationStyleAttachment. However,
     11        many data sources around the system (both first and third party) have not adopted this API, which makes it
     12        difficult to determine whether a given item provider should be treated as a file or not. In this bug in
     13        particular, no preferred presentation style is set, so we fail to handle the paste command by inserting an
     14        attachment element.
     15
     16        However, most apps around the system that write file or attachment-like data to the pasteboard will at least
     17        offer a suggested name for the file, in the form of -[NSItemProvider suggestedName]. To address this, instead of
     18        relying solely on the preferredPresentationStyle, additionally take a suggested name as an indicator that the
     19        item is probably a file.
     20
     21        In fact, Pasteboard::fileContentState already has similar logic to check for either a suggested file name or
     22        explicitly specified presentation style. We pull this out into a separate helper method on PasteboardItemInfo,
     23        and use it for both Pasteboard::fileContentState and prefersAttachmentRepresentation.
     24
     25        Tests:  WKAttachmentTestsIOS.InsertPastedContactAsAttachment
     26                WKAttachmentTestsIOS.InsertPastedMapItemAsAttachment
     27
     28        * editing/cocoa/WebContentReaderCocoa.mm:
     29        (WebCore::mimeTypeFromContentType):
     30
     31        Work around <rdar://problem/49478229> by using the "text/vcard" MIME type to handle "public.vcard". CoreServices
     32        currently maps "public.vcard" to "text/directory" when using UTTypeCopyPreferredTagWithClass, despite the SPI
     33        -[NSURLFileTypeMappings MIMETypeForExtension:] returning "text/vcard" for a ".vcf" file.
     34
     35        * platform/PasteboardItemInfo.h:
     36        (WebCore::PasteboardItemInfo::canBeTreatedAsAttachmentOrFile const):
     37
     38        Add a helper method to determine whether the PasteboardItemInfo prefers to be represented as inline data, or an
     39        attachment, or neither. This differs slightly from the existing value of preferredPresentationStyle in that we
     40        consider having a suggested file name as a strong indicator that the item should be treated as an attachment,
     41        even if the presentation style is unspecified.
     42
     43        * platform/cocoa/PasteboardCocoa.mm:
     44        (WebCore::Pasteboard::fileContentState):
     45
     46        Use PasteboardItemInfo::canBeTreatedAsAttachmentOrFile().
     47
     48        * platform/ios/PasteboardIOS.mm:
     49        (WebCore::prefersAttachmentRepresentation):
     50
     51        Use PasteboardItemInfo::canBeTreatedAsAttachmentOrFile().
     52
    1532019-04-01  Tim Horton  <timothy_horton@apple.com>
    254
  • trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm

    r243695 r243713  
    223223static String mimeTypeFromContentType(const String& contentType)
    224224{
     225    if (contentType == String(kUTTypeVCard)) {
     226        // CoreServices erroneously reports that "public.vcard" maps to "text/directory", rather
     227        // than either "text/vcard" or "text/x-vcard". Work around this by special casing the
     228        // "public.vcard" UTI type. See <rdar://problem/49478229> for more detail.
     229        return "text/vcard"_s;
     230    }
    225231    return isDeclaredUTI(contentType) ? MIMETypeFromUTI(contentType) : contentType;
    226232}
  • trunk/Source/WebCore/platform/PasteboardItemInfo.h

    r241749 r243713  
    5555
    5656        return pathsForFileUpload[index];
     57    }
     58
     59    // The preferredPresentationStyle flag is platform API used by drag or copy sources to explicitly indicate
     60    // that the data being written to the item provider should be treated as an attachment; unfortunately, not
     61    // all clients attempt to set this flag, so we additionally take having a suggested filename as a strong
     62    // indicator that the item should be treated as an attachment or file.
     63    bool canBeTreatedAsAttachmentOrFile() const
     64    {
     65        switch (preferredPresentationStyle) {
     66        case PasteboardItemPresentationStyle::Inline:
     67            return false;
     68        case PasteboardItemPresentationStyle::Attachment:
     69            return true;
     70        case PasteboardItemPresentationStyle::Unspecified:
     71            return !suggestedFileName.isEmpty();
     72        }
     73        ASSERT_NOT_REACHED();
     74        return false;
    5775    }
    5876
  • trunk/Source/WebCore/platform/cocoa/PasteboardCocoa.mm

    r238795 r243713  
    144144        // whether or not the pasteboard contains items that represent files. An example of when this gets tricky
    145145        // is differentiating between cases where the user is dragging a plain text file, versus selected text.
    146         // Some common signs that indicate a file drop as opposed to dropping inline data are:
    147         //
    148         //  1. Multiple items - the system generally does not give opportunities to flock multiple pieces of
    149         //     selected text.
    150         //  2. Preferred attachment presentation style - this means the source has explicitly marked the item
    151         //     as a file-like entity, as opposed to inline data.
    152         //  3. A suggested name - this means that the source has explicitly specified a potential file name for
    153         //     the item when dropped.
    154         //  4. The presence of any other declared non-text data in the same item indicates that the content being
    155         //     dropped can take on another non-text format, which could be a file.
    156         //
    157         // If none of these four conditions are satisfied, it's very likely that the content being dropped is just
     146        // Also, the presence of any other declared non-text data in the same item indicates that the content
     147        // being dropped can take on another non-text format, which could be a file.
     148        // If the item can't be treated as an attachment, it's very likely that the content being dropped is just
    158149        // an inline piece of text, with no files in the pasteboard (and therefore, no risk of leaking file paths
    159150        // to web content). In cases such as these, we should not suppress DataTransfer access.
    160151        auto items = platformStrategies()->pasteboardStrategy()->allPasteboardItemInfo(m_pasteboardName);
    161152        mayContainFilePaths = items.size() != 1 || notFound != items.findMatching([] (auto& item) {
    162             if (item.preferredPresentationStyle != PasteboardItemPresentationStyle::Unspecified)
    163                 return item.preferredPresentationStyle == PasteboardItemPresentationStyle::Attachment;
    164 
    165             return !item.suggestedFileName.isEmpty() || item.isNonTextType || item.containsFileURLAndFileUploadContent;
     153            return item.canBeTreatedAsAttachmentOrFile() || item.isNonTextType || item.containsFileURLAndFileUploadContent;
    166154        });
    167155    }
  • trunk/Source/WebCore/platform/ios/PasteboardIOS.mm

    r243695 r243713  
    271271        return false;
    272272
    273     if (info.preferredPresentationStyle == PasteboardItemPresentationStyle::Attachment)
    274         return true;
    275 
    276     return UTTypeConformsTo(contentTypeForHighestFidelityItem.createCFString().get(), kUTTypeVCard);
     273    if (info.preferredPresentationStyle == PasteboardItemPresentationStyle::Inline)
     274        return false;
     275
     276    return info.canBeTreatedAsAttachmentOrFile() || UTTypeConformsTo(contentTypeForHighestFidelityItem.createCFString().get(), kUTTypeVCard);
    277277}
    278278
  • trunk/Source/WebKit/ChangeLog

    r243712 r243713  
     12019-04-01  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Unable to copy and paste a PDF from Notes into Mail compose body
     4        https://bugs.webkit.org/show_bug.cgi?id=196442
     5        <rdar://problem/48573098>
     6
     7        Reviewed by Tim Horton.
     8
     9        Relax the -canPerformAction: logic in the case of pasting an attachment. Consider an NSItemProvider to possibly
     10        paste as an attachment if either it has a preferred presentation style of UIPreferredPresentationStyleAttachment
     11        or has a style of UIPreferredPresentationStyleUnspecified, but has a suggested file name.
     12
     13        This allows for the "Paste" action to be shown in the callout menu when copying and pasting a non-text file.
     14
     15        * UIProcess/ios/WKContentViewInteraction.mm:
     16        (-[WKContentView canPerformActionForWebView:withSender:]):
     17
    1182019-04-01  Tim Horton  <timothy_horton@apple.com>
    219
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r243630 r243713  
    28272827        if (editorState.isContentRichlyEditable && _webView.configuration._attachmentElementEnabled) {
    28282828            for (NSItemProvider *itemProvider in pasteboard.itemProviders) {
    2829                 if (itemProvider.preferredPresentationStyle == UIPreferredPresentationStyleAttachment && itemProvider.web_fileUploadContentTypes.count)
     2829                auto preferredPresentationStyle = itemProvider.preferredPresentationStyle;
     2830                if (preferredPresentationStyle == UIPreferredPresentationStyleInline)
     2831                    continue;
     2832
     2833                if (preferredPresentationStyle == UIPreferredPresentationStyleUnspecified && !itemProvider.suggestedName.length)
     2834                    continue;
     2835
     2836                if (itemProvider.web_fileUploadContentTypes.count)
    28302837                    return YES;
    28312838            }
  • trunk/Tools/ChangeLog

    r243712 r243713  
     12019-04-01  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Unable to copy and paste a PDF from Notes into Mail compose body
     4        https://bugs.webkit.org/show_bug.cgi?id=196442
     5        <rdar://problem/48573098>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add new API tests to exercise pasting CNContact and MKMapItem-backed item providers. Additionally, adjust an
     10        existing test that pastes a PDF file as an attachment to not require UIPreferredPresentationStyleAttachment
     11        to be specified on the item providers.
     12
     13        * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
     14        (TestWebKitAPI::mapItemForTesting):
     15        (TestWebKitAPI::contactItemForTesting):
     16
    1172019-04-01  Tim Horton  <timothy_horton@apple.com>
    218
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm

    r242934 r243713  
    18051805}
    18061806
    1807 TEST(WKAttachmentTestsIOS, InsertDroppedMapItemAsAttachment)
     1807static RetainPtr<NSItemProvider> mapItemForTesting()
    18081808{
    18091809    auto placemark = adoptNS([allocMKPlacemarkInstance() initWithCoordinate:CLLocationCoordinate2DMake(37.3327, -122.0053)]);
    18101810    auto mapItem = adoptNS([allocMKMapItemInstance() initWithPlacemark:placemark.get()]);
    1811     [mapItem setName:@"Apple Park"];
     1811    [mapItem setName:@"Apple Park.vcf"];
    18121812
    18131813    auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
    18141814    [itemProvider registerObject:mapItem.get() visibility:NSItemProviderRepresentationVisibilityAll];
    18151815    [itemProvider setSuggestedName:[mapItem name]];
    1816 
     1816    return itemProvider;
     1817}
     1818
     1819static RetainPtr<NSItemProvider> contactItemForTesting()
     1820{
     1821    auto contact = adoptNS([allocCNMutableContactInstance() init]);
     1822    [contact setGivenName:@"Foo"];
     1823    [contact setFamilyName:@"Bar"];
     1824
     1825    auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
     1826    [itemProvider registerObject:contact.get() visibility:NSItemProviderRepresentationVisibilityAll];
     1827    [itemProvider setSuggestedName:@"Foo Bar.vcf"];
     1828    return itemProvider;
     1829}
     1830
     1831TEST(WKAttachmentTestsIOS, InsertDroppedMapItemAsAttachment)
     1832{
     1833    auto itemProvider = mapItemForTesting();
    18171834    auto webView = webViewForTestingAttachments();
    18181835    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
     
    18331850TEST(WKAttachmentTestsIOS, InsertDroppedContactAsAttachment)
    18341851{
    1835     auto contact = adoptNS([allocCNMutableContactInstance() init]);
    1836     [contact setGivenName:@"Foo"];
    1837     [contact setFamilyName:@"Bar"];
    1838 
    1839     auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
    1840     [itemProvider registerObject:contact.get() visibility:NSItemProviderRepresentationVisibilityAll];
    1841     [itemProvider setSuggestedName:@"Foo Bar"];
    1842 
     1852    auto itemProvider = contactItemForTesting();
    18431853    auto webView = webViewForTestingAttachments();
    18441854    auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
     
    18551865}
    18561866
     1867TEST(WKAttachmentTestsIOS, InsertPastedContactAsAttachment)
     1868{
     1869    UIPasteboard.generalPasteboard.itemProviders = @[ contactItemForTesting().autorelease() ];
     1870    auto webView = webViewForTestingAttachments();
     1871    ObserveAttachmentUpdatesForScope observer(webView.get());
     1872    [webView paste:nil];
     1873
     1874    [webView expectElementCount:0 querySelector:@"a"];
     1875    EXPECT_WK_STREQ("Foo Bar.vcf", [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').title"]);
     1876    EXPECT_WK_STREQ("text/vcard", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
     1877    EXPECT_EQ(1U, observer.observer().inserted.count);
     1878    _WKAttachment *attachment = observer.observer().inserted.firstObject;
     1879    EXPECT_WK_STREQ("Foo Bar.vcf", attachment.info.name);
     1880    EXPECT_WK_STREQ("text/vcard", attachment.info.contentType);
     1881}
     1882
     1883TEST(WKAttachmentTestsIOS, InsertPastedMapItemAsAttachment)
     1884{
     1885    UIApplicationInitialize();
     1886    UIPasteboard.generalPasteboard.itemProviders = @[ mapItemForTesting().autorelease() ];
     1887    auto webView = webViewForTestingAttachments();
     1888    ObserveAttachmentUpdatesForScope observer(webView.get());
     1889    [webView paste:nil];
     1890
     1891    NSURL *pastedLinkURL = [NSURL URLWithString:[webView valueOfAttribute:@"href" forQuerySelector:@"a"]];
     1892    [webView expectElementTag:@"A" toComeBefore:@"ATTACHMENT"];
     1893    EXPECT_WK_STREQ("maps.apple.com", pastedLinkURL.host);
     1894    EXPECT_WK_STREQ("Apple Park.vcf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
     1895    EXPECT_WK_STREQ("text/vcard", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
     1896    EXPECT_EQ(1U, observer.observer().inserted.count);
     1897    _WKAttachment *attachment = observer.observer().inserted.firstObject;
     1898    EXPECT_WK_STREQ("Apple Park.vcf", attachment.info.name);
     1899    EXPECT_WK_STREQ("text/vcard", attachment.info.contentType);
     1900}
     1901
    18571902TEST(WKAttachmentTestsIOS, InsertPastedFilesAsAttachments)
    18581903{
    18591904    auto pdfItem = adoptNS([[NSItemProvider alloc] init]);
    18601905    [pdfItem setSuggestedName:@"doc"];
    1861     [pdfItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
    18621906    [pdfItem registerData:testPDFData() type:(__bridge NSString *)kUTTypePDF];
    18631907
    18641908    auto textItem = adoptNS([[NSItemProvider alloc] init]);
    18651909    [textItem setSuggestedName:@"hello"];
    1866     [textItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
    18671910    [textItem registerData:[@"helloworld" dataUsingEncoding:NSUTF8StringEncoding] type:(__bridge NSString *)kUTTypePlainText];
    18681911
Note: See TracChangeset for help on using the changeset viewer.