[Clipboard API] Refactor Pasteboard::read() to take an optional item index
https://bugs.webkit.org/show_bug.cgi?id=203161

Reviewed by Tim Horton.

Source/WebCore:

Adds an optional `itemIndex` argument to Pasteboard::read(PasteboardPlainText&) and
Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy). See below for more details.

Tests:  CopyHTML.ItemTypesWhenCopyingWebContent
        PasteWebArchive.WebArchiveTypeIdentifier

* editing/mac/EditorMac.mm:
(WebCore::Editor::dataSelectionForPasteboard):

Recognize "com.apple.webarchive" alongside "Apple Web Archive pasteboard type" when writing and reading from the
platform pasteboard on macOS. We add support for this here because the existing private type cannot be written
to an NSPasteboardItem, since it does not conform to a valid UTI format. Luckily, there already exists a UTI
that represents a web archive, so we can use it instead.

We need to write and read web archive data from NSPasteboardItem in order to support the case where there are
multiple items in the pasteboard that contain different web archive data.

* platform/Pasteboard.h:
* platform/StaticPasteboard.h:
* platform/gtk/PasteboardGtk.cpp:
(WebCore::Pasteboard::read):
* platform/ios/PasteboardIOS.mm:
(WebCore::Pasteboard::read):

Read the string from `itemIndex` if specified; otherwise, fall back to index 0. This could actually be fixed in
the future to scan all pasteboard items for a suitable string instead of falling back on the first item, but for
now, we maintain the existing behavior.

(WebCore::Pasteboard::readRespectingUTIFidelities):

If an `itemIndex` is specified, ignore all other item indices when looking for suitable content.

* platform/libwpe/PasteboardLibWPE.cpp:
(WebCore::Pasteboard::read):
* platform/mac/DragDataMac.mm:
(WebCore::DragData::containsCompatibleContent const):
* platform/mac/PasteboardMac.mm:
(WebCore::Pasteboard::write):
(WebCore::readStringAtPreferredItemIndex):
(WebCore::readBufferAtPreferredItemIndex):

Add helper methods to read strings and buffers from the pasteboard, at an optional `itemIndex` if specified.

(WebCore::Pasteboard::read):

Adopt the helper functions when reading plain text and web content, and respect the given `itemIndex`. To do
this, we need to read both legacy and modern types from the pasteboard, instead of just legacy types. This is
because NSPasteboardItem on macOS only accepts and provides data in the form of modern pasteboard types.

* platform/mac/PlatformPasteboardMac.mm:
(WebCore::safeTypeForDOMToReadAndWriteForPlatformType):
(WebCore::PlatformPasteboard::readBuffer const):
(WebCore::PlatformPasteboard::readString const):

Fix these methods to accept platform pasteboard types instead of MIME types, to match the behavior of existing
readBuffer and readString methods in PlatformPasteboardIOS.

* platform/win/PasteboardWin.cpp:
(WebCore::Pasteboard::read):

Source/WebKit:

* Shared/mac/PasteboardTypes.mm:
(WebKit::PasteboardTypes::forEditing):
(WebKit::PasteboardTypes::forSelection):

Support "com.apple.webarchive" alongside the private "Apple Web Archive pasteboard type".

* UIProcess/Cocoa/WebViewImpl.mm:
(WebKit::WebViewImpl::setPromisedDataForImage):

Tools:

Add a couple of new API tests to verify that the web archive type identifier ("com.apple.webarchive") is (1)
written to the pasteboard when copying a rich text selection, and (2) is read when attempting to paste web
content.

* TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm:
* TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251316 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index fba828a..c5436d7 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,70 @@
+2019-10-18  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Clipboard API] Refactor Pasteboard::read() to take an optional item index
+        https://bugs.webkit.org/show_bug.cgi?id=203161
+
+        Reviewed by Tim Horton.
+
+        Adds an optional `itemIndex` argument to Pasteboard::read(PasteboardPlainText&) and
+        Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy). See below for more details.
+
+        Tests:  CopyHTML.ItemTypesWhenCopyingWebContent
+                PasteWebArchive.WebArchiveTypeIdentifier
+
+        * editing/mac/EditorMac.mm:
+        (WebCore::Editor::dataSelectionForPasteboard):
+
+        Recognize "com.apple.webarchive" alongside "Apple Web Archive pasteboard type" when writing and reading from the
+        platform pasteboard on macOS. We add support for this here because the existing private type cannot be written
+        to an NSPasteboardItem, since it does not conform to a valid UTI format. Luckily, there already exists a UTI
+        that represents a web archive, so we can use it instead.
+
+        We need to write and read web archive data from NSPasteboardItem in order to support the case where there are
+        multiple items in the pasteboard that contain different web archive data.
+
+        * platform/Pasteboard.h:
+        * platform/StaticPasteboard.h:
+        * platform/gtk/PasteboardGtk.cpp:
+        (WebCore::Pasteboard::read):
+        * platform/ios/PasteboardIOS.mm:
+        (WebCore::Pasteboard::read):
+
+        Read the string from `itemIndex` if specified; otherwise, fall back to index 0. This could actually be fixed in
+        the future to scan all pasteboard items for a suitable string instead of falling back on the first item, but for
+        now, we maintain the existing behavior.
+
+        (WebCore::Pasteboard::readRespectingUTIFidelities):
+
+        If an `itemIndex` is specified, ignore all other item indices when looking for suitable content.
+
+        * platform/libwpe/PasteboardLibWPE.cpp:
+        (WebCore::Pasteboard::read):
+        * platform/mac/DragDataMac.mm:
+        (WebCore::DragData::containsCompatibleContent const):
+        * platform/mac/PasteboardMac.mm:
+        (WebCore::Pasteboard::write):
+        (WebCore::readStringAtPreferredItemIndex):
+        (WebCore::readBufferAtPreferredItemIndex):
+
+        Add helper methods to read strings and buffers from the pasteboard, at an optional `itemIndex` if specified.
+
+        (WebCore::Pasteboard::read):
+
+        Adopt the helper functions when reading plain text and web content, and respect the given `itemIndex`. To do
+        this, we need to read both legacy and modern types from the pasteboard, instead of just legacy types. This is
+        because NSPasteboardItem on macOS only accepts and provides data in the form of modern pasteboard types.
+
+        * platform/mac/PlatformPasteboardMac.mm:
+        (WebCore::safeTypeForDOMToReadAndWriteForPlatformType):
+        (WebCore::PlatformPasteboard::readBuffer const):
+        (WebCore::PlatformPasteboard::readString const):
+
+        Fix these methods to accept platform pasteboard types instead of MIME types, to match the behavior of existing
+        readBuffer and readString methods in PlatformPasteboardIOS.
+
+        * platform/win/PasteboardWin.cpp:
+        (WebCore::Pasteboard::read):
+
 2019-10-18  Devin Rousso  <drousso@apple.com>
 
         [ASAN] Fix WebGPU tests after r250258
diff --git a/Source/WebCore/editing/mac/EditorMac.mm b/Source/WebCore/editing/mac/EditorMac.mm
index c12350c2..80abbe2 100644
--- a/Source/WebCore/editing/mac/EditorMac.mm
+++ b/Source/WebCore/editing/mac/EditorMac.mm
@@ -185,7 +185,7 @@
     if (!canCopy())
         return nullptr;
 
-    if (pasteboardType == WebArchivePboardType)
+    if (pasteboardType == WebArchivePboardType || pasteboardType == String(kUTTypeWebArchive))
         return selectionInWebArchiveFormat();
 
     if (pasteboardType == String(legacyRTFDPasteboardType()))
diff --git a/Source/WebCore/platform/Pasteboard.h b/Source/WebCore/platform/Pasteboard.h
index d518a95..3141325 100644
--- a/Source/WebCore/platform/Pasteboard.h
+++ b/Source/WebCore/platform/Pasteboard.h
@@ -198,8 +198,8 @@
     virtual WEBCORE_EXPORT void clear();
     virtual WEBCORE_EXPORT void clear(const String& type);
 
-    virtual WEBCORE_EXPORT void read(PasteboardPlainText&);
-    virtual WEBCORE_EXPORT void read(PasteboardWebContentReader&, WebContentReadingPolicy = WebContentReadingPolicy::AnyType);
+    virtual WEBCORE_EXPORT void read(PasteboardPlainText&, Optional<size_t> itemIndex = WTF::nullopt);
+    virtual WEBCORE_EXPORT void read(PasteboardWebContentReader&, WebContentReadingPolicy = WebContentReadingPolicy::AnyType, Optional<size_t> itemIndex = WTF::nullopt);
     virtual WEBCORE_EXPORT void read(PasteboardFileReader&);
 
     virtual WEBCORE_EXPORT void write(const Color&);
@@ -282,7 +282,7 @@
 private:
 #if PLATFORM(IOS_FAMILY)
     bool respectsUTIFidelities() const;
-    void readRespectingUTIFidelities(PasteboardWebContentReader&, WebContentReadingPolicy);
+    void readRespectingUTIFidelities(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t>);
 
     enum class ReaderResult {
         ReadType,
diff --git a/Source/WebCore/platform/StaticPasteboard.h b/Source/WebCore/platform/StaticPasteboard.h
index c9bff6f..0e764ea 100644
--- a/Source/WebCore/platform/StaticPasteboard.h
+++ b/Source/WebCore/platform/StaticPasteboard.h
@@ -56,8 +56,8 @@
     void clear() final;
     void clear(const String& type) final;
 
-    void read(PasteboardPlainText&) final { }
-    void read(PasteboardWebContentReader&, WebContentReadingPolicy) final { }
+    void read(PasteboardPlainText&, Optional<size_t> = WTF::nullopt) final { }
+    void read(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t> = WTF::nullopt) final { }
 
     void write(const PasteboardURL&) final { }
     void write(const PasteboardImage&) final { }
diff --git a/Source/WebCore/platform/gtk/PasteboardGtk.cpp b/Source/WebCore/platform/gtk/PasteboardGtk.cpp
index 62a772b..ec97f13 100644
--- a/Source/WebCore/platform/gtk/PasteboardGtk.cpp
+++ b/Source/WebCore/platform/gtk/PasteboardGtk.cpp
@@ -28,6 +28,7 @@
 #include "PlatformStrategies.h"
 #include "SelectionData.h"
 #include <wtf/NeverDestroyed.h>
+#include <wtf/Optional.h>
 #include <wtf/URL.h>
 
 namespace WebCore {
@@ -231,13 +232,13 @@
 }
 #endif
 
-void Pasteboard::read(PasteboardPlainText& text)
+void Pasteboard::read(PasteboardPlainText& text, Optional<size_t>)
 {
     readFromClipboard();
     text.text = m_selectionData->text();
 }
 
-void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy)
+void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t>)
 {
 }
 
