Use blob URL when pasting RTFD instead of overriding DocumentLoader
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Oct 2017 11:06:15 +0000 (11:06 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Oct 2017 11:06:15 +0000 (11:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177801
<rdar://problem/34542270>

Reviewed by Wenson Hsieh.

Source/WebCore:

Before this patch, pasting RTFD resulted in images and other subresources in RTFD are being placed
into the document using WebKit fake URL, and DocumentLoader was overridden to return the appropriate
data upon resource requests. This is bad because there is no mechanism for websites to access its content.

Like r222119 and r208451, this patch fixes thie problem by using a blob URL instead of a WebKit fake URL.
This patch also adds a Blob::create variant which takes a SharedBuffer.

API Tests: PasteRTFD

* editing/WebCorePasteboardFileReader.cpp:
(WebCore::WebCorePasteboardFileReader::readBuffer):
* editing/cocoa/WebContentReaderCocoa.mm:
(WebCore::DeferredLoadingScope): Extracted out of createFragmentAndAddResources for clarity.
(WebCore::DeferredLoadingScope::DeferredLoadingScope):
(WebCore::DeferredLoadingScope::~DeferredLoadingScope):
(WebCore::createFragmentAndAddResources): Instead of adding resources to document loader, replace each
URL in the document by a blob URL.
(WebCore::WebContentReader::readImage):
* editing/markup.cpp:
(WebCore::replaceSubresourceURLs): Added. A helper to replace each URL in the document fragment by a blob
URL created for each subresource. This won't work for iframes or srcset but that's okay for now since DOM
constructed from RTFD doesn't use either.
* editing/markup.h:
* fileapi/Blob.cpp:
(WebCore::Blob::Blob): Added a variant which takes a SharedBuffer.
* fileapi/Blob.h:
(WebCore::Blob::create): Ditto.

Source/WebKit:

Fixed the assertion failure when RTFD content is empty.

* UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:
(WebKit::WebPasteboardProxy::getPasteboardBufferForType):

Tools:

Added a regression test for an assertion failure when pasting an empty RTFD as well as a test for pasting
RTFD with an image, which should result in an image element with a blob URL.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm:
* TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm:
* TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@222839 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchBodyOwner.cpp
Source/WebCore/editing/WebCorePasteboardFileReader.cpp
Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm
Source/WebCore/editing/markup.cpp
Source/WebCore/editing/markup.h
Source/WebCore/fileapi/Blob.cpp
Source/WebCore/fileapi/Blob.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm
Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html [new file with mode: 0644]

index 25461e5..31c6c86 100644 (file)
@@ -1,3 +1,39 @@
+2017-10-04  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Use blob URL when pasting RTFD instead of overriding DocumentLoader
+        https://bugs.webkit.org/show_bug.cgi?id=177801
+        <rdar://problem/34542270>
+
+        Reviewed by Wenson Hsieh.
+
+        Before this patch, pasting RTFD resulted in images and other subresources in RTFD are being placed
+        into the document using WebKit fake URL, and DocumentLoader was overridden to return the appropriate
+        data upon resource requests. This is bad because there is no mechanism for websites to access its content.
+
+        Like r222119 and r208451, this patch fixes thie problem by using a blob URL instead of a WebKit fake URL.
+        This patch also adds a Blob::create variant which takes a SharedBuffer.
+
+        API Tests: PasteRTFD
+
+        * editing/WebCorePasteboardFileReader.cpp:
+        (WebCore::WebCorePasteboardFileReader::readBuffer):
+        * editing/cocoa/WebContentReaderCocoa.mm:
+        (WebCore::DeferredLoadingScope): Extracted out of createFragmentAndAddResources for clarity.
+        (WebCore::DeferredLoadingScope::DeferredLoadingScope):
+        (WebCore::DeferredLoadingScope::~DeferredLoadingScope):
+        (WebCore::createFragmentAndAddResources): Instead of adding resources to document loader, replace each
+        URL in the document by a blob URL.
+        (WebCore::WebContentReader::readImage):
+        * editing/markup.cpp:
+        (WebCore::replaceSubresourceURLs): Added. A helper to replace each URL in the document fragment by a blob
+        URL created for each subresource. This won't work for iframes or srcset but that's okay for now since DOM
+        constructed from RTFD doesn't use either.
+        * editing/markup.h:
+        * fileapi/Blob.cpp:
+        (WebCore::Blob::Blob): Added a variant which takes a SharedBuffer.
+        * fileapi/Blob.h:
+        (WebCore::Blob::create): Ditto.
+
 2017-10-04  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         REGRESSION(r222392): [WPE][GTK] Many forms tests are failing due to broken event timestamps
index 68b4f99..9de0211 100644 (file)
@@ -108,7 +108,7 @@ void FetchBodyOwner::arrayBuffer(Ref<DeferredPromise>&& promise)
 void FetchBodyOwner::blob(Ref<DeferredPromise>&& promise)
 {
     if (isBodyNullOrOpaque()) {
-        promise->resolve<IDLInterface<Blob>>(Blob::create({ }, Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType))));
+        promise->resolve<IDLInterface<Blob>>(Blob::create(Vector<uint8_t> { }, Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType))));
         return;
     }
     if (isDisturbedOrLocked()) {
index 2d0b64a..f68a00e 100644 (file)
@@ -40,9 +40,7 @@ void WebCorePasteboardFileReader::readFilename(const String& filename)
 
 void WebCorePasteboardFileReader::readBuffer(const String& filename, const String& type, Ref<SharedBuffer>&& buffer)
 {
-    Vector<uint8_t> data;
-    data.append(buffer->data(), buffer->size());
-    files.append(File::create(Blob::create(WTFMove(data), type), filename));
+    files.append(File::create(Blob::create(buffer.get(), type), filename));
 }
 
 }
