Changeset 223440 in webkit


Ignore:
Timestamp:
Oct 16, 2017 2:44:28 PM (7 years ago)
Author:
rniwa@webkit.org
Message:

Cannot access images included in the content pasted from Microsoft Word
https://bugs.webkit.org/show_bug.cgi?id=124391
<rdar://problem/26862741>

Reviewed by Antti Koivisto.

Source/WebCore:

The bug is caused by the fact Microsoft Word generates HTML content which references an image using file URL.
Because the websites don't have access to arbtirary file URLs, this prevents editors such as TinyMCE to save
those images.

This patch fixes the problem by converting file URLs for images and all other subresources in the web archive
generated by Microsoft Word by blob URLs like r222839 for RTF/RTFD and r222119 for images.

To avoid revealing privacy sensitive information such as the absolute local file path to the user's home directory
Microsoft Word and other applications in the system includes in the web archive placed in the system pasteboard,
this patch also introduces the mechanism to sanitize when the HTML content is read by DataTransfer's getData.

This patch also introduces the sanitization for when writing HTML into the pasteboard since other applications
in the syste which is capable to processing web archives are not necessarily equipped to pretect itself and the
rest of the system from potentially dangerous JavaScript included in the web archive placed in the system pasteboard.

Finally, this patch expands the list of clipboard types that are exposed as "text/html" to the Web platform by
adding the capability to convert RTF, RTFD, and web archive into HTML markup by introducing WebContentMarkupReader,
a new subclass of PasteboardWebContentReader which creates a HTML markup instead of a document fragment. Most of
the sanitization process happens in this new class, and will be expanded to WebContentReader to make pasting safer.

Tests: editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url.html

editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin.html
editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying.html
editing/pasteboard/data-transfer-set-data-sanitlize-html-when-dragging-in-null-origin.html
http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html
CopyHTML.Sanitizes
DataInteractionTests.DataTransferSanitizeHTML
PasteRTF.ExposesHTMLTypeInDataTransfer
PasteRTFD.ExposesHTMLTypeInDataTransfer
PasteRTFD.ImageElementUsesBlobURLInHTML
PasteWebArchive.ExposesHTMLTypeInDataTransfer

  • dom/DataTransfer.cpp:

(WebCore::originIdentifierForDocument): Moved to Document::originIdentifierForPasteboard.
(WebCore::DataTransfer::createForCopyAndPaste):
(WebCore::DataTransfer::getDataForItem const): Use WebContentMarkupReader read HTMl content so that we can read
web arhive, RTF, and RTFD as text/html.
(WebCore::DataTransfer::getData const):
(WebCore::DataTransfer::setData):
(WebCore::DataTransfer::setDataFromItemList): Sanitize the HTML before placing into the system pasteboard.
(WebCore::DataTransfer::createForDragStartEvent):
(WebCore::DataTransfer::createForDrop):
(WebCore::DataTransfer::createForUpdatingDropTarget):

  • dom/DataTransfer.h:
  • dom/DataTransfer.idl:
  • dom/DataTransferItem.cpp:

(WebCore::DataTransferItem::getAsString const):

  • dom/Document.cpp:

(WebCore::Document::originIdentifierForPasteboard): Renamed from uniqueIdentifier. Moved the code to use the origin
string and then falling back to the UUID here from originIdentifierForDocument in DataTransfer.cpp.

  • dom/Document.h:
  • editing/WebContentReader.cpp:

(WebCore::WebContentMarkupReader::shouldSanitize const): Added.

  • editing/WebContentReader.h:

(WebCore::WebContentMarkupReader): Added.
(WebCore::WebContentMarkupReader::WebContentMarkupReader):

  • editing/cocoa/WebContentReaderCocoa.mm:

(WebCore::createFragmentFromWebArchive): Extracted out of WebContentReader::readWebArchive to share code.
(WebCore::WebContentReader::readWebArchive):
(WebCore::WebContentMarkupReader::readWebArchive): Added. Reads the web archive, replace all subresource URLs by
blob URLs, and re-generate the markup using our copy & paste code. The last step is requied to strip away any privacy
sensitive information as well as potentially dangerous JavaScript code.
(WebCore::stripMicrosoftPrefix): Extracted out of WebContentReader::readHTML to share code.
(WebCore::WebContentReader::readHTML):
(WebCore::WebContentMarkupReader::readHTML): Added. Only sanitize the markup when it comes from a different origin.
(WebCore::WebContentReader::readRTFD): Added a nullity check for frame.document().
(WebCore::WebContentMarkupReader::readRTFD): Added.
(WebCore::WebContentMarkupReader::readRTF): Added.

  • editing/markup.h:
  • editing/markup.cpp:

(WebCore::createPageForSanitizingWebContent): Added.
(WebCore::sanitizeMarkup): Added. This function "pastes" the markup into a new isolated document then reserializes
using our serialization code for copy. It strips away all invisible information such as comments, and strips away
event handlers and script elements to remove potentially dangerous scripts.

  • platform/Pasteboard.h:
  • platform/ios/PasteboardIOS.mm:

(WebCore::Pasteboard::readPasteboardWebContentDataForType): Now that this code can be called by DataTransfer, added
the checks for the change count to make sure we stop letting web content read if the pasteboard had been changed by
some other applications. To do this, turned this function into a member of Pasteboard. Also changed the return type
to an enum with tri-state to exist the loop early in the call sites.
(WebCore::Pasteboard::read):
(WebCore::Pasteboard::readRespectingUTIFidelities):

  • platform/ios/PlatformPasteboardIOS.mm:

(WebCore::safeTypeForDOMToReadAndWriteForPlatformType): Treat RTF, RTFD, and web archive as HTML.

  • platform/mac/PasteboardMac.mm:

(WebCore::Pasteboard::read): Add the change count checks now that this code can be called by DataTransfer.

  • platform/mac/PlatformPasteboardMac.mm:

(WebCore::safeTypeForDOMToReadAndWriteForPlatformType): Treat RTF, RTFD, and web archive as HTML.

Tools:

Added tests for sanitizing HTML contents for copy & paste and drag & drop.

  • TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
  • TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm: Added.

(readHTMLFromPasteboard): Added.
(createWebViewWithCustomPasteboardDataEnabled): Added.
(CopyHTML.Sanitizes): Added.

  • TestWebKitAPI/Tests/WebKitCocoa/CopyURL.mm:

(createWebViewWithCustomPasteboardDataEnabled): Added to enable more tests on bots.

  • TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm:

(writeRTFToPasteboard): Added.
(createWebViewWithCustomPasteboardDataEnabled): Added.
(createHelloWorldString): Added.
(PasteRTF.ExposesHTMLTypeInDataTransfer): Added.
(PasteRTFD.ExposesHTMLTypeInDataTransfer): Added.
(PasteRTFD.ImageElementUsesBlobURLInHTML): Added.

  • TestWebKitAPI/Tests/WebKitCocoa/copy-html.html: Added.
  • TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html: Store the clipboardData contents for

PasteRTF.ExposesHTMLTypeInDataTransfer and PasteRTFD.ExposesHTMLTypeInDataTransfer.

  • TestWebKitAPI/Tests/ios/DataInteractionTests.mm:

(DataInteractionTests.DataTransferSanitizeHTML):

LayoutTests:

Added tests for copying & pasting and dragging & dropping HTML contents.

  • TestExpectations:
  • editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt: Rebaselined.
  • editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Ditto.
  • editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html: Modified the test to strip away platform specific

inline style properties.

  • editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url-expected.txt: Added.
  • editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url.html: Added.
  • editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-expected.txt: Added.
  • editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin-expected.txt: Added.
  • editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin.html: Added.
  • editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying.html: Added.
  • editing/pasteboard/data-transfer-set-data-sanitizes-html-when-dragging-in-null-origin-expected.txt: Added.
  • editing/pasteboard/data-transfer-set-data-sanitizes-html-when-dragging-in-null-origin.html: Added.
  • editing/pasteboard/data-transfer-set-data-sanitizes-url-when-dragging-in-null-origin.html: Removed the superflous

call to setTimeout that was errornously added during debugging. Also updated the test to not claim all URL and
HTML values are read in the same origin, and updated the assertion for cross-origin case as it's now sanitized.

  • editing/pasteboard/onpaste-text-html-expected.txt: Rebaselined. The order of CSS properties have changed.
  • http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html-expected.txt: Added.
  • http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html: Added.
  • http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html:
  • http/tests/security/clipboard/resources/copy-html.html: Added.
  • http/tests/security/clipboard/resources/copy-url.html: Renamed from copy.html.
  • platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Remoevd.
  • platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Remoevd.
  • platform/mac-wk1/TestExpectations:
Location:
trunk
Files:
15 added
3 deleted
37 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r223428 r223440  
     12017-10-15  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Cannot access images included in the content pasted from Microsoft Word
     4        https://bugs.webkit.org/show_bug.cgi?id=124391
     5        <rdar://problem/26862741>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Added tests for copying & pasting and dragging & dropping HTML contents.
     10
     11        * TestExpectations:
     12        * editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt: Rebaselined.
     13        * editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Ditto.
     14        * editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html: Modified the test to strip away platform specific
     15        inline style properties.
     16        * editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url-expected.txt: Added.
     17        * editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url.html: Added.
     18        * editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-expected.txt: Added.
     19        * editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin-expected.txt: Added.
     20        * editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin.html: Added.
     21        * editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying.html: Added.
     22        * editing/pasteboard/data-transfer-set-data-sanitizes-html-when-dragging-in-null-origin-expected.txt: Added.
     23        * editing/pasteboard/data-transfer-set-data-sanitizes-html-when-dragging-in-null-origin.html: Added.
     24        * editing/pasteboard/data-transfer-set-data-sanitizes-url-when-dragging-in-null-origin.html: Removed the superflous
     25        call to setTimeout that was errornously added during debugging. Also updated the test to not claim all URL and
     26        HTML values are read in the same origin, and updated the assertion for cross-origin case as it's now sanitized.
     27        * editing/pasteboard/onpaste-text-html-expected.txt: Rebaselined. The order of CSS properties have changed.
     28        * http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html-expected.txt: Added.
     29        * http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html: Added.
     30        * http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html:
     31        * http/tests/security/clipboard/resources/copy-html.html: Added.
     32        * http/tests/security/clipboard/resources/copy-url.html: Renamed from copy.html.
     33        * platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Remoevd.
     34        * platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt: Remoevd.
     35        * platform/mac-wk1/TestExpectations:
     36
    1372017-10-16  Ross Kirsling  <ross.kirsling@sony.com>
    238
  • trunk/LayoutTests/TestExpectations

    r223414 r223440  
    7575editing/pasteboard/data-transfer-get-data-on-drop-url.html [ Skip ]
    7676editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Skip ]
     77editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html [ Skip ]
    7778editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html [ Skip ]
     79
    7880editing/pasteboard/drag-end-crash-accessing-item-list.html [ Skip ]
    7981editing/pasteboard/data-transfer-item-list-add-file-on-drag.html [ Skip ]
  • trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt

    r222595 r223440  
    66    },
    77    "drop": {
    8         "text/html": "<strong style=\"font-family: -apple-system; font-size: 150px; font-style: normal; font-variant-caps: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: nowrap; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; color: purple;\">Rich text</strong>",
     8        "text/html": "<strong style=\"font-style: normal; font-variant-caps: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-family: -apple-system; font-size: 150px; white-space: nowrap; color: purple;\">Rich text</strong>",
    99        "text/plain": "Rich text"
    1010    }
  • trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt

    r222595 r223440  
    22{
    33    "paste": {
    4         "text/html": "<span style=\"caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: -apple-system; font-size: 150px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: nowrap; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;\">Rich text</span>",
     4        "text/html": "<span style=\"...\"\">Rich text</span>",
    55        "text/plain": "Rich text"
    66    }
  • trunk/LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html

    r222595 r223440  
    3636    const eventData = {};
    3737    for (const type of event.clipboardData.types)
    38         eventData[type] = event.clipboardData.getData(type);
     38        eventData[type] = event.clipboardData.getData(type).replace(/style="[^"]+"/g, 'style="...""');
    3939    result[event.type] = eventData;
    4040    output.textContent = JSON.stringify(result, null, "    ");
  • trunk/LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin-expected.txt

    r223278 r223440  
    55
    66dragstart in the null origin document:
    7 PASS urlReadInSameDocument is "http://webkit.org/b/🤔?x=8 + 6"
    8 PASS htmlReadInSameDocument is "testing"
    9 PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]"
    10 PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
     7PASS url is "http://webkit.org/b/🤔?x=8 + 6"
     8PASS html is "testing"
     9PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]"
     10PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
    1111
    1212dragover in the null origin document:
    13 PASS urlReadInSameDocument is ""
    14 PASS htmlReadInSameDocument is ""
    15 PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]"
    16 PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
     13PASS url is ""
     14PASS html is ""
     15PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]"
     16PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
    1717
    1818drop in the null origin document:
    19 PASS urlReadInSameDocument is "http://webkit.org/b/🤔?x=8 + 6"
    20 PASS htmlReadInSameDocument is "testing"
    21 PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]"
    22 PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
     19PASS url is "http://webkit.org/b/🤔?x=8 + 6"
     20PASS html is "testing"
     21PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]"
     22PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
    2323
    2424dragstart in the null origin document:
    25 PASS urlReadInSameDocument is "http://webkit.org/b/🤔?x=8 + 6"
    26 PASS htmlReadInSameDocument is "testing"
    27 PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]"
    28 PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
     25PASS url is "http://webkit.org/b/🤔?x=8 + 6"
     26PASS html is "testing"
     27PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]"
     28PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
    2929
    3030dragover in the file URL document:
    31 PASS urlReadInSameDocument is ""
    32 PASS htmlReadInSameDocument is ""
    33 PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]"
    34 PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
     31PASS url is ""
     32PASS html is ""
     33PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]"
     34PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
    3535
    3636drop in the file URL document:
    37 PASS urlReadInSameDocument is "http://webkit.org/b/%F0%9F%A4%94?x=8%20+%206"
    38 PASS htmlReadInSameDocument is "testing"
    39 PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]"
    40 PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
     37PASS url is "http://webkit.org/b/%F0%9F%A4%94?x=8%20+%206"
     38PASS html.includes("testing") is true
     39PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]"
     40PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]"
    4141PASS successfullyParsed is true
    4242
  • trunk/LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html

    r223278 r223440  
    8484    switch (kind) {
    8585    case 'dragstart':
    86         urlReadInSameDocument = event.data.url;
    87         shouldBeEqualToString('urlReadInSameDocument', originalURL);
    88         htmlReadInSameDocument = event.data.html;
    89         shouldBeEqualToString('htmlReadInSameDocument', "testing");
    90         typesInSameDocument = event.data.types;
    91         shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/uri-list","text/html"]');
    92         itemsInSameDocument = event.data.items;
    93         shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]');
     86        url = event.data.url;
     87        shouldBeEqualToString('url', originalURL);
     88        html = event.data.html;
     89        shouldBeEqualToString('html', "testing");
     90        types = event.data.types;
     91        shouldBeEqualToString('JSON.stringify(types)', '["text/uri-list","text/html"]');
     92        items = event.data.items;
     93        shouldBeEqualToString('JSON.stringify(items)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]');
    9494        break;
    9595    case 'dragover':
    96         urlReadInSameDocument = event.data.url;
    97         shouldBeEqualToString('urlReadInSameDocument', '');
    98         htmlReadInSameDocument = event.data.html;
    99         shouldBeEqualToString('htmlReadInSameDocument', '');
    100         typesInSameDocument = event.data.types;
    101         shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/uri-list","text/html"]');
    102         itemsInSameDocument = event.data.items;
    103         shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]');
     96        url = event.data.url;
     97        shouldBeEqualToString('url', '');
     98        html = event.data.html;
     99        shouldBeEqualToString('html', '');
     100        types = event.data.types;
     101        shouldBeEqualToString('JSON.stringify(types)', '["text/uri-list","text/html"]');
     102        items = event.data.items;
     103        shouldBeEqualToString('JSON.stringify(items)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]');
    104104        break;
    105105    case 'drop':
    106         urlReadInSameDocument = event.data.url;
    107         shouldBeEqualToString('urlReadInSameDocument', event.data.documentLabel.includes('null') ? originalURL : (new URL(originalURL)).href);
    108         htmlReadInSameDocument = event.data.html;
    109         shouldBeEqualToString('htmlReadInSameDocument', "testing");
    110         typesInSameDocument = event.data.types;
    111         shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/uri-list","text/html"]');
    112         itemsInSameDocument = event.data.items;
    113         shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]');
     106        url = event.data.url;
     107        isSameOrigin = event.data.documentLabel.includes('null');
     108        shouldBeEqualToString('url', isSameOrigin ? originalURL : (new URL(originalURL)).href);
     109        html = event.data.html;
     110        if (isSameOrigin)
     111            shouldBeEqualToString('html', 'testing');
     112        else
     113            shouldBeTrue('html.includes("testing")');
     114        types = event.data.types;
     115        shouldBeEqualToString('JSON.stringify(types)', '["text/uri-list","text/html"]');
     116        items = event.data.items;
     117        shouldBeEqualToString('JSON.stringify(items)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]');
    114118        if (!event.data.documentLabel.includes('null')) {
    115119            document.getElementById('container').remove();
     
    159163}
    160164
    161 setTimeout(finishJSTest, 3000);
    162 
    163165</script>
    164166<script src="../../resources/js-test-post.js"></script>
  • trunk/LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt

    r220706 r223440  
    11CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the clipboard during an onpaste event.
    2 CONSOLE MESSAGE: line 23: text/html: <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-size: medium; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;">This test verifies that we can get text/html from the clipboard during an onpaste event.<span class="Apple-converted-space"> </span></span>
     2CONSOLE MESSAGE: line 23: text/html: <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; float: none; display: inline !important;">This test verifies that we can get text/html from the clipboard during an onpaste event.<span class="Apple-converted-space"> </span></span>
    33This test verifies that we can get text/html from the clipboard during an onpaste event. This test requires DRT.
    44Paste content in this div.This test verifies that we can get text/html from the clipboard during an onpaste event. 
  • trunk/LayoutTests/fast/events/ondrop-text-html-expected.txt

    r220706 r223440  
    11CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the drag object during an ondrop event.
    2 CONSOLE MESSAGE: line 23: text/html: <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-size: medium; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;">This test verifies that we can get text/html from the drag object during an ondrop event.<span class="Apple-converted-space"> </span></span>
     2CONSOLE MESSAGE: line 23: text/html: <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; float: none; display: inline !important;">This test verifies that we can get text/html from the drag object during an ondrop event.<span class="Apple-converted-space"> </span></span>
    33This test verifies that we can get text/html from the drag object during an ondrop event. This test requires DRT.
    44PASS
  • trunk/LayoutTests/http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html

    r223195 r223440  
    3434
    3535</script>
    36 <iframe src="http://localhost:8000/security/clipboard/resources/copy.html"></iframe>
     36<iframe src="http://localhost:8000/security/clipboard/resources/copy-url.html"></iframe>
    3737<div id="destination" onpaste="doPaste(event)" contenteditable>3. Paste here</div>
    3838<script src="/resources/js-test-post.js"></script>
  • trunk/LayoutTests/platform/mac-wk1/TestExpectations

    r223413 r223440  
    1313editing/pasteboard/data-transfer-get-data-on-drop-url.html [ Pass ]
    1414editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Pass ]
     15editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html [ Pass ]
    1516editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html [ Pass ]
    1617editing/pasteboard/drag-end-crash-accessing-item-list.html [ Pass ]
  • trunk/LayoutTests/platform/win/TestExpectations

    r223407 r223440  
    11361136
    11371137# Custom pasteboard data is not supported on Windows.
    1138 http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html
     1138http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html [ Skip ]
     1139http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html [ Skip ]
    11391140
    11401141webkit.org/b/140783 [ Release ] editing/pasteboard/copy-standalone-image.html [ Failure ImageOnlyFailure ]
  • trunk/Source/WebCore/ChangeLog

    r223438 r223440  
     12017-10-15  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Cannot access images included in the content pasted from Microsoft Word
     4        https://bugs.webkit.org/show_bug.cgi?id=124391
     5        <rdar://problem/26862741>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        The bug is caused by the fact Microsoft Word generates HTML content which references an image using file URL.
     10        Because the websites don't have access to arbtirary file URLs, this prevents editors such as TinyMCE to save
     11        those images.
     12
     13        This patch fixes the problem by converting file URLs for images and all other subresources in the web archive
     14        generated by Microsoft Word by blob URLs like r222839 for RTF/RTFD and r222119 for images.
     15
     16        To avoid revealing privacy sensitive information such as the absolute local file path to the user's home directory
     17        Microsoft Word and other applications in the system includes in the web archive placed in the system pasteboard,
     18        this patch also introduces the mechanism to sanitize when the HTML content is read by DataTransfer's getData.
     19
     20        This patch also introduces the sanitization for when writing HTML into the pasteboard since other applications
     21        in the syste which is capable to processing web archives are not necessarily equipped to pretect itself and the
     22        rest of the system from potentially dangerous JavaScript included in the web archive placed in the system pasteboard.
     23
     24        Finally, this patch expands the list of clipboard types that are exposed as "text/html" to the Web platform by
     25        adding the capability to convert RTF, RTFD, and web archive into HTML markup by introducing WebContentMarkupReader,
     26        a new subclass of PasteboardWebContentReader which creates a HTML markup instead of a document fragment. Most of
     27        the sanitization process happens in this new class, and will be expanded to WebContentReader to make pasting safer.
     28
     29        Tests: editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url.html
     30               editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin.html
     31               editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying.html
     32               editing/pasteboard/data-transfer-set-data-sanitlize-html-when-dragging-in-null-origin.html
     33               http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html
     34               CopyHTML.Sanitizes
     35               DataInteractionTests.DataTransferSanitizeHTML
     36               PasteRTF.ExposesHTMLTypeInDataTransfer
     37               PasteRTFD.ExposesHTMLTypeInDataTransfer
     38               PasteRTFD.ImageElementUsesBlobURLInHTML
     39               PasteWebArchive.ExposesHTMLTypeInDataTransfer
     40
     41        * dom/DataTransfer.cpp:
     42        (WebCore::originIdentifierForDocument): Moved to Document::originIdentifierForPasteboard.
     43        (WebCore::DataTransfer::createForCopyAndPaste):
     44        (WebCore::DataTransfer::getDataForItem const): Use WebContentMarkupReader read HTMl content so that we can read
     45        web arhive, RTF, and RTFD as text/html.
     46        (WebCore::DataTransfer::getData const):
     47        (WebCore::DataTransfer::setData):
     48        (WebCore::DataTransfer::setDataFromItemList): Sanitize the HTML before placing into the system pasteboard.
     49        (WebCore::DataTransfer::createForDragStartEvent):
     50        (WebCore::DataTransfer::createForDrop):
     51        (WebCore::DataTransfer::createForUpdatingDropTarget):
     52        * dom/DataTransfer.h:
     53        * dom/DataTransfer.idl:
     54        * dom/DataTransferItem.cpp:
     55        (WebCore::DataTransferItem::getAsString const):
     56        * dom/Document.cpp:
     57        (WebCore::Document::originIdentifierForPasteboard): Renamed from uniqueIdentifier. Moved the code to use the origin
     58        string and then falling back to the UUID here from originIdentifierForDocument in DataTransfer.cpp.
     59        * dom/Document.h:
     60        * editing/WebContentReader.cpp:
     61        (WebCore::WebContentMarkupReader::shouldSanitize const): Added.
     62        * editing/WebContentReader.h:
     63        (WebCore::WebContentMarkupReader): Added.
     64        (WebCore::WebContentMarkupReader::WebContentMarkupReader):
     65        * editing/cocoa/WebContentReaderCocoa.mm:
     66        (WebCore::createFragmentFromWebArchive): Extracted out of WebContentReader::readWebArchive to share code.
     67        (WebCore::WebContentReader::readWebArchive):
     68        (WebCore::WebContentMarkupReader::readWebArchive): Added. Reads the web archive, replace all subresource URLs by
     69        blob URLs, and re-generate the markup using our copy & paste code. The last step is requied to strip away any privacy
     70        sensitive information as well as potentially dangerous JavaScript code.
     71        (WebCore::stripMicrosoftPrefix): Extracted out of WebContentReader::readHTML to share code.
     72        (WebCore::WebContentReader::readHTML):
     73        (WebCore::WebContentMarkupReader::readHTML): Added. Only sanitize the markup when it comes from a different origin.
     74        (WebCore::WebContentReader::readRTFD): Added a nullity check for frame.document().
     75        (WebCore::WebContentMarkupReader::readRTFD): Added.
     76        (WebCore::WebContentMarkupReader::readRTF): Added.
     77        * editing/markup.h:
     78        * editing/markup.cpp:
     79        (WebCore::createPageForSanitizingWebContent): Added.
     80        (WebCore::sanitizeMarkup): Added. This function "pastes" the markup into a new isolated document then reserializes
     81        using our serialization code for copy. It strips away all invisible information such as comments, and strips away
     82        event handlers and script elements to remove potentially dangerous scripts.
     83        * platform/Pasteboard.h:
     84        * platform/ios/PasteboardIOS.mm:
     85        (WebCore::Pasteboard::readPasteboardWebContentDataForType): Now that this code can be called by DataTransfer, added
     86        the checks for the change count to make sure we stop letting web content read if the pasteboard had been changed by
     87        some other applications. To do this, turned this function into a member of Pasteboard. Also changed the return type
     88        to an enum with tri-state to exist the loop early in the call sites.
     89        (WebCore::Pasteboard::read):
     90        (WebCore::Pasteboard::readRespectingUTIFidelities):
     91        * platform/ios/PlatformPasteboardIOS.mm:
     92        (WebCore::safeTypeForDOMToReadAndWriteForPlatformType): Treat RTF, RTFD, and web archive as HTML.
     93        * platform/mac/PasteboardMac.mm:
     94        (WebCore::Pasteboard::read): Add the change count checks now that this code can be called by DataTransfer.
     95        * platform/mac/PlatformPasteboardMac.mm:
     96        (WebCore::safeTypeForDOMToReadAndWriteForPlatformType): Treat RTF, RTFD, and web archive as HTML.
     97
    1982017-10-16  Ryan Haddad  <ryanhaddad@apple.com>
    299
  • trunk/Source/WebCore/dom/DataTransfer.cpp

    r223340 r223440  
    3131#include "DataTransferItem.h"
    3232#include "DataTransferItemList.h"
     33#include "DocumentFragment.h"
    3334#include "DragData.h"
    3435#include "Editor.h"
     
    4344#include "StaticPasteboard.h"
    4445#include "URLParser.h"
     46#include "WebContentReader.h"
    4547#include "WebCorePasteboardFileReader.h"
     48#include "markup.h"
    4649
    4750namespace WebCore {
     
    7982}
    8083
    81 static String originIdentifierForDocument(Document& document)
    82 {
    83     auto origin = document.securityOrigin().toString();
    84     if (origin == "null")
    85         return document.uniqueIdentifier();
    86     return origin;
    87 }
    88 
    8984Ref<DataTransfer> DataTransfer::createForCopyAndPaste(Document& document, StoreMode storeMode, std::unique_ptr<Pasteboard>&& pasteboard)
    9085{
    9186    auto dataTransfer = adoptRef(*new DataTransfer(storeMode, WTFMove(pasteboard)));
    92     dataTransfer->m_originIdentifier = originIdentifierForDocument(document);
     87    dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
    9388    return dataTransfer;
    9489}
     
    147142}
    148143
    149 String DataTransfer::getDataForItem(const String& type) const
     144String DataTransfer::getDataForItem(Document& document, const String& type) const
    150145{
    151146    if (!canReadData())
     
    165160        return m_pasteboard->readString(lowercaseType);
    166161
    167     bool isSameOrigin = false;
    168     if (is<StaticPasteboard>(*m_pasteboard)) {
    169         // StaticPasteboard is only used to stage data written by websites before being committed to the system pasteboard.
    170         isSameOrigin = true;
    171     } else if (!m_originIdentifier.isNull()) {
    172         String originOfPasteboard = m_pasteboard->readOrigin();
    173         isSameOrigin = m_originIdentifier == originOfPasteboard;
    174     }
    175 
    176     if (!isSameOrigin) {
    177         if (!Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType))
     162    // StaticPasteboard is only used to stage data written by websites before being committed to the system pasteboard.
     163    bool isSameOrigin = is<StaticPasteboard>(*m_pasteboard) || (!m_originIdentifier.isNull() && m_originIdentifier == m_pasteboard->readOrigin());
     164    if (isSameOrigin) {
     165        String value = m_pasteboard->readStringInCustomData(lowercaseType);
     166        if (!value.isNull())
     167            return value;
     168    }
     169    if (!Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType))
     170        return { };
     171
     172    if (!is<StaticPasteboard>(*m_pasteboard) && type == "text/html") {
     173        if (!document.frame())
    178174            return { };
    179         return m_pasteboard->readString(lowercaseType);
    180     }
    181 
    182     String value = m_pasteboard->readStringInCustomData(lowercaseType);
    183     if (value.isNull() && Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType))
    184         value = m_pasteboard->readString(lowercaseType);
    185     return value;
    186 }
    187 
    188 String DataTransfer::getData(const String& type) const
    189 {
    190     return getDataForItem(normalizeType(type));
     175        WebContentMarkupReader reader { *document.frame() };
     176        m_pasteboard->read(reader);
     177        return reader.markup;
     178    }
     179
     180    return m_pasteboard->readString(type);
     181}
     182
     183String DataTransfer::getData(Document& document, const String& type) const
     184{
     185    return getDataForItem(document, normalizeType(type));
    191186}
    192187
     
    223218
    224219    String sanitizedData;
    225     if (type == "text/uri-list") {
     220    if (type == "text/html")
     221        sanitizedData = sanitizeMarkup(data);
     222    else if (type == "text/uri-list") {
    226223        auto url = URLParser(data).result();
    227224        if (url.isValid())
    228225            sanitizedData = url.string();
    229     } else if (type == "text/plain" || type == "text/html")
     226    } else if (type == "text/plain")
    230227        sanitizedData = data; // Nothing to sanitize.
    231228
     
    431428{
    432429    auto dataTransfer = adoptRef(*new DataTransfer(StoreMode::ReadWrite, std::make_unique<StaticPasteboard>(), Type::DragAndDropData));
    433     dataTransfer->m_originIdentifier = originIdentifierForDocument(document);
     430    dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
    434431    return dataTransfer;
    435432}
     
    439436    auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Readonly, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
    440437    dataTransfer->setSourceOperation(sourceOperation);
    441     dataTransfer->m_originIdentifier = originIdentifierForDocument(document);
     438    dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
    442439    return dataTransfer;
    443440}
     
    454451    auto dataTransfer = adoptRef(*new DataTransfer(mode, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData));
    455452    dataTransfer->setSourceOperation(sourceOperation);
    456     dataTransfer->m_originIdentifier = originIdentifierForDocument(document);
     453    dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard();
    457454    return dataTransfer;
    458455}
  • trunk/Source/WebCore/dom/DataTransfer.h

    r223340 r223440  
    6565    void clearData(const String& type = String());
    6666
    67     String getData(const String& type) const;
    68     String getDataForItem(const String& type) const;
     67    String getData(Document&, const String& type) const;
     68    String getDataForItem(Document&, const String& type) const;
    6969
    7070    void setData(const String& type, const String& data);
  • trunk/Source/WebCore/dom/DataTransfer.idl

    r220627 r223440  
    3939
    4040    readonly attribute FrozenArray<DOMString> types;
    41     DOMString getData(DOMString format);
     41    [CallWith=Document] DOMString getData(DOMString format);
    4242    void setData(DOMString format, DOMString data);
    4343    void clearData(optional DOMString format);
  • trunk/Source/WebCore/dom/DataTransferItem.cpp

    r223034 r223440  
    3535#include "DOMFileSystem.h"
    3636#include "DataTransferItemList.h"
     37#include "Document.h"
    3738#include "File.h"
    3839#include "FileSystem.h"
     
    8687}
    8788
    88 void DataTransferItem::getAsString(ScriptExecutionContext& context, RefPtr<StringCallback>&& callback) const
     89void DataTransferItem::getAsString(Document& document, RefPtr<StringCallback>&& callback) const
    8990{
    9091    if (!callback || !m_list || m_file)
     
    9697
    9798    // FIXME: Make this async.
    98     callback->scheduleCallback(context, dataTransfer.getDataForItem(m_type));
     99    callback->scheduleCallback(document, dataTransfer.getDataForItem(document, m_type));
    99100}
    100101
  • trunk/Source/WebCore/dom/DataTransferItem.h

    r222904 r223440  
    6161    String kind() const;
    6262    String type() const;
    63     void getAsString(ScriptExecutionContext&, RefPtr<StringCallback>&&) const;
     63    void getAsString(Document&, RefPtr<StringCallback>&&) const;
    6464    RefPtr<File> getAsFile() const;
    6565    RefPtr<FileSystemEntry> getAsEntry(ScriptExecutionContext&) const;
  • trunk/Source/WebCore/dom/DataTransferItem.idl

    r221481 r223440  
    3737    readonly attribute DOMString type;
    3838
    39     [CallWith=ScriptExecutionContext] void getAsString(StringCallback? callback);
     39    [CallWith=Document] void getAsString(StringCallback? callback);
    4040    File getAsFile();
    4141
  • trunk/Source/WebCore/dom/Document.cpp

    r223425 r223440  
    53015301#endif
    53025302
    5303 String Document::uniqueIdentifier()
    5304 {
     5303String Document::originIdentifierForPasteboard()
     5304{
     5305    auto origin = securityOrigin().toString();
     5306    if (origin != "null")
     5307        return origin;
    53055308    if (!m_uniqueIdentifier)
    53065309        m_uniqueIdentifier = "null:" + createCanonicalUUIDString();
  • trunk/Source/WebCore/dom/Document.h

    r223425 r223440  
    957957    uint64_t domTreeVersion() const { return m_domTreeVersion; }
    958958
    959     String uniqueIdentifier();
     959    String originIdentifierForPasteboard();
    960960
    961961    // XPathEvaluator methods
  • trunk/Source/WebCore/editing/WebContentReader.cpp

    r222062 r223440  
    2727#include "WebContentReader.h"
    2828
     29#include "Document.h"
    2930#include "DocumentFragment.h"
    3031
     
    3940}
    4041
     42bool WebContentMarkupReader::shouldSanitize() const
     43{
     44    return frame.document() && frame.document()->originIdentifierForPasteboard() != contentOrigin;
    4145}
    4246
     47}
     48
  • trunk/Source/WebCore/editing/WebContentReader.h

    r223140 r223440  
    6666};
    6767
     68class WebContentMarkupReader final : public PasteboardWebContentReader {
     69public:
     70    Frame& frame;
     71    String markup;
     72
     73    explicit WebContentMarkupReader(Frame& frame)
     74        : frame(frame)
     75    {
     76    }
     77
     78private:
     79    bool shouldSanitize() const;
     80
     81#if PLATFORM(COCOA)
     82    bool readWebArchive(SharedBuffer&) override;
     83    bool readFilenames(const Vector<String>&) override { return false; }
     84    bool readHTML(const String&) override;
     85    bool readRTFD(SharedBuffer&) override;
     86    bool readRTF(SharedBuffer&) override;
     87    bool readImage(Ref<SharedBuffer>&&, const String&) override { return false; }
     88    bool readURL(const URL&, const String&) override { return false; }
     89#endif
     90    bool readPlainText(const String&) override { return false; }
     91};
     92
    6893#if PLATFORM(COCOA) && defined(__OBJC__)
    6994struct FragmentAndResources {
  • trunk/Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm

    r223140 r223440  
    4040#import "HTMLImageElement.h"
    4141#import "LegacyWebArchive.h"
     42#import "MainFrame.h"
    4243#import "Page.h"
    4344#import "Settings.h"
     45#import "SocketProvider.h"
    4446#import "WebArchiveResourceFromNSAttributedString.h"
    4547#import "WebArchiveResourceWebResourceHandler.h"
     
    180182}
    181183
    182 bool WebContentReader::readWebArchive(SharedBuffer& buffer)
    183 {
    184     if (frame.settings().preferMIMETypeForImages() || !frame.document())
    185         return false;
    186 
     184struct FragmentAndArchive {
     185    Ref<DocumentFragment> fragment;
     186    Ref<Archive> archive;
     187};
     188
     189static std::optional<FragmentAndArchive> createFragmentFromWebArchive(Document& document, SharedBuffer& buffer, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
     190{
    187191    auto archive = LegacyWebArchive::create(URL(), buffer);
    188192    if (!archive)
    189         return false;
     193        return std::nullopt;
    190194
    191195    RefPtr<ArchiveResource> mainResource = archive->mainResource();
    192196    if (!mainResource)
    193         return false;
     197        return std::nullopt;
    194198
    195199    auto type = mainResource->mimeType();
    196     if (!frame.loader().client().canShowMIMETypeAsHTML(type))
     200    if (!canShowMIMETypeAsHTML(type))
     201        return std::nullopt;
     202
     203    auto markupString = String::fromUTF8(mainResource->data().data(), mainResource->data().size());
     204    auto fragment = createFragmentFromMarkup(document, markupString, mainResource->url(), DisallowScriptingAndPluginContent);
     205
     206    return FragmentAndArchive { WTFMove(fragment), archive.releaseNonNull() };
     207}
     208
     209bool WebContentReader::readWebArchive(SharedBuffer& buffer)
     210{
     211    if (frame.settings().preferMIMETypeForImages() || !frame.document())
    197212        return false;
    198213
    199214    DeferredLoadingScope scope(frame);
    200     auto markupString = String::fromUTF8(mainResource->data().data(), mainResource->data().size());
    201     addFragment(createFragmentFromMarkup(*frame.document(), markupString, mainResource->url(), DisallowScriptingAndPluginContent));
    202 
     215    auto result = createFragmentFromWebArchive(*frame.document(), buffer, [&] (const String& type) {
     216        return frame.loader().client().canShowMIMETypeAsHTML(type);
     217    });
     218    if (!result)
     219        return false;
     220
     221    fragment = WTFMove(result->fragment);
    203222    if (DocumentLoader* loader = frame.loader().documentLoader())
    204         loader->addAllArchiveResources(*archive);
    205 
    206     return true;
    207 }
    208 
    209 bool WebContentReader::readHTML(const String& string)
    210 {
    211     String stringOmittingMicrosoftPrefix = string;
    212    
     223        loader->addAllArchiveResources(result->archive.get());
     224
     225    return true;
     226}
     227
     228bool WebContentMarkupReader::readWebArchive(SharedBuffer& buffer)
     229{
     230    auto page = createPageForSanitizingWebContent();
     231    Document* stagingDocument = page->mainFrame().document();
     232    ASSERT(stagingDocument);
     233
     234    DeferredLoadingScope scope(frame);
     235    auto result = createFragmentFromWebArchive(*stagingDocument, buffer, [&] (const String& type) {
     236        return frame.loader().client().canShowMIMETypeAsHTML(type);
     237    });
     238    if (!result)
     239        return false;
     240
     241    HashMap<AtomicString, AtomicString> blobURLMap;
     242    for (const Ref<ArchiveResource>& subresource : result->archive->subresources()) {
     243        auto blob = Blob::create(subresource->data(), subresource->mimeType());
     244        String blobURL = DOMURL::createObjectURL(*frame.document(), blob);
     245        blobURLMap.set(subresource->url().string(), blobURL);
     246    }
     247    replaceSubresourceURLs(result->fragment.get(), WTFMove(blobURLMap));
     248
     249    auto* bodyElement = stagingDocument->body();
     250    ASSERT(bodyElement);
     251    bodyElement->appendChild(result->fragment);
     252
     253    auto range = Range::create(*stagingDocument);
     254    range->selectNodeContents(*bodyElement);
     255    markup = createMarkup(range.get(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
     256
     257    return true;
     258}
     259
     260static String stripMicrosoftPrefix(const String& string)
     261{
    213262#if PLATFORM(MAC)
    214263    // This code was added to make HTML paste from Microsoft Word on Mac work, back in 2004.
     
    218267        size_t location = string.findIgnoringCase("<html");
    219268        if (location != notFound)
    220             stringOmittingMicrosoftPrefix = string.substring(location);
    221     }
    222 #endif
    223 
     269            return string.substring(location);
     270    }
     271#endif
     272    return string;
     273}
     274
     275bool WebContentReader::readHTML(const String& string)
     276{
     277    if (frame.settings().preferMIMETypeForImages() || !frame.document())
     278        return false;
     279    Document& document = *frame.document();
     280
     281    String stringOmittingMicrosoftPrefix = stripMicrosoftPrefix(string);
    224282    if (stringOmittingMicrosoftPrefix.isEmpty())
    225283        return false;
    226284
     285    addFragment(createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent));
     286    return true;
     287}
     288
     289bool WebContentMarkupReader::readHTML(const String& string)
     290{
    227291    if (!frame.document())
    228292        return false;
    229     Document& document = *frame.document();
    230 
    231     addFragment(createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent));
    232     return true;
     293
     294    String rawHTML = stripMicrosoftPrefix(string);
     295    if (shouldSanitize())
     296        markup = sanitizeMarkup(rawHTML);
     297    else
     298        markup = rawHTML;
     299
     300    return !markup.isEmpty();
    233301}
    234302
    235303bool WebContentReader::readRTFD(SharedBuffer& buffer)
    236304{
    237     if (frame.settings().preferMIMETypeForImages())
     305    if (frame.settings().preferMIMETypeForImages() || !frame.document())
    238306        return false;
    239307
     
    246314}
    247315
     316bool WebContentMarkupReader::readRTFD(SharedBuffer& buffer)
     317{
     318    if (!frame.document())
     319        return false;
     320    auto fragment = createFragmentAndAddResources(frame, adoptNS([[NSAttributedString alloc] initWithRTFD:buffer.createNSData().get() documentAttributes:nullptr]).get());
     321    markup = createMarkup(*fragment);
     322    return true;
     323}
     324
    248325bool WebContentReader::readRTF(SharedBuffer& buffer)
    249326{
     
    256333    addFragment(fragment.releaseNonNull());
    257334
     335    return true;
     336}
     337
     338bool WebContentMarkupReader::readRTF(SharedBuffer& buffer)
     339{
     340    if (!frame.document())
     341        return false;
     342    auto fragment = createFragmentAndAddResources(frame, adoptNS([[NSAttributedString alloc] initWithRTF:buffer.createNSData().get() documentAttributes:nullptr]).get());
     343    if (!fragment)
     344        return false;
     345    markup = createMarkup(*fragment);
    258346    return true;
    259347}
  • trunk/Source/WebCore/editing/markup.cpp

    r222839 r223440  
    3535#include "CSSValue.h"
    3636#include "CSSValueKeywords.h"
     37#include "CacheStorageProvider.h"
    3738#include "ChildListMutationScope.h"
    3839#include "DocumentFragment.h"
     
    4142#include "Editing.h"
    4243#include "Editor.h"
     44#include "EditorClient.h"
    4345#include "ElementIterator.h"
     46#include "EmptyClients.h"
    4447#include "File.h"
    4548#include "Frame.h"
     
    5659#include "HTMLTextAreaElement.h"
    5760#include "HTMLTextFormControlElement.h"
    58 #include "URL.h"
     61#include "LibWebRTCProvider.h"
     62#include "MainFrame.h"
    5963#include "MarkupAccumulator.h"
    6064#include "NodeList.h"
     65#include "Page.h"
     66#include "PageConfiguration.h"
    6167#include "Range.h"
    6268#include "RenderBlock.h"
    6369#include "Settings.h"
     70#include "SocketProvider.h"
    6471#include "StyleProperties.h"
    6572#include "TextIterator.h"
    6673#include "TypedElementDescendantIterator.h"
     74#include "URL.h"
    6775#include "VisibleSelection.h"
    6876#include "VisibleUnits.h"
     
    136144        change.apply();
    137145}
     146
     147std::unique_ptr<Page> createPageForSanitizingWebContent()
     148{
     149    PageConfiguration pageConfiguration(createEmptyEditorClient(), SocketProvider::create(), LibWebRTCProvider::create(), CacheStorageProvider::create());
     150
     151    fillWithEmptyClients(pageConfiguration);
     152   
     153    auto page = std::make_unique<Page>(WTFMove(pageConfiguration));
     154    page->settings().setMediaEnabled(false);
     155    page->settings().setScriptEnabled(false);
     156    page->settings().setPluginsEnabled(false);
     157    page->settings().setAcceleratedCompositingEnabled(false);
     158
     159    Frame& frame = page->mainFrame();
     160    frame.setView(FrameView::create(frame));
     161    frame.init();
     162
     163    FrameLoader& loader = frame.loader();
     164    static char markup[] = "<!DOCTYPE html><html><body></body></html>";
     165    ASSERT(loader.activeDocumentLoader());
     166    loader.activeDocumentLoader()->writer().setMIMEType("text/html");
     167    loader.activeDocumentLoader()->writer().begin();
     168    loader.activeDocumentLoader()->writer().addData(markup, sizeof(markup));
     169    loader.activeDocumentLoader()->writer().end();
     170
     171    return page;
     172}
     173
     174
     175String sanitizeMarkup(const String& rawHTML)
     176{
     177    auto page = createPageForSanitizingWebContent();
     178    Document* stagingDocument = page->mainFrame().document();
     179    ASSERT(stagingDocument);
     180    auto* bodyElement = stagingDocument->body();
     181    ASSERT(bodyElement);
     182
     183    auto fragment = createFragmentFromMarkup(*stagingDocument, rawHTML, emptyString(), DisallowScriptingAndPluginContent);
     184    bodyElement->appendChild(fragment.get());
     185
     186    auto range = Range::create(*stagingDocument);
     187    range->selectNodeContents(*bodyElement);
     188    return createMarkup(range.get(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
     189}
     190
    138191   
    139192class StyledMarkupAccumulator final : public MarkupAccumulator {
  • trunk/Source/WebCore/editing/markup.h

    r222839 r223440  
    4343class URL;
    4444class Node;
     45class Page;
    4546class QualifiedName;
    4647class Range;
    4748
    4849void replaceSubresourceURLs(Ref<DocumentFragment>&&, HashMap<AtomicString, AtomicString>&&);
     50std::unique_ptr<Page> createPageForSanitizingWebContent();
     51String sanitizeMarkup(const String&);
    4952
    5053enum EChildrenOnly { IncludeNode, ChildrenOnly };
  • trunk/Source/WebCore/platform/Pasteboard.h

    r223340 r223440  
    128128class PasteboardWebContentReader {
    129129public:
     130    String contentOrigin;
     131
    130132    virtual ~PasteboardWebContentReader() { }
    131133
    132 #if !(PLATFORM(GTK) || PLATFORM(WIN))
     134#if PLATFORM(COCOA)
    133135    virtual bool readWebArchive(SharedBuffer&) = 0;
    134136    virtual bool readFilenames(const Vector<String>&) = 0;
     
    271273    bool respectsUTIFidelities() const;
    272274    void readRespectingUTIFidelities(PasteboardWebContentReader&);
     275
     276    enum class ReaderResult {
     277        ReadType,
     278        DidNotReadType,
     279        PasteboardWasChangedExternally
     280    };
     281    ReaderResult readPasteboardWebContentDataForType(PasteboardWebContentReader&, PasteboardStrategy&, NSString *type, int itemIndex);
    273282#endif
    274283
  • trunk/Source/WebCore/platform/ios/PasteboardIOS.mm

    r223140 r223440  
    163163}
    164164
    165 static bool readPasteboardWebContentDataForType(PasteboardWebContentReader& reader, PasteboardStrategy& strategy, NSString *type, int itemIndex, const String& pasteboardName)
     165Pasteboard::ReaderResult Pasteboard::readPasteboardWebContentDataForType(PasteboardWebContentReader& reader, PasteboardStrategy& strategy, NSString *type, int itemIndex)
    166166{
    167167    if ([type isEqualToString:WebArchivePboardType]) {
    168         auto buffer = strategy.readBufferFromPasteboard(itemIndex, WebArchivePboardType, pasteboardName);
    169         return buffer && reader.readWebArchive(*buffer);
     168        auto buffer = strategy.readBufferFromPasteboard(itemIndex, WebArchivePboardType, m_pasteboardName);
     169        if (m_changeCount != changeCount())
     170            return ReaderResult::PasteboardWasChangedExternally;
     171        return buffer && reader.readWebArchive(*buffer) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
    170172    }
    171173
    172174    if ([type isEqualToString:(NSString *)kUTTypeHTML]) {
    173         String htmlString = strategy.readStringFromPasteboard(itemIndex, kUTTypeHTML, pasteboardName);
    174         return !htmlString.isNull() && reader.readHTML(htmlString);
     175        String htmlString = strategy.readStringFromPasteboard(itemIndex, kUTTypeHTML, m_pasteboardName);
     176        if (m_changeCount != changeCount())
     177            return ReaderResult::PasteboardWasChangedExternally;
     178        return !htmlString.isNull() && reader.readHTML(htmlString) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
    175179    }
    176180
    177181    if ([type isEqualToString:(NSString *)kUTTypeFlatRTFD]) {
    178         RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeFlatRTFD, pasteboardName);
    179         return buffer && reader.readRTFD(*buffer);
     182        RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeFlatRTFD, m_pasteboardName);
     183        if (m_changeCount != changeCount())
     184            return ReaderResult::PasteboardWasChangedExternally;
     185        return buffer && reader.readRTFD(*buffer) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
    180186    }
    181187
    182188    if ([type isEqualToString:(NSString *)kUTTypeRTF]) {
    183         RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeRTF, pasteboardName);
    184         return buffer && reader.readRTF(*buffer);
     189        RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeRTF, m_pasteboardName);
     190        if (m_changeCount != changeCount())
     191            return ReaderResult::PasteboardWasChangedExternally;
     192        return buffer && reader.readRTF(*buffer) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
    185193    }
    186194
    187195    if ([supportedImageTypes() containsObject:type]) {
    188         RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, type, pasteboardName);
    189         return buffer && reader.readImage(buffer.releaseNonNull(), type);
     196        RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, type, m_pasteboardName);
     197        if (m_changeCount != changeCount())
     198            return ReaderResult::PasteboardWasChangedExternally;
     199        return buffer && reader.readImage(buffer.releaseNonNull(), type) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
    190200    }
    191201
    192202    if ([type isEqualToString:(NSString *)kUTTypeURL]) {
    193203        String title;
    194         URL url = strategy.readURLFromPasteboard(itemIndex, kUTTypeURL, pasteboardName, title);
    195         return !url.isNull() && reader.readURL(url, title);
     204        URL url = strategy.readURLFromPasteboard(itemIndex, kUTTypeURL, m_pasteboardName, title);
     205        if (m_changeCount != changeCount())
     206            return ReaderResult::PasteboardWasChangedExternally;
     207        return !url.isNull() && reader.readURL(url, title) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
    196208    }
    197209
    198210    if (UTTypeConformsTo((CFStringRef)type, kUTTypePlainText)) {
    199         String string = strategy.readStringFromPasteboard(itemIndex, kUTTypePlainText, pasteboardName);
    200         return !string.isNull() && reader.readPlainText(string);
     211        String string = strategy.readStringFromPasteboard(itemIndex, kUTTypePlainText, m_pasteboardName);
     212        if (m_changeCount != changeCount())
     213            return ReaderResult::PasteboardWasChangedExternally;
     214        return !string.isNull() && reader.readPlainText(string) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
    201215    }
    202216
    203217    if (UTTypeConformsTo((CFStringRef)type, kUTTypeText)) {
    204         String string = strategy.readStringFromPasteboard(itemIndex, kUTTypeText, pasteboardName);
    205         return !string.isNull() && reader.readPlainText(string);
    206     }
    207 
    208     return false;
     218        String string = strategy.readStringFromPasteboard(itemIndex, kUTTypeText, m_pasteboardName);
     219        if (m_changeCount != changeCount())
     220            return ReaderResult::PasteboardWasChangedExternally;
     221        return !string.isNull() && reader.readPlainText(string) ? ReaderResult::ReadType : ReaderResult::DidNotReadType;
     222    }
     223
     224    return ReaderResult::DidNotReadType;
    209225}
    210226
     
    223239        return;
    224240
     241    reader.contentOrigin = readOrigin();
     242
    225243    NSArray *types = supportedWebContentPasteboardTypes();
    226244    int numberOfTypes = [types count];
     
    228246    for (int i = 0; i < numberOfItems; i++) {
    229247        for (int typeIndex = 0; typeIndex < numberOfTypes; typeIndex++) {
    230             if (readPasteboardWebContentDataForType(reader, strategy, [types objectAtIndex:typeIndex], i, m_pasteboardName))
     248            auto result = readPasteboardWebContentDataForType(reader, strategy, [types objectAtIndex:typeIndex], i);
     249            if (result == ReaderResult::PasteboardWasChangedExternally)
     250                return;
     251            if (result == ReaderResult::ReadType)
    231252                break;
    232253        }
     
    252273        strategy.getTypesByFidelityForItemAtIndex(typesForItemInOrderOfFidelity, index, m_pasteboardName);
    253274        for (auto& type : typesForItemInOrderOfFidelity) {
    254             if (readPasteboardWebContentDataForType(reader, strategy, type, index, m_pasteboardName))
     275            auto result = readPasteboardWebContentDataForType(reader, strategy, type, index);
     276            if (result == ReaderResult::PasteboardWasChangedExternally)
     277                return;
     278            if (result == ReaderResult::ReadType)
    255279                break;
    256280        }
  • trunk/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm

    r223410 r223440  
    356356        return ASCIILiteral("text/plain");
    357357
    358     if (UTTypeConformsTo(cfType.get(), kUTTypeHTML))
     358    if (UTTypeConformsTo(cfType.get(), kUTTypeHTML) || UTTypeConformsTo(cfType.get(), (CFStringRef)WebArchivePboardType)
     359        || UTTypeConformsTo(cfType.get(), kUTTypeRTF) || UTTypeConformsTo(cfType.get(), kUTTypeFlatRTFD))
    359360        return ASCIILiteral("text/html");
    360361
  • trunk/Source/WebCore/platform/mac/PasteboardMac.mm

    r223140 r223440  
    333333    strategy.getTypes(types, m_pasteboardName);
    334334
     335    reader.contentOrigin = readOrigin();
     336
    335337    if (types.contains(WebArchivePboardType)) {
    336338        if (auto buffer = strategy.bufferForType(WebArchivePboardType, m_pasteboardName)) {
    337             if (reader.readWebArchive(*buffer))
     339            if (m_changeCount != changeCount() || reader.readWebArchive(*buffer))
    338340                return;
    339341        }
     
    343345        Vector<String> paths;
    344346        strategy.getPathnamesForType(paths, NSFilenamesPboardType, m_pasteboardName);
    345         if (reader.readFilenames(paths))
     347        if (m_changeCount != changeCount() || reader.readFilenames(paths))
    346348            return;
    347349    }
     
    349351    if (types.contains(String(NSHTMLPboardType))) {
    350352        String string = strategy.stringForType(NSHTMLPboardType, m_pasteboardName);
    351         if (!string.isNull() && reader.readHTML(string))
     353        if (m_changeCount != changeCount() || (!string.isNull() && reader.readHTML(string)))
    352354            return;
    353355    }
     
    355357    if (types.contains(String(NSRTFDPboardType))) {
    356358        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSRTFDPboardType, m_pasteboardName)) {
    357             if (reader.readRTFD(*buffer))
     359            if (m_changeCount != changeCount() || reader.readRTFD(*buffer))
    358360                return;
    359361        }
     
    362364    if (types.contains(String(NSRTFPboardType))) {
    363365        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSRTFPboardType, m_pasteboardName)) {
    364             if (reader.readRTF(*buffer))
     366            if (m_changeCount != changeCount() || reader.readRTF(*buffer))
    365367                return;
    366368        }
     
    369371    if (types.contains(String(NSTIFFPboardType))) {
    370372        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSTIFFPboardType, m_pasteboardName)) {
    371             if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/tiff")))
     373            if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/tiff")))
    372374                return;
    373375        }
     
    376378    if (types.contains(String(NSPDFPboardType))) {
    377379        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSPDFPboardType, m_pasteboardName)) {
    378             if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("application/pdf")))
     380            if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("application/pdf")))
    379381                return;
    380382        }
     
    383385    if (types.contains(String(kUTTypePNG))) {
    384386        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypePNG, m_pasteboardName)) {
    385             if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/png")))
     387            if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/png")))
    386388                return;
    387389        }
     
    390392    if (types.contains(String(kUTTypeJPEG))) {
    391393        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypeJPEG, m_pasteboardName)) {
    392             if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/jpeg")))
     394            if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/jpeg")))
    393395                return;
    394396        }
     
    398400        URL url = strategy.url(m_pasteboardName);
    399401        String title = strategy.stringForType(WebURLNamePboardType, m_pasteboardName);
    400         if (!url.isNull() && reader.readURL(url, title))
     402        if (m_changeCount != changeCount() || (!url.isNull() && reader.readURL(url, title)))
    401403            return;
    402404    }
     
    404406    if (types.contains(String(NSStringPboardType))) {
    405407        String string = strategy.stringForType(NSStringPboardType, m_pasteboardName);
    406         if (!string.isNull() && reader.readPlainText(string))
     408        if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string)))
    407409            return;
    408410    }
     
    410412    if (types.contains(String(kUTTypeUTF8PlainText))) {
    411413        String string = strategy.stringForType(kUTTypeUTF8PlainText, m_pasteboardName);
    412         if (!string.isNull() && reader.readPlainText(string))
     414        if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string)))
    413415            return;
    414416    }
  • trunk/Source/WebCore/platform/mac/PlatformPasteboardMac.mm

    r223340 r223440  
    108108        return ASCIILiteral("text/uri-list");
    109109
    110     if (platformType == String(NSHTMLPboardType))
     110    if (platformType == String(NSHTMLPboardType) || platformType == String(WebArchivePboardType)
     111        || platformType == String(NSRTFDPboardType) || platformType == String(NSRTFPboardType))
    111112        return ASCIILiteral("text/html");
    112113
  • trunk/Tools/ChangeLog

    r223433 r223440  
     12017-10-15  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Cannot access images included in the content pasted from Microsoft Word
     4        https://bugs.webkit.org/show_bug.cgi?id=124391
     5        <rdar://problem/26862741>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Added tests for sanitizing HTML contents for copy & paste and drag & drop.
     10
     11        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
     12        * TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm: Added.
     13        (readHTMLFromPasteboard): Added.
     14        (createWebViewWithCustomPasteboardDataEnabled): Added.
     15        (CopyHTML.Sanitizes): Added.
     16
     17        * TestWebKitAPI/Tests/WebKitCocoa/CopyURL.mm:
     18        (createWebViewWithCustomPasteboardDataEnabled): Added to enable more tests on bots.
     19
     20        * TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm:
     21        (writeRTFToPasteboard): Added.
     22        (createWebViewWithCustomPasteboardDataEnabled): Added.
     23        (createHelloWorldString): Added.
     24        (PasteRTF.ExposesHTMLTypeInDataTransfer): Added.
     25        (PasteRTFD.ExposesHTMLTypeInDataTransfer): Added.
     26        (PasteRTFD.ImageElementUsesBlobURLInHTML): Added.
     27
     28        * TestWebKitAPI/Tests/WebKitCocoa/copy-html.html: Added.
     29        * TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html: Store the clipboardData contents for
     30        PasteRTF.ExposesHTMLTypeInDataTransfer and PasteRTFD.ExposesHTMLTypeInDataTransfer.
     31
     32        * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
     33        (DataInteractionTests.DataTransferSanitizeHTML):
     34
    1352017-10-16  Youenn Fablet  <youenn@apple.com>
    236
  • trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

    r223427 r223440  
    543543                9B0786A51C5885C300D159E3 /* InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B0786A41C5885C300D159E3 /* InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp */; };
    544544                9B19CDA01F06DFE3000548DD /* NetworkProcessCrashWithPendingConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B19CD9E1F06DFE3000548DD /* NetworkProcessCrashWithPendingConnection.mm */; };
     545                9B1F6F781F90558400B55744 /* CopyHTML.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B1056411F9045C700D5583F /* CopyHTML.mm */; };
     546                9B1F6F791F90559E00B55744 /* copy-html.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B1056421F9047CC00D5583F /* copy-html.html */; };
     547                9B2346421F943E2700DB1D23 /* PasteWebArchive.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B2346411F943A2400DB1D23 /* PasteWebArchive.mm */; };
    545548                9B26FCCA159D16DE00CC3765 /* HTMLFormCollectionNamedItem.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B26FCB4159D15E700CC3765 /* HTMLFormCollectionNamedItem.html */; };
    546549                9B270FEE1DDC2C0B002D53F3 /* closed-shadow-tree-test.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B270FED1DDC25FD002D53F3 /* closed-shadow-tree-test.html */; };
     
    844847                                CD0BD0A81F79982D001AB2CF /* ContextMenuImgWithVideo.html in Copy Resources */,
    845848                                5C2936961D5C00ED00DEAB1E /* CookieMessage.html in Copy Resources */,
     849                                9B1F6F791F90559E00B55744 /* copy-html.html in Copy Resources */,
    846850                                9B62630C1F8C25C8007EE29B /* copy-url.html in Copy Resources */,
    847851                                7AEAD4811E20122700416EFE /* CrossPartitionFileSchemeAccess.html in Copy Resources */,
     
    15081512                9B0786A21C58830F00D159E3 /* InjectedBundleMakeAllShadowRootsOpen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedBundleMakeAllShadowRootsOpen.cpp; sourceTree = "<group>"; };
    15091513                9B0786A41C5885C300D159E3 /* InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp; sourceTree = "<group>"; };
     1514                9B1056411F9045C700D5583F /* CopyHTML.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CopyHTML.mm; sourceTree = "<group>"; };
     1515                9B1056421F9047CC00D5583F /* copy-html.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "copy-html.html"; sourceTree = "<group>"; };
    15101516                9B19CD9E1F06DFE3000548DD /* NetworkProcessCrashWithPendingConnection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NetworkProcessCrashWithPendingConnection.mm; path = WebKit/NetworkProcessCrashWithPendingConnection.mm; sourceTree = "<group>"; };
     1517                9B2346411F943A2400DB1D23 /* PasteWebArchive.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PasteWebArchive.mm; sourceTree = "<group>"; };
    15111518                9B26FC6B159D061000CC3765 /* HTMLFormCollectionNamedItem.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HTMLFormCollectionNamedItem.mm; sourceTree = "<group>"; };
    15121519                9B26FCB4159D15E700CC3765 /* HTMLFormCollectionNamedItem.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = HTMLFormCollectionNamedItem.html; sourceTree = "<group>"; };
     
    19871994                                5CA1DED81F74A87100E71BD3 /* ContentRuleListNotification.mm */,
    19881995                                5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */,
     1996                                9B1056411F9045C700D5583F /* CopyHTML.mm */,
    19891997                                9999108A1F393C8B008AD455 /* Copying.mm */,
    19901998                                9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */,
     
    20272035                                9BDCCD851F7D0B0700009A18 /* PasteImage.mm */,
    20282036                                9BDD95561F83683600D20C60 /* PasteRTFD.mm */,
     2037                                9B2346411F943A2400DB1D23 /* PasteWebArchive.mm */,
    20292038                                3FCC4FE41EC4E8520076E37C /* PictureInPictureDelegate.mm */,
    20302039                                83BAEE8C1EF4625500DDE894 /* PluginLoadClientPolicies.mm */,
     
    22372246                                A16F66B91C40EA2000BD4D24 /* ContentFiltering.html */,
    22382247                                5C2936941D5BFD1900DEAB1E /* CookieMessage.html */,
     2248                                9B1056421F9047CC00D5583F /* copy-html.html */,
    22392249                                9B62630B1F8C2510007EE29B /* copy-url.html */,
    22402250                                F4AB57891F65164B00DB0DA1 /* custom-draggable-div.html */,
     
    32303240                                5C2936931D5BF70D00DEAB1E /* CookieAcceptPolicy.mm in Sources */,
    32313241                                51D1249B1E785425002B2820 /* CookieManager.cpp in Sources */,
     3242                                9B1F6F781F90558400B55744 /* CopyHTML.mm in Sources */,
    32323243                                9999108B1F393C96008AD455 /* Copying.mm in Sources */,
    32333244                                9B7A37C41F8AEBA5004AA228 /* CopyURL.mm in Sources */,
     
    33903401                                9BDCCD871F7D0B0700009A18 /* PasteImage.mm in Sources */,
    33913402                                9BDD95581F83683600D20C60 /* PasteRTFD.mm in Sources */,
     3403                                9B2346421F943E2700DB1D23 /* PasteWebArchive.mm in Sources */,
    33923404                                7C83E0531D0A643A00FEBCF3 /* PendingAPIRequestURL.cpp in Sources */,
    33933405                                3FCC4FE51EC4E8520076E37C /* PictureInPictureDelegate.mm in Sources */,
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyURL.mm

    r223195 r223440  
    5858#endif
    5959
     60static RetainPtr<TestWKWebView> createWebViewWithCustomPasteboardDataEnabled()
     61{
     62    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
     63    auto preferences = (WKPreferencesRef)[[webView configuration] preferences];
     64    WKPreferencesSetDataTransferItemsEnabled(preferences, true);
     65    WKPreferencesSetCustomPasteboardDataEnabled(preferences, true);
     66    return webView;
     67}
     68
    6069TEST(CopyURL, ValidURL)
    6170{
    62     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
     71    auto webView = createWebViewWithCustomPasteboardDataEnabled();
    6372    [webView synchronouslyLoadTestPageNamed:@"copy-url"];
    6473    [webView stringByEvaluatingJavaScript:@"URLToCopy = 'http://webkit.org/b/123';"];
     
    7180}
    7281
    73 // FIXME: We should add a mechanism to enable custom pasteboard data in older OS for testing purposes.
    74 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED > 101300)
    75 
    7682TEST(CopyURL, UnescapedURL)
    7783{
    78     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
     84    auto webView = createWebViewWithCustomPasteboardDataEnabled();
    7985    [webView synchronouslyLoadTestPageNamed:@"copy-url"];
    8086    [webView stringByEvaluatingJavaScript:@"URLToCopy = 'http://webkit.org/b/\u4F60\u597D;?x=8 + 6';"];
     
    8995TEST(CopyURL, MalformedURL)
    9096{
    91     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
     97    auto webView = createWebViewWithCustomPasteboardDataEnabled();
    9298    [webView synchronouslyLoadTestPageNamed:@"copy-url"];
    9399    [webView stringByEvaluatingJavaScript:@"URLToCopy = 'bad url';"];
     
    100106}
    101107
    102 #endif
    103 
    104108#endif // WK_API_ENABLED && PLATFORM(MAC)
    105109
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm

    r222839 r223440  
    3030#import "PlatformUtilities.h"
    3131#import "TestWKWebView.h"
     32#import <WebKit/WKPreferencesPrivate.h>
     33#import <WebKit/WKPreferencesRefPrivate.h>
     34#import <WebKit/WKWebViewConfigurationPrivate.h>
    3235#import <wtf/RetainPtr.h>
    3336#import <wtf/text/WTFString.h>
     
    4245
    4346#if PLATFORM(MAC)
     47void writeRTFToPasteboard(NSData *data)
     48{
     49    [[NSPasteboard generalPasteboard] declareTypes:@[NSPasteboardTypeRTF] owner:nil];
     50    [[NSPasteboard generalPasteboard] setData:data forType:NSPasteboardTypeRTF];
     51}
     52
    4453void writeRTFDToPasteboard(NSData *data)
    4554{
     
    5766@end
    5867
     68void writeRTFToPasteboard(NSData *data)
     69{
     70    [[UIPasteboard generalPasteboard] setItems:@[@{ (NSString *)kUTTypeRTF : data}]];
     71}
     72
    5973void writeRTFDToPasteboard(NSData *data)
    6074{
     
    6377#endif
    6478
     79static RetainPtr<TestWKWebView> createWebViewWithCustomPasteboardDataEnabled()
     80{
     81    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
     82    auto preferences = (WKPreferencesRef)[[webView configuration] preferences];
     83    WKPreferencesSetDataTransferItemsEnabled(preferences, true);
     84    WKPreferencesSetCustomPasteboardDataEnabled(preferences, true);
     85    return webView;
     86}
     87
     88static RetainPtr<NSAttributedString> createHelloWorldString()
     89{
     90    auto hello = adoptNS([[NSAttributedString alloc] initWithString:@"hello" attributes:@{ NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) }]);
     91    auto world = adoptNS([[NSAttributedString alloc] initWithString:@", world" attributes:@{ }]);
     92    auto string = adoptNS([[NSMutableAttributedString alloc] init]);
     93    [string appendAttributedString:hello.get()];
     94    [string appendAttributedString:world.get()];
     95    return string;
     96}
     97
     98TEST(PasteRTF, ExposesHTMLTypeInDataTransfer)
     99{
     100    auto webView = createWebViewWithCustomPasteboardDataEnabled();
     101    [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
     102
     103    auto string = createHelloWorldString();
     104    writeRTFToPasteboard([string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }]);
     105    [webView paste:nil];
     106
     107    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"]);
     108    [webView stringByEvaluatingJavaScript:@"editor.innerHTML = clipboardData.values[0]; editor.focus()"];
     109    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue);
     110    [webView stringByEvaluatingJavaScript:@"getSelection().modify('move', 'forward', 'lineboundary')"];
     111    EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue);
     112    EXPECT_WK_STREQ("hello, world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
     113}
     114
    65115TEST(PasteRTFD, EmptyRTFD)
    66116{
    67     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
     117    auto webView = createWebViewWithCustomPasteboardDataEnabled();
    68118    [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><html><body><div id='editor' contenteditable></div></body></html>"];
    69119
     
    73123}
    74124
    75 TEST(PasteRTFD, ImageElementsUseBlobURL)
     125TEST(PasteRTFD, ExposesHTMLTypeInDataTransfer)
    76126{
    77     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
     127    auto webView = createWebViewWithCustomPasteboardDataEnabled();
     128    [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
     129
     130    auto string = createHelloWorldString();
     131    writeRTFDToPasteboard([string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }]);
     132    [webView paste:nil];
     133
     134    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"]);
     135    [webView stringByEvaluatingJavaScript:@"editor.innerHTML = clipboardData.values[0]; editor.focus()"];
     136    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue);
     137    [webView stringByEvaluatingJavaScript:@"getSelection().modify('move', 'forward', 'lineboundary')"];
     138    EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue);
     139    EXPECT_WK_STREQ("hello, world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
     140}
     141
     142TEST(PasteRTFD, ImageElementUsesBlobURL)
     143{
     144    auto webView = createWebViewWithCustomPasteboardDataEnabled();
    78145    [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
    79146
     
    91158}
    92159
     160TEST(PasteRTFD, ImageElementUsesBlobURLInHTML)
     161{
     162    auto webView = createWebViewWithCustomPasteboardDataEnabled();
     163    [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
     164
     165    auto *pngData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sunset-in-cupertino-200px" ofType:@"png" inDirectory:@"TestWebKitAPI.resources"]];
     166    auto attachment = adoptNS([[NSTextAttachment alloc] initWithData:pngData ofType:(NSString *)kUTTypePNG]);
     167    NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment.get()];
     168    NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }];
     169
     170    writeRTFDToPasteboard(RTFDData);
     171    [webView paste:nil];
     172
     173    [webView waitForMessage:@"loaded"];
     174    EXPECT_WK_STREQ("[\"text/html\"]", [webView stringByEvaluatingJavaScript:@"JSON.stringify(clipboardData.types)"]);
     175    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"imageElement = (new DOMParser).parseFromString(clipboardData.values[0], 'text/html').querySelector('img'); !!imageElement"].boolValue);
     176    EXPECT_WK_STREQ("blob:", [webView stringByEvaluatingJavaScript:@"new URL(imageElement.src).protocol"]);
     177}
     178
    93179#endif // WK_API_ENABLED && PLATFORM(MAC)
    94180
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html

    r222839 r223440  
    77editor.focus();
    88
     9var clipboardData = {};
    910editor.addEventListener('paste', (event) => {
     11    clipboardData.types = Array.from(event.clipboardData.types);
     12    clipboardData.items = Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type}));
     13    clipboardData.values = clipboardData.types.map((type) => event.clipboardData.getData(type));
     14    clipboardData.files = Array.from(event.clipboardData.files);
     15
    1016    setTimeout(() => {
    1117       let img = document.querySelector('img');
  • trunk/Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm

    r223340 r223440  
    17651765}
    17661766
     1767TEST(DataInteractionTests, DataTransferSanitizeHTML)
     1768{
     1769    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     1770    [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"];
     1771    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
     1772
     1773    [webView stringByEvaluatingJavaScript:@"select(rich)"];
     1774    [webView stringByEvaluatingJavaScript:@"customData = { 'text/html' : '<meta content=\"secret\">"
     1775        "<b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>' }"];
     1776    [webView stringByEvaluatingJavaScript:@"writeCustomData = true"];
     1777
     1778    __block bool done = false;
     1779    [simulator.get() setOverridePerformDropBlock:^NSArray<UIDragItem *> *(id <UIDropSession> session)
     1780    {
     1781        EXPECT_EQ(1UL, session.items.count);
     1782        auto *item = session.items[0].itemProvider;
     1783        EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(NSString *)kUTTypeHTML]);
     1784        [item loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML completionHandler:^(NSData *data, NSError *error) {
     1785            NSString *markup = [[[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding] autorelease];
     1786            EXPECT_TRUE([markup containsString:@"hello"]);
     1787            EXPECT_TRUE([markup containsString:@", world"]);
     1788            EXPECT_FALSE([markup containsString:@"secret"]);
     1789            EXPECT_FALSE([markup containsString:@"dangerousCode"]);
     1790            done = true;
     1791        }];
     1792        return session.items;
     1793    }];
     1794    [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)];
     1795
     1796    checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{
     1797        @"dragover": @{
     1798            @"text/html": @"",
     1799        },
     1800        @"drop": @{
     1801            @"text/html": @"<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>",
     1802        }
     1803    });
     1804    TestWebKitAPI::Util::run(&done);
     1805}
     1806
    17671807#endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300
    17681808
Note: See TracChangeset for help on using the changeset viewer.