[Clipboard API] Refactor Pasteboard::read() to take an optional item index
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Oct 2019 00:47:30 +0000 (00:47 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Oct 2019 00:47:30 +0000 (00:47 +0000)
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: https://svn.webkit.org/repository/webkit/trunk@251316 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
Source/WebCore/ChangeLog
Source/WebCore/editing/mac/EditorMac.mm
Source/WebCore/platform/Pasteboard.h
Source/WebCore/platform/StaticPasteboard.h
Source/WebCore/platform/gtk/PasteboardGtk.cpp
Source/WebCore/platform/ios/PasteboardIOS.mm
Source/WebCore/platform/libwpe/PasteboardLibWPE.cpp
Source/WebCore/platform/mac/DragDataMac.mm
Source/WebCore/platform/mac/PasteboardMac.mm
Source/WebCore/platform/mac/PlatformPasteboardMac.mm
Source/WebCore/platform/win/PasteboardWin.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/mac/PasteboardTypes.mm
Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/CopyHTML.mm
Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm

index fba828a..c5436d7 100644 (file)
@@ -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
index c12350c..80abbe2 100644 (file)
@@ -185,7 +185,7 @@ RefPtr<SharedBuffer> Editor::dataSelectionForPasteboard(const String& pasteboard
     if (!canCopy())
         return nullptr;
 
-    if (pasteboardType == WebArchivePboardType)
+    if (pasteboardType == WebArchivePboardType || pasteboardType == String(kUTTypeWebArchive))
         return selectionInWebArchiveFormat();
 
     if (pasteboardType == String(legacyRTFDPasteboardType()))
index d518a95..3141325 100644 (file)
@@ -198,8 +198,8 @@ public:
     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 @@ public:
 private:
 #if PLATFORM(IOS_FAMILY)
     bool respectsUTIFidelities() const;
-    void readRespectingUTIFidelities(PasteboardWebContentReader&, WebContentReadingPolicy);
+    void readRespectingUTIFidelities(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t>);
 
     enum class ReaderResult {
         ReadType,
index c9bff6f..0e764ea 100644 (file)
@@ -56,8 +56,8 @@ public:
     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 { }
index 62a772b..ec97f13 100644 (file)
@@ -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 @@ void Pasteboard::setDragImage(DragImage, const IntPoint&)
 }
 #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>)
 {
 }
 
index e1ce2f3..f6273f0 100644 (file)
@@ -150,18 +150,20 @@ bool Pasteboard::canSmartReplace()
     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 @@ static bool prefersAttachmentRepresentation(const PasteboardItemInfo& info)
     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 @@ void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolic
     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 @@ bool Pasteboard::respectsUTIFidelities() const
     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();
index 368f937..7d78009 100644 (file)
@@ -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::clear(const String&)
 {
 }
 
-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();
 }
index 64e4894..aa3b03b 100644 (file)
@@ -237,6 +237,7 @@ bool DragData::containsCompatibleContent(DraggingPurpose purpose) const
     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()))
index c1ecef1..a3e4b29 100644 (file)
@@ -129,8 +129,10 @@ void Pasteboard::write(const PasteboardWebContent& content)
 
     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 @@ void Pasteboard::write(const PasteboardWebContent& content)
 
     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 @@ void Pasteboard::write(const PasteboardImage& pasteboardImage)
     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 @@ static String joinPathnames(const Vector<String>& pathnames)
     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 @@ void Pasteboard::read(PasteboardPlainText& text)
     }
 
     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 @@ void Pasteboard::read(PasteboardPlainText& text)
     }
 
     // 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 @@ void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolic
     }
 
     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 @@ void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolic
         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 @@ void Pasteboard::read(PasteboardWebContentReader& reader, WebContentReadingPolic
 
     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;
     }
index 718126a..f5e4c40 100644 (file)
@@ -186,7 +186,7 @@ static const char* safeTypeForDOMToReadAndWriteForPlatformType(const String& pla
     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 @@ RefPtr<SharedBuffer> PlatformPasteboard::readBuffer(size_t index, const String&
     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 @@ String PlatformPasteboard::readString(size_t index, const String& type) const
     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
index d261c21..45a37ca 100644 (file)
@@ -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 @@ bool Pasteboard::canSmartReplace()
     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::write(const PasteboardWebContent&)
 {
 }
 
-void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy)
+void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy, Optional<size_t>)
 {
 }
 
index 2dc3ce3..d68e3e9 100644 (file)
@@ -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
index 712f9d7..7013281 100644 (file)
@@ -46,7 +46,7 @@ static inline NSArray *retain(NSArray *array)
     
 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::forImagesWithArchive()
 
 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;
 }
     
index 75e19ae..6575da6 100644 (file)
@@ -4218,8 +4218,11 @@ void WebViewImpl::setPromisedDataForImage(WebCore::Image* image, NSString *filen
     [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;
 }
index ea20e67..d0819d7 100644 (file)
@@ -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
index 312c0d9..413ee3d 100644 (file)
@@ -87,4 +87,26 @@ TEST(CopyHTML, Sanitizes)
     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)
index 3f5b6f1..b785f5c 100644 (file)
@@ -240,6 +240,28 @@ TEST(PasteWebArchive, StripsMSOListWhenMissingMSOHTMLElement)
     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)