Title: [226093] trunk
Revision
226093
Author
wenson_hs...@apple.com
Date
2017-12-18 20:03:21 -0800 (Mon, 18 Dec 2017)

Log Message

[Attachment Support] Support representing pasted or dropped content using attachment elements
https://bugs.webkit.org/show_bug.cgi?id=180892
<rdar://problem/36064210>

Reviewed by Tim Horton.

Source/WebCore:

Support dropping and pasting attributed strings that contain NSTextAttachments. Teaches
replaceRichContentWithAttachmentsIfNecessary to replace object elements with attachments; see comments below for
more details.

Test: WKAttachmentTests.InsertPastedAttributedStringContainingMultipleAttachments

* editing/WebContentReader.h:

Add BlobReplacementInfo, which contains a map of blob URLs to replacement Blobs, as well as a map of blob URLs
to replaced subresource URLs.

(WebCore::BlobReplacementInfo::isEmpty const):
* editing/cocoa/EditorCocoa.mm:
(WebCore::Editor::replaceSelectionWithAttributedString):
* editing/cocoa/WebArchiveResourceFromNSAttributedString.h:
* editing/cocoa/WebArchiveResourceFromNSAttributedString.mm:

Implement -[WebArchiveResourceFromNSAttributedString MIMEType]. UIFoundation asks for -MIMEType in the process
of generating markup from NSTextAttachments; this currently causes the web process to crash on an unrecognized
selector.

Additionally, work around <rdar://problem/36074429>, a UIFoundation bug in which all but a few hard-coded file
extensions actually yield MIME types that are more specific than "application/octet-stream". This can safely be
removed once <rdar://problem/36074429> is addressed.

(-[WebArchiveResourceFromNSAttributedString MIMEType]):
* editing/cocoa/WebContentReaderCocoa.mm:
(WebCore::replaceRichContentWithAttachmentsIfNecessary):

Try to replace object elements with attachments, and also tweak the title of the attachment's File to use the
replaced subresource's filename if possible. Additionally, abstracts out information for replacing object or
image elements (formerly a pair of { File, Element }) into a separate struct, and add a AttachmentDisplayMode
parameter to determine whether the attachment should be presented in-line (in the case of images), or as an icon.

(WebCore::attributesForAttributedStringConversion):

Only exclude object elements from being generated from NSTextAttachments if the attachment element runtime
feature is disabled, or !ENABLE(ATTACHMENT_ELEMENT).

(WebCore::createFragmentAndAddResources):

Additionally keep track of a mapping from blob URL ="" replaced subresource URL. In all the places where we
previously only plumbed a map of blob URL ="" Blob, use a BlobReplacementInfo struct instead, which now includes
a map from blob URL ="" replaced URL.

(WebCore::sanitizeMarkupWithArchive):
(WebCore::WebContentReader::readWebArchive):
(WebCore::WebContentMarkupReader::readWebArchive):
(WebCore::WebContentReader::readRTFD):
(WebCore::WebContentMarkupReader::readRTFD):
(WebCore::WebContentReader::readRTF):
(WebCore::WebContentMarkupReader::readRTF):
(WebCore::WebContentReader::readImage):

Tools:

Adds a new API test to exercise pasting an attributed string with multiple attachments of different types.

* TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
(testZIPData):
(platformCopyRichTextWithMultipleAttachments):
(TestWebKitAPI::TEST):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (226092 => 226093)