index 25359ad..e73220c 100644 (file)
@@ -122,6 +122,38 @@ static FragmentAndResources createFragment(Frame& frame, NSAttributedString *str
 
 #endif
 
+class DeferredLoadingScope {
+public:
+    DeferredLoadingScope(Frame& frame)
+        : m_frame(frame)
+        , m_cachedResourceLoader(frame.document()->cachedResourceLoader())
+    {
+        if (!frame.page()->defersLoading()) {
+            frame.page()->setDefersLoading(true);
+            m_didEnabledDeferredLoading = true;
+        }
+
+        if (m_cachedResourceLoader->imagesEnabled()) {
+            m_cachedResourceLoader->setImagesEnabled(false);
+            m_didDisableImage = true;
+        }
+    }
+
+    ~DeferredLoadingScope()
+    {
+        if (m_didEnabledDeferredLoading)
+            m_cachedResourceLoader->setImagesEnabled(true);
+        if (m_didDisableImage)
+            m_frame->page()->setDefersLoading(false);
+    }
+
+private:
+    Ref<Frame> m_frame;
+    Ref<CachedResourceLoader> m_cachedResourceLoader;
+    bool m_didEnabledDeferredLoading { false };
+    bool m_didDisableImage { false };
+};
+
 RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttributedString *string)
 {
     if (!frame.page() || !frame.document())
@@ -131,25 +163,18 @@ RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttribute
     if (!document.isHTMLDocument() || !string)
         return nullptr;
 
-    bool wasDeferringCallbacks = frame.page()->defersLoading();
-    if (!wasDeferringCallbacks)
-        frame.page()->setDefersLoading(true);
-
-    auto& cachedResourceLoader = document.cachedResourceLoader();
-    bool wasImagesEnabled = cachedResourceLoader.imagesEnabled();
-    if (wasImagesEnabled)
-        cachedResourceLoader.setImagesEnabled(false);
-
+    DeferredLoadingScope scope(frame);
     auto fragmentAndResources = createFragment(frame, string);
-    if (auto* loader = frame.loader().documentLoader()) {
-        for (auto& resource : fragmentAndResources.resources)
-            loader->addArchiveResource(WTFMove(resource));
-    }
+    if (!fragmentAndResources.fragment)
+        return nullptr;
 
-    if (wasImagesEnabled)
-        cachedResourceLoader.setImagesEnabled(true);
-    if (!wasDeferringCallbacks)
-        frame.page()->setDefersLoading(false);
+    HashMap<AtomicString, AtomicString> blobURLMap;
+    for (const Ref<ArchiveResource>& subresource : fragmentAndResources.resources) {
+        auto blob = Blob::create(subresource->data(), subresource->mimeType());
+        String blobURL = DOMURL::createObjectURL(document, blob);
+        blobURLMap.set(subresource->url().string(), blobURL);
+    }
+    replaceSubresourceURLs(*fragmentAndResources.fragment, WTFMove(blobURLMap));
 
     return WTFMove(fragmentAndResources.fragment);
 }