diff --git a/Source/WebCore/platform/ios/PasteboardIOS.mm b/Source/WebCore/platform/ios/PasteboardIOS.mm
index e1ce2f3..f6273f0 100644
--- a/Source/WebCore/platform/ios/PasteboardIOS.mm
+++ b/Source/WebCore/platform/ios/PasteboardIOS.mm
@@ -150,18 +150,20 @@
     return true;
 }
 
-void Pasteboard::read(PasteboardPlainText& text)
+void Pasteboard::read(PasteboardPlainText& text, Optional<size_t> itemIndex)
 {
+    auto itemIndexToQuery = itemIndex.valueOr(0);
+
     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
-    text.text = strategy.readStringFromPasteboard(0, kUTTypeURL, m_pasteboardName);
+    text.text = strategy.readStringFromPasteboard(itemIndexToQuery, kUTTypeURL, m_pasteboardName);
     if (!text.text.isEmpty()) {
         text.isURL = true;
         return;
     }
 
-    text.text = strategy.readStringFromPasteboard(0, kUTTypePlainText, m_pasteboardName);
+    text.text = strategy.readStringFromPasteboard(itemIndexToQuery, kUTTypePlainText, m_pasteboardName);
     if (text.text.isEmpty())
-        text.text = strategy.readStringFromPasteboard(0, kUTTypeText, m_pasteboardName);
+        text.text = strategy.readStringFromPasteboard(itemIndexToQuery, kUTTypeText, m_pasteboardName);
 
     text.isURL = false;
 }
@@ -276,17 +278,17 @@
     return info.canBeTreatedAsAttachmentOrFile() || UTTypeConformsTo(contentTypeForHighestFidelityItem.createCFString().get(), kUTTypeVCard);
 }
 
-void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolicy policy)
+void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolicy policy, Optional<size_t> itemIndex)
 {
     reader.contentOrigin = readOrigin();
     if (respectsUTIFidelities()) {
-        readRespectingUTIFidelities(reader, policy);
+        readRespectingUTIFidelities(reader, policy, itemIndex);
         return;
     }
 
     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
 
-    int numberOfItems = strategy.getPasteboardItemsCount(m_pasteboardName);
+    size_t numberOfItems = strategy.getPasteboardItemsCount(m_pasteboardName);
 
     if (!numberOfItems)
         return;
@@ -300,7 +302,10 @@
     bool canReadAttachment = false;
 #endif
 
-    for (int i = 0; i < numberOfItems; i++) {
+    for (size_t i = 0; i < numberOfItems; i++) {
+        if (itemIndex && i != *itemIndex)
+            continue;
+
         auto info = strategy.informationForItemAtIndex(i, m_pasteboardName);
 #if ENABLE(ATTACHMENT_ELEMENT)
         if (canReadAttachment && prefersAttachmentRepresentation(info)) {
@@ -336,11 +341,14 @@
     return m_pasteboardName == "data interaction pasteboard";
 }
 
-void Pasteboard::readRespectingUTIFidelities(PasteboardWebContentReader& reader, WebContentReadingPolicy policy)
+void Pasteboard::readRespectingUTIFidelities(PasteboardWebContentReader& reader, WebContentReadingPolicy policy, Optional<size_t> itemIndex)
 {
     ASSERT(respectsUTIFidelities());
     auto& strategy = *platformStrategies()->pasteboardStrategy();
     for (NSUInteger index = 0, numberOfItems = strategy.getPasteboardItemsCount(m_pasteboardName); index < numberOfItems; ++index) {
+        if (itemIndex && index != *itemIndex)
+            continue;
+
 #if ENABLE(ATTACHMENT_ELEMENT)
         auto info = strategy.informationForItemAtIndex(index, m_pasteboardName);
         auto attachmentFilePath = info.pathForHighestFidelityItem();
diff --git a/Source/WebCore/platform/libwpe/PasteboardLibWPE.cpp b/Source/WebCore/platform/libwpe/PasteboardLibWPE.cpp
index 368f937..7d78009 100644
--- a/Source/WebCore/platform/libwpe/PasteboardLibWPE.cpp
+++ b/Source/WebCore/platform/libwpe/PasteboardLibWPE.cpp
@@ -31,6 +31,7 @@
 #include "NotImplemented.h"
 #include "PasteboardStrategy.h"
 #include "PlatformStrategies.h"
+#include <wtf/Optional.h>
 
 namespace WebCore {
 
@@ -94,12 +95,12 @@
 {
 }
 
-void Pasteboard::read(PasteboardPlainText& text)
+void Pasteboard::read(PasteboardPlainText& text, Optional<size_t>)
 {
     text.text = platformStrategies()->pasteboardStrategy()->readStringFromPasteboard(0, "text/plain;charset=utf-8", name());
 }
 
-void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy)
+void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t>)
 {
     notImplemented();
 }
diff --git a/Source/WebCore/platform/mac/DragDataMac.mm b/Source/WebCore/platform/mac/DragDataMac.mm
index 64e4894..aa3b03b 100644
--- a/Source/WebCore/platform/mac/DragDataMac.mm
+++ b/Source/WebCore/platform/mac/DragDataMac.mm
@@ -237,6 +237,7 @@
     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
     return types.contains(String(WebArchivePboardType))
         || types.contains(htmlPasteboardType())
+        || types.contains(String(kUTTypeWebArchive))
 #if PLATFORM(MAC)
         || types.contains(String(legacyFilenamesPasteboardType()))
         || types.contains(String(legacyFilesPromisePasteboardType()))
diff --git a/Source/WebCore/platform/mac/PasteboardMac.mm b/Source/WebCore/platform/mac/PasteboardMac.mm
index c1ecef1..a3e4b29 100644
--- a/Source/WebCore/platform/mac/PasteboardMac.mm
+++ b/Source/WebCore/platform/mac/PasteboardMac.mm
@@ -129,8 +129,10 @@
 
     if (content.canSmartCopyOrDelete)
         types.append(WebSmartPastePboardType);
-    if (content.dataInWebArchiveFormat)
+    if (content.dataInWebArchiveFormat) {
         types.append(WebArchivePboardType);
+        types.append(kUTTypeWebArchive);
+    }
     if (content.dataInRTFDFormat)
         types.append(String(legacyRTFDPasteboardType()));
     if (content.dataInRTFFormat)
@@ -144,13 +146,18 @@
 
     m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName);
 
+    // FIXME: The following code should be refactored, such that it only requires a single call out to the client layer.
+    // In WebKit2, this currently results in many unnecessary synchronous round-trip IPC messages.
+
     ASSERT(content.clientTypes.size() == content.clientData.size());
     for (size_t i = 0, size = content.clientTypes.size(); i < size; ++i)
         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.clientData[i].get(), content.clientTypes[i], m_pasteboardName);
     if (content.canSmartCopyOrDelete)
         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(nullptr, WebSmartPastePboardType, m_pasteboardName);
-    if (content.dataInWebArchiveFormat)
+    if (content.dataInWebArchiveFormat) {
         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInWebArchiveFormat.get(), WebArchivePboardType, m_pasteboardName);
+        m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInWebArchiveFormat.get(), kUTTypeWebArchive, m_pasteboardName);
+    }
     if (content.dataInRTFDFormat)
         m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInRTFDFormat.get(), legacyRTFDPasteboardType(), m_pasteboardName);
     if (content.dataInRTFFormat)