--- trunk/Source/WebCore/ChangeLog	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Source/WebCore/ChangeLog	2017-12-19 04:03:21 UTC (rev 226093)
@@ -1,3 +1,65 @@
+2017-12-18  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [Attachment Support] Support representing pasted or dropped content using attachment elements
+        https://bugs.webkit.org/show_bug.cgi?id=180892
+        <rdar://problem/36064210>
+
+        Reviewed by Tim Horton.
+
+        Support dropping and pasting attributed strings that contain NSTextAttachments. Teaches
+        replaceRichContentWithAttachmentsIfNecessary to replace object elements with attachments; see comments below for
+        more details.
+
+        Test: WKAttachmentTests.InsertPastedAttributedStringContainingMultipleAttachments
+
+        * editing/WebContentReader.h:
+
+        Add BlobReplacementInfo, which contains a map of blob URLs to replacement Blobs, as well as a map of blob URLs
+        to replaced subresource URLs.
+
+        (WebCore::BlobReplacementInfo::isEmpty const):
+        * editing/cocoa/EditorCocoa.mm:
+        (WebCore::Editor::replaceSelectionWithAttributedString):
+        * editing/cocoa/WebArchiveResourceFromNSAttributedString.h:
+        * editing/cocoa/WebArchiveResourceFromNSAttributedString.mm:
+
+        Implement -[WebArchiveResourceFromNSAttributedString MIMEType]. UIFoundation asks for -MIMEType in the process
+        of generating markup from NSTextAttachments; this currently causes the web process to crash on an unrecognized
+        selector.
+
+        Additionally, work around <rdar://problem/36074429>, a UIFoundation bug in which all but a few hard-coded file
+        extensions actually yield MIME types that are more specific than "application/octet-stream". This can safely be
+        removed once <rdar://problem/36074429> is addressed.
+
+        (-[WebArchiveResourceFromNSAttributedString MIMEType]):
+        * editing/cocoa/WebContentReaderCocoa.mm:
+        (WebCore::replaceRichContentWithAttachmentsIfNecessary):
+
+        Try to replace object elements with attachments, and also tweak the title of the attachment's File to use the
+        replaced subresource's filename if possible. Additionally, abstracts out information for replacing object or
+        image elements (formerly a pair of { File, Element }) into a separate struct, and add a AttachmentDisplayMode
+        parameter to determine whether the attachment should be presented in-line (in the case of images), or as an icon.
+
+        (WebCore::attributesForAttributedStringConversion):
+
+        Only exclude object elements from being generated from NSTextAttachments if the attachment element runtime
+        feature is disabled, or !ENABLE(ATTACHMENT_ELEMENT).
+
+        (WebCore::createFragmentAndAddResources):
+
+        Additionally keep track of a mapping from blob URL ="" replaced subresource URL. In all the places where we
+        previously only plumbed a map of blob URL ="" Blob, use a BlobReplacementInfo struct instead, which now includes
+        a map from blob URL ="" replaced URL.
+
+        (WebCore::sanitizeMarkupWithArchive):
+        (WebCore::WebContentReader::readWebArchive):
+        (WebCore::WebContentMarkupReader::readWebArchive):
+        (WebCore::WebContentReader::readRTFD):
+        (WebCore::WebContentMarkupReader::readRTFD):
+        (WebCore::WebContentReader::readRTF):
+        (WebCore::WebContentMarkupReader::readRTF):
+        (WebCore::WebContentReader::readImage):
+
 2017-12-18  Youenn Fablet  <you...@apple.com>
 
         Service worker served response tainting should keep its tainting

Modified: trunk/Source/WebCore/editing/WebContentReader.h (226092 => 226093)


--- trunk/Source/WebCore/editing/WebContentReader.h	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Source/WebCore/editing/WebContentReader.h	2017-12-19 04:03:21 UTC (rev 226093)
@@ -106,8 +106,14 @@
     Vector<Ref<ArchiveResource>> resources;
 };
 
-void replaceRichContentWithAttachmentsIfNecessary(DocumentFragment&, HashMap<AtomicString, RefPtr<Blob>>&& urlToBlobMap);
-RefPtr<DocumentFragment> createFragmentAndAddResources(Frame&, NSAttributedString*, HashMap<AtomicString, RefPtr<Blob>>& urlToBlobMap);
+struct BlobReplacementInfo {
+    HashMap<AtomicString, RefPtr<Blob>> blobURLToBlobMap;
+    HashMap<AtomicString, AtomicString> blobURLToReplacedURLMap;
+    bool isEmpty() const { return blobURLToBlobMap.isEmpty(); }
+};
+
+void replaceRichContentWithAttachmentsIfNecessary(DocumentFragment&, BlobReplacementInfo&&);
+RefPtr<DocumentFragment> createFragmentAndAddResources(Frame&, NSAttributedString*, BlobReplacementInfo&);
 #endif
 
 }