@@ -225,9 +250,7 @@ bool WebContentReader::readPlainText(const String& text)
 
 bool WebContentReader::readImage(Ref<SharedBuffer>&& buffer, const String& type)
 {
-    Vector<uint8_t> data;
-    data.append(buffer->data(), buffer->size());
-    auto blob = Blob::create(WTFMove(data), type);
+    auto blob = Blob::create(buffer.get(), type);
     ASSERT(frame.document());
     auto& document = *frame.document();
     String blobURL = DOMURL::createObjectURL(document, blob);
index a5bde9f..e738656 100644 (file)
@@ -116,6 +116,25 @@ static void completeURLs(DocumentFragment* fragment, const String& baseURL)
     for (auto& change : changes)
         change.apply();
 }
+
+void replaceSubresourceURLs(Ref<DocumentFragment>&& fragment, HashMap<AtomicString, AtomicString>&& replacementMap)
+{
+    Vector<AttributeChange> changes;
+    for (auto& element : descendantsOfType<Element>(fragment)) {
+        if (!element.hasAttributes())
+            continue;
+        for (const Attribute& attribute : element.attributesIterator()) {
+            // FIXME: This won't work for srcset.
+            if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty()) {
+                auto replacement = replacementMap.get(attribute.value());
+                if (!replacement.isNull())
+                    changes.append({ &element, attribute.name(), replacement });
+            }
+        }
+    }
+    for (auto& change : changes)
+        change.apply();
+}
     
 class StyledMarkupAccumulator final : public MarkupAccumulator {
 public:
index ac94693..2c7b303 100644 (file)
@@ -29,6 +29,7 @@
 #include "FragmentScriptingPermission.h"
 #include "HTMLInterchange.h"
 #include <wtf/Forward.h>
+#include <wtf/HashMap.h>
 
 namespace WebCore {
 
@@ -44,6 +45,8 @@ class Node;
 class QualifiedName;
 class Range;
 
+void replaceSubresourceURLs(Ref<DocumentFragment>&&, HashMap<AtomicString, AtomicString>&&);
+
 enum EChildrenOnly { IncludeNode, ChildrenOnly };
 enum EAbsoluteURLs { DoNotResolveURLs, ResolveAllURLs, ResolveNonLocalURLs };
 enum EFragmentSerialization { HTMLFragmentSerialization, XMLFragmentSerialization };
index ef28d57..a7cd4e4 100644 (file)
@@ -36,6 +36,7 @@
 #include "BlobURL.h"
 #include "File.h"
 #include "ScriptExecutionContext.h"
+#include "SharedBuffer.h"
 #include "ThreadableBlobRegistry.h"
 #include <wtf/NeverDestroyed.h>
 #include <wtf/text/CString.h>
@@ -96,6 +97,19 @@ Blob::Blob(Vector<BlobPartVariant>&& blobPartVariants, const BlobPropertyBag& pr
     ThreadableBlobRegistry::registerBlobURL(m_internalURL, builder.finalize(), m_type);
 }
 
+Blob::Blob(const SharedBuffer& buffer, const String& contentType)
+    : m_type(contentType)
+    , m_size(buffer.size())
+{
+    Vector<uint8_t> data;
+    data.append(buffer.data(), buffer.size());
+
+    Vector<BlobPart> blobParts;
+    blobParts.append(BlobPart(WTFMove(data)));
+    m_internalURL = BlobURL::createInternalURL();
+    ThreadableBlobRegistry::registerBlobURL(m_internalURL, WTFMove(blobParts), contentType);
+}
+
 Blob::Blob(Vector<uint8_t>&& data, const String& contentType)
     : m_type(contentType)
     , m_size(data.size())
index 9f42423..caeddc8 100644 (file)
@@ -45,6 +45,7 @@ namespace WebCore {
 
 class Blob;
 class ScriptExecutionContext;
+class SharedBuffer;
 
 using BlobPartVariant = Variant<RefPtr<JSC::ArrayBufferView>, RefPtr<JSC::ArrayBuffer>, RefPtr<Blob>, String>;
 
@@ -60,6 +61,11 @@ public:
         return adoptRef(*new Blob(WTFMove(blobPartVariants), propertyBag));
     }
 
+    static Ref<Blob> create(const SharedBuffer& buffer, const String& contentType)
+    {
+        return adoptRef(*new Blob(buffer, contentType));
+    }
+
     static Ref<Blob> create(Vector<uint8_t>&& data, const String& contentType)
     {
         return adoptRef(*new Blob(WTFMove(data), contentType));
@@ -99,6 +105,7 @@ public:
 protected:
     Blob();
     Blob(Vector<BlobPartVariant>&&, const BlobPropertyBag&);
+    Blob(const SharedBuffer&, const String& contentType);
     Blob(Vector<uint8_t>&&, const String& contentType);
 
     enum ReferencingExistingBlobConstructor { referencingExistingBlobConstructor };
index 4bf7e8b..c147c61 100644 (file)
@@ -1,3 +1,16 @@
+2017-10-04  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Use blob URL when pasting RTFD instead of overriding DocumentLoader
+        https://bugs.webkit.org/show_bug.cgi?id=177801
+        <rdar://problem/34542270>
+
+        Reviewed by Wenson Hsieh.
+
+        Fixed the assertion failure when RTFD content is empty.
+
+        * UIProcess/Cocoa/WebPasteboardProxyCocoa.mm:
+        (WebKit::WebPasteboardProxy::getPasteboardBufferForType):
+
 2017-10-04  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         REGRESSION(r222392): [WPE][GTK] Many forms tests are failing due to broken event timestamps
index 4c95125..576d243 100644 (file)
@@ -75,6 +75,8 @@ void WebPasteboardProxy::getPasteboardBufferForType(const String& pasteboardName
     if (!buffer)
         return;
     size = buffer->size();
+    if (!size)
+        return;
     RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::allocate(size);
     if (!sharedMemoryBuffer)
         return;
index 34bf7eb..9cf4db4 100644 (file)
@@ -1,3 +1,19 @@
+2017-10-04  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Use blob URL when pasting RTFD instead of overriding DocumentLoader
+        https://bugs.webkit.org/show_bug.cgi?id=177801
+        <rdar://problem/34542270>
+
+        Reviewed by Wenson Hsieh.
+
+        Added a regression test for an assertion failure when pasting an empty RTFD as well as a test for pasting
+        RTFD with an image, which should result in an image element with a blob URL.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/PasteImage.mm:
+        * TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm:
+        * TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html:
+
 2017-10-03  Gustavo Noronha Silva  <gustavo.noronha@collabora.co.uk>
 
         [GLib] Let WebCore know of low power situations
index 52c286a..52c19df 100644 (file)
                9B26FCCA159D16DE00CC3765 /* HTMLFormCollectionNamedItem.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B26FCB4159D15E700CC3765 /* HTMLFormCollectionNamedItem.html */; };
                9B270FEE1DDC2C0B002D53F3 /* closed-shadow-tree-test.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B270FED1DDC25FD002D53F3 /* closed-shadow-tree-test.html */; };
                9B4F8FA7159D52DD002D9F94 /* HTMLCollectionNamedItem.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B4F8FA6159D52CA002D9F94 /* HTMLCollectionNamedItem.html */; };
+               9B7D740F1F8378770006C432 /* paste-rtfd.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B7D740E1F8377E60006C432 /* paste-rtfd.html */; };
                9BD4239A1E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BD423991E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm */; };
                9BD4239C1E04C01C00200395 /* chinese-character-with-image.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9BD4239B1E04BFD000200395 /* chinese-character-with-image.html */; };
                9BD6D3A21F7B218300BD4962 /* sunset-in-cupertino-100px.tiff in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9BD6D3A11F7B202100BD4962 /* sunset-in-cupertino-100px.tiff */; };
                9BD6D3A51F7B218300BD4962 /* sunset-in-cupertino-600px.jpg in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9BD6D39E1F7B201E00BD4962 /* sunset-in-cupertino-600px.jpg */; };
                9BD6D3A71F7B21DC00BD4962 /* paste-image.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9BD6D3A61F7B21CC00BD4962 /* paste-image.html */; };
                9BDCCD871F7D0B0700009A18 /* PasteImage.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BDCCD851F7D0B0700009A18 /* PasteImage.mm */; };
+               9BDD95581F83683600D20C60 /* PasteRTFD.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BDD95561F83683600D20C60 /* PasteRTFD.mm */; };
                9C64DC321D76198A004B598E /* YouTubePluginReplacement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9C64DC311D76198A004B598E /* YouTubePluginReplacement.cpp */; };
                A10F047E1E3AD29C00C95E19 /* NSFileManagerExtras.mm in Sources */ = {isa = PBXBuildFile; fileRef = A10F047C1E3AD29C00C95E19 /* NSFileManagerExtras.mm */; };
                A1146A8D1D2D7115000FE710 /* ContentFiltering.mm in Sources */ = {isa = PBXBuildFile; fileRef = A1146A8A1D2D704F000FE710 /* ContentFiltering.mm */; };
                                A57A34F216AF6B2B00C2501F /* PageVisibilityStateWithWindowChanges.html in Copy Resources */,
                                A1409AD91E7254D4004949D9 /* password-protected.pages in Copy Resources */,
                                9BD6D3A71F7B21DC00BD4962 /* paste-image.html in Copy Resources */,
+                               9B7D740F1F8378770006C432 /* paste-rtfd.html in Copy Resources */,
                                3FCC4FE81EC4E8CA0076E37C /* PictureInPictureDelegate.html in Copy Resources */,
                                F415086D1DA040C50044BE9B /* play-audio-on-click.html in Copy Resources */,
                                F41AB9A81EF4696B0083FA08 /* prevent-operation.html in Copy Resources */,
                9B4F8FA3159D52B1002D9F94 /* HTMLCollectionNamedItem.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HTMLCollectionNamedItem.mm; sourceTree = "<group>"; };
                9B4F8FA6159D52CA002D9F94 /* HTMLCollectionNamedItem.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = HTMLCollectionNamedItem.html; sourceTree = "<group>"; };
                9B79164F1BD89D0D00D50B8F /* FirstResponderScrollingPosition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FirstResponderScrollingPosition.mm; sourceTree = "<group>"; };
+               9B7D740E1F8377E60006C432 /* paste-rtfd.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "paste-rtfd.html"; sourceTree = "<group>"; };
                9BD423991E04BD9800200395 /* AttributedSubstringForProposedRangeWithImage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AttributedSubstringForProposedRangeWithImage.mm; sourceTree = "<group>"; };
                9BD4239B1E04BFD000200395 /* chinese-character-with-image.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "chinese-character-with-image.html"; sourceTree = "<group>"; };
                9BD6D39E1F7B201E00BD4962 /* sunset-in-cupertino-600px.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "sunset-in-cupertino-600px.jpg"; sourceTree = "<group>"; };
                9BD6D3A11F7B202100BD4962 /* sunset-in-cupertino-100px.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "sunset-in-cupertino-100px.tiff"; sourceTree = "<group>"; };
                9BD6D3A61F7B21CC00BD4962 /* paste-image.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "paste-image.html"; sourceTree = "<group>"; };
                9BDCCD851F7D0B0700009A18 /* PasteImage.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PasteImage.mm; sourceTree = "<group>"; };
+               9BDD95561F83683600D20C60 /* PasteRTFD.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PasteRTFD.mm; sourceTree = "<group>"; };
                9C64DC311D76198A004B598E /* YouTubePluginReplacement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YouTubePluginReplacement.cpp; sourceTree = "<group>"; };
                A10F047C1E3AD29C00C95E19 /* NSFileManagerExtras.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NSFileManagerExtras.mm; sourceTree = "<group>"; };
                A1146A8A1D2D704F000FE710 /* ContentFiltering.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ContentFiltering.mm; sourceTree = "<group>"; };
                                CEA6CF2219CCF5BD0064F5A7 /* OpenAndCloseWindow.mm */,
                                CEBCA12E1E3A660100C73293 /* OverrideContentSecurityPolicy.mm */,
                                9BDCCD851F7D0B0700009A18 /* PasteImage.mm */,
+                               9BDD95561F83683600D20C60 /* PasteRTFD.mm */,
                                3FCC4FE41EC4E8520076E37C /* PictureInPictureDelegate.mm */,
                                83BAEE8C1EF4625500DDE894 /* PluginLoadClientPolicies.mm */,
                                C95501BE19AD2FAF0049BE3E /* Preferences.mm */,
                                CEBCA1371E3A803400C73293 /* page-without-csp.html */,
                                A1409AD81E7254AC004949D9 /* password-protected.pages */,
                                9BD6D3A61F7B21CC00BD4962 /* paste-image.html */,
+                               9B7D740E1F8377E60006C432 /* paste-rtfd.html */,
                                3FCC4FE61EC4E87E0076E37C /* PictureInPictureDelegate.html */,
                                F415086C1DA040C10044BE9B /* play-audio-on-click.html */,
                                F41AB9941EF4692C0083FA08 /* prevent-operation.html */,
                                7C83E0511D0A641800FEBCF3 /* ParsedContentRange.cpp in Sources */,
                                7CCE7F0A1A411AE600447C4C /* PasteboardNotifications.mm in Sources */,
                                9BDCCD871F7D0B0700009A18 /* PasteImage.mm in Sources */,
+                               9BDD95581F83683600D20C60 /* PasteRTFD.mm in Sources */,
                                7C83E0531D0A643A00FEBCF3 /* PendingAPIRequestURL.cpp in Sources */,
                                3FCC4FE51EC4E8520076E37C /* PictureInPictureDelegate.mm in Sources */,
                                7CCE7EAF1A411A3800447C4C /* PlatformUtilities.cpp in Sources */,
index 05dac1d..4b7fc3c 100644 (file)
 
 #if WK_API_ENABLED && PLATFORM(COCOA)
 
-#import "AppKitSPI.h"
 #import "PlatformUtilities.h"
 #import "TestWKWebView.h"
-#import <wtf/BlockPtr.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/text/WTFString.h>
 
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm
new file mode 100644 (file)
index 0000000..2de5aa6
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if WK_API_ENABLED && PLATFORM(COCOA)
+
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import <wtf/RetainPtr.h>
+#import <wtf/text/WTFString.h>
+
+#if PLATFORM(IOS)
+#include <MobileCoreServices/MobileCoreServices.h>
+#endif
+
+@interface WKWebView ()
+- (void)paste:(id)sender;
+@end
+
+#if PLATFORM(MAC)
+void writeRTFDToPasteboard(NSData *data)
+{
+    [[NSPasteboard generalPasteboard] declareTypes:@[NSRTFDPboardType] owner:nil];
+    [[NSPasteboard generalPasteboard] setData:data forType:NSRTFDPboardType];
+}
+#else
+
+@interface NSAttributedString ()
+- (id)initWithRTF:(NSData *)data documentAttributes:(NSDictionary **)dict;
+- (id)initWithRTFD:(NSData *)data documentAttributes:(NSDictionary **)dict;
+- (NSData *)RTFFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict;
+- (NSData *)RTFDFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict;
+- (BOOL)containsAttachments;
+@end
+
+void writeRTFDToPasteboard(NSData *data)
+{
+    [[UIPasteboard generalPasteboard] setItems:@[@{ (NSString *)kUTTypeFlatRTFD : data}]];
+}
+#endif
+
+TEST(PasteRTFD, EmptyRTFD)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
+    [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><html><body><div id='editor' contenteditable></div></body></html>"];
+
+    writeRTFDToPasteboard([NSData data]);
+    [webView stringByEvaluatingJavaScript:@"document.getElementById('editor').focus()"];
+    [webView paste:nil];
+}
+
+TEST(PasteRTFD, ImageElementsUseBlobURL)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
+    [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
+
+    auto *pngData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sunset-in-cupertino-200px" ofType:@"png" inDirectory:@"TestWebKitAPI.resources"]];
+    auto attachment = adoptNS([[NSTextAttachment alloc] initWithData:pngData ofType:(NSString *)kUTTypePNG]);
+    NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment.get()];
+    NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }];
+
+    writeRTFDToPasteboard(RTFDData);
+    [webView paste:nil];
+
+    [webView waitForMessage:@"loaded"];
+    EXPECT_WK_STREQ("200", [webView stringByEvaluatingJavaScript:@"imageElement = document.querySelector('img'); imageElement.width"]);
+    EXPECT_WK_STREQ("blob:", [webView stringByEvaluatingJavaScript:@"url = new URL(imageElement.src).protocol"]);
+}
+
+#endif // WK_API_ENABLED && PLATFORM(MAC)
+
+
+
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html b/Tools/TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html
new file mode 100644 (file)
index 0000000..58b6098
--- /dev/null
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="editor" contenteditable></div>
+<script>
+const editor = document.getElementById('editor');
+editor.focus();
+
+editor.addEventListener('paste', (event) => {
+    setTimeout(() => {
+       let img = document.querySelector('img');
+       if (img.complete)
+           notifyLoaded();
+       else
+           img.onload = notifyLoaded;
+    }, 0);
+});
+
+function notifyLoaded()
+{
+    webkit.messageHandlers.testHandler.postMessage('loaded');
+}
+
+</script>
+</body>
+</html>