@@ -258,13 +265,17 @@
     ASSERT(MIMETypeRegistry::isSupportedImageMIMEType(pasteboardImage.resourceMIMEType));
 
     auto types = writableTypesForImage();
-    if (pasteboardImage.dataInWebArchiveFormat)
+    if (pasteboardImage.dataInWebArchiveFormat) {
         types.append(WebArchivePboardType);
+        types.append(kUTTypeWebArchive);
+    }
 
     m_changeCount = writeURLForTypes(types, m_pasteboardName, pasteboardImage.url);
     m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::create(imageData).ptr(), legacyTIFFPasteboardType(), m_pasteboardName);
-    if (pasteboardImage.dataInWebArchiveFormat)
-        m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(pasteboardImage.dataInWebArchiveFormat.get(), WebArchivePboardType, m_pasteboardName);
+    if (auto archiveData = pasteboardImage.dataInWebArchiveFormat) {
+        m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(archiveData.get(), WebArchivePboardType, m_pasteboardName);
+        m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(archiveData.get(), kUTTypeWebArchive, m_pasteboardName);
+    }
     writeFileWrapperAsRTFDAttachment(fileWrapper(pasteboardImage), m_pasteboardName, m_changeCount);
 }
 
@@ -293,28 +304,55 @@
     return builder.toString();
 }
 