Modified: trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm (226092 => 226093)


--- trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Source/WebCore/editing/cocoa/EditorCocoa.mm	2017-12-19 04:03:21 UTC (rev 226093)
@@ -222,9 +222,9 @@
         return;
 
     if (m_frame.selection().selection().isContentRichlyEditable()) {
-        HashMap<AtomicString, RefPtr<Blob>> urlToBlobMap;
-        if (auto fragment = createFragmentAndAddResources(m_frame, attributedString, urlToBlobMap)) {
-            replaceRichContentWithAttachmentsIfNecessary(*fragment, WTFMove(urlToBlobMap));
+        BlobReplacementInfo replacementInfo;
+        if (auto fragment = createFragmentAndAddResources(m_frame, attributedString, replacementInfo)) {
+            replaceRichContentWithAttachmentsIfNecessary(*fragment, WTFMove(replacementInfo));
             if (shouldInsertFragment(*fragment, selectedRange().get(), EditorInsertAction::Pasted))
                 pasteAsFragment(fragment.releaseNonNull(), false, false, mailBlockquoteHandling);
         }

Modified: trunk/Source/WebCore/editing/cocoa/WebArchiveResourceFromNSAttributedString.h (226092 => 226093)


--- trunk/Source/WebCore/editing/cocoa/WebArchiveResourceFromNSAttributedString.h	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Source/WebCore/editing/cocoa/WebArchiveResourceFromNSAttributedString.h	2017-12-19 04:03:21 UTC (rev 226093)
@@ -36,6 +36,7 @@
     RefPtr<WebCore::ArchiveResource> resource;
 }
 - (instancetype)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName;
+- (NSString *)MIMEType;
 - (NSURL *)URL;
 
 @end

Modified: trunk/Source/WebCore/editing/cocoa/WebArchiveResourceFromNSAttributedString.mm (226092 => 226093)


--- trunk/Source/WebCore/editing/cocoa/WebArchiveResourceFromNSAttributedString.mm	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Source/WebCore/editing/cocoa/WebArchiveResourceFromNSAttributedString.mm	2017-12-19 04:03:21 UTC (rev 226093)
@@ -27,6 +27,7 @@
 #import "WebArchiveResourceFromNSAttributedString.h"
 
 #import "ArchiveResource.h"
+#import "MIMETypeRegistry.h"
 
 using namespace WebCore;
 
@@ -42,6 +43,13 @@
         return nil;
     }
 
+    if ([MIMEType isEqualToString:@"application/octet-stream"]) {
+        // FIXME: This is a workaround for <rdar://problem/36074429>, and can be removed once that is fixed.
+        auto mimeTypeFromURL = MIMETypeRegistry::getMIMETypeForExtension(URL.pathExtension);
+        if (!mimeTypeFromURL.isEmpty())
+            MIMEType = mimeTypeFromURL;
+    }
+
     resource = ArchiveResource::create(SharedBuffer::create(adoptNS([data copy]).get()), URL, MIMEType, textEncodingName, frameName, { });
     if (!resource) {
         [self release];
@@ -51,6 +59,11 @@
     return self;
 }
 
