[macOS] Copying a table from the Numbers app and pasting into iCloud Numbers fails
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Mar 2018 11:33:20 +0000 (11:33 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Mar 2018 11:33:20 +0000 (11:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=183485
<rdar://problem/38041984>

Reviewed by Ryosuke Niwa.

Source/WebCore:

After r222656, WebKit now treats raw image data on the pasteboard as files for the purposes of computing
DataTransfer.files and DataTransfer.types. However, this is combined with existing policies that suppress
DataTransfer.getData and DataTransfer.setData when the pasteboard contains files (generalized to copy/paste in
r222688). This means we now don't allow web pages to access "text/plain" in the case where the user copies part
of a table from the native Numbers app since Numbers additionally writes a snapshot of the table to the platform
pasteboard.

This restriction on getData/setData was intended to prevent web pages from extracting users' file paths when
pasting or dropping, so it doesn't make sense to enforce this restriction even when there is only in-memory
image data on the pasteboard. To fix this bug, we make Pasteboard::fileContentState() differentiate between
cases where there are (real) files on the pasteboard, and cases where we've fallen back to treating image data
as files.

Rebaselined existing LayoutTests to match new behavior.
Also covered by 4 new API tests:
    - PasteMixedContent.ImageDataAndPlainText
    - PasteMixedContent.ImageDataAndPlainTextAndURL
    - PasteMixedContent.ImageDataAndPlainTextAndURLAndHTML
    - UIPasteboardTests.DataTransferGetDataWhenPastingImageAndText

* dom/DataTransfer.cpp:
(WebCore::DataTransfer::shouldSuppressGetAndSetDataToAvoidExposingFilePaths const):

If custom pasteboard data is enabled, suppress getData and setData if and only if we might actually expose file
paths (see Pasteboard::fileContentState).

(WebCore::DataTransfer::types const):

Only allow "text/html" or "text/uri-list" in the case where there are actual files in the pasteboard. If there's
only image data, add all of the DOM-safe types back into the list of types.

* platform/Pasteboard.h:
* platform/StaticPasteboard.h:

Add an enum type to represent the result of Pasteboard::fileContentState.
-   NoFileOrImageData indicates that there was nothing on the pasteboard that could be considered a file
    from the point of view of the page.
-   InMemoryImage indicates that there are no files on the pasteboard, but there is image data that we consider
    to be files, exposed via DataTransfer API.
-   MayContainFilePaths indicates that there might be file paths on the pasteboard. This means that the source
    has either written file paths to the pasteboard (for example, through NSFilenamesPboardType) or the source
    has written image data along with a URL type of some sort that does not match one of the allowed URL schemes
    that are safe to expose (currently, these are http-family, data, or blob).

* platform/cocoa/PasteboardCocoa.mm:
(WebCore::Pasteboard::fileContentState):

Refactor to return one of the three enum types described above.

(WebCore::Pasteboard::containsFiles): Deleted.
* platform/gtk/PasteboardGtk.cpp:
(WebCore::Pasteboard::fileContentState):
(WebCore::Pasteboard::containsFiles): Deleted.
* platform/win/PasteboardWin.cpp:
(WebCore::Pasteboard::fileContentState):
(WebCore::Pasteboard::containsFiles): Deleted.
* platform/wpe/PasteboardWPE.cpp:
(WebCore::Pasteboard::fileContentState):
(WebCore::Pasteboard::containsFiles): Deleted.

Adjust for Pasteboard::fileContentState() tweaks.

Tools:

Add new API tests to cover scenarios in which we paste image data alongside text data.

* TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:
(TestWebKitAPI::TEST):

LayoutTests:

Rebaseline some pasteboard-related layout tests, in which we now expose text/plain alongside files that were
written to the DataTransfer by the page itself.

* editing/pasteboard/data-transfer-item-list-add-file-multiple-times-expected.txt:
* editing/pasteboard/data-transfer-item-list-add-file-on-copy-expected.txt:
* editing/pasteboard/data-transfer-item-list-add-file-on-drag-expected.txt:

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

15 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/pasteboard/data-transfer-item-list-add-file-multiple-times-expected.txt
LayoutTests/editing/pasteboard/data-transfer-item-list-add-file-on-copy-expected.txt
LayoutTests/editing/pasteboard/data-transfer-item-list-add-file-on-drag-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/dom/DataTransfer.cpp
Source/WebCore/platform/Pasteboard.h
Source/WebCore/platform/StaticPasteboard.h
Source/WebCore/platform/cocoa/PasteboardCocoa.mm
Source/WebCore/platform/gtk/PasteboardGtk.cpp
Source/WebCore/platform/win/PasteboardWin.cpp
Source/WebCore/platform/wpe/PasteboardWPE.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm
Tools/TestWebKitAPI/Tests/ios/UIPasteboardTests.mm

index 02ffbfc..7cfdb65 100644 (file)
@@ -1,3 +1,18 @@
+2018-03-10  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [macOS] Copying a table from the Numbers app and pasting into iCloud Numbers fails
+        https://bugs.webkit.org/show_bug.cgi?id=183485
+        <rdar://problem/38041984>
+
+        Reviewed by Ryosuke Niwa.
+
+        Rebaseline some pasteboard-related layout tests, in which we now expose text/plain alongside files that were
+        written to the DataTransfer by the page itself.
+
+        * editing/pasteboard/data-transfer-item-list-add-file-multiple-times-expected.txt:
+        * editing/pasteboard/data-transfer-item-list-add-file-on-copy-expected.txt:
+        * editing/pasteboard/data-transfer-item-list-add-file-on-drag-expected.txt:
+
 2018-03-09  Chris Dumez  <cdumez@apple.com>
 
         inspector/page/frameScheduledNavigation.html has different output with async policy delegates
index 6ef1038..03a4b6e 100644 (file)
@@ -10,6 +10,7 @@ operation performed at each step.
 {
     "data": {
         "Files": "",
+        "text/plain": "plain text string",
         "text/uri-list": "https://webkit.org"
     },
     "items": [
@@ -88,6 +89,7 @@ operation performed at each step.
 {
     "data": {
         "Files": "",
+        "text/plain": "plain text string",
         "text/uri-list": "https://webkit.org"
     },
     "items": [
@@ -153,6 +155,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "plain text string",
         "text/uri-list": "https://webkit.org"
     },
     "items": [
@@ -204,6 +207,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "plain text string",
         "text/uri-list": "https://webkit.org"
     },
     "items": [
@@ -248,7 +252,8 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
-        "text/html": "<strong>some styled text</strong>"
+        "text/html": "<strong>some styled text</strong>",
+        "text/plain": "some plain text"
     },
     "items": [
         {
index 29bc949..2707450 100644 (file)
@@ -24,7 +24,8 @@ operation performed at each step.
 2. After adding a file of custom type
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
@@ -54,7 +55,8 @@ operation performed at each step.
 3. After adding the first plain text file
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
@@ -98,7 +100,8 @@ operation performed at each step.
 4. After removing the last file
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
@@ -130,6 +133,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "hello world",
         "text/html": "<a>goodbye world</a>"
     },
     "items": [
@@ -166,6 +170,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "hello world",
         "text/html": "<a>goodbye world</a>"
     },
     "items": [
@@ -216,6 +221,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "hello world",
         "text/html": "<a>goodbye world</a>"
     },
     "items": [
@@ -252,7 +258,8 @@ removedItem.getAsFile() should be null: null
 8. After removing the HTML string
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
index 07c06cc..155899d 100644 (file)
@@ -24,7 +24,8 @@ operation performed at each step.
 2. After adding a file of custom type
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
@@ -54,7 +55,8 @@ operation performed at each step.
 3. After adding the first plain text file
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
@@ -98,7 +100,8 @@ operation performed at each step.
 4. After removing the last file
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
@@ -130,6 +133,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "hello world",
         "text/html": "<a>goodbye world</a>"
     },
     "items": [
@@ -166,6 +170,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "hello world",
         "text/html": "<a>goodbye world</a>"
     },
     "items": [
@@ -216,6 +221,7 @@ removedItem.getAsFile() should be null: null
 {
     "data": {
         "Files": "",
+        "text/plain": "hello world",
         "text/html": "<a>goodbye world</a>"
     },
     "items": [
@@ -252,7 +258,8 @@ removedItem.getAsFile() should be null: null
 8. After removing the HTML string
 {
     "data": {
-        "Files": ""
+        "Files": "",
+        "text/plain": "hello world"
     },
     "items": [
         {
index 0a24f6a..5b46b66 100644 (file)
@@ -1,3 +1,73 @@
+2018-03-10  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [macOS] Copying a table from the Numbers app and pasting into iCloud Numbers fails
+        https://bugs.webkit.org/show_bug.cgi?id=183485
+        <rdar://problem/38041984>
+
+        Reviewed by Ryosuke Niwa.
+
+        After r222656, WebKit now treats raw image data on the pasteboard as files for the purposes of computing
+        DataTransfer.files and DataTransfer.types. However, this is combined with existing policies that suppress
+        DataTransfer.getData and DataTransfer.setData when the pasteboard contains files (generalized to copy/paste in
+        r222688). This means we now don't allow web pages to access "text/plain" in the case where the user copies part
+        of a table from the native Numbers app since Numbers additionally writes a snapshot of the table to the platform
+        pasteboard.
+
+        This restriction on getData/setData was intended to prevent web pages from extracting users' file paths when
+        pasting or dropping, so it doesn't make sense to enforce this restriction even when there is only in-memory
+        image data on the pasteboard. To fix this bug, we make Pasteboard::fileContentState() differentiate between
+        cases where there are (real) files on the pasteboard, and cases where we've fallen back to treating image data
+        as files.
+
+        Rebaselined existing LayoutTests to match new behavior.
+        Also covered by 4 new API tests:
+            - PasteMixedContent.ImageDataAndPlainText
+            - PasteMixedContent.ImageDataAndPlainTextAndURL
+            - PasteMixedContent.ImageDataAndPlainTextAndURLAndHTML
+            - UIPasteboardTests.DataTransferGetDataWhenPastingImageAndText
+
+        * dom/DataTransfer.cpp:
+        (WebCore::DataTransfer::shouldSuppressGetAndSetDataToAvoidExposingFilePaths const):
+
+        If custom pasteboard data is enabled, suppress getData and setData if and only if we might actually expose file
+        paths (see Pasteboard::fileContentState).
+
+        (WebCore::DataTransfer::types const):
+
+        Only allow "text/html" or "text/uri-list" in the case where there are actual files in the pasteboard. If there's
+        only image data, add all of the DOM-safe types back into the list of types.
+
+        * platform/Pasteboard.h:
+        * platform/StaticPasteboard.h:
+
+        Add an enum type to represent the result of Pasteboard::fileContentState.
+        -   NoFileOrImageData indicates that there was nothing on the pasteboard that could be considered a file
+            from the point of view of the page.
+        -   InMemoryImage indicates that there are no files on the pasteboard, but there is image data that we consider
+            to be files, exposed via DataTransfer API.
+        -   MayContainFilePaths indicates that there might be file paths on the pasteboard. This means that the source
+            has either written file paths to the pasteboard (for example, through NSFilenamesPboardType) or the source
+            has written image data along with a URL type of some sort that does not match one of the allowed URL schemes
+            that are safe to expose (currently, these are http-family, data, or blob).
+
+        * platform/cocoa/PasteboardCocoa.mm:
+        (WebCore::Pasteboard::fileContentState):
+
+        Refactor to return one of the three enum types described above.
+
+        (WebCore::Pasteboard::containsFiles): Deleted.
+        * platform/gtk/PasteboardGtk.cpp:
+        (WebCore::Pasteboard::fileContentState):
+        (WebCore::Pasteboard::containsFiles): Deleted.
+        * platform/win/PasteboardWin.cpp:
+        (WebCore::Pasteboard::fileContentState):
+        (WebCore::Pasteboard::containsFiles): Deleted.
+        * platform/wpe/PasteboardWPE.cpp:
+        (WebCore::Pasteboard::fileContentState):
+        (WebCore::Pasteboard::containsFiles): Deleted.
+
+        Adjust for Pasteboard::fileContentState() tweaks.
+
 2018-03-09  Chris Fleizach  <cfleizach@apple.com>
 
         AX: WebKit seems to be running spell checker even on non-editable content text
index 3e71a73..769e51e 100644 (file)
@@ -204,7 +204,7 @@ bool DataTransfer::shouldSuppressGetAndSetDataToAvoidExposingFilePaths() const
 {
     if (!forFileDrag() && !RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled())
         return false;
-    return m_pasteboard->containsFiles();
+    return m_pasteboard->fileContentState() == Pasteboard::FileContentState::MayContainFilePaths;
 }
 
 void DataTransfer::setData(const String& type, const String& data)
@@ -291,7 +291,7 @@ Vector<String> DataTransfer::types(AddFilesType addFilesType) const
     if (!RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled()) {
         auto types = m_pasteboard->typesForLegacyUnsafeBindings();
         ASSERT(!types.contains("Files"));
-        if (m_pasteboard->containsFiles() && addFilesType == AddFilesType::Yes)
+        if (m_pasteboard->fileContentState() != Pasteboard::FileContentState::NoFileOrImageData && addFilesType == AddFilesType::Yes)
             types.append("Files");
         return types;
     }
@@ -301,10 +301,17 @@ Vector<String> DataTransfer::types(AddFilesType addFilesType) const
         return item->isFile();
     });
 
-    if (hasFileBackedItem || m_pasteboard->containsFiles()) {
+    auto fileContentState = m_pasteboard->fileContentState();
+    if (hasFileBackedItem || fileContentState != Pasteboard::FileContentState::NoFileOrImageData) {
         Vector<String> types;
         if (addFilesType == AddFilesType::Yes)
             types.append(ASCIILiteral("Files"));
+
+        if (fileContentState != Pasteboard::FileContentState::MayContainFilePaths) {
+            types.appendVector(WTFMove(safeTypes));
+            return types;
+        }
+
         if (safeTypes.contains("text/uri-list"))
             types.append(ASCIILiteral("text/uri-list"));
         if (safeTypes.contains("text/html") && RuntimeEnabledFeatures::sharedFeatures().customPasteboardDataEnabled())
index 38bd3eb..e88e28b 100644 (file)
@@ -220,7 +220,8 @@ public:
 
     virtual WEBCORE_EXPORT void writeCustomData(const PasteboardCustomData&);
 
-    virtual WEBCORE_EXPORT bool containsFiles();
+    enum class FileContentState { NoFileOrImageData, InMemoryImage, MayContainFilePaths };
+    virtual WEBCORE_EXPORT FileContentState fileContentState();
     virtual WEBCORE_EXPORT bool canSmartReplace();
 
     virtual WEBCORE_EXPORT void writeMarkup(const String& markup);
index 007e43a..eedd702 100644 (file)
@@ -61,7 +61,7 @@ public:
 
     void writeCustomData(const PasteboardCustomData&) final { }
 
-    bool containsFiles() final { return false; }
+    Pasteboard::FileContentState fileContentState() final { return FileContentState::NoFileOrImageData; }
     bool canSmartReplace() final { return false; }
 
     void writeMarkup(const String&) final { }
index a5e155e..a286519 100644 (file)
@@ -134,21 +134,35 @@ bool Pasteboard::shouldTreatCocoaTypeAsFile(const String& cocoaType)
     return cocoaTypeToImageType(cocoaType) != ImageType::Invalid;
 }
 
-bool Pasteboard::containsFiles()
+Pasteboard::FileContentState Pasteboard::fileContentState()
 {
-    if (!platformStrategies()->pasteboardStrategy()->getNumberOfFiles(m_pasteboardName)) {
+    bool mayContainFilePaths = platformStrategies()->pasteboardStrategy()->getNumberOfFiles(m_pasteboardName);
+    if (!mayContainFilePaths) {
         Vector<String> cocoaTypes;
         platformStrategies()->pasteboardStrategy()->getTypes(cocoaTypes, m_pasteboardName);
         if (cocoaTypes.findMatching([](const String& cocoaType) { return shouldTreatCocoaTypeAsFile(cocoaType); }) == notFound)
-            return false;
+            return FileContentState::NoFileOrImageData;
+
+        bool containsURL = notFound != cocoaTypes.findMatching([] (auto& cocoaType) {
+#if PLATFORM(MAC)
+            if (cocoaType == String(legacyURLPasteboardType()))
+                return true;
+#endif
+            return cocoaType == String(kUTTypeURL);
+        });
+        mayContainFilePaths = containsURL && !Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(readString(ASCIILiteral("text/uri-list")));
     }
 
     // Enforce changeCount ourselves for security. We check after reading instead of before to be
     // sure it doesn't change between our testing the change count and accessing the data.
     if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName))
-        return false;
+        return FileContentState::NoFileOrImageData;
 
-    return true;
+    // Even when there's only image data in the pasteboard and no file representations, we still run the risk of exposing file paths
+    // to the page if the app has written image data to the pasteboard with a corresponding file path as plain text. An example of
+    // this is copying an image with a local `src` in Safari. To mitigate this, we additionally require that the app has not also
+    // written URLs to the pasteboard, as this would suggest that the plain text data might contain file paths.
+    return mayContainFilePaths ? FileContentState::MayContainFilePaths : FileContentState::InMemoryImage;
 }
 
 Vector<String> Pasteboard::typesSafeForBindings(const String& origin)
index 99d1203..e9fc9ea 100644 (file)
@@ -318,10 +318,10 @@ String Pasteboard::readStringInCustomData(const String&)
     return { };
 }
 
-bool Pasteboard::containsFiles()
+Pasteboard::FileContentState Pasteboard::fileContentState()
 {
     readFromClipboard();
-    return !m_selectionData->filenames().isEmpty();
+    return m_selectionData->filenames().isEmpty() ? FileContentState::NoFileOrImageData : FileContentState::MayContainFilePaths;
 }
 
 void Pasteboard::writeMarkup(const String&)
index 46650f0..a2e29fa 100644 (file)
@@ -315,12 +315,12 @@ struct PasteboardFileCounter final : PasteboardFileReader {
     unsigned count { 0 };
 };
 
-bool Pasteboard::containsFiles()
+Pasteboard::FileContentState Pasteboard::fileContentState()
 {
     // FIXME: This implementation can be slightly more efficient by avoiding calls to DragQueryFileW.
     PasteboardFileCounter reader;
     read(reader);
-    return reader.count;
+    return reader.count ? FileContentState::MayContainFilePaths : FileContentState::NoFileOrImageData;
 }
 
 void Pasteboard::read(PasteboardFileReader& reader)
index 1eb2d29..8a1513e 100644 (file)
@@ -125,10 +125,10 @@ void Pasteboard::write(const PasteboardWebContent& content)
     platformStrategies()->pasteboardStrategy()->writeToPasteboard(content);
 }
 
-bool Pasteboard::containsFiles()
+Pasteboard::FileContentState Pasteboard::fileContentState()
 {
     notImplemented();
-    return false;
+    return FileContentState::NoFileOrImageData;
 }
 
 bool Pasteboard::canSmartReplace()
index a9691c3..a3829b4 100644 (file)
@@ -1,3 +1,18 @@
+2018-03-10  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [macOS] Copying a table from the Numbers app and pasting into iCloud Numbers fails
+        https://bugs.webkit.org/show_bug.cgi?id=183485
+        <rdar://problem/38041984>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add new API tests to cover scenarios in which we paste image data alongside text data.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/PasteMixedContent.mm:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/ios/UIPasteboardTests.mm:
+        (TestWebKitAPI::TEST):
+
 2018-03-09  Zalan Bujtas  <zalan@apple.com>
 
         [LayoutReloaded] Initial commit -block formatting context.
index 0dd33e1..04114ab 100644 (file)
@@ -194,6 +194,49 @@ TEST(PasteMixedContent, ImageFileWithHTMLAndURL)
     EXPECT_FALSE([[webView stringByEvaluatingJavaScript:@"rawHTMLData.textContent"] containsString:@"script"]);
 }
 
+TEST(PasteMixedContent, ImageDataAndPlainText)
+{
+    auto webView = setUpWebView();
+    writeTypesAndDataToPasteboard(NSPasteboardTypeString, @"Hello world", NSPasteboardTypePNG, [NSData dataWithContentsOfFile:imagePath()], nil);
+    [webView paste:nil];
+
+    EXPECT_WK_STREQ("Files, text/plain", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
+    EXPECT_WK_STREQ("(STRING, text/plain), (FILE, image/png)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
+    EXPECT_WK_STREQ("('image.png', image/png)", [webView stringByEvaluatingJavaScript:@"files.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
+    EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]);
+}
+
+TEST(PasteMixedContent, ImageDataAndPlainTextAndURL)
+{
+    auto webView = setUpWebView();
+    writeTypesAndDataToPasteboard(NSPasteboardTypeString, imagePath(), NSURLPboardType, imagePath(), NSPasteboardTypePNG, [NSData dataWithContentsOfFile:imagePath()], nil);
+    [webView paste:nil];
+
+    EXPECT_WK_STREQ("Files", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
+    EXPECT_WK_STREQ("(FILE, image/png)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
+    EXPECT_WK_STREQ("('image.png', image/png)", [webView stringByEvaluatingJavaScript:@"files.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]);
+}
+
+TEST(PasteMixedContent, ImageDataAndPlainTextAndURLAndHTML)
+{
+    auto webView = setUpWebView();
+    writeTypesAndDataToPasteboard(NSPasteboardTypeString, imagePath(), NSURLPboardType, imagePath(), NSPasteboardTypeHTML, markupString(), NSPasteboardTypePNG, [NSData dataWithContentsOfFile:imagePath()], nil);
+    [webView paste:nil];
+
+    EXPECT_WK_STREQ("Files, text/html", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
+    EXPECT_WK_STREQ("(STRING, text/html), (FILE, image/png)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
+    EXPECT_WK_STREQ("('image.png', image/png)", [webView stringByEvaluatingJavaScript:@"files.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
+    EXPECT_WK_STREQ("HELLO WORLD", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]);
+    EXPECT_FALSE([[webView stringByEvaluatingJavaScript:@"rawHTMLData.textContent"] containsString:@"script"]);
+}
+
 } // namespace TestWebKitAPI
 
 #endif // PLATFORM(MAC) && WK_API_ENABLED
index 2550562..4334855 100644 (file)
@@ -194,6 +194,30 @@ TEST(UIPasteboardTests, DataTransferGetDataWhenPastingPlatformRepresentations)
     });
 }
 
+TEST(UIPasteboardTests, DataTransferGetDataWhenPastingImageAndText)
+{
+    auto webView = setUpWebViewForPasteboardTests(@"DataTransfer");
+    auto copiedText = retainPtr(@"Apple Inc.");
+    auto itemProvider = adoptNS([[NSItemProvider alloc] init]);
+    [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypePNG visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[] (DataLoadCompletionBlock completionHandler) -> NSProgress * {
+        completionHandler([NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"icon" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"]], nil);
+        return nil;
+    }];
+    [itemProvider registerDataRepresentationForTypeIdentifier:(NSString *)kUTTypeUTF8PlainText visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[copiedText] (DataLoadCompletionBlock completionHandler) -> NSProgress * {
+        completionHandler([copiedText dataUsingEncoding:NSUTF8StringEncoding], nil);
+        return nil;
+    }];
+    [UIPasteboard generalPasteboard].itemProviders = @[ itemProvider.get() ];
+    [webView paste:nil];
+
+    EXPECT_WK_STREQ("Files, text/plain", [webView stringByEvaluatingJavaScript:@"types.textContent"]);
+    EXPECT_WK_STREQ("(STRING, text/plain), (FILE, image/png)", [webView stringByEvaluatingJavaScript:@"items.textContent"]);
+    EXPECT_WK_STREQ("('image.png', image/png)", [webView stringByEvaluatingJavaScript:@"files.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]);
+    EXPECT_WK_STREQ("Apple Inc.", [webView stringByEvaluatingJavaScript:@"textData.textContent"]);
+    EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]);
+}
+
 TEST(UIPasteboardTests, DataTransferSetDataCannotWritePlatformTypes)
 {
     auto webView = setUpWebViewForPasteboardTests(@"dump-datatransfer-types");