-void Pasteboard::read(PasteboardPlainText& text)
+static String readStringAtPreferredItemIndex(const String& type, Optional<size_t> itemIndex, PasteboardStrategy& strategy, const String& pasteboardName)
 {
-    PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
+    if (itemIndex)
+        return strategy.readStringFromPasteboard(*itemIndex, type, pasteboardName);
+    return strategy.stringForType(type, pasteboardName);
+}
+
+static RefPtr<SharedBuffer> readBufferAtPreferredItemIndex(const String& type, Optional<size_t> itemIndex, PasteboardStrategy& strategy, const String& pasteboardName)
+{
+    if (itemIndex)
+        return strategy.readBufferFromPasteboard(*itemIndex, type, pasteboardName);
+    return strategy.bufferForType(type, pasteboardName);
+}
+
+void Pasteboard::read(PasteboardPlainText& text, Optional<size_t> itemIndex)
+{
+    auto& strategy = *platformStrategies()->pasteboardStrategy();
 
     Vector<String> types;
-    strategy.getTypes(types, m_pasteboardName);
+    if (itemIndex)
+        types = strategy.informationForItemAtIndex(*itemIndex , m_pasteboardName).platformTypesByFidelity;
+    else
+        strategy.getTypes(types, m_pasteboardName);
 
     if (types.contains(String(NSPasteboardTypeString))) {
-        text.text = strategy.stringForType(NSPasteboardTypeString, m_pasteboardName);
+        text.text = readStringAtPreferredItemIndex(NSPasteboardTypeString, itemIndex, strategy, m_pasteboardName);
         text.isURL = false;
         return;
     }
 
     if (types.contains(String(legacyStringPasteboardType()))) {
-        text.text = strategy.stringForType(legacyStringPasteboardType(), m_pasteboardName);
+        text.text = readStringAtPreferredItemIndex(legacyStringPasteboardType(), itemIndex, strategy, m_pasteboardName);
         text.isURL = false;
         return;
     }
     
     if (types.contains(String(legacyRTFDPasteboardType()))) {
-        if (RefPtr<SharedBuffer> data = strategy.bufferForType(legacyRTFDPasteboardType(), m_pasteboardName)) {
-            if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTFD:data->createNSData().get() documentAttributes:NULL])) {
+        if (auto data = readBufferAtPreferredItemIndex(legacyRTFDPasteboardType(), itemIndex, strategy, m_pasteboardName)) {
+            if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTFD:data->createNSData().get() documentAttributes:nil])) {
+                text.text = [attributedString string];
+                text.isURL = false;
+                return;
+            }
+        }
+    }
+
+    if (types.contains(String(NSPasteboardTypeRTFD))) {
+        if (auto data = readBufferAtPreferredItemIndex(NSPasteboardTypeRTFD, itemIndex, strategy, m_pasteboardName)) {
+            if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTFD:data->createNSData().get() documentAttributes:nil])) {
                 text.text = [attributedString string];
                 text.isURL = false;
                 return;
@@ -323,8 +361,18 @@
     }
 
     if (types.contains(String(legacyRTFPasteboardType()))) {
-        if (RefPtr<SharedBuffer> data = strategy.bufferForType(legacyRTFPasteboardType(), m_pasteboardName)) {
-            if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTF:data->createNSData().get() documentAttributes:NULL])) {
+        if (auto data = readBufferAtPreferredItemIndex(legacyRTFPasteboardType(), itemIndex, strategy, m_pasteboardName)) {
+            if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTF:data->createNSData().get() documentAttributes:nil])) {
+                text.text = [attributedString string];
+                text.isURL = false;
+                return;
+            }
+        }
+    }
+
+    if (types.contains(String(NSPasteboardTypeRTF))) {
+        if (auto data = readBufferAtPreferredItemIndex(NSPasteboardTypeRTF, itemIndex, strategy, m_pasteboardName)) {
+            if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTF:data->createNSData().get() documentAttributes:nil])) {
                 text.text = [attributedString string];
                 text.isURL = false;
                 return;
@@ -347,21 +395,31 @@
     }
 
     // FIXME: The code above looks at the types vector first, but this just gets the string without checking. Why the difference?
-    text.text = strategy.stringForType(legacyURLPasteboardType(), m_pasteboardName);
+    text.text = readStringAtPreferredItemIndex(legacyURLPasteboardType(), itemIndex, strategy, m_pasteboardName);
     text.isURL = !text.text.isNull();
 }
 