+- (NSString *)MIMEType
+{
+    return resource->mimeType();
+}
+
 - (NSURL *)URL
 {
     return resource->url();

Modified: trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm (226092 => 226093)


--- trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm	2017-12-19 04:03:21 UTC (rev 226093)
@@ -42,6 +42,7 @@
 #import "HTMLBodyElement.h"
 #import "HTMLIFrameElement.h"
 #import "HTMLImageElement.h"
+#import "HTMLObjectElement.h"
 #import "LegacyWebArchive.h"
 #import "MainFrame.h"
 #import "Page.h"
@@ -50,6 +51,7 @@
 #import "Settings.h"
 #import "SocketProvider.h"
 #import "TypedElementDescendantIterator.h"
+#import "URLParser.h"
 #import "WebArchiveResourceFromNSAttributedString.h"
 #import "WebArchiveResourceWebResourceHandler.h"
 #import "WebNSAttributedStringExtras.h"
@@ -76,37 +78,58 @@
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
-void replaceRichContentWithAttachmentsIfNecessary(DocumentFragment& fragment, HashMap<AtomicString, RefPtr<Blob>>&& urlToBlobMap)
+struct AttachmentReplacementInfo {
+    AttachmentDisplayMode displayMode;
+    Ref<File> file;
+    Ref<Element> elementToReplace;
+};
+
+void replaceRichContentWithAttachmentsIfNecessary(DocumentFragment& fragment, BlobReplacementInfo&& replacementInfo)
 {
-    if (!RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled() || urlToBlobMap.isEmpty())
+    if (!RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled() || replacementInfo.isEmpty())
         return;
 
     Vector<Ref<Element>> elementsToRemove;
-    Vector<std::pair<Ref<File>, Ref<Element>>> filesForElementsToReplace;
+    Vector<AttachmentReplacementInfo> attachmentReplacementInfo;
     for (auto& image : descendantsOfType<HTMLImageElement>(fragment)) {
         auto url = ""
+        if (url.isEmpty())
+            continue;
 
+        auto blob = replacementInfo.blobURLToBlobMap.get(url);
+        if (!blob)
+            continue;
+
+        auto title = URLParser { replacementInfo.blobURLToReplacedURLMap.get(url) }.result().lastPathComponent();
+        if (title.isEmpty())
+            title = AtomicString("media");
+
+        attachmentReplacementInfo.append({ AttachmentDisplayMode::InPlace, File::create(*blob, title), image });
+    }
+
+    for (auto& object : descendantsOfType<HTMLObjectElement>(fragment)) {
+        auto url = ""
         if (url.isEmpty()) {
-            elementsToRemove.append(image);
+            elementsToRemove.append(object);
             continue;
         }
 
-        auto blob = urlToBlobMap.get(url);
+        auto blob = replacementInfo.blobURLToBlobMap.get(url);
         if (!blob) {
-            elementsToRemove.append(image);
+            elementsToRemove.append(object);
             continue;
         }
 
-        auto title = image.attributeWithoutSynchronization(HTMLNames::titleAttr);
+        auto title = URLParser { replacementInfo.blobURLToReplacedURLMap.get(url) }.result().lastPathComponent();
         if (title.isEmpty())
-            title = AtomicString("media");
+            title = AtomicString("file");
 
-        filesForElementsToReplace.append({ File::create(*blob, title), image });
+        attachmentReplacementInfo.append({ AttachmentDisplayMode::AsIcon, File::create(*blob, title), object });
     }
 
-    for (auto& fileAndElement : filesForElementsToReplace) {
-        auto& file = fileAndElement.first;
-        auto& elementToReplace = fileAndElement.second;
+    for (auto& info : attachmentReplacementInfo) {
+        auto file = WTFMove(info.file);
+        auto elementToReplace = WTFMove(info.elementToReplace);
         auto parent = makeRefPtr(elementToReplace->parentNode());
         if (!parent)
             continue;
@@ -114,7 +137,7 @@
         auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, fragment.document());
         attachment->setUniqueIdentifier(createCanonicalUUIDString());
         attachment->setFile(WTFMove(file), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
-        attachment->updateDisplayMode(AttachmentDisplayMode::InPlace);
+        attachment->updateDisplayMode(info.displayMode);
         parent->replaceChild(attachment, elementToReplace);
     }
 
@@ -135,7 +158,7 @@
 static NSDictionary *attributesForAttributedStringConversion()
 {
     // This function needs to be kept in sync with identically named one in WebKitLegacy, which is used on older OS versions.
-    RetainPtr<NSArray> excludedElements = adoptNS([[NSArray alloc] initWithObjects:
+    RetainPtr<NSMutableArray> excludedElements = adoptNS([[NSMutableArray alloc] initWithObjects:
         // Omit style since we want style to be inline so the fragment can be easily inserted.
         @"style",
         // Omit xml so the result is not XHTML.
@@ -144,9 +167,17 @@
         @"doctype", @"html", @"head", @"body",
         // Omit deprecated tags.
         @"applet", @"basefont", @"center", @"dir", @"font", @"menu", @"s", @"strike", @"u",
+#if !ENABLE(ATTACHMENT_ELEMENT)
         // Omit object so no file attachments are part of the fragment.
-        @"object", nil]);
+        @"object",
+#endif
+        nil]);
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+    if (!RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled())
+        [excludedElements addObject:@"object"];
+#endif
+
 #if PLATFORM(IOS)
     static NSString * const NSExcludedElementsDocumentAttribute = @"ExcludedElements";
 #endif
@@ -220,7 +251,7 @@
     bool m_didDisableImage { false };
 };
 
-RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttributedString *string, HashMap<AtomicString, RefPtr<Blob>>& urlToBlobMap)
+RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttributedString *string, BlobReplacementInfo& replacementInfo)
 {
     if (!frame.page() || !frame.document())
         return nullptr;
@@ -239,7 +270,8 @@
         auto blob = Blob::create(subresource->data(), subresource->mimeType());
         String blobURL = DOMURL::createObjectURL(document, blob);
         blobURLMap.set(subresource->url().string(), blobURL);
-        urlToBlobMap.set(blobURL, WTFMove(blob));
+        replacementInfo.blobURLToBlobMap.set(blobURL, WTFMove(blob));
+        replacementInfo.blobURLToReplacedURLMap.set(blobURL, subresource->url().string());
     }
     replaceSubresourceURLs(*fragmentAndResources.fragment, WTFMove(blobURLMap));
 
@@ -269,7 +301,7 @@
     return MarkupAndArchive { String::fromUTF8(mainResource->data().data(), mainResource->data().size()), mainResource.releaseNonNull(), archive.releaseNonNull() };
 }
 
-static String sanitizeMarkupWithArchive(Document& destinationDocument, MarkupAndArchive& markupAndArchive, const std::function<bool(const String)>& canShowMIMETypeAsHTML, HashMap<AtomicString, RefPtr<Blob>>& urlToBlobMap)
+static String sanitizeMarkupWithArchive(Document& destinationDocument, MarkupAndArchive& markupAndArchive, const std::function<bool(const String)>& canShowMIMETypeAsHTML, BlobReplacementInfo& replacementInfo)
 {
     auto page = createPageForSanitizingWebContent();
     Document* stagingDocument = page->mainFrame().document();
@@ -281,7 +313,8 @@
         auto blob = Blob::create(subresource->data(), subresource->mimeType());
         String blobURL = DOMURL::createObjectURL(destinationDocument, blob);
         blobURLMap.set(subresource->url().string(), blobURL);
-        urlToBlobMap.set(blobURL, WTFMove(blob));
+        replacementInfo.blobURLToBlobMap.set(blobURL, WTFMove(blob));
+        replacementInfo.blobURLToReplacedURLMap.set(blobURL, subresource->url().string());
     }
 
     auto contentOrigin = SecurityOrigin::create(markupAndArchive.mainResource->url());
@@ -297,7 +330,7 @@
         auto subframeURL = subframeMainResource->url();
         MarkupAndArchive subframeContent = { String::fromUTF8(subframeMainResource->data().data(), subframeMainResource->data().size()),
             subframeMainResource.releaseNonNull(), subframeArchive.copyRef() };
-        auto subframeMarkup = sanitizeMarkupWithArchive(destinationDocument, subframeContent, canShowMIMETypeAsHTML, urlToBlobMap);
+        auto subframeMarkup = sanitizeMarkupWithArchive(destinationDocument, subframeContent, canShowMIMETypeAsHTML, replacementInfo);
 
         CString utf8 = subframeMarkup.utf8();
         Vector<uint8_t> blobBuffer;
@@ -307,7 +340,8 @@
 
         String subframeBlobURL = DOMURL::createObjectURL(destinationDocument, blob);
         blobURLMap.set(subframeURL.string(), subframeBlobURL);
-        urlToBlobMap.set(subframeBlobURL, WTFMove(blob));
+        replacementInfo.blobURLToBlobMap.set(subframeBlobURL, WTFMove(blob));
+        replacementInfo.blobURLToReplacedURLMap.set(subframeBlobURL, subframeURL.string());
     }
 
     replaceSubresourceURLs(fragment.get(), WTFMove(blobURLMap));
@@ -338,16 +372,16 @@
         return true;
     }
 
-    HashMap<AtomicString, RefPtr<Blob>> urlToBlobMap;
+    BlobReplacementInfo replacementInfo;
     String sanitizedMarkup = sanitizeMarkupWithArchive(*frame.document(), *result, [&] (const String& type) {
         return frame.loader().client().canShowMIMETypeAsHTML(type);
-    }, urlToBlobMap);
+    }, replacementInfo);
     fragment = createFragmentFromMarkup(*frame.document(), sanitizedMarkup, blankURL(), DisallowScriptingAndPluginContent);
 
     if (!fragment)
         return false;
 
-    replaceRichContentWithAttachmentsIfNecessary(*fragment, WTFMove(urlToBlobMap));
+    replaceRichContentWithAttachmentsIfNecessary(*fragment, WTFMove(replacementInfo));
     return true;
 }
 
@@ -367,10 +401,10 @@
         return true;
     }
 
-    HashMap<AtomicString, RefPtr<Blob>> urlToBlobMap;
+    BlobReplacementInfo replacementInfo;
     markup = sanitizeMarkupWithArchive(*frame.document(), *result, [&] (const String& type) {
         return frame.loader().client().canShowMIMETypeAsHTML(type);
-    }, urlToBlobMap);
+    }, replacementInfo);
 
     return true;
 }
@@ -420,12 +454,12 @@
 
 static RefPtr<DocumentFragment> createFragmentFromAttributedString(Frame& frame, NSAttributedString *string)
 {
-    HashMap<AtomicString, RefPtr<Blob>> urlToBlobMap;
-    auto fragment = createFragmentAndAddResources(frame, string, urlToBlobMap);
+    BlobReplacementInfo replacementInfo;
+    auto fragment = createFragmentAndAddResources(frame, string, replacementInfo);
     if (!fragment)
         return nullptr;
 
-    replaceRichContentWithAttachmentsIfNecessary(*fragment, WTFMove(urlToBlobMap));
+    replaceRichContentWithAttachmentsIfNecessary(*fragment, WTFMove(replacementInfo));
     return fragment;
 }
 
@@ -504,7 +538,7 @@
     if (!fragment)
         return false;
 
-    replaceRichContentWithAttachmentsIfNecessary(*fragment, {{ blobURL, WTFMove(blob) }});
+    replaceRichContentWithAttachmentsIfNecessary(*fragment, {{{ blobURL, WTFMove(blob) }}, { }});
     return true;
 }
 

Modified: trunk/Tools/ChangeLog (226092 => 226093)


--- trunk/Tools/ChangeLog	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Tools/ChangeLog	2017-12-19 04:03:21 UTC (rev 226093)
@@ -1,3 +1,18 @@
+2017-12-18  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [Attachment Support] Support representing pasted or dropped content using attachment elements
+        https://bugs.webkit.org/show_bug.cgi?id=180892
+        <rdar://problem/36064210>
+
+        Reviewed by Tim Horton.
+
+        Adds a new API test to exercise pasting an attributed string with multiple attachments of different types.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+        (testZIPData):
+        (platformCopyRichTextWithMultipleAttachments):
+        (TestWebKitAPI::TEST):
+
 2017-12-18  Brady Eidson  <beid...@apple.com>
 
         Add ability to API test Service Workers via a custom protocol.

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm (226092 => 226093)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm	2017-12-19 02:15:24 UTC (rev 226092)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm	2017-12-19 04:03:21 UTC (rev 226093)
@@ -136,6 +136,12 @@
     return webView;
 }
 
+static NSData *testZIPData()
+{
+    NSURL *zipFileURL = [[NSBundle mainBundle] URLForResource:@"compressed-files" withExtension:@"zip" subdirectory:@"TestWebKitAPI.resources"];
+    return [NSData dataWithContentsOfURL:zipFileURL];
+}
+
 static NSData *testHTMLData()
 {
     return [@"<a href=''>This is some HTML data</a>" dataUsingEncoding:NSUTF8StringEncoding];
@@ -308,6 +314,28 @@
 
 #pragma mark - Platform testing helper functions
 
+void platformCopyRichTextWithMultipleAttachments()
+{
+    auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(NSString *)kUTTypePNG]);
+    auto pdf = adoptNS([[NSTextAttachment alloc] initWithData:testPDFData() ofType:(NSString *)kUTTypePDF]);
+    auto zip = adoptNS([[NSTextAttachment alloc] initWithData:testZIPData() ofType:(NSString *)kUTTypeZipArchive]);
+
+    auto richText = adoptNS([[NSMutableAttributedString alloc] init]);
+    [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:image.get()]];
+    [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:pdf.get()]];
+    [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:zip.get()]];
+
+#if PLATFORM(MAC)
+    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+    [pasteboard clearContents];
+    [pasteboard writeObjects:@[ richText.get() ]];
+#elif PLATFORM(IOS)
+    auto item = adoptNS([[NSItemProvider alloc] init]);
+    [item registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
+    [UIPasteboard generalPasteboard].itemProviders = @[ item.get() ];
+#endif
+}
+
 void platformCopyRichTextWithImage()
 {
     auto richText = adoptNS([[NSMutableAttributedString alloc] init]);
@@ -800,6 +828,48 @@
     }
 }
 
+TEST(WKAttachmentTests, InsertPastedAttributedStringContainingMultipleAttachments)
+{
+    platformCopyRichTextWithMultipleAttachments();
+
+    RetainPtr<_WKAttachment> imageAttachment;
+    RetainPtr<_WKAttachment> zipAttachment;
+    RetainPtr<_WKAttachment> pdfAttachment;
+    auto webView = webViewForTestingAttachments();
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
+        EXPECT_EQ(0U, observer.observer().removed.count);
+        EXPECT_EQ(3U, observer.observer().inserted.count);
+        for (_WKAttachment *attachment in observer.observer().inserted) {
+            NSData *data = "" synchronouslyRequestData:nil];
+            if ([data isEqualToData:testZIPData()])
+                zipAttachment = attachment;
+            else if ([data isEqualToData:testPDFData()])
+                pdfAttachment = attachment;
+            else if ([data isEqualToData:testImageData()])
+                imageAttachment = attachment;
+        }
+    }
+
+    EXPECT_TRUE(zipAttachment && imageAttachment && pdfAttachment);
+    EXPECT_EQ(3, [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"].integerValue);
+    EXPECT_WK_STREQ("image/png", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
+    EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
+    EXPECT_WK_STREQ("application/zip", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[2].getAttribute('type')"]);
+
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
+        [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
+        NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
+        EXPECT_EQ(3U, removedAttachments.count);
+        EXPECT_TRUE([removedAttachments containsObject:zipAttachment.get()]);
+        EXPECT_TRUE([removedAttachments containsObject:imageAttachment.get()]);
+        EXPECT_TRUE([removedAttachments containsObject:pdfAttachment.get()]);
+    }
+}
+
 #pragma mark - Platform-specific tests
 
 #if PLATFORM(MAC)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to