WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Fixed one more test
bug-124391-20171013165620.patch (text/plain), 130.70 KB, created by
Ryosuke Niwa
on 2017-10-13 16:56:21 PDT
(
hide
)
Description:
Fixed one more test
Filename:
MIME Type:
Creator:
Ryosuke Niwa
Created:
2017-10-13 16:56:21 PDT
Size:
130.70 KB
patch
obsolete
>Index: Source/WebCore/ChangeLog >=================================================================== >--- Source/WebCore/ChangeLog (revision 223280) >+++ Source/WebCore/ChangeLog (working copy) >@@ -1,3 +1,103 @@ >+2017-10-13 Ryosuke Niwa <rniwa@webkit.org> >+ >+ 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 NOBODY (OOPS!). >+ >+ 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 >+ PasteRTF.ExposesHTMLTypeInDataTransfer >+ PasteRTFD.ExposesHTMLTypeInDataTransfer >+ PasteRTFD.ImageElementUsesBlobURLInHTML >+ DataInteractionTests.DataTransferSanitizeHTML >+ >+ * 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/DataTransferItemList.cpp: >+ (WebCore::DataTransferItemList::add): >+ * dom/DataTransferItemList.h: >+ * dom/DataTransferItemList.idl: >+ * 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. >+ > 2017-10-13 Jer Noble <jer.noble@apple.com> > > Performance: do pixel conformance and texturing in a single step. >Index: Source/WebCore/dom/DataTransfer.cpp >=================================================================== >--- Source/WebCore/dom/DataTransfer.cpp (revision 223280) >+++ Source/WebCore/dom/DataTransfer.cpp (working copy) >@@ -30,6 +30,7 @@ > #include "CachedImageClient.h" > #include "DataTransferItem.h" > #include "DataTransferItemList.h" >+#include "DocumentFragment.h" > #include "DragData.h" > #include "Editor.h" > #include "FileList.h" >@@ -42,7 +43,9 @@ > #include "Settings.h" > #include "StaticPasteboard.h" > #include "URLParser.h" >+#include "WebContentReader.h" > #include "WebCorePasteboardFileReader.h" >+#include "markup.h" > > namespace WebCore { > >@@ -78,18 +81,10 @@ DataTransfer::DataTransfer(StoreMode mod > #endif > } > >-static String originIdentifierForDocument(Document& document) >-{ >- auto origin = document.securityOrigin().toString(); >- if (origin == "null") >- return document.uniqueIdentifier(); >- return origin; >-} >- > Ref<DataTransfer> DataTransfer::createForCopyAndPaste(Document& document, StoreMode storeMode, std::unique_ptr<Pasteboard>&& pasteboard) > { > auto dataTransfer = adoptRef(*new DataTransfer(storeMode, WTFMove(pasteboard))); >- dataTransfer->m_originIdentifier = originIdentifierForDocument(document); >+ dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); > return dataTransfer; > } > >@@ -146,10 +141,10 @@ void DataTransfer::clearData(const Strin > m_itemList->didClearStringData(normalizedType); > } > >-String DataTransfer::getDataForItem(const String& type) const >+String DataTransfer::getDataForItem(Document& document, const String& type) const > { > if (!canReadData()) >- return String(); >+ return { }; > > if ((forFileDrag() || Settings::customPasteboardDataEnabled()) && m_pasteboard->containsFiles()) > return { }; >@@ -158,47 +153,49 @@ String DataTransfer::getDataForItem(cons > if (!Settings::customPasteboardDataEnabled()) > return m_pasteboard->readString(lowercaseType); > >- bool isSameOrigin = false; >- if (is<StaticPasteboard>(*m_pasteboard)) { >- // StaticPasteboard is only used to stage data written by websites before being committed to the system pasteboard. >- isSameOrigin = true; >- } else if (!m_originIdentifier.isNull()) { >- String originOfPasteboard = m_pasteboard->readOrigin(); >- isSameOrigin = m_originIdentifier == originOfPasteboard; >+ // StaticPasteboard is only used to stage data written by websites before being committed to the system pasteboard. >+ bool isSameOrigin = is<StaticPasteboard>(*m_pasteboard) || (!m_originIdentifier.isNull() && m_originIdentifier == m_pasteboard->readOrigin()); >+ if (isSameOrigin) { >+ String value = m_pasteboard->readStringInCustomData(lowercaseType); >+ if (!value.isNull()) >+ return value; > } >+ if (!Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType)) >+ return { }; > >- if (!isSameOrigin) { >- if (!Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType)) >+ if (!is<StaticPasteboard>(*m_pasteboard) && type == "text/html") { >+ if (!document.frame()) > return { }; >- return m_pasteboard->readString(lowercaseType); >+ WebContentMarkupReader reader { *document.frame() }; >+ m_pasteboard->read(reader); >+ return reader.markup; > } > >- String value = m_pasteboard->readStringInCustomData(lowercaseType); >- if (value.isNull() && Pasteboard::isSafeTypeForDOMToReadAndWrite(lowercaseType)) >- value = m_pasteboard->readString(lowercaseType); >- return value; >+ return m_pasteboard->readString(type); > } > >-String DataTransfer::getData(const String& type) const >+String DataTransfer::getData(ScriptExecutionContext& context, const String& type) const > { >- return getDataForItem(normalizeType(type)); >+ if (!is<Document>(context)) >+ return { }; >+ return getDataForItem(downcast<Document>(context), normalizeType(type)); > } > >-void DataTransfer::setData(const String& type, const String& data) >+void DataTransfer::setData(ScriptExecutionContext& context, const String& type, const String& data) > { >- if (!canWriteData()) >+ if (!canWriteData() || !is<Document>(context)) > return; > > if ((forFileDrag() || Settings::customPasteboardDataEnabled()) && m_pasteboard->containsFiles()) > return; > > auto normalizedType = normalizeType(type); >- setDataFromItemList(normalizedType, data); >+ setDataFromItemList(downcast<Document>(context), normalizedType, data); > if (m_itemList) > m_itemList->didSetStringData(normalizedType); > } > >-void DataTransfer::setDataFromItemList(const String& type, const String& data) >+void DataTransfer::setDataFromItemList(Document&, const String& type, const String& data) > { > ASSERT(canWriteData()); > RELEASE_ASSERT(is<StaticPasteboard>(*m_pasteboard)); >@@ -209,11 +206,13 @@ void DataTransfer::setDataFromItemList(c > } > > String sanitizedData; >- if (type == "text/uri-list") { >+ if (type == "text/html") >+ sanitizedData = sanitizeMarkup(data); >+ else if (type == "text/uri-list") { > auto url = URLParser(data).result(); > if (url.isValid()) > sanitizedData = url.string(); >- } else if (type == "text/plain" || type == "text/html") >+ } else if (type == "text/plain") > sanitizedData = data; // Nothing to sanitize. > > if (sanitizedData != data) >@@ -411,7 +410,7 @@ Ref<DataTransfer> DataTransfer::createFo > Ref<DataTransfer> DataTransfer::createForDragStartEvent(Document& document) > { > auto dataTransfer = adoptRef(*new DataTransfer(StoreMode::ReadWrite, std::make_unique<StaticPasteboard>(), Type::DragAndDropData)); >- dataTransfer->m_originIdentifier = originIdentifierForDocument(document); >+ dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); > return dataTransfer; > } > >@@ -419,7 +418,7 @@ Ref<DataTransfer> DataTransfer::createFo > { > auto dataTransfer = adoptRef(*new DataTransfer(DataTransfer::StoreMode::Readonly, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData)); > dataTransfer->setSourceOperation(sourceOperation); >- dataTransfer->m_originIdentifier = originIdentifierForDocument(document); >+ dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); > return dataTransfer; > } > >@@ -434,7 +433,7 @@ Ref<DataTransfer> DataTransfer::createFo > #endif > auto dataTransfer = adoptRef(*new DataTransfer(mode, WTFMove(pasteboard), draggingFiles ? Type::DragAndDropFiles : Type::DragAndDropData)); > dataTransfer->setSourceOperation(sourceOperation); >- dataTransfer->m_originIdentifier = originIdentifierForDocument(document); >+ dataTransfer->m_originIdentifier = document.originIdentifierForPasteboard(); > return dataTransfer; > } > >Index: Source/WebCore/dom/DataTransfer.h >=================================================================== >--- Source/WebCore/dom/DataTransfer.h (revision 223280) >+++ Source/WebCore/dom/DataTransfer.h (working copy) >@@ -39,6 +39,7 @@ class Element; > class FileList; > class File; > class Pasteboard; >+class ScriptExecutionContext; > > class DataTransfer : public RefCounted<DataTransfer> { > public: >@@ -64,11 +65,11 @@ public: > > void clearData(const String& type = String()); > >- String getData(const String& type) const; >- String getDataForItem(const String& type) const; >+ String getData(ScriptExecutionContext&, const String& type) const; >+ String getDataForItem(Document&, const String& type) const; > >- void setData(const String& type, const String& data); >- void setDataFromItemList(const String& type, const String& data); >+ void setData(ScriptExecutionContext&, const String& type, const String& data); >+ void setDataFromItemList(Document&, const String& type, const String& data); > > void setDragImage(Element*, int x, int y); > >Index: Source/WebCore/dom/DataTransfer.idl >=================================================================== >--- Source/WebCore/dom/DataTransfer.idl (revision 223280) >+++ Source/WebCore/dom/DataTransfer.idl (working copy) >@@ -38,8 +38,8 @@ > void setDragImage(Element? image, long x, long y); // FIXME: Element argument is not nullable in the HTML standard. > > readonly attribute FrozenArray<DOMString> types; >- DOMString getData(DOMString format); >- void setData(DOMString format, DOMString data); >+ [CallWith=ScriptExecutionContext] DOMString getData(DOMString format); >+ [CallWith=ScriptExecutionContext] void setData(DOMString format, DOMString data); > void clearData(optional DOMString format); > // FIXME: files should use [SameObject] once that is supported. > readonly attribute FileList files; >Index: Source/WebCore/dom/DataTransferItem.cpp >=================================================================== >--- Source/WebCore/dom/DataTransferItem.cpp (revision 223280) >+++ Source/WebCore/dom/DataTransferItem.cpp (working copy) >@@ -34,6 +34,7 @@ > > #include "DOMFileSystem.h" > #include "DataTransferItemList.h" >+#include "Document.h" > #include "File.h" > #include "FileSystem.h" > #include "FileSystemDirectoryEntry.h" >@@ -91,11 +92,11 @@ void DataTransferItem::getAsString(Scrip > return; > > auto& dataTransfer = m_list->dataTransfer(); >- if (!dataTransfer.canReadData()) >+ if (!dataTransfer.canReadData() || !is<Document>(context)) > return; > > // FIXME: Make this async. >- callback->scheduleCallback(context, dataTransfer.getDataForItem(m_type)); >+ callback->scheduleCallback(context, dataTransfer.getDataForItem(downcast<Document>(context), m_type)); > } > > RefPtr<File> DataTransferItem::getAsFile() const >Index: Source/WebCore/dom/DataTransferItemList.cpp >=================================================================== >--- Source/WebCore/dom/DataTransferItemList.cpp (revision 223280) >+++ Source/WebCore/dom/DataTransferItemList.cpp (working copy) >@@ -27,6 +27,7 @@ > #include "DataTransferItemList.h" > > #include "DataTransferItem.h" >+#include "Document.h" > #include "FileList.h" > #include "Pasteboard.h" > #include "Settings.h" >@@ -60,9 +61,9 @@ static bool shouldExposeTypeInItemList(c > return Settings::customPasteboardDataEnabled() || Pasteboard::isSafeTypeForDOMToReadAndWrite(type); > } > >-ExceptionOr<RefPtr<DataTransferItem>> DataTransferItemList::add(const String& data, const String& type) >+ExceptionOr<RefPtr<DataTransferItem>> DataTransferItemList::add(ScriptExecutionContext& context, const String& data, const String& type) > { >- if (!m_dataTransfer.canWriteData()) >+ if (!m_dataTransfer.canWriteData() || !is<Document>(context)) > return nullptr; > > for (auto& item : ensureItems()) { >@@ -75,7 +76,7 @@ ExceptionOr<RefPtr<DataTransferItem>> Da > if (!shouldExposeTypeInItemList(lowercasedType)) > return nullptr; > >- m_dataTransfer.setDataFromItemList(lowercasedType, data); >+ m_dataTransfer.setDataFromItemList(downcast<Document>(context), lowercasedType, data); > ASSERT(m_items); > m_items->append(DataTransferItem::create(m_weakPtrFactory.createWeakPtr(*this), lowercasedType)); > return m_items->last().ptr(); >Index: Source/WebCore/dom/DataTransferItemList.h >=================================================================== >--- Source/WebCore/dom/DataTransferItemList.h (revision 223280) >+++ Source/WebCore/dom/DataTransferItemList.h (working copy) >@@ -58,7 +58,7 @@ public: > // DOM API > unsigned length() const; > RefPtr<DataTransferItem> item(unsigned index); >- ExceptionOr<RefPtr<DataTransferItem>> add(const String& data, const String& type); >+ ExceptionOr<RefPtr<DataTransferItem>> add(ScriptExecutionContext&, const String& data, const String& type); > RefPtr<DataTransferItem> add(Ref<File>&&); > ExceptionOr<void> remove(unsigned index); > void clear(); >Index: Source/WebCore/dom/DataTransferItemList.idl >=================================================================== >--- Source/WebCore/dom/DataTransferItemList.idl (revision 223280) >+++ Source/WebCore/dom/DataTransferItemList.idl (working copy) >@@ -38,7 +38,7 @@ > readonly attribute long length; > getter DataTransferItem item(unsigned long index); > >- [MayThrowException] DataTransferItem? add(DOMString data, DOMString type); >+ [MayThrowException, CallWith=ScriptExecutionContext] DataTransferItem? add(DOMString data, DOMString type); > DataTransferItem? add(File file); > > [MayThrowException] void remove(unsigned long index); >Index: Source/WebCore/dom/Document.cpp >=================================================================== >--- Source/WebCore/dom/Document.cpp (revision 223280) >+++ Source/WebCore/dom/Document.cpp (working copy) >@@ -5300,8 +5300,11 @@ bool Document::isTelephoneNumberParsingA > > #endif > >-String Document::uniqueIdentifier() >+String Document::originIdentifierForPasteboard() > { >+ auto origin = securityOrigin().toString(); >+ if (origin != "null") >+ return origin; > if (!m_uniqueIdentifier) > m_uniqueIdentifier = "null:" + createCanonicalUUIDString(); > return m_uniqueIdentifier; >Index: Source/WebCore/dom/Document.h >=================================================================== >--- Source/WebCore/dom/Document.h (revision 223280) >+++ Source/WebCore/dom/Document.h (working copy) >@@ -957,7 +957,7 @@ public: > void incDOMTreeVersion() { m_domTreeVersion = ++s_globalTreeVersion; } > uint64_t domTreeVersion() const { return m_domTreeVersion; } > >- String uniqueIdentifier(); >+ String originIdentifierForPasteboard(); > > // XPathEvaluator methods > WEBCORE_EXPORT ExceptionOr<Ref<XPathExpression>> createExpression(const String& expression, RefPtr<XPathNSResolver>&&); >Index: Source/WebCore/editing/WebContentReader.cpp >=================================================================== >--- Source/WebCore/editing/WebContentReader.cpp (revision 223280) >+++ Source/WebCore/editing/WebContentReader.cpp (working copy) >@@ -26,6 +26,7 @@ > #include "config.h" > #include "WebContentReader.h" > >+#include "Document.h" > #include "DocumentFragment.h" > > namespace WebCore { >@@ -38,5 +39,10 @@ void WebContentReader::addFragment(Ref<D > fragment->appendChild(newFragment.get()); > } > >+bool WebContentMarkupReader::shouldSanitize() const >+{ >+ return frame.document() && frame.document()->originIdentifierForPasteboard() != contentOrigin; >+} >+ > } > >Index: Source/WebCore/editing/WebContentReader.h >=================================================================== >--- Source/WebCore/editing/WebContentReader.h (revision 223280) >+++ Source/WebCore/editing/WebContentReader.h (working copy) >@@ -65,6 +65,31 @@ private: > bool readPlainText(const String&) override; > }; > >+class WebContentMarkupReader final : public PasteboardWebContentReader { >+public: >+ Frame& frame; >+ String markup; >+ >+ explicit WebContentMarkupReader(Frame& frame) >+ : frame(frame) >+ { >+ } >+ >+private: >+ bool shouldSanitize() const; >+ >+#if PLATFORM(COCOA) >+ bool readWebArchive(SharedBuffer&) override; >+ bool readFilenames(const Vector<String>&) override { return false; } >+ bool readHTML(const String&) override; >+ bool readRTFD(SharedBuffer&) override; >+ bool readRTF(SharedBuffer&) override; >+ bool readImage(Ref<SharedBuffer>&&, const String&) override { return false; } >+ bool readURL(const URL&, const String&) override { return false; } >+#endif >+ bool readPlainText(const String&) override { return false; } >+}; >+ > #if PLATFORM(COCOA) && defined(__OBJC__) > struct FragmentAndResources { > RefPtr<DocumentFragment> fragment; >Index: Source/WebCore/editing/markup.cpp >=================================================================== >--- Source/WebCore/editing/markup.cpp (revision 223280) >+++ Source/WebCore/editing/markup.cpp (working copy) >@@ -34,13 +34,16 @@ > #include "CSSPropertyNames.h" > #include "CSSValue.h" > #include "CSSValueKeywords.h" >+#include "CacheStorageProvider.h" > #include "ChildListMutationScope.h" > #include "DocumentFragment.h" > #include "DocumentLoader.h" > #include "DocumentType.h" > #include "Editing.h" > #include "Editor.h" >+#include "EditorClient.h" > #include "ElementIterator.h" >+#include "EmptyClients.h" > #include "File.h" > #include "Frame.h" > #include "FrameLoader.h" >@@ -55,15 +58,20 @@ > #include "HTMLTableElement.h" > #include "HTMLTextAreaElement.h" > #include "HTMLTextFormControlElement.h" >-#include "URL.h" >+#include "LibWebRTCProvider.h" >+#include "MainFrame.h" > #include "MarkupAccumulator.h" > #include "NodeList.h" >+#include "Page.h" >+#include "PageConfiguration.h" > #include "Range.h" > #include "RenderBlock.h" > #include "Settings.h" >+#include "SocketProvider.h" > #include "StyleProperties.h" > #include "TextIterator.h" > #include "TypedElementDescendantIterator.h" >+#include "URL.h" > #include "VisibleSelection.h" > #include "VisibleUnits.h" > #include <wtf/StdLibExtras.h> >@@ -135,6 +143,51 @@ void replaceSubresourceURLs(Ref<Document > for (auto& change : changes) > change.apply(); > } >+ >+std::unique_ptr<Page> createPageForSanitizingWebContent() >+{ >+ PageConfiguration pageConfiguration(createEmptyEditorClient(), SocketProvider::create(), LibWebRTCProvider::create(), CacheStorageProvider::create()); >+ >+ fillWithEmptyClients(pageConfiguration); >+ >+ auto page = std::make_unique<Page>(WTFMove(pageConfiguration)); >+ page->settings().setMediaEnabled(false); >+ page->settings().setScriptEnabled(false); >+ page->settings().setPluginsEnabled(false); >+ page->settings().setAcceleratedCompositingEnabled(false); >+ >+ Frame& frame = page->mainFrame(); >+ frame.setView(FrameView::create(frame)); >+ frame.init(); >+ >+ FrameLoader& loader = frame.loader(); >+ static char markup[] = "<!DOCTYPE html><html><body></body></html>"; >+ ASSERT(loader.activeDocumentLoader()); >+ loader.activeDocumentLoader()->writer().setMIMEType("text/html"); >+ loader.activeDocumentLoader()->writer().begin(); >+ loader.activeDocumentLoader()->writer().addData(markup, sizeof(markup)); >+ loader.activeDocumentLoader()->writer().end(); >+ >+ return page; >+} >+ >+ >+String sanitizeMarkup(const String& rawHTML) >+{ >+ auto page = createPageForSanitizingWebContent(); >+ Document* stagingDocument = page->mainFrame().document(); >+ ASSERT(stagingDocument); >+ auto* bodyElement = stagingDocument->body(); >+ ASSERT(bodyElement); >+ >+ auto fragment = createFragmentFromMarkup(*stagingDocument, rawHTML, emptyString(), DisallowScriptingAndPluginContent); >+ bodyElement->appendChild(fragment.get()); >+ >+ auto range = Range::create(*stagingDocument); >+ range->selectNodeContents(*bodyElement); >+ return createMarkup(range.get(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs); >+} >+ > > class StyledMarkupAccumulator final : public MarkupAccumulator { > public: >Index: Source/WebCore/editing/markup.h >=================================================================== >--- Source/WebCore/editing/markup.h (revision 223280) >+++ Source/WebCore/editing/markup.h (working copy) >@@ -42,10 +42,13 @@ class Frame; > class HTMLElement; > class URL; > class Node; >+class Page; > class QualifiedName; > class Range; > > void replaceSubresourceURLs(Ref<DocumentFragment>&&, HashMap<AtomicString, AtomicString>&&); >+std::unique_ptr<Page> createPageForSanitizingWebContent(); >+String sanitizeMarkup(const String&); > > enum EChildrenOnly { IncludeNode, ChildrenOnly }; > enum EAbsoluteURLs { DoNotResolveURLs, ResolveAllURLs, ResolveNonLocalURLs }; >Index: Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm >=================================================================== >--- Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm (revision 223280) >+++ Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm (working copy) >@@ -39,8 +39,10 @@ > #import "HTMLBodyElement.h" > #import "HTMLImageElement.h" > #import "LegacyWebArchive.h" >+#import "MainFrame.h" > #import "Page.h" > #import "Settings.h" >+#import "SocketProvider.h" > #import "WebArchiveResourceFromNSAttributedString.h" > #import "WebArchiveResourceWebResourceHandler.h" > #import "WebNSAttributedStringExtras.h" >@@ -179,37 +181,84 @@ RefPtr<DocumentFragment> createFragmentA > return WTFMove(fragmentAndResources.fragment); > } > >-bool WebContentReader::readWebArchive(SharedBuffer& buffer) >-{ >- if (frame.settings().preferMIMETypeForImages() || !frame.document()) >- return false; >+struct FragmentAndArchive { >+ Ref<DocumentFragment> fragment; >+ Ref<Archive> archive; >+}; > >+static std::optional<FragmentAndArchive> createFragmentFromWebArchive(Document& document, SharedBuffer& buffer, const std::function<bool(const String)>& canShowMIMETypeAsHTML) >+{ > auto archive = LegacyWebArchive::create(URL(), buffer); > if (!archive) >- return false; >+ return std::nullopt; > > RefPtr<ArchiveResource> mainResource = archive->mainResource(); > if (!mainResource) >- return false; >+ return std::nullopt; > > auto type = mainResource->mimeType(); >- if (!frame.loader().client().canShowMIMETypeAsHTML(type)) >+ if (!canShowMIMETypeAsHTML(type)) >+ return std::nullopt; >+ >+ auto markupString = String::fromUTF8(mainResource->data().data(), mainResource->data().size()); >+ auto fragment = createFragmentFromMarkup(document, markupString, mainResource->url(), DisallowScriptingAndPluginContent); >+ >+ return FragmentAndArchive { WTFMove(fragment), archive.releaseNonNull() }; >+} >+ >+bool WebContentReader::readWebArchive(SharedBuffer& buffer) >+{ >+ if (frame.settings().preferMIMETypeForImages() || !frame.document()) > return false; > > DeferredLoadingScope scope(frame); >- auto markupString = String::fromUTF8(mainResource->data().data(), mainResource->data().size()); >- addFragment(createFragmentFromMarkup(*frame.document(), markupString, mainResource->url(), DisallowScriptingAndPluginContent)); >+ auto result = createFragmentFromWebArchive(*frame.document(), buffer, [&] (const String& type) { >+ return frame.loader().client().canShowMIMETypeAsHTML(type); >+ }); >+ if (!result) >+ return false; > >+ fragment = WTFMove(result->fragment); > if (DocumentLoader* loader = frame.loader().documentLoader()) >- loader->addAllArchiveResources(*archive); >+ loader->addAllArchiveResources(result->archive.get()); > > return true; > } > >-bool WebContentReader::readHTML(const String& string) >+bool WebContentMarkupReader::readWebArchive(SharedBuffer& buffer) >+{ >+ auto page = createPageForSanitizingWebContent(); >+ Document* stagingDocument = page->mainFrame().document(); >+ ASSERT(stagingDocument); >+ >+ DeferredLoadingScope scope(frame); >+ auto result = createFragmentFromWebArchive(*stagingDocument, buffer, [&] (const String& type) { >+ return frame.loader().client().canShowMIMETypeAsHTML(type); >+ }); >+ if (!result) >+ return false; >+ >+ HashMap<AtomicString, AtomicString> blobURLMap; >+ for (const Ref<ArchiveResource>& subresource : result->archive->subresources()) { >+ auto blob = Blob::create(subresource->data(), subresource->mimeType()); >+ String blobURL = DOMURL::createObjectURL(*frame.document(), blob); >+ blobURLMap.set(subresource->url().string(), blobURL); >+ } >+ replaceSubresourceURLs(result->fragment.get(), WTFMove(blobURLMap)); >+ >+ auto* bodyElement = stagingDocument->body(); >+ ASSERT(bodyElement); >+ bodyElement->appendChild(result->fragment); >+ >+ auto range = Range::create(*stagingDocument); >+ range->selectNodeContents(*bodyElement); >+ markup = createMarkup(range.get(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs); >+ >+ return true; >+} >+ >+static String stripMicrosoftPrefix(const String& string) > { >- String stringOmittingMicrosoftPrefix = string; >- > #if PLATFORM(MAC) > // This code was added to make HTML paste from Microsoft Word on Mac work, back in 2004. > // It's a simple-minded way to ignore the CF_HTML clipboard format, just skipping over the >@@ -217,24 +266,43 @@ bool WebContentReader::readHTML(const St > if (string.startsWith("Version:")) { > size_t location = string.findIgnoringCase("<html"); > if (location != notFound) >- stringOmittingMicrosoftPrefix = string.substring(location); >+ return string.substring(location); > } > #endif >+ return string; >+} > >- if (stringOmittingMicrosoftPrefix.isEmpty()) >+bool WebContentReader::readHTML(const String& string) >+{ >+ if (frame.settings().preferMIMETypeForImages() || !frame.document()) > return false; >+ Document& document = *frame.document(); > >- if (!frame.document()) >+ String stringOmittingMicrosoftPrefix = stripMicrosoftPrefix(string); >+ if (stringOmittingMicrosoftPrefix.isEmpty()) > return false; >- Document& document = *frame.document(); > > addFragment(createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent)); > return true; > } > >+bool WebContentMarkupReader::readHTML(const String& string) >+{ >+ if (!frame.document()) >+ return false; >+ >+ String rawHTML = stripMicrosoftPrefix(string); >+ if (shouldSanitize()) >+ markup = sanitizeMarkup(rawHTML); >+ else >+ markup = rawHTML; >+ >+ return !markup.isEmpty(); >+} >+ > bool WebContentReader::readRTFD(SharedBuffer& buffer) > { >- if (frame.settings().preferMIMETypeForImages()) >+ if (frame.settings().preferMIMETypeForImages() || !frame.document()) > return false; > > auto fragment = createFragmentAndAddResources(frame, adoptNS([[NSAttributedString alloc] initWithRTFD:buffer.createNSData().get() documentAttributes:nullptr]).get()); >@@ -245,6 +313,15 @@ bool WebContentReader::readRTFD(SharedBu > return true; > } > >+bool WebContentMarkupReader::readRTFD(SharedBuffer& buffer) >+{ >+ if (!frame.document()) >+ return false; >+ auto fragment = createFragmentAndAddResources(frame, adoptNS([[NSAttributedString alloc] initWithRTFD:buffer.createNSData().get() documentAttributes:nullptr]).get()); >+ markup = createMarkup(*fragment); >+ return true; >+} >+ > bool WebContentReader::readRTF(SharedBuffer& buffer) > { > if (frame.settings().preferMIMETypeForImages()) >@@ -258,6 +335,17 @@ bool WebContentReader::readRTF(SharedBuf > return true; > } > >+bool WebContentMarkupReader::readRTF(SharedBuffer& buffer) >+{ >+ if (!frame.document()) >+ return false; >+ auto fragment = createFragmentAndAddResources(frame, adoptNS([[NSAttributedString alloc] initWithRTF:buffer.createNSData().get() documentAttributes:nullptr]).get()); >+ if (!fragment) >+ return false; >+ markup = createMarkup(*fragment); >+ return true; >+} >+ > bool WebContentReader::readPlainText(const String& text) > { > if (!allowPlainText) >Index: Source/WebCore/platform/Pasteboard.h >=================================================================== >--- Source/WebCore/platform/Pasteboard.h (revision 223280) >+++ Source/WebCore/platform/Pasteboard.h (working copy) >@@ -127,9 +127,11 @@ struct PasteboardImage { > > class PasteboardWebContentReader { > public: >+ String contentOrigin; >+ > virtual ~PasteboardWebContentReader() { } > >-#if !(PLATFORM(GTK) || PLATFORM(WIN)) >+#if PLATFORM(COCOA) > virtual bool readWebArchive(SharedBuffer&) = 0; > virtual bool readFilenames(const Vector<String>&) = 0; > virtual bool readHTML(const String&) = 0; >@@ -269,6 +271,13 @@ private: > #if PLATFORM(IOS) > bool respectsUTIFidelities() const; > void readRespectingUTIFidelities(PasteboardWebContentReader&); >+ >+ enum class ReaderResult { >+ Read, >+ DidNotRead, >+ ContentChanged >+ }; >+ ReaderResult readPasteboardWebContentDataForType(PasteboardWebContentReader&, PasteboardStrategy&, NSString *type, int itemIndex); > #endif > > #if PLATFORM(WIN) >Index: Source/WebCore/platform/ios/PasteboardIOS.mm >=================================================================== >--- Source/WebCore/platform/ios/PasteboardIOS.mm (revision 223280) >+++ Source/WebCore/platform/ios/PasteboardIOS.mm (working copy) >@@ -162,50 +162,66 @@ static NSArray* supportedImageTypes() > return @[(id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF]; > } > >-static bool readPasteboardWebContentDataForType(PasteboardWebContentReader& reader, PasteboardStrategy& strategy, NSString *type, int itemIndex, const String& pasteboardName) >+Pasteboard::ReaderResult Pasteboard::readPasteboardWebContentDataForType(PasteboardWebContentReader& reader, PasteboardStrategy& strategy, NSString *type, int itemIndex) > { > if ([type isEqualToString:WebArchivePboardType]) { >- auto buffer = strategy.readBufferFromPasteboard(itemIndex, WebArchivePboardType, pasteboardName); >- return buffer && reader.readWebArchive(*buffer); >+ auto buffer = strategy.readBufferFromPasteboard(itemIndex, WebArchivePboardType, m_pasteboardName); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return buffer && reader.readWebArchive(*buffer) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > > if ([type isEqualToString:(NSString *)kUTTypeHTML]) { >- String htmlString = strategy.readStringFromPasteboard(itemIndex, kUTTypeHTML, pasteboardName); >- return !htmlString.isNull() && reader.readHTML(htmlString); >+ String htmlString = strategy.readStringFromPasteboard(itemIndex, kUTTypeHTML, m_pasteboardName); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return !htmlString.isNull() && reader.readHTML(htmlString) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > > if ([type isEqualToString:(NSString *)kUTTypeFlatRTFD]) { >- RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeFlatRTFD, pasteboardName); >- return buffer && reader.readRTFD(*buffer); >+ RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeFlatRTFD, m_pasteboardName); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return buffer && reader.readRTFD(*buffer) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > > if ([type isEqualToString:(NSString *)kUTTypeRTF]) { >- RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeRTF, pasteboardName); >- return buffer && reader.readRTF(*buffer); >+ RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, kUTTypeRTF, m_pasteboardName); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return buffer && reader.readRTF(*buffer) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > > if ([supportedImageTypes() containsObject:type]) { >- RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, type, pasteboardName); >- return buffer && reader.readImage(buffer.releaseNonNull(), type); >+ RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(itemIndex, type, m_pasteboardName); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return buffer && reader.readImage(buffer.releaseNonNull(), type) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > > if ([type isEqualToString:(NSString *)kUTTypeURL]) { > String title; >- URL url = strategy.readURLFromPasteboard(itemIndex, kUTTypeURL, pasteboardName, title); >- return !url.isNull() && reader.readURL(url, title); >+ URL url = strategy.readURLFromPasteboard(itemIndex, kUTTypeURL, m_pasteboardName, title); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return !url.isNull() && reader.readURL(url, title) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > > if (UTTypeConformsTo((CFStringRef)type, kUTTypePlainText)) { >- String string = strategy.readStringFromPasteboard(itemIndex, kUTTypePlainText, pasteboardName); >- return !string.isNull() && reader.readPlainText(string); >+ String string = strategy.readStringFromPasteboard(itemIndex, kUTTypePlainText, m_pasteboardName); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return !string.isNull() && reader.readPlainText(string) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > > if (UTTypeConformsTo((CFStringRef)type, kUTTypeText)) { >- String string = strategy.readStringFromPasteboard(itemIndex, kUTTypeText, pasteboardName); >- return !string.isNull() && reader.readPlainText(string); >+ String string = strategy.readStringFromPasteboard(itemIndex, kUTTypeText, m_pasteboardName); >+ if (m_changeCount != changeCount()) >+ return ReaderResult::ContentChanged; >+ return !string.isNull() && reader.readPlainText(string) ? ReaderResult::Read : ReaderResult::DidNotRead; > } > >- return false; >+ return ReaderResult::DidNotRead; > } > > void Pasteboard::read(PasteboardWebContentReader& reader) >@@ -222,12 +238,17 @@ void Pasteboard::read(PasteboardWebConte > if (!numberOfItems) > return; > >+ reader.contentOrigin = readOrigin(); >+ > NSArray *types = supportedWebContentPasteboardTypes(); > int numberOfTypes = [types count]; > > for (int i = 0; i < numberOfItems; i++) { > for (int typeIndex = 0; typeIndex < numberOfTypes; typeIndex++) { >- if (readPasteboardWebContentDataForType(reader, strategy, [types objectAtIndex:typeIndex], i, m_pasteboardName)) >+ auto result = readPasteboardWebContentDataForType(reader, strategy, [types objectAtIndex:typeIndex], i); >+ if (result == ReaderResult::ContentChanged) >+ return; >+ if (result == ReaderResult::Read) > break; > } > } >@@ -251,7 +272,10 @@ void Pasteboard::readRespectingUTIFideli > Vector<String> typesForItemInOrderOfFidelity; > strategy.getTypesByFidelityForItemAtIndex(typesForItemInOrderOfFidelity, index, m_pasteboardName); > for (auto& type : typesForItemInOrderOfFidelity) { >- if (readPasteboardWebContentDataForType(reader, strategy, type, index, m_pasteboardName)) >+ auto result = readPasteboardWebContentDataForType(reader, strategy, type, index); >+ if (result == ReaderResult::ContentChanged) >+ return; >+ if (result == ReaderResult::Read) > break; > } > } >Index: Source/WebCore/platform/ios/PlatformPasteboardIOS.mm >=================================================================== >--- Source/WebCore/platform/ios/PlatformPasteboardIOS.mm (revision 223280) >+++ Source/WebCore/platform/ios/PlatformPasteboardIOS.mm (working copy) >@@ -334,7 +334,8 @@ static const char *safeTypeForDOMToReadA > if (UTTypeConformsTo(cfType.get(), kUTTypePlainText)) > return ASCIILiteral("text/plain"); > >- if (UTTypeConformsTo(cfType.get(), kUTTypeHTML)) >+ if (UTTypeConformsTo(cfType.get(), kUTTypeHTML) || UTTypeConformsTo(cfType.get(), (CFStringRef)WebArchivePboardType) >+ || UTTypeConformsTo(cfType.get(), kUTTypeRTF) || UTTypeConformsTo(cfType.get(), kUTTypeFlatRTFD)) > return ASCIILiteral("text/html"); > > if (UTTypeConformsTo(cfType.get(), kUTTypeURL)) >Index: Source/WebCore/platform/mac/PasteboardMac.mm >=================================================================== >--- Source/WebCore/platform/mac/PasteboardMac.mm (revision 223280) >+++ Source/WebCore/platform/mac/PasteboardMac.mm (working copy) >@@ -332,9 +332,11 @@ void Pasteboard::read(PasteboardWebConte > Vector<String> types; > strategy.getTypes(types, m_pasteboardName); > >+ reader.contentOrigin = readOrigin(); >+ > if (types.contains(WebArchivePboardType)) { > if (auto buffer = strategy.bufferForType(WebArchivePboardType, m_pasteboardName)) { >- if (reader.readWebArchive(*buffer)) >+ if (m_changeCount != changeCount() || reader.readWebArchive(*buffer)) > return; > } > } >@@ -342,54 +344,54 @@ void Pasteboard::read(PasteboardWebConte > if (types.contains(String(NSFilenamesPboardType))) { > Vector<String> paths; > strategy.getPathnamesForType(paths, NSFilenamesPboardType, m_pasteboardName); >- if (reader.readFilenames(paths)) >+ if (m_changeCount != changeCount() || reader.readFilenames(paths)) > return; > } > > if (types.contains(String(NSHTMLPboardType))) { > String string = strategy.stringForType(NSHTMLPboardType, m_pasteboardName); >- if (!string.isNull() && reader.readHTML(string)) >+ if (m_changeCount != changeCount() || (!string.isNull() && reader.readHTML(string))) > return; > } > > if (types.contains(String(NSRTFDPboardType))) { > if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSRTFDPboardType, m_pasteboardName)) { >- if (reader.readRTFD(*buffer)) >+ if (m_changeCount != changeCount() || reader.readRTFD(*buffer)) > return; > } > } > > if (types.contains(String(NSRTFPboardType))) { > if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSRTFPboardType, m_pasteboardName)) { >- if (reader.readRTF(*buffer)) >+ if (m_changeCount != changeCount() || reader.readRTF(*buffer)) > return; > } > } > > if (types.contains(String(NSTIFFPboardType))) { > if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSTIFFPboardType, m_pasteboardName)) { >- if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/tiff"))) >+ if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/tiff"))) > return; > } > } > > if (types.contains(String(NSPDFPboardType))) { > if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSPDFPboardType, m_pasteboardName)) { >- if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("application/pdf"))) >+ if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("application/pdf"))) > return; > } > } > > if (types.contains(String(kUTTypePNG))) { > if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypePNG, m_pasteboardName)) { >- if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/png"))) >+ if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/png"))) > return; > } > } > > if (types.contains(String(kUTTypeJPEG))) { > if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypeJPEG, m_pasteboardName)) { >- if (reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/jpeg"))) >+ if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), ASCIILiteral("image/jpeg"))) > return; > } > } >@@ -397,19 +399,19 @@ void Pasteboard::read(PasteboardWebConte > if (types.contains(String(NSURLPboardType))) { > URL url = strategy.url(m_pasteboardName); > String title = strategy.stringForType(WebURLNamePboardType, m_pasteboardName); >- if (!url.isNull() && reader.readURL(url, title)) >+ if (m_changeCount != changeCount() || (!url.isNull() && reader.readURL(url, title))) > return; > } > > if (types.contains(String(NSStringPboardType))) { > String string = strategy.stringForType(NSStringPboardType, m_pasteboardName); >- if (!string.isNull() && reader.readPlainText(string)) >+ if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string))) > return; > } > > if (types.contains(String(kUTTypeUTF8PlainText))) { > String string = strategy.stringForType(kUTTypeUTF8PlainText, m_pasteboardName); >- if (!string.isNull() && reader.readPlainText(string)) >+ if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string))) > return; > } > } >Index: Source/WebCore/platform/mac/PlatformPasteboardMac.mm >=================================================================== >--- Source/WebCore/platform/mac/PlatformPasteboardMac.mm (revision 223280) >+++ Source/WebCore/platform/mac/PlatformPasteboardMac.mm (working copy) >@@ -101,7 +101,8 @@ static const char* safeTypeForDOMToReadA > if (platformType == String(NSURLPboardType)) > return ASCIILiteral("text/uri-list"); > >- if (platformType == String(NSHTMLPboardType)) >+ if (platformType == String(NSHTMLPboardType) || platformType == String(WebArchivePboardType) >+ || platformType == String(NSRTFDPboardType) || platformType == String(NSRTFPboardType)) > return ASCIILiteral("text/html"); > > return nullptr; >Index: Tools/ChangeLog >=================================================================== >--- Tools/ChangeLog (revision 223280) >+++ Tools/ChangeLog (working copy) >@@ -1,3 +1,37 @@ >+2017-10-13 Ryosuke Niwa <rniwa@webkit.org> >+ >+ 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 NOBODY (OOPS!). >+ >+ 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): >+ > 2017-10-12 Chris Dumez <cdumez@apple.com> > > import-w3c-tests modifies test sources and sometimes causes them to fail >Index: Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj >=================================================================== >--- Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (revision 223280) >+++ Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (working copy) >@@ -542,6 +542,8 @@ > 9999108B1F393C96008AD455 /* Copying.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9999108A1F393C8B008AD455 /* Copying.mm */; }; > 9B0786A51C5885C300D159E3 /* InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B0786A41C5885C300D159E3 /* InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp */; }; > 9B19CDA01F06DFE3000548DD /* NetworkProcessCrashWithPendingConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B19CD9E1F06DFE3000548DD /* NetworkProcessCrashWithPendingConnection.mm */; }; >+ 9B1F6F781F90558400B55744 /* CopyHTML.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B1056411F9045C700D5583F /* CopyHTML.mm */; }; >+ 9B1F6F791F90559E00B55744 /* copy-html.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B1056421F9047CC00D5583F /* copy-html.html */; }; > 9B26FCCA159D16DE00CC3765 /* HTMLFormCollectionNamedItem.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B26FCB4159D15E700CC3765 /* HTMLFormCollectionNamedItem.html */; }; > 9B270FEE1DDC2C0B002D53F3 /* closed-shadow-tree-test.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B270FED1DDC25FD002D53F3 /* closed-shadow-tree-test.html */; }; > 9B4F8FA7159D52DD002D9F94 /* HTMLCollectionNamedItem.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 9B4F8FA6159D52CA002D9F94 /* HTMLCollectionNamedItem.html */; }; >@@ -843,6 +845,7 @@ > 5142B2731517C8C800C32B19 /* ContextMenuCanCopyURL.html in Copy Resources */, > CD0BD0A81F79982D001AB2CF /* ContextMenuImgWithVideo.html in Copy Resources */, > 5C2936961D5C00ED00DEAB1E /* CookieMessage.html in Copy Resources */, >+ 9B1F6F791F90559E00B55744 /* copy-html.html in Copy Resources */, > 9B62630C1F8C25C8007EE29B /* copy-url.html in Copy Resources */, > 7AEAD4811E20122700416EFE /* CrossPartitionFileSchemeAccess.html in Copy Resources */, > F4AB578A1F65165400DB0DA1 /* custom-draggable-div.html in Copy Resources */, >@@ -1506,6 +1509,8 @@ > 9999108A1F393C8B008AD455 /* Copying.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Copying.mm; sourceTree = "<group>"; }; > 9B0786A21C58830F00D159E3 /* InjectedBundleMakeAllShadowRootsOpen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedBundleMakeAllShadowRootsOpen.cpp; sourceTree = "<group>"; }; > 9B0786A41C5885C300D159E3 /* InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedBundleMakeAllShadowRootsOpen_Bundle.cpp; sourceTree = "<group>"; }; >+ 9B1056411F9045C700D5583F /* CopyHTML.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CopyHTML.mm; sourceTree = "<group>"; }; >+ 9B1056421F9047CC00D5583F /* copy-html.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "copy-html.html"; sourceTree = "<group>"; }; > 9B19CD9E1F06DFE3000548DD /* NetworkProcessCrashWithPendingConnection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NetworkProcessCrashWithPendingConnection.mm; path = WebKit/NetworkProcessCrashWithPendingConnection.mm; sourceTree = "<group>"; }; > 9B26FC6B159D061000CC3765 /* HTMLFormCollectionNamedItem.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HTMLFormCollectionNamedItem.mm; sourceTree = "<group>"; }; > 9B26FCB4159D15E700CC3765 /* HTMLFormCollectionNamedItem.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = HTMLFormCollectionNamedItem.html; sourceTree = "<group>"; }; >@@ -1986,6 +1991,7 @@ > A14FC5891B89927100D107EB /* ContentFilteringPlugIn.mm */, > 5CA1DED81F74A87100E71BD3 /* ContentRuleListNotification.mm */, > 5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */, >+ 9B1056411F9045C700D5583F /* CopyHTML.mm */, > 9999108A1F393C8B008AD455 /* Copying.mm */, > 9B7A37C21F8AEBA5004AA228 /* CopyURL.mm */, > 2DC4CF761D2D9DD800ECCC94 /* DataDetection.mm */, >@@ -2236,6 +2242,7 @@ > F4A32ECA1F0642F40047C544 /* contenteditable-in-iframe.html */, > A16F66B91C40EA2000BD4D24 /* ContentFiltering.html */, > 5C2936941D5BFD1900DEAB1E /* CookieMessage.html */, >+ 9B1056421F9047CC00D5583F /* copy-html.html */, > 9B62630B1F8C2510007EE29B /* copy-url.html */, > F4AB57891F65164B00DB0DA1 /* custom-draggable-div.html */, > F486B1CF1F6794FF00F34BDD /* DataTransfer-setDragImage.html */, >@@ -3229,6 +3236,7 @@ > CD0BD0A61F79924D001AB2CF /* ContextMenuImgWithVideo.mm in Sources */, > 5C2936931D5BF70D00DEAB1E /* CookieAcceptPolicy.mm in Sources */, > 51D1249B1E785425002B2820 /* CookieManager.cpp in Sources */, >+ 9B1F6F781F90558400B55744 /* CopyHTML.mm in Sources */, > 9999108B1F393C96008AD455 /* Copying.mm in Sources */, > 9B7A37C41F8AEBA5004AA228 /* CopyURL.mm in Sources */, > 7CCE7EAC1A411A3400447C4C /* Counters.cpp in Sources */, >Index: Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm >=================================================================== >--- Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm (nonexistent) >+++ Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm (working copy) >@@ -0,0 +1,90 @@ >+ >+/* >+ * Copyright (C) 2017 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' >+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS >+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR >+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF >+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS >+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN >+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) >+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF >+ * THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+ >+#if WK_API_ENABLED && PLATFORM(COCOA) >+ >+#import "PlatformUtilities.h" >+#import "TestWKWebView.h" >+#import <WebKit/WKPreferencesPrivate.h> >+#import <wtf/RetainPtr.h> >+#import <wtf/text/WTFString.h> >+ >+#if PLATFORM(IOS) >+#include <MobileCoreServices/MobileCoreServices.h> >+#endif >+ >+@interface WKWebView () >+- (void)copy:(id)sender; >+- (void)paste:(id)sender; >+@end >+ >+#if PLATFORM(MAC) >+NSString *readHTMLFromPasteboard() >+{ >+ return [[NSPasteboard generalPasteboard] stringForType:NSHTMLPboardType]; >+} >+#else >+NSString *readHTMLFromPasteboard() >+{ >+ id value = [[UIPasteboard generalPasteboard] valueForPasteboardType:(NSString *)kUTTypeHTML]; >+ if ([value isKindOfClass:[NSData class]]) >+ value = [[[NSString alloc] initWithData:(NSData *)value encoding:NSUTF8StringEncoding] autorelease]; >+ ASSERT([value isKindOfClass:[NSString class]]); >+ return (NSString *)value; >+} >+#endif >+ >+static RetainPtr<TestWKWebView> createWebViewWithCustomPasteboardDataEnabled() >+{ >+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); >+ auto preferences = (WKPreferencesRef)[[webView configuration] preferences]; >+ WKPreferencesSetDataTransferItemsEnabled(preferences, true); >+ WKPreferencesSetCustomPasteboardDataEnabled(preferences, true); >+ return webView; >+} >+ >+TEST(CopyHTML, Sanitizes) >+{ >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); >+ [webView synchronouslyLoadTestPageNamed:@"copy-html"]; >+ [webView stringByEvaluatingJavaScript:@"HTMLToCopy = '<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b>" >+ "<!-- secret-->, world<script>dangerousCode()</script>';"]; >+ [webView copy:nil]; >+ [webView paste:nil]; >+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"didCopy"].boolValue); >+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"didPaste"].boolValue); >+ EXPECT_WK_STREQ("<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>", >+ [webView stringByEvaluatingJavaScript:@"pastedHTML"]); >+ String htmlInNativePasteboard = readHTMLFromPasteboard(); >+ EXPECT_TRUE(htmlInNativePasteboard.contains("hello")); >+ EXPECT_TRUE(htmlInNativePasteboard.contains(", world")); >+ EXPECT_FALSE(htmlInNativePasteboard.contains("secret")); >+ EXPECT_FALSE(htmlInNativePasteboard.contains("dangerousCode")); >+} >+ >+#endif // WK_API_ENABLED && PLATFORM(MAC) >Index: Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyURL.mm >=================================================================== >--- Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyURL.mm (revision 223280) >+++ Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyURL.mm (working copy) >@@ -57,9 +57,18 @@ NSString *readURLFromPasteboard() > } > #endif > >-TEST(CopyURL, ValidURL) >+static RetainPtr<TestWKWebView> createWebViewWithCustomPasteboardDataEnabled() > { > auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); >+ auto preferences = (WKPreferencesRef)[[webView configuration] preferences]; >+ WKPreferencesSetDataTransferItemsEnabled(preferences, true); >+ WKPreferencesSetCustomPasteboardDataEnabled(preferences, true); >+ return webView; >+} >+ >+TEST(CopyURL, ValidURL) >+{ >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); > [webView synchronouslyLoadTestPageNamed:@"copy-url"]; > [webView stringByEvaluatingJavaScript:@"URLToCopy = 'http://webkit.org/b/123';"]; > [webView copy:nil]; >@@ -70,12 +79,9 @@ TEST(CopyURL, ValidURL) > EXPECT_WK_STREQ(@"http://webkit.org/b/123", readURLFromPasteboard()); > } > >-// FIXME: We should add a mechanism to enable custom pasteboard data in older OS for testing purposes. >-#if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED > 101300) >- > TEST(CopyURL, UnescapedURL) > { >- auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); > [webView synchronouslyLoadTestPageNamed:@"copy-url"]; > [webView stringByEvaluatingJavaScript:@"URLToCopy = 'http://webkit.org/b/\u4F60\u597D;?x=8 + 6';"]; > [webView copy:nil]; >@@ -88,7 +94,7 @@ TEST(CopyURL, UnescapedURL) > > TEST(CopyURL, MalformedURL) > { >- auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); > [webView synchronouslyLoadTestPageNamed:@"copy-url"]; > [webView stringByEvaluatingJavaScript:@"URLToCopy = 'bad url';"]; > [webView copy:nil]; >@@ -99,7 +105,5 @@ TEST(CopyURL, MalformedURL) > EXPECT_WK_STREQ(@"", readURLFromPasteboard()); > } > >-#endif >- > #endif // WK_API_ENABLED && PLATFORM(MAC) > >Index: Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm >=================================================================== >--- Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm (revision 223280) >+++ Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteRTFD.mm (working copy) >@@ -29,6 +29,9 @@ > > #import "PlatformUtilities.h" > #import "TestWKWebView.h" >+#import <WebKit/WKPreferencesPrivate.h> >+#import <WebKit/WKPreferencesRefPrivate.h> >+#import <WebKit/WKWebViewConfigurationPrivate.h> > #import <wtf/RetainPtr.h> > #import <wtf/text/WTFString.h> > >@@ -41,6 +44,12 @@ - (void)paste:(id)sender; > @end > > #if PLATFORM(MAC) >+void writeRTFToPasteboard(NSData *data) >+{ >+ [[NSPasteboard generalPasteboard] declareTypes:@[NSPasteboardTypeRTF] owner:nil]; >+ [[NSPasteboard generalPasteboard] setData:data forType:NSPasteboardTypeRTF]; >+} >+ > void writeRTFDToPasteboard(NSData *data) > { > [[NSPasteboard generalPasteboard] declareTypes:@[NSRTFDPboardType] owner:nil]; >@@ -56,15 +65,56 @@ - (NSData *)RTFDFromRange:(NSRange)range > - (BOOL)containsAttachments; > @end > >+void writeRTFToPasteboard(NSData *data) >+{ >+ [[UIPasteboard generalPasteboard] setItems:@[@{ (NSString *)kUTTypeRTF : data}]]; >+} >+ > void writeRTFDToPasteboard(NSData *data) > { > [[UIPasteboard generalPasteboard] setItems:@[@{ (NSString *)kUTTypeFlatRTFD : data}]]; > } > #endif > >-TEST(PasteRTFD, EmptyRTFD) >+static RetainPtr<TestWKWebView> createWebViewWithCustomPasteboardDataEnabled() > { > auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); >+ auto preferences = (WKPreferencesRef)[[webView configuration] preferences]; >+ WKPreferencesSetDataTransferItemsEnabled(preferences, true); >+ WKPreferencesSetCustomPasteboardDataEnabled(preferences, true); >+ return webView; >+} >+ >+static RetainPtr<NSAttributedString> createHelloWorldString() >+{ >+ auto hello = adoptNS([[NSAttributedString alloc] initWithString:@"hello" attributes:@{ NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) }]); >+ auto world = adoptNS([[NSAttributedString alloc] initWithString:@", world" attributes:@{ }]); >+ auto string = adoptNS([[NSMutableAttributedString alloc] init]); >+ [string appendAttributedString:hello.get()]; >+ [string appendAttributedString:world.get()]; >+ return string; >+} >+ >+TEST(PasteRTF, ExposesHTMLTypeInDataTransfer) >+{ >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); >+ [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"]; >+ >+ auto string = createHelloWorldString(); >+ writeRTFToPasteboard([string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }]); >+ [webView paste:nil]; >+ >+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"]); >+ [webView stringByEvaluatingJavaScript:@"editor.innerHTML = clipboardData.values[0]; editor.focus()"]; >+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue); >+ [webView stringByEvaluatingJavaScript:@"getSelection().modify('move', 'forward', 'lineboundary')"]; >+ EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue); >+ EXPECT_WK_STREQ("hello, world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]); >+} >+ >+TEST(PasteRTFD, EmptyRTFD) >+{ >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); > [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><html><body><div id='editor' contenteditable></div></body></html>"]; > > writeRTFDToPasteboard([NSData data]); >@@ -72,9 +122,26 @@ TEST(PasteRTFD, EmptyRTFD) > [webView paste:nil]; > } > >-TEST(PasteRTFD, ImageElementsUseBlobURL) >+TEST(PasteRTFD, ExposesHTMLTypeInDataTransfer) > { >- auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); >+ [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"]; >+ >+ auto string = createHelloWorldString(); >+ writeRTFDToPasteboard([string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }]); >+ [webView paste:nil]; >+ >+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"clipboardData.types.includes('text/html')"]); >+ [webView stringByEvaluatingJavaScript:@"editor.innerHTML = clipboardData.values[0]; editor.focus()"]; >+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue); >+ [webView stringByEvaluatingJavaScript:@"getSelection().modify('move', 'forward', 'lineboundary')"]; >+ EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"document.queryCommandState('underline')"].boolValue); >+ EXPECT_WK_STREQ("hello, world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]); >+} >+ >+TEST(PasteRTFD, ImageElementUsesBlobURL) >+{ >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); > [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"]; > > auto *pngData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sunset-in-cupertino-200px" ofType:@"png" inDirectory:@"TestWebKitAPI.resources"]]; >@@ -90,6 +157,25 @@ TEST(PasteRTFD, ImageElementsUseBlobURL) > EXPECT_WK_STREQ("blob:", [webView stringByEvaluatingJavaScript:@"url = new URL(imageElement.src).protocol"]); > } > >+TEST(PasteRTFD, ImageElementUsesBlobURLInHTML) >+{ >+ auto webView = createWebViewWithCustomPasteboardDataEnabled(); >+ [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"]; >+ >+ auto *pngData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"sunset-in-cupertino-200px" ofType:@"png" inDirectory:@"TestWebKitAPI.resources"]]; >+ auto attachment = adoptNS([[NSTextAttachment alloc] initWithData:pngData ofType:(NSString *)kUTTypePNG]); >+ NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment.get()]; >+ NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:@{ }]; >+ >+ writeRTFDToPasteboard(RTFDData); >+ [webView paste:nil]; >+ >+ [webView waitForMessage:@"loaded"]; >+ EXPECT_WK_STREQ("[\"text/html\"]", [webView stringByEvaluatingJavaScript:@"JSON.stringify(clipboardData.types)"]); >+ EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"imageElement = (new DOMParser).parseFromString(clipboardData.values[0], 'text/html').querySelector('img'); !!imageElement"].boolValue); >+ EXPECT_WK_STREQ("blob:", [webView stringByEvaluatingJavaScript:@"new URL(imageElement.src).protocol"]); >+} >+ > #endif // WK_API_ENABLED && PLATFORM(MAC) > > >Index: Tools/TestWebKitAPI/Tests/WebKitCocoa/copy-html.html >=================================================================== >--- Tools/TestWebKitAPI/Tests/WebKitCocoa/copy-html.html (nonexistent) >+++ Tools/TestWebKitAPI/Tests/WebKitCocoa/copy-html.html (working copy) >@@ -0,0 +1,27 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<div id="editor" oncopy="copy(event)" onpaste="paste(event)" contenteditable>some text</div> >+<script> >+ >+var HTMLToCopy = null; >+var didCopy = false; >+var didPaste = false; >+ >+function copy(event) { >+ if (HTMLToCopy) { >+ event.clipboardData.setData('text/html', HTMLToCopy); >+ event.preventDefault(); >+ } >+ didCopy = true; >+} >+ >+function paste(event) { >+ pastedHTML = event.clipboardData.getData('text/html'); >+ didPaste = true; >+} >+ >+editor.focus(); >+</script> >+</body> >+</html> >Index: Tools/TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html >=================================================================== >--- Tools/TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html (revision 223280) >+++ Tools/TestWebKitAPI/Tests/WebKitCocoa/paste-rtfd.html (working copy) >@@ -6,7 +6,13 @@ > const editor = document.getElementById('editor'); > editor.focus(); > >+var clipboardData = {}; > editor.addEventListener('paste', (event) => { >+ clipboardData.types = Array.from(event.clipboardData.types); >+ clipboardData.items = Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})); >+ clipboardData.values = clipboardData.types.map((type) => event.clipboardData.getData(type)); >+ clipboardData.files = Array.from(event.clipboardData.files); >+ > setTimeout(() => { > let img = document.querySelector('img'); > if (img.complete) >Index: Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm >=================================================================== >--- Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm (revision 223280) >+++ Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm (working copy) >@@ -1742,6 +1742,46 @@ TEST(DataInteractionTests, DataTransferS > }); > } > >+TEST(DataInteractionTests, DataTransferSanitizeHTML) >+{ >+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); >+ [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; >+ auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]); >+ >+ [webView stringByEvaluatingJavaScript:@"select(rich)"]; >+ [webView stringByEvaluatingJavaScript:@"customData = { 'text/html' : '<meta content=\"secret\">" >+ "<b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>' }"]; >+ [webView stringByEvaluatingJavaScript:@"writeCustomData = true"]; >+ >+ __block bool done = false; >+ [simulator.get() setOverridePerformDropBlock:^NSArray<UIDragItem *> *(id <UIDropSession> session) >+ { >+ EXPECT_EQ(1UL, session.items.count); >+ auto *item = session.items[0].itemProvider; >+ EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(NSString *)kUTTypeHTML]); >+ [item loadDataRepresentationForTypeIdentifier:(NSString *)kUTTypeHTML completionHandler:^(NSData *data, NSError *error) { >+ NSString *markup = [[[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding] autorelease]; >+ EXPECT_TRUE([markup containsString:@"hello"]); >+ EXPECT_TRUE([markup containsString:@", world"]); >+ EXPECT_FALSE([markup containsString:@"secret"]); >+ EXPECT_FALSE([markup containsString:@"dangerousCode"]); >+ done = true; >+ }]; >+ return session.items; >+ }]; >+ [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; >+ >+ checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ >+ @"dragover": @{ >+ @"text/html": @"", >+ }, >+ @"drop": @{ >+ @"text/html": @"<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>", >+ } >+ }); >+ TestWebKitAPI::Util::run(&done); >+} >+ > #endif // __IPHONE_OS_VERSION_MIN_REQUIRED >= 110300 > > } // namespace TestWebKitAPI >Index: WebKit.xcworkspace/xcshareddata/xcschemes/All Source.xcscheme >=================================================================== >--- WebKit.xcworkspace/xcshareddata/xcschemes/All Source.xcscheme (revision 223280) >+++ WebKit.xcworkspace/xcshareddata/xcschemes/All Source.xcscheme (working copy) >@@ -25,7 +25,7 @@ > <BuildActionEntries> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -39,7 +39,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -53,7 +53,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -67,7 +67,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -81,7 +81,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -95,7 +95,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -109,7 +109,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -123,7 +123,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -137,7 +137,7 @@ > </BuildActionEntry> > <BuildActionEntry > buildForTesting = "YES" >- buildForRunning = "YES" >+ buildForRunning = "NO" > buildForProfiling = "YES" > buildForArchiving = "YES" > buildForAnalyzing = "YES"> >@@ -162,10 +162,10 @@ > <MacroExpansion> > <BuildableReference > BuildableIdentifier = "primary" >- BlueprintIdentifier = "8D1107260486CEB800E47090" >- BuildableName = "MiniBrowser.app" >- BlueprintName = "MiniBrowser" >- ReferencedContainer = "container:Tools/MiniBrowser/MiniBrowser.xcodeproj"> >+ BlueprintIdentifier = "9340994A08540CAE007F3BC8" >+ BuildableName = "DumpRenderTree" >+ BlueprintName = "DumpRenderTree" >+ ReferencedContainer = "container:Tools/DumpRenderTree/DumpRenderTree.xcodeproj"> > </BuildableReference> > </MacroExpansion> > <AdditionalOptions> >@@ -186,12 +186,18 @@ > runnableDebuggingMode = "0"> > <BuildableReference > BuildableIdentifier = "primary" >- BlueprintIdentifier = "8D1107260486CEB800E47090" >- BuildableName = "MiniBrowser.app" >- BlueprintName = "MiniBrowser" >- ReferencedContainer = "container:Tools/MiniBrowser/MiniBrowser.xcodeproj"> >+ BlueprintIdentifier = "9340994A08540CAE007F3BC8" >+ BuildableName = "DumpRenderTree" >+ BlueprintName = "DumpRenderTree" >+ ReferencedContainer = "container:Tools/DumpRenderTree/DumpRenderTree.xcodeproj"> > </BuildableReference> > </BuildableProductRunnable> >+ <CommandLineArguments> >+ <CommandLineArgument >+ argument = "../../LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html" >+ isEnabled = "YES"> >+ </CommandLineArgument> >+ </CommandLineArguments> > <AdditionalOptions> > </AdditionalOptions> > </LaunchAction> >Index: LayoutTests/ChangeLog >=================================================================== >--- LayoutTests/ChangeLog (revision 223280) >+++ LayoutTests/ChangeLog (working copy) >@@ -1,3 +1,39 @@ >+2017-10-13 Ryosuke Niwa <rniwa@webkit.org> >+ >+ 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 NOBODY (OOPS!). >+ >+ 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: >+ > 2017-10-13 Wenson Hsieh <wenson_hsieh@apple.com> > > "text/html" data is not exposed when dragging and dropping across origins >Index: LayoutTests/TestExpectations >=================================================================== >--- LayoutTests/TestExpectations (revision 223280) >+++ LayoutTests/TestExpectations (working copy) >@@ -77,7 +77,9 @@ editing/pasteboard/data-transfer-get-dat > editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html [ Skip ] > editing/pasteboard/data-transfer-get-data-on-drop-url.html [ Skip ] > editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Skip ] >+editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html [ Skip ] > editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html [ Skip ] >+ > editing/pasteboard/drag-end-crash-accessing-item-list.html [ Skip ] > editing/pasteboard/data-transfer-item-list-add-file-on-drag.html [ Skip ] > editing/pasteboard/data-transfer-items-drop-file.html [ Skip ] >Index: LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt (revision 223280) >+++ LayoutTests/editing/pasteboard/data-transfer-get-data-on-drop-rich-text-expected.txt (working copy) >@@ -5,7 +5,7 @@ Rich text > "text/plain": "" > }, > "drop": { >- "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>", >+ "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>", > "text/plain": "Rich text" > } > } >Index: LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt (revision 223280) >+++ LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt (working copy) >@@ -1,7 +1,7 @@ > Rich text > { > "paste": { >- "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>", >+ "text/html": "<span style=\"...\"\">Rich text</span>", > "text/plain": "Rich text" > } > } >Index: LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html (revision 223280) >+++ LayoutTests/editing/pasteboard/data-transfer-get-data-on-paste-rich-text.html (working copy) >@@ -35,7 +35,7 @@ result = {}; > function updateResultWithEvent(event) { > const eventData = {}; > for (const type of event.clipboardData.types) >- eventData[type] = event.clipboardData.getData(type); >+ eventData[type] = event.clipboardData.getData(type).replace(/style="[^"]+"/g, 'style="...""'); > result[event.type] = eventData; > output.textContent = JSON.stringify(result, null, " "); > event.preventDefault(); >Index: LayoutTests/editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url-expected.txt (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url-expected.txt (working copy) >@@ -0,0 +1,25 @@ >+This tests getData strips away secrets and dangerous code when copying inside a null origin document. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS typesInNullOrigin.includes("text/html") is true >+PASS htmlInNullOrigin.includes("secret") is false >+PASS htmlInNullOrigin.includes("dangerousCode") is false >+PASS parsedTree = (new DOMParser).parseFromString(htmlInNullOrigin, "text/html"); !!parsedTree.querySelector("b"); is true >+PASS parsedTree.querySelector("b").textContent is "16th President of the United States:" >+PASS (new URL(parsedTree.querySelector("img").src)).protocol is "blob:" >+PASS parsedTree.querySelector("img").src.includes("resources/abe.png") is false >+PASS itemsInNullOrigin.some((item) => item.kind == "string" && item.type == "text/html") is true >+PASS typesInNullOrigin.includes("text/html") is true >+PASS htmlInSameDocument.includes("secret") is false >+PASS htmlInSameDocument.includes("dangerousCode") is false >+PASS parsedTree = (new DOMParser).parseFromString(htmlInNullOrigin, "text/html"); !!parsedTree.querySelector("b"); is true >+PASS parsedTree.querySelector("b").textContent is "16th President of the United States:" >+FAIL (new URL(parsedTree.querySelector("img").src)).protocol should be file:. Was blob:. >+FAIL parsedTree.querySelector("img").src.includes("resources/abe.png") should be true. Was false. >+PASS itemsInSameDocument.some((item) => item.kind == "string" && item.type == "text/html") is true >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url.html >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url.html (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-get-data-on-pasting-html-uses-blob-url.html (working copy) >@@ -0,0 +1,104 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="../../resources/js-test-pre.js"></script> >+<div id="container"> >+<button id="copy" onclick="runTest()">1. Copy</button> >+<div><br></div> >+<div id="source" oncopy="doCopy(event)" contenteditable> >+ <b>16th President of the United States:</b> >+ <!-- secret--> >+ <img src="../resources/abe.png" alt="Abraham Lincoln" onmouseover="dangerousCode()"> >+ <script>function dangerousCode() { } dangerousCode();</script> >+</div> >+<div id="destination" onpaste="doPaste(event)" contenteditable>3. Paste here</div></div> >+<script> >+ >+description('This tests getData strips away secrets and dangerous code when copying inside a null origin document.'); >+jsTestIsAsync = true; >+ >+if (window.internals) >+ internals.settings.setCustomPasteboardDataEnabled(true); >+ >+function runTest() { >+ document.getElementById('source').focus(); >+ document.execCommand('selectAll'); >+ document.execCommand('copy'); >+} >+ >+const container = document.getElementById('container'); >+function doCopy(event) { >+ const iframe = document.createElement('iframe'); >+ container.insertBefore(iframe, container.lastChild); >+ iframe.src = `data:text/html;charset=utf-8,<!DOCTYPE html> >+ <div id="destination" onpaste="doPaste(event)" contenteditable>2. Paste here</div> >+ <script> >+ >+ function doPaste(event) { >+ event.preventDefault(); >+ parent.postMessage({ >+ html: event.clipboardData.getData('text/html'), >+ types: event.clipboardData.types, >+ items: Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})), >+ }, '*'); >+ }; >+ >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+ >+ </scri` + 'pt>'; >+} >+ >+onmessage = (event) => { >+ typesInNullOrigin = event.data.types; >+ shouldBeTrue('typesInNullOrigin.includes("text/html")'); >+ >+ htmlInNullOrigin = event.data.html; >+ shouldBeFalse('htmlInNullOrigin.includes("secret")'); >+ shouldBeFalse('htmlInNullOrigin.includes("dangerousCode")'); >+ shouldBeTrue('parsedTree = (new DOMParser).parseFromString(htmlInNullOrigin, "text/html"); !!parsedTree.querySelector("b");'); >+ shouldBeEqualToString('parsedTree.querySelector("b").textContent', '16th President of the United States:'); >+ shouldBeEqualToString('(new URL(parsedTree.querySelector("img").src)).protocol', 'blob:'); >+ shouldBeFalse('parsedTree.querySelector("img").src.includes("resources/abe.png")'); >+ >+ itemsInNullOrigin = event.data.items; >+ shouldBeTrue('itemsInNullOrigin.some((item) => item.kind == "string" && item.type == "text/html")'); >+ >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+} >+ >+function doPaste(event) { >+ event.preventDefault(); >+ >+ typesInSameDocument = event.clipboardData.types; >+ shouldBeTrue('typesInNullOrigin.includes("text/html")'); >+ >+ htmlInSameDocument = event.clipboardData.getData('text/html'); >+ shouldBeFalse('htmlInSameDocument.includes("secret")'); >+ shouldBeFalse('htmlInSameDocument.includes("dangerousCode")'); >+ shouldBeTrue('parsedTree = (new DOMParser).parseFromString(htmlInNullOrigin, "text/html"); !!parsedTree.querySelector("b");'); >+ shouldBeEqualToString('parsedTree.querySelector("b").textContent', '16th President of the United States:'); >+ >+ // FIXME: These two test cases fail. >+ shouldBeEqualToString('(new URL(parsedTree.querySelector("img").src)).protocol', 'file:'); >+ shouldBeTrue('parsedTree.querySelector("img").src.includes("resources/abe.png")'); >+ >+ itemsInSameDocument = Array.from(event.clipboardData.items); >+ shouldBeTrue('itemsInSameDocument.some((item) => item.kind == "string" && item.type == "text/html")'); >+ >+ container.remove(); >+ finishJSTest(); >+} >+ >+if (window.testRunner) >+ window.onload = runTest; >+ >+successfullyParsed = true; >+ >+</script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin-expected.txt (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin-expected.txt (working copy) >@@ -0,0 +1,41 @@ >+This tests calling setData to set a html in a null origin document. The URL should be sanitized when reading in another document. >+To manually test, drag and drop the "1. Drag this" above to "2. Drop here" and "3. Drop here". >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+dragstart in the null origin document: >+PASS html is "<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>" >+PASS types.includes("text/html") is true >+PASS items.some((item) => item.kind == "string" && item.type == "text/html") is true >+ >+dragover in the null origin document: >+PASS html is "" >+PASS types.includes("text/html") is true >+PASS items.some((item) => item.kind == "string" && item.type == "text/html") is true >+ >+drop in the null origin document: >+PASS html is "<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>" >+PASS types.includes("text/html") is true >+PASS items.some((item) => item.kind == "string" && item.type == "text/html") is true >+ >+dragstart in the null origin document: >+PASS html is "<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>" >+PASS types.includes("text/html") is true >+PASS items.some((item) => item.kind == "string" && item.type == "text/html") is true >+ >+dragover in the file URL document: >+PASS html is "" >+PASS types.includes("text/html") is true >+PASS items.some((item) => item.kind == "string" && item.type == "text/html") is true >+ >+drop in the file URL document: >+PASS html.includes("hello") is true >+PASS html.includes(", world") is true >+PASS html.includes("secret") is false >+PASS html.includes("dangerousCode") is false >+PASS types.includes("text/html") is true >+PASS items.some((item) => item.kind == "string" && item.type == "text/html") is true >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html (working copy) >@@ -0,0 +1,164 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<div id="container"> >+<div id="destination">3. Drop here</div> >+</div> >+<div id="description"></div> >+<div id="console"></div> >+<script src="../../resources/js-test-pre.js"></script> >+<script> >+ >+description('This tests calling setData to set a html in a null origin document. The URL should be sanitized when reading in another document.<br>' >+ + 'To manually test, drag and drop the "1. Drag this" above to "2. Drop here" and "3. Drop here".'); >+jsTestIsAsync = true; >+ >+if (window.internals) >+ internals.settings.setCustomPasteboardDataEnabled(true); >+ >+const iframe = document.createElement('iframe'); >+const destination = document.getElementById('destination'); >+document.getElementById('container').prepend(iframe); >+ >+iframe.src = `data:text/html;charset=utf-8,<!DOCTYPE html> >+<div id="source" draggable="true">1. Drag this</div> >+<div id="destination" onpaste="paste(event)">2. Drop here</div> >+<script> >+ >+const source = document.getElementById('source'); >+const destination = document.getElementById('destination'); >+const originalHTML = '<meta content="secret"><b onmouseover="dangerousCode()">hello</b><!-- secret-->, world<script>dangerousCode()</scr' + 'ipt>'; >+parent.postMessage({kind: 'originalHTML', originalHTML}, '*'); >+ >+function postContent(kind, dataTransfer) { >+ parent.postMessage({ >+ kind, >+ documentLabel: 'the null origin document', >+ html: dataTransfer.getData('text/html'), >+ types: dataTransfer.types, >+ items: Array.from(dataTransfer.items).map((item) => ({kind: item.kind, type: item.type})), >+ }, '*'); >+} >+ >+let postDragOver; >+source.addEventListener("dragstart", (event) => { >+ postDragOver = false; >+ event.dataTransfer.setData('text/html', originalHTML); >+ postContent('dragstart', event.dataTransfer); >+}); >+destination.addEventListener("dragover", (event) => { >+ event.preventDefault(); >+ if (postDragOver) >+ return; >+ postDragOver = true; >+ postContent('dragover', event.dataTransfer); >+}); >+destination.addEventListener("drop", (event) => { >+ postContent('drop', event.dataTransfer); >+ destination.remove(); >+ top.postMessage({kind: 'secondDrop'}, '*'); >+}); >+ >+if (window.eventSender) { >+ const iframeOffset = {x: 10, y: 10}; >+ eventSender.mouseMoveTo(iframeOffset.x + source.offsetLeft + 5, iframeOffset.y + source.offsetTop + 5); >+ eventSender.mouseDown(); >+ eventSender.leapForward(500); >+ eventSender.mouseMoveTo(iframeOffset.x + destination.offsetLeft + 5, iframeOffset.y + destination.offsetTop + 5); >+ eventSender.mouseUp(); >+} >+ >+</scri` + 'pt>'; >+ >+onmessage = (event) => { >+ const kind = event.data.kind; >+ if (kind == 'originalHTML') { >+ originalHTML = event.data.originalHTML; >+ return; >+ } >+ if (kind == 'secondDrop') >+ return doSecondDrop(event.data.postContent); >+ debug(`${kind} in ${event.data.documentLabel}:`); >+ switch (kind) { >+ case 'dragstart': >+ html = event.data.html; >+ shouldBeEqualToString('html', originalHTML); >+ types = event.data.types; >+ shouldBeTrue('types.includes("text/html")'); >+ items = event.data.items; >+ shouldBeTrue('items.some((item) => item.kind == "string" && item.type == "text/html")'); >+ break; >+ case 'dragover': >+ html = event.data.html; >+ shouldBeEqualToString('html', ''); >+ types = event.data.types; >+ shouldBeTrue('types.includes("text/html")'); >+ items = event.data.items; >+ shouldBeTrue('items.some((item) => item.kind == "string" && item.type == "text/html")'); >+ break; >+ case 'drop': >+ html = event.data.html; >+ if (event.data.documentLabel.includes('null')) >+ shouldBeEqualToString('html', originalHTML); >+ else { >+ shouldBeTrue('html.includes("hello")'); >+ shouldBeTrue('html.includes(", world")'); >+ shouldBeFalse('html.includes("secret")'); >+ shouldBeFalse('html.includes("dangerousCode")'); >+ } >+ types = event.data.types; >+ shouldBeTrue('types.includes("text/html")'); >+ items = event.data.items; >+ shouldBeTrue('items.some((item) => item.kind == "string" && item.type == "text/html")'); >+ if (!event.data.documentLabel.includes('null')) { >+ document.getElementById('container').remove(); >+ finishJSTest(); >+ } >+ break; >+ } >+ debug(''); >+} >+ >+function postContent(kind, dataTransfer) { >+ window.postMessage({ >+ kind, >+ documentLabel: 'the file URL document', >+ html: dataTransfer.getData('text/html'), >+ types: dataTransfer.types, >+ items: Array.from(dataTransfer.items).map((item) => ({kind: item.kind, type: item.type})), >+ }, '*'); >+} >+ >+let postDragOver = false; >+destination.addEventListener("dragover", (event) => { >+ event.preventDefault(); >+ if (postDragOver) >+ return; >+ postDragOver = true; >+ postContent('dragover', event.dataTransfer); >+}); >+destination.addEventListener("drop", (event) => { >+ postContent('drop', event.dataTransfer); >+}); >+ >+function doSecondDrop(postContent) { >+ postDragOver = false; >+ if (!window.eventSender) >+ return; >+ >+ eventSender.leapForward(500); >+ eventSender.mouseMoveTo(iframe.offsetLeft + 10, iframe.offsetTop + 10); >+ eventSender.mouseDown(); >+ eventSender.leapForward(500); >+ >+ const destinationRect = destination.getBoundingClientRect(); >+ eventSender.mouseMoveTo(destination.offsetLeft + 5, destination.offsetTop + 5); >+ eventSender.mouseUp(); >+} >+ >+setTimeout(finishJSTest, 3000); >+ >+</script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin-expected.txt (revision 223280) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin-expected.txt (working copy) >@@ -4,40 +4,40 @@ To manually test, drag and drop the "1. > On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". > > dragstart in the null origin document: >-PASS urlReadInSameDocument is "http://webkit.org/b/ð¤?x=8 + 6" >-PASS htmlReadInSameDocument is "testing" >-PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]" >-PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS url is "http://webkit.org/b/ð¤?x=8 + 6" >+PASS html is "testing" >+PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]" >+PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" > > dragover in the null origin document: >-PASS urlReadInSameDocument is "" >-PASS htmlReadInSameDocument is "" >-PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]" >-PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS url is "" >+PASS html is "" >+PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]" >+PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" > > drop in the null origin document: >-PASS urlReadInSameDocument is "http://webkit.org/b/ð¤?x=8 + 6" >-PASS htmlReadInSameDocument is "testing" >-PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]" >-PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS url is "http://webkit.org/b/ð¤?x=8 + 6" >+PASS html is "testing" >+PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]" >+PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" > > dragstart in the null origin document: >-PASS urlReadInSameDocument is "http://webkit.org/b/ð¤?x=8 + 6" >-PASS htmlReadInSameDocument is "testing" >-PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]" >-PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS url is "http://webkit.org/b/ð¤?x=8 + 6" >+PASS html is "testing" >+PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]" >+PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" > > dragover in the file URL document: >-PASS urlReadInSameDocument is "" >-PASS htmlReadInSameDocument is "" >-PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]" >-PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS url is "" >+PASS html is "" >+PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]" >+PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" > > drop in the file URL document: >-PASS urlReadInSameDocument is "http://webkit.org/b/%F0%9F%A4%94?x=8%20+%206" >-PASS htmlReadInSameDocument is "testing" >-PASS JSON.stringify(typesInSameDocument) is "[\"text/uri-list\",\"text/html\"]" >-PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS url is "http://webkit.org/b/%F0%9F%A4%94?x=8%20+%206" >+PASS html.includes("testing") is true >+PASS JSON.stringify(types) is "[\"text/uri-list\",\"text/html\"]" >+PASS JSON.stringify(items) is "[{\"kind\":\"string\",\"type\":\"text/uri-list\"},{\"kind\":\"string\",\"type\":\"text/html\"}]" > PASS successfullyParsed is true > > TEST COMPLETE >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html (revision 223280) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html (working copy) >@@ -83,34 +83,38 @@ onmessage = (event) => { > debug(`${kind} in ${event.data.documentLabel}:`); > switch (kind) { > case 'dragstart': >- urlReadInSameDocument = event.data.url; >- shouldBeEqualToString('urlReadInSameDocument', originalURL); >- htmlReadInSameDocument = event.data.html; >- shouldBeEqualToString('htmlReadInSameDocument', "testing"); >- typesInSameDocument = event.data.types; >- shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/uri-list","text/html"]'); >- itemsInSameDocument = event.data.items; >- shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]'); >+ url = event.data.url; >+ shouldBeEqualToString('url', originalURL); >+ html = event.data.html; >+ shouldBeEqualToString('html', "testing"); >+ types = event.data.types; >+ shouldBeEqualToString('JSON.stringify(types)', '["text/uri-list","text/html"]'); >+ items = event.data.items; >+ shouldBeEqualToString('JSON.stringify(items)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]'); > break; > case 'dragover': >- urlReadInSameDocument = event.data.url; >- shouldBeEqualToString('urlReadInSameDocument', ''); >- htmlReadInSameDocument = event.data.html; >- shouldBeEqualToString('htmlReadInSameDocument', ''); >- typesInSameDocument = event.data.types; >- shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/uri-list","text/html"]'); >- itemsInSameDocument = event.data.items; >- shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]'); >+ url = event.data.url; >+ shouldBeEqualToString('url', ''); >+ html = event.data.html; >+ shouldBeEqualToString('html', ''); >+ types = event.data.types; >+ shouldBeEqualToString('JSON.stringify(types)', '["text/uri-list","text/html"]'); >+ items = event.data.items; >+ shouldBeEqualToString('JSON.stringify(items)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]'); > break; > case 'drop': >- urlReadInSameDocument = event.data.url; >- shouldBeEqualToString('urlReadInSameDocument', event.data.documentLabel.includes('null') ? originalURL : (new URL(originalURL)).href); >- htmlReadInSameDocument = event.data.html; >- shouldBeEqualToString('htmlReadInSameDocument', "testing"); >- typesInSameDocument = event.data.types; >- shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/uri-list","text/html"]'); >- itemsInSameDocument = event.data.items; >- shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]'); >+ url = event.data.url; >+ isSameOrigin = event.data.documentLabel.includes('null'); >+ shouldBeEqualToString('url', isSameOrigin ? originalURL : (new URL(originalURL)).href); >+ html = event.data.html; >+ if (isSameOrigin) >+ shouldBeEqualToString('html', 'testing'); >+ else >+ shouldBeTrue('html.includes("testing")'); >+ types = event.data.types; >+ shouldBeEqualToString('JSON.stringify(types)', '["text/uri-list","text/html"]'); >+ items = event.data.items; >+ shouldBeEqualToString('JSON.stringify(items)', '[{"kind":"string","type":"text/uri-list"},{"kind":"string","type":"text/html"}]'); > if (!event.data.documentLabel.includes('null')) { > document.getElementById('container').remove(); > finishJSTest(); >@@ -158,8 +162,6 @@ function doSecondDrop(postContent) { > eventSender.mouseUp(); > } > >-setTimeout(finishJSTest, 3000); >- > </script> > <script src="../../resources/js-test-post.js"></script> > </body> >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-expected.txt (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-expected.txt (working copy) >@@ -0,0 +1,16 @@ >+This tests getData strips away secrets and dangerous code when copying HTML in a regular origin and pasting inside a null origin document. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS JSON.stringify(typesInSameDocument) is "[\"text/html\"]" >+PASS htmlInSameDocument is "<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>" >+PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS htmlInAnotherDocument.includes("secret") is false >+PASS htmlInAnotherDocument.includes("dangerousCode") is false >+PASS b = (new DOMParser).parseFromString(htmlInAnotherDocument, "text/html").querySelector("b"); b.textContent is "hello" >+PASS b.parentNode.textContent is "hello, world" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin-expected.txt (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin-expected.txt (working copy) >@@ -0,0 +1,18 @@ >+This tests getData strips away secrets and dangerous code when copying inside a null origin document. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS JSON.stringify(typesInNullOrigin) is "[\"text/html\"]" >+PASS htmlInNullOrigin.includes("secret") is false >+PASS htmlInNullOrigin.includes("dangerousCode") is false >+PASS b = (new DOMParser).parseFromString(htmlInNullOrigin, "text/html").querySelector("b"); b.textContent is "hello" >+PASS b.parentNode.textContent is "hello, world" >+PASS JSON.stringify(itemsInNullOrigin) is "[{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS event.clipboardData.getData("text/html") is "<meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world<script>dangerousCode()</script>" >+PASS JSON.stringify(event.clipboardData.types) is "[\"text/html\"]" >+PASS JSON.stringify(Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type}))) is "[{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin.html >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin.html (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying-in-null-origin.html (working copy) >@@ -0,0 +1,86 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="../../resources/js-test-pre.js"></script> >+<div id="container"> >+<button id="copy" onclick="runTest()">1. Copy</button> >+<div><br></div> >+<div id="source" oncopy="doCopy(event)" contenteditable>some text</div> >+<div id="destination" onpaste="doPaste(event)" contenteditable>3. Paste here</div> >+</div> >+<script> >+ >+description('This tests getData strips away secrets and dangerous code when copying inside a null origin document.'); >+jsTestIsAsync = true; >+ >+if (window.internals) >+ internals.settings.setCustomPasteboardDataEnabled(true); >+ >+function runTest() { >+ document.getElementById('source').focus(); >+ document.execCommand('selectAll'); >+ document.execCommand('copy'); >+} >+ >+var originalHTML = '<meta content="secret"><b onmouseover="dangerousCode()">hello</b><!-- secret-->, world<script>dangerousCode()</scr' + 'ipt>'; >+function doCopy(event) { >+ event.clipboardData.setData('text/html', originalHTML); >+ event.preventDefault(); >+ >+ const iframe = document.createElement('iframe'); >+ document.getElementById('container').insertBefore(iframe, iframe.lastChild); >+ iframe.src = `data:text/html;charset=utf-8,<!DOCTYPE html> >+ <div id="destination" onpaste="doPaste(event)" contenteditable>2. Paste here</div> >+ <script> >+ >+ function doPaste(event) { >+ event.preventDefault(); >+ parent.postMessage({ >+ html: event.clipboardData.getData('text/html'), >+ types: event.clipboardData.types, >+ items: Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})), >+ }, '*'); >+ }; >+ >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+ >+ </scri` + 'pt>'; >+} >+ >+onmessage = (event) => { >+ typesInNullOrigin = event.data.types; >+ shouldBeEqualToString('JSON.stringify(typesInNullOrigin)', '["text/html"]'); >+ >+ htmlInNullOrigin = event.data.html; >+ shouldBeFalse('htmlInNullOrigin.includes("secret")'); >+ shouldBeFalse('htmlInNullOrigin.includes("dangerousCode")'); >+ shouldBeEqualToString('b = (new DOMParser).parseFromString(htmlInNullOrigin, "text/html").querySelector("b"); b.textContent', 'hello'); >+ shouldBeEqualToString('b.parentNode.textContent', 'hello, world'); >+ >+ itemsInNullOrigin = event.data.items; >+ shouldBeEqualToString('JSON.stringify(itemsInNullOrigin)', '[{"kind":"string","type":"text/html"}]'); >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+} >+ >+function doPaste(event) { >+ shouldBeEqualToString('event.clipboardData.getData("text/html")', originalHTML); >+ shouldBeEqualToString('JSON.stringify(event.clipboardData.types)', '["text/html"]'); >+ shouldBeEqualToString('JSON.stringify(Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})))', >+ '[{"kind":"string","type":"text/html"}]'); >+ document.getElementById('container').remove(); >+ finishJSTest(); >+} >+ >+if (window.testRunner) >+ window.onload = runTest; >+ >+successfullyParsed = true; >+ >+</script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying.html >=================================================================== >--- LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying.html (nonexistent) >+++ LayoutTests/editing/pasteboard/data-transfer-set-data-sanitizes-html-when-copying.html (working copy) >@@ -0,0 +1,95 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="../../resources/js-test-pre.js"></script> >+<div id="container"> >+<div id="destination" onpaste="doPaste(event)" contenteditable>3. Paste here</div> >+</div> >+<script> >+ >+description('This tests getData strips away secrets and dangerous code when copying HTML in a regular origin and pasting inside a null origin document.'); >+jsTestIsAsync = true; >+ >+if (window.internals) >+ internals.settings.setCustomPasteboardDataEnabled(true); >+ >+const iframe = document.createElement('iframe'); >+document.getElementById('container').prepend(iframe); >+iframe.src = `data:text/html;charset=utf-8,<!DOCTYPE html> >+<button id="copy" onclick="runTest()">1. Copy</button> >+<div><br></div> >+<div id="source" oncopy="doCopy(event)" contenteditable>some text</div> >+<div id="destination" onpaste="doPaste(event)" contenteditable>2. Paste here</div> >+<script> >+ >+const originalHTML = '<meta content="secret"><b onmouseover="dangerousCode()">hello</b><!-- secret-->, world<script>dangerousCode()</scr' + 'ipt>'; >+ >+function runTest() { >+ document.getElementById('source').focus(); >+ document.execCommand('selectAll'); >+ document.execCommand('copy'); >+} >+ >+function doCopy(event) { >+ event.clipboardData.setData('text/html', originalHTML); >+ event.preventDefault(); >+ setTimeout(() => { >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+ }, 0); >+}; >+ >+function doPaste(event) { >+ event.preventDefault(); >+ parent.postMessage({ >+ originalHTML, >+ html: event.clipboardData.getData('text/html'), >+ types: event.clipboardData.types, >+ items: Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})), >+ }, '*'); >+}; >+ >+document.getElementById('destination').focus(); >+if (window.testRunner) >+ runTest(); >+ document.execCommand('paste'); >+ >+</scri` + 'pt>'; >+ >+function doCopy(event) { >+ event.clipboardData.setData('text/html', originalHTML); >+ event.preventDefault(); >+} >+ >+onmessage = (event) => { >+ typesInSameDocument = event.data.types; >+ shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/html"]'); >+ htmlInSameDocument = event.data.html; >+ originalHTML = event.data.originalHTML; >+ shouldBeEqualToString('htmlInSameDocument', originalHTML); >+ >+ itemsInSameDocument = event.data.items; >+ shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/html"}]'); >+ >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+} >+ >+function doPaste(event) { >+ htmlInAnotherDocument = event.clipboardData.getData("text/html"); >+ shouldBeFalse('htmlInAnotherDocument.includes("secret")'); >+ shouldBeFalse('htmlInAnotherDocument.includes("dangerousCode")'); >+ shouldBeEqualToString('b = (new DOMParser).parseFromString(htmlInAnotherDocument, "text/html").querySelector("b"); b.textContent', 'hello'); >+ shouldBeEqualToString('b.parentNode.textContent', 'hello, world'); >+ document.getElementById('container').remove(); >+ finishJSTest(); >+} >+ >+successfullyParsed = true; >+ >+</script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt >=================================================================== >--- LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt (revision 223280) >+++ LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt (working copy) >@@ -1,5 +1,5 @@ > CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the clipboard during an onpaste event. >-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> >+CONSOLE 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> > This test verifies that we can get text/html from the clipboard during an onpaste event. This test requires DRT. > Paste content in this div.This test verifies that we can get text/html from the clipboard during an onpaste event. > PASS >Index: LayoutTests/fast/events/ondrop-text-html-expected.txt >=================================================================== >--- LayoutTests/fast/events/ondrop-text-html-expected.txt (revision 223280) >+++ LayoutTests/fast/events/ondrop-text-html-expected.txt (working copy) >@@ -1,4 +1,4 @@ > CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the drag object during an ondrop event. >-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> >+CONSOLE 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> > This test verifies that we can get text/html from the drag object during an ondrop event. This test requires DRT. > PASS >Index: LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html-expected.txt >=================================================================== >--- LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html-expected.txt (nonexistent) >+++ LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html-expected.txt (working copy) >@@ -0,0 +1,15 @@ >+This tests calling setData with a HTML in a null origin document. The HTML should be sanitized in another document >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS htmlReadInSameDocument is "\n <meta content=\"secret\"><b onmouseover=\"dangerousCode()\">hello</b><!-- secret-->, world\n <script>function dangerousCode() { }</script>\n" >+PASS JSON.stringify(typesInSameDocument) is "[\"text/html\"]" >+PASS JSON.stringify(itemsInSameDocument) is "[{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS event.clipboardData.getData("html") is "" >+PASS JSON.stringify(event.clipboardData.types) is "[\"text/html\"]" >+PASS JSON.stringify(Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type}))) is "[{\"kind\":\"string\",\"type\":\"text/html\"}]" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html >=================================================================== >--- LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html (nonexistent) >+++ LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-sanitizes-html.html (working copy) >@@ -0,0 +1,40 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<script src="/resources/js-test-pre.js"></script> >+<script> >+ >+description('This tests calling setData with a HTML in a null origin document. The HTML should be sanitized in another document'); >+jsTestIsAsync = true; >+ >+if (window.internals) >+ internals.settings.setCustomPasteboardDataEnabled(true); >+ >+onmessage = (event) => { >+ originalHTML = event.data.originalHTML; >+ htmlReadInSameDocument = event.data.html; >+ shouldBeEqualToString('htmlReadInSameDocument', originalHTML); >+ typesInSameDocument = event.data.types; >+ shouldBeEqualToString('JSON.stringify(typesInSameDocument)', '["text/html"]'); >+ itemsInSameDocument = event.data.items; >+ shouldBeEqualToString('JSON.stringify(itemsInSameDocument)', '[{"kind":"string","type":"text/html"}]'); >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+} >+ >+function doPaste(event) { >+ shouldBeEqualToString('event.clipboardData.getData("html")', ''); >+ shouldBeEqualToString('JSON.stringify(event.clipboardData.types)', '["text/html"]'); >+ shouldBeEqualToString('JSON.stringify(Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})))', >+ '[{"kind":"string","type":"text/html"}]'); >+ document.getElementById('destination').remove(); >+ finishJSTest(); >+} >+ >+</script> >+<iframe src="http://localhost:8000/security/clipboard/resources/copy-html.html"></iframe> >+<div id="destination" onpaste="doPaste(event)" contenteditable>3. Paste here</div> >+<script src="/resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html >=================================================================== >--- LayoutTests/http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html (revision 223280) >+++ LayoutTests/http/tests/security/clipboard/copy-paste-url-across-origin-sanitizes-url.html (working copy) >@@ -33,7 +33,7 @@ function doPaste(event) { > } > > </script> >-<iframe src="http://localhost:8000/security/clipboard/resources/copy.html"></iframe> >+<iframe src="http://localhost:8000/security/clipboard/resources/copy-url.html"></iframe> > <div id="destination" onpaste="doPaste(event)" contenteditable>3. Paste here</div> > <script src="/resources/js-test-post.js"></script> > </body> >Index: LayoutTests/http/tests/security/clipboard/resources/copy-html.html >=================================================================== >--- LayoutTests/http/tests/security/clipboard/resources/copy-html.html (nonexistent) >+++ LayoutTests/http/tests/security/clipboard/resources/copy-html.html (working copy) >@@ -0,0 +1,42 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<button onclick="runTest()">1. Copy</button> >+<div><br></div> >+<div id="source" oncopy="copy(event)" contenteditable> >+ <meta content="secret"><b onmouseover="dangerousCode()">hello</b><!-- secret-->, world >+ <script>function dangerousCode() { }</script> >+</div> >+<div id="destination" onpaste="paste(event)" contenteditable>2. Paste here</div> >+<script> >+ >+const originalHTML = document.getElementById('source').innerHTML; >+function copy(event) { >+ event.clipboardData.setData('text/html', originalHTML); >+ event.preventDefault(); >+} >+ >+function paste(event) { >+ parent.postMessage({ >+ originalHTML, >+ html: event.clipboardData.getData('text/html'), >+ types: event.clipboardData.types, >+ items: Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})), >+ }, '*'); >+} >+ >+function runTest() { >+ document.getElementById('source').focus(); >+ document.execCommand('selectAll'); >+ document.execCommand('copy'); >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+} >+ >+if (window.testRunner) >+ runTest(); >+ >+</script> >+</body> >+</html> >Index: LayoutTests/http/tests/security/clipboard/resources/copy-url.html >=================================================================== >--- LayoutTests/http/tests/security/clipboard/resources/copy-url.html (nonexistent) >+++ LayoutTests/http/tests/security/clipboard/resources/copy-url.html (working copy) >@@ -0,0 +1,39 @@ >+<!DOCTYPE html> >+<html> >+<body> >+<button onclick="runTest()">1. Copy</button> >+<div><br></div> >+<div id="source" oncopy="copy(event)" contenteditable>http://webkit.org/b/🤔?x=8 + 6</div> >+<div id="destination" onpaste="paste(event)" contenteditable>2. Paste here</div> >+<script> >+ >+const originalURL = document.getElementById('source').textContent; >+function copy(event) { >+ event.clipboardData.setData('url', originalURL); >+ event.preventDefault(); >+} >+ >+function paste(event) { >+ parent.postMessage({ >+ originalURL, >+ url: event.clipboardData.getData('url'), >+ types: event.clipboardData.types, >+ items: Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})), >+ }, '*'); >+} >+ >+function runTest() { >+ document.getElementById('source').focus(); >+ document.execCommand('selectAll'); >+ document.execCommand('copy'); >+ document.getElementById('destination').focus(); >+ if (window.testRunner) >+ document.execCommand('paste'); >+} >+ >+if (window.testRunner) >+ runTest(); >+ >+</script> >+</body> >+</html> >Index: LayoutTests/http/tests/security/clipboard/resources/copy.html >=================================================================== >--- LayoutTests/http/tests/security/clipboard/resources/copy.html (revision 223280) >+++ LayoutTests/http/tests/security/clipboard/resources/copy.html (nonexistent) >@@ -1,39 +0,0 @@ >-<!DOCTYPE html> >-<html> >-<body> >-<button onclick="runTest()">1. Copy</button> >-<div><br></div> >-<div id="source" oncopy="copy(event)" contenteditable>http://webkit.org/b/🤔?x=8 + 6</div> >-<div id="destination" onpaste="paste(event)" contenteditable>2. Paste here</div> >-<script> >- >-const originalURL = document.getElementById('source').textContent; >-function copy(event) { >- event.clipboardData.setData('url', originalURL); >- event.preventDefault(); >-} >- >-function paste(event) { >- parent.postMessage({ >- originalURL, >- url: event.clipboardData.getData('url'), >- types: event.clipboardData.types, >- items: Array.from(event.clipboardData.items).map((item) => ({kind: item.kind, type: item.type})), >- }, '*'); >-} >- >-function runTest() { >- document.getElementById('source').focus(); >- document.execCommand('selectAll'); >- document.execCommand('copy'); >- document.getElementById('destination').focus(); >- if (window.testRunner) >- document.execCommand('paste'); >-} >- >-if (window.testRunner) >- runTest(); >- >-</script> >-</body> >-</html> >Index: LayoutTests/platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt >=================================================================== >--- LayoutTests/platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt (revision 223280) >+++ LayoutTests/platform/ios-wk1/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt (nonexistent) >@@ -1,7 +0,0 @@ >-Rich text >-{ >- "paste": { >- "text/plain": "Rich text", >- "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-tap-highlight-color: rgba(26, 26, 26, 0.301961); -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; display: inline !important; float: none;\">Rich text</span>" >- } >-} >Index: LayoutTests/platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt >=================================================================== >--- LayoutTests/platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt (revision 223280) >+++ LayoutTests/platform/ios-wk2/editing/pasteboard/data-transfer-get-data-on-paste-rich-text-expected.txt (nonexistent) >@@ -1,7 +0,0 @@ >-Rich text >-{ >- "paste": { >- "text/plain": "Rich text", >- "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>" >- } >-} >Index: LayoutTests/platform/mac-wk1/TestExpectations >=================================================================== >--- LayoutTests/platform/mac-wk1/TestExpectations (revision 223280) >+++ LayoutTests/platform/mac-wk1/TestExpectations (working copy) >@@ -12,6 +12,7 @@ editing/pasteboard/data-transfer-get-dat > editing/pasteboard/data-transfer-get-data-on-drop-rich-text.html [ Pass ] > editing/pasteboard/data-transfer-get-data-on-drop-url.html [ Pass ] > editing/pasteboard/data-transfer-is-unique-for-dragenter-and-dragleave.html [ Pass ] >+editing/pasteboard/data-transfer-set-data-sanitize-html-when-dragging-in-null-origin.html [ Pass ] > editing/pasteboard/data-transfer-set-data-sanitize-url-when-dragging-in-null-origin.html [ Pass ] > editing/pasteboard/drag-end-crash-accessing-item-list.html [ Pass ] > editing/pasteboard/data-transfer-item-list-add-file-on-drag.html [ Pass ]
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 124391
:
217003
|
299902
|
299904
|
299912
|
299914
|
323624
|
323648
|
323649
|
323654
|
323682
|
323709
|
323716
|
323721
|
323769
|
323773
|
323774
|
323798
|
323853
|
323885
|
323886
|
323888
|
332098