-void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolicy policy)
+void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolicy policy, Optional<size_t> itemIndex)
 {
-    PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
+    auto& strategy = *platformStrategies()->pasteboardStrategy();
 
     Vector<String> types;
-    strategy.getTypes(types, m_pasteboardName);
+    if (itemIndex)
+        types = strategy.informationForItemAtIndex(*itemIndex , m_pasteboardName).platformTypesByFidelity;
+    else
+        strategy.getTypes(types, m_pasteboardName);
 
     reader.contentOrigin = readOrigin();
 
     if (types.contains(WebArchivePboardType)) {
-        if (auto buffer = strategy.bufferForType(WebArchivePboardType, m_pasteboardName)) {
+        if (auto buffer = readBufferAtPreferredItemIndex(WebArchivePboardType, itemIndex, strategy, m_pasteboardName)) {
+            if (m_changeCount != changeCount() || reader.readWebArchive(*buffer))
+                return;
+        }
+    }
+
+    if (types.contains(String(kUTTypeWebArchive))) {
+        if (auto buffer = readBufferAtPreferredItemIndex(kUTTypeWebArchive, itemIndex, strategy, m_pasteboardName)) {
             if (m_changeCount != changeCount() || reader.readWebArchive(*buffer))
                 return;
         }
@@ -380,20 +438,40 @@
     }
 
     if (types.contains(String(legacyHTMLPasteboardType()))) {
-        String string = strategy.stringForType(legacyHTMLPasteboardType(), m_pasteboardName);
+        String string = readStringAtPreferredItemIndex(legacyHTMLPasteboardType(), itemIndex, strategy, m_pasteboardName);
+        if (m_changeCount != changeCount() || (!string.isNull() && reader.readHTML(string)))
+            return;
+    }
+
+    if (types.contains(String(NSPasteboardTypeHTML))) {
+        String string = readStringAtPreferredItemIndex(NSPasteboardTypeHTML, itemIndex, strategy, m_pasteboardName);
         if (m_changeCount != changeCount() || (!string.isNull() && reader.readHTML(string)))
             return;
     }
 
     if (types.contains(String(legacyRTFDPasteboardType()))) {
-        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyRTFDPasteboardType(), m_pasteboardName)) {
+        if (auto buffer = readBufferAtPreferredItemIndex(legacyRTFDPasteboardType(), itemIndex, strategy, m_pasteboardName)) {
+            if (m_changeCount != changeCount() || reader.readRTFD(*buffer))
+                return;
+        }
+    }
+
+    if (types.contains(String(NSPasteboardTypeRTFD))) {
+        if (auto buffer = readBufferAtPreferredItemIndex(NSPasteboardTypeRTFD, itemIndex, strategy, m_pasteboardName)) {
             if (m_changeCount != changeCount() || reader.readRTFD(*buffer))
                 return;
         }
     }
 
     if (types.contains(String(legacyRTFPasteboardType()))) {
-        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyRTFPasteboardType(), m_pasteboardName)) {
+        if (auto buffer = readBufferAtPreferredItemIndex(legacyRTFPasteboardType(), itemIndex, strategy, m_pasteboardName)) {
+            if (m_changeCount != changeCount() || reader.readRTF(*buffer))
+                return;
+        }
+    }
+
+    if (types.contains(String(NSPasteboardTypeRTF))) {
+        if (auto buffer = readBufferAtPreferredItemIndex(NSPasteboardTypeRTF, itemIndex, strategy, m_pasteboardName)) {
             if (m_changeCount != changeCount() || reader.readRTF(*buffer))
                 return;
         }
@@ -403,28 +481,42 @@
         return;
 
     if (types.contains(String(legacyTIFFPasteboardType()))) {
-        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyTIFFPasteboardType(), m_pasteboardName)) {
+        if (auto buffer = readBufferAtPreferredItemIndex(legacyTIFFPasteboardType(), itemIndex, strategy, m_pasteboardName)) {
+            if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "image/tiff"_s))
+                return;
+        }
+    }
+
+    if (types.contains(String(NSPasteboardTypeTIFF))) {
+        if (auto buffer = readBufferAtPreferredItemIndex(NSPasteboardTypeTIFF, itemIndex, strategy, m_pasteboardName)) {
             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "image/tiff"_s))
                 return;
         }
     }
 
     if (types.contains(String(legacyPDFPasteboardType()))) {
-        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(legacyPDFPasteboardType(), m_pasteboardName)) {
+        if (auto buffer = readBufferAtPreferredItemIndex(legacyPDFPasteboardType(), itemIndex, strategy, m_pasteboardName)) {
+            if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "application/pdf"_s))
+                return;
+        }
+    }
+
+    if (types.contains(String(NSPasteboardTypePDF))) {
+        if (auto buffer = readBufferAtPreferredItemIndex(NSPasteboardTypePDF, itemIndex, strategy, m_pasteboardName)) {
             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "application/pdf"_s))
                 return;
         }
     }
 
     if (types.contains(String(kUTTypePNG))) {
-        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypePNG, m_pasteboardName)) {
+        if (auto buffer = readBufferAtPreferredItemIndex(kUTTypePNG, itemIndex, strategy, m_pasteboardName)) {
             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "image/png"_s))
                 return;
         }
     }
 
     if (types.contains(String(kUTTypeJPEG))) {
-        if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypeJPEG, m_pasteboardName)) {
+        if (auto buffer = readBufferAtPreferredItemIndex(kUTTypeJPEG, itemIndex, strategy, m_pasteboardName)) {
             if (m_changeCount != changeCount() || reader.readImage(buffer.releaseNonNull(), "image/jpeg"_s))
                 return;
         }
@@ -432,13 +524,13 @@
 
     if (types.contains(String(legacyURLPasteboardType()))) {
         URL url = strategy.url(m_pasteboardName);
-        String title = strategy.stringForType(WebURLNamePboardType, m_pasteboardName);
+        String title = readStringAtPreferredItemIndex(WebURLNamePboardType, itemIndex, strategy, m_pasteboardName);
         if (m_changeCount != changeCount() || (!url.isNull() && reader.readURL(url, title)))
             return;
     }
 
     if (types.contains(String(legacyStringPasteboardType()))) {
-        String string = strategy.stringForType(legacyStringPasteboardType(), m_pasteboardName);
+        String string = readStringAtPreferredItemIndex(legacyStringPasteboardType(), itemIndex, strategy, m_pasteboardName);
         if (m_changeCount != changeCount() || (!string.isNull() && reader.readPlainText(string)))
             return;
     }
diff --git a/Source/WebCore/platform/mac/PlatformPasteboardMac.mm b/Source/WebCore/platform/mac/PlatformPasteboardMac.mm
index 718126a..f5e4c40 100644
--- a/Source/WebCore/platform/mac/PlatformPasteboardMac.mm
+++ b/Source/WebCore/platform/mac/PlatformPasteboardMac.mm
@@ -186,7 +186,7 @@
     if (platformType == String(legacyURLPasteboardType()))
         return "text/uri-list"_s;
 
-    if (platformType == String(legacyHTMLPasteboardType()) || platformType == String(WebArchivePboardType)
+    if (platformType == String(legacyHTMLPasteboardType()) || platformType == String(WebArchivePboardType) || platformType == String(kUTTypeWebArchive)
         || platformType == String(legacyRTFDPasteboardType()) || platformType == String(legacyRTFPasteboardType()))
         return "text/html"_s;
 
@@ -420,11 +420,7 @@
     if (!item)
         return { };
 
-    auto platformType = modernPasteboardTypeForWebSafeMIMEType(type);
-    if (!platformType)
-        return nullptr;
-
-    if (NSData *data = [item dataForType:platformType]) {
+    if (NSData *data = [item dataForType:type]) {
         auto nsData = adoptNS(data.copy);
         return SharedBuffer::create(nsData.get());
     }
@@ -438,11 +434,7 @@
     if (!item)
         return { };
 
-    auto platformType = modernPasteboardTypeForWebSafeMIMEType(type);
-    if (!platformType)
-        return { };
-
-    return [item stringForType:platformType];
+    return [item stringForType:type];
 }
 
 URL PlatformPasteboard::readURL(size_t index, String& title) const
diff --git a/Source/WebCore/platform/win/PasteboardWin.cpp b/Source/WebCore/platform/win/PasteboardWin.cpp
index d261c21..45a37ca 100644
--- a/Source/WebCore/platform/win/PasteboardWin.cpp
+++ b/Source/WebCore/platform/win/PasteboardWin.cpp
@@ -48,6 +48,7 @@
 #include "TextEncoding.h"
 #include "WebCoreInstanceHandle.h"
 #include "markup.h"
+#include <wtf/Optional.h>
 #include <wtf/URL.h>
 #include <wtf/WindowsExtras.h>
 #include <wtf/text/CString.h>
@@ -788,7 +789,7 @@
     return ::IsClipboardFormatAvailable(WebSmartPasteFormat);
 }
 
-void Pasteboard::read(PasteboardPlainText& text)
+void Pasteboard::read(PasteboardPlainText& text, Optional<size_t>)
 {
     if (::IsClipboardFormatAvailable(CF_UNICODETEXT) && ::OpenClipboard(m_owner)) {
         if (HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT)) {
@@ -1073,7 +1074,7 @@
 {
 }
 
-void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy)
+void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t>)
 {
 }
 
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index 2dc3ce3..d68e3e9 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,19 @@
+2019-10-18  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Clipboard API] Refactor Pasteboard::read() to take an optional item index
+        https://bugs.webkit.org/show_bug.cgi?id=203161
+
+        Reviewed by Tim Horton.
+
+        * Shared/mac/PasteboardTypes.mm:
+        (WebKit::PasteboardTypes::forEditing):
+        (WebKit::PasteboardTypes::forSelection):
+
+        Support "com.apple.webarchive" alongside the private "Apple Web Archive pasteboard type".
+
+        * UIProcess/Cocoa/WebViewImpl.mm:
+        (WebKit::WebViewImpl::setPromisedDataForImage):
+
 2019-10-18  Ryosuke Niwa  <rniwa@webkit.org>
 
         [iOS] REGRESSION(r251269): fast/events/ios/rotation/do-not-shrink-to-fit-content-after-rotation.html and fast/events/ios/rotation/layout-viewport-during-safari-type-rotation.html fail
diff --git a/Source/WebKit/Shared/mac/PasteboardTypes.mm b/Source/WebKit/Shared/mac/PasteboardTypes.mm
index 712f9d7..7013281 100644
--- a/Source/WebKit/Shared/mac/PasteboardTypes.mm
+++ b/Source/WebKit/Shared/mac/PasteboardTypes.mm
@@ -46,7 +46,7 @@
     
 NSArray* PasteboardTypes::forEditing()
 {
-    static NSArray *types = retain([NSArray arrayWithObjects:WebArchivePboardType, WebCore::legacyHTMLPasteboardType(), WebCore::legacyFilenamesPasteboardType(), WebCore::legacyTIFFPasteboardType(), WebCore::legacyPDFPasteboardType(),
+    static NSArray *types = retain([NSArray arrayWithObjects:WebArchivePboardType, (__bridge NSString *)kUTTypeWebArchive, WebCore::legacyHTMLPasteboardType(), WebCore::legacyFilenamesPasteboardType(), WebCore::legacyTIFFPasteboardType(), WebCore::legacyPDFPasteboardType(),
         WebCore::legacyURLPasteboardType(), WebCore::legacyRTFDPasteboardType(), WebCore::legacyRTFPasteboardType(), WebCore::legacyStringPasteboardType(), WebCore::legacyColorPasteboardType(), (__bridge NSString *)kUTTypePNG, nil]);
     return types;
 }
@@ -71,7 +71,7 @@
 
 NSArray* PasteboardTypes::forSelection()
 {
-    static NSArray *types = retain([NSArray arrayWithObjects:WebArchivePboardType, WebCore::legacyRTFDPasteboardType(), WebCore::legacyRTFPasteboardType(), WebCore::legacyStringPasteboardType(), nil]);
+    static NSArray *types = retain([NSArray arrayWithObjects:WebArchivePboardType, (__bridge NSString *)kUTTypeWebArchive, WebCore::legacyRTFDPasteboardType(), WebCore::legacyRTFPasteboardType(), WebCore::legacyStringPasteboardType(), nil]);
     return types;
 }
     
diff --git a/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm b/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm
index 75e19ae..6575da6 100644
--- a/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm
+++ b/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm
@@ -4218,8 +4218,11 @@
     [pasteboard declareTypes:types.get() owner:m_view.getAutoreleased()];
     setFileAndURLTypes(filename, extension, title, url, visibleURL, pasteboard);
 
-    if (archiveBuffer)
-        [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType];
+    if (archiveBuffer) {
+        auto nsData = archiveBuffer->createNSData();
+        [pasteboard setData:nsData.get() forType:(__bridge NSString *)kUTTypeWebArchive];
+        [pasteboard setData:nsData.get() forType:PasteboardTypes::WebArchivePboardType];
+    }
 
     m_promisedImage = image;
 }
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index ea20e67..d0819d7 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,17 @@
+2019-10-18  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Clipboard API] Refactor Pasteboard::read() to take an optional item index
+        https://bugs.webkit.org/show_bug.cgi?id=203161
+
+        Reviewed by Tim Horton.
+
+        Add a couple of new API tests to verify that the web archive type identifier ("com.apple.webarchive") is (1)
+        written to the pasteboard when copying a rich text selection, and (2) is read when attempting to paste web
+        content.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm:
+        * TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm:
+
 2019-10-18  Jonathan Bedard  <jbedard@apple.com>
 
         Python 3: Add support in webkitpy.common.watchlist
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm
index 312c0d9..413ee3d 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm
@@ -87,4 +87,26 @@
     EXPECT_FALSE(htmlInNativePasteboard.contains("dangerousCode"));
 }
 
+#if PLATFORM(MAC)
+
+TEST(CopyHTML, ItemTypesWhenCopyingWebContent)
+{
+    auto webView = createWebViewWithCustomPasteboardDataEnabled();
+    [webView synchronouslyLoadHTMLString:@"<strong style='color: rgb(255, 0, 0);'>This is some text to copy.</strong>"];
+    [webView stringByEvaluatingJavaScript:@"getSelection().selectAllChildren(document.body)"];
+    [webView copy:nil];
+    [webView waitForNextPresentationUpdate];
+
+    NSArray<NSPasteboardItem *> *items = NSPasteboard.generalPasteboard.pasteboardItems;
+    EXPECT_EQ(1U, items.count);
+
+    NSArray<NSPasteboardType> *types = items.firstObject.types;
+    EXPECT_TRUE([types containsObject:(__bridge NSString *)kUTTypeWebArchive]);
+    EXPECT_TRUE([types containsObject:(__bridge NSString *)NSPasteboardTypeRTF]);
+    EXPECT_TRUE([types containsObject:(__bridge NSString *)NSPasteboardTypeString]);
+    EXPECT_TRUE([types containsObject:(__bridge NSString *)NSPasteboardTypeHTML]);
+}
+
 #endif // PLATFORM(MAC)
+
+#endif // PLATFORM(COCOA)
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm
index 3f5b6f1..b785f5c 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm
@@ -240,6 +240,28 @@
     EXPECT_WK_STREQ("rgb(255, 0, 0)", [webView stringByEvaluatingJavaScript:@"document.queryCommandValue('foreColor')"]);
 }
 
+TEST(PasteWebArchive, WebArchiveTypeIdentifier)
+{
+    NSURL *url = [NSURL URLWithString:@"file:///some-file.html"];
+    NSString *markup = @"<strong style='color: rgb(255, 0, 0);'>This is some text to copy.</strong>";
+
+    auto mainResource = adoptNS([[WebResource alloc] initWithData:[markup dataUsingEncoding:NSUTF8StringEncoding] URL:url MIMEType:@"text/html" textEncodingName:@"utf-8" frameName:nil]);
+    auto archive = adoptNS([[WebArchive alloc] initWithMainResource:mainResource.get() subresources:nil subframeArchives:nil]);
+
+    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+    [pasteboard declareTypes:[NSArray arrayWithObject:(__bridge NSString *)kUTTypeWebArchive] owner:nil];
+    [pasteboard setData:[archive data] forType:(__bridge NSString *)kUTTypeWebArchive];
+
+    auto webView = createWebViewWithCustomPasteboardDataEnabled();
+    [webView synchronouslyLoadTestPageNamed:@"paste-rtfd"];
+    [webView paste:nil];
+
+    EXPECT_WK_STREQ("[\"text/html\"]", [webView stringByEvaluatingJavaScript:@"JSON.stringify(clipboardData.types)"]);
+    [webView evaluateJavaScript:@"docment.body.innerHTML = clipboardData.values[0]" completionHandler:nil];
+    EXPECT_WK_STREQ("This is some text to copy.", [webView stringByEvaluatingJavaScript:@"document.querySelector('strong').textContent"]);
+    EXPECT_WK_STREQ("rgb(255, 0, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(document.querySelector('strong')).color"]);
+}
+
 #endif // PLATFORM(MAC)