[Clipboard API] [iOS] Refactor logic for suppressing URL data when the pasteboard may contain files
https://bugs.webkit.org/show_bug.cgi?id=203065

Reviewed by Tim Horton.

Platform-specific pasteboard refactoring before tackling webkit.org/b/203021. See below for details. No new
tests, as there is no change in behavior.

* platform/Pasteboard.h:
(WebCore::Pasteboard::changeCount const):

Add a stub for Pasteboard::changeCount() on non-Cocoa platforms.

* platform/ios/PlatformPasteboardIOS.mm:
(WebCore::safeTypeForDOMToReadAndWriteForPlatformType):

Move this helper function farther up the file, so it can be used in more places.

(WebCore::webSafeTypes):

Add a separate helper to compute the list of web-safe MIME types (for instance, "text/plain") given a list of
platform types, and a function to determine whether to avoid exposing a given URL to the DOM.

(WebCore::shouldTreatAtLeastOneTypeAsFile):

Split this out into a separate helper function, so that it can be invoked when determining whether a given
NSItemProvider is suspected to contain file paths. In the case where we're getting the full list of types that
are safe for the DOM to read, we want to pass in the entire pasteboard object and iterate through all of the
available types in search for something that could feasibly accompany a file URL.

In the case of computing the web-safe types per item, we only have an item provider, in which case we only
iterate through the types in that NSItemProvider. See call sites below for examples.

(WebCore::PlatformPasteboard::informationForItemAtIndex):

Invoke the new `webSafeTypes` helper method.

(WebCore::pasteboardMayContainFilePaths):
(WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):

Invoke the new `webSafeTypes` helper method.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251224 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 9894fd2..0402fed 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,47 @@
+2019-10-16  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Clipboard API] [iOS] Refactor logic for suppressing URL data when the pasteboard may contain files
+        https://bugs.webkit.org/show_bug.cgi?id=203065
+
+        Reviewed by Tim Horton.
+
+        Platform-specific pasteboard refactoring before tackling webkit.org/b/203021. See below for details. No new
+        tests, as there is no change in behavior.
+
+        * platform/Pasteboard.h:
+        (WebCore::Pasteboard::changeCount const):
+
+        Add a stub for Pasteboard::changeCount() on non-Cocoa platforms.
+
+        * platform/ios/PlatformPasteboardIOS.mm:
+        (WebCore::safeTypeForDOMToReadAndWriteForPlatformType):
+
+        Move this helper function farther up the file, so it can be used in more places.
+
+        (WebCore::webSafeTypes):
+
+        Add a separate helper to compute the list of web-safe MIME types (for instance, "text/plain") given a list of
+        platform types, and a function to determine whether to avoid exposing a given URL to the DOM.
+
+        (WebCore::shouldTreatAtLeastOneTypeAsFile):
+
+        Split this out into a separate helper function, so that it can be invoked when determining whether a given
+        NSItemProvider is suspected to contain file paths. In the case where we're getting the full list of types that
+        are safe for the DOM to read, we want to pass in the entire pasteboard object and iterate through all of the
+        available types in search for something that could feasibly accompany a file URL.
+
+        In the case of computing the web-safe types per item, we only have an item provider, in which case we only
+        iterate through the types in that NSItemProvider. See call sites below for examples.
+
+        (WebCore::PlatformPasteboard::informationForItemAtIndex):
+
+        Invoke the new `webSafeTypes` helper method.
+
+        (WebCore::pasteboardMayContainFilePaths):
+        (WebCore::PlatformPasteboard::typesSafeForDOMToReadAndWrite const):
+
+        Invoke the new `webSafeTypes` helper method.
+
 2019-10-16  Brent Fulgham  <bfulgham@apple.com>
 
         [FTW] Correct radial gradient handling of various radius orderings
diff --git a/Source/WebCore/platform/Pasteboard.h b/Source/WebCore/platform/Pasteboard.h
index d37c3c5..d518a95 100644
--- a/Source/WebCore/platform/Pasteboard.h
+++ b/Source/WebCore/platform/Pasteboard.h
@@ -253,6 +253,8 @@
     WEBCORE_EXPORT static NSArray *supportedFileUploadPasteboardTypes();
     long changeCount() const;
     const PasteboardCustomData& readCustomData();
+#else
+    long changeCount() const { return 0; }
 #endif
 
 #if PLATFORM(COCOA)
diff --git a/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm b/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm
index 31b2070..b356525 100644
--- a/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm
+++ b/Source/WebCore/platform/ios/PlatformPasteboardIOS.mm
@@ -97,6 +97,45 @@
 
 #if PASTEBOARD_SUPPORTS_ITEM_PROVIDERS
 
+static const char *safeTypeForDOMToReadAndWriteForPlatformType(const String& platformType)
+{
+    auto cfType = platformType.createCFString();
+    if (UTTypeConformsTo(cfType.get(), kUTTypePlainText))
+        return "text/plain"_s;
+
+    if (UTTypeConformsTo(cfType.get(), kUTTypeHTML) || UTTypeConformsTo(cfType.get(), (CFStringRef)WebArchivePboardType)
+        || UTTypeConformsTo(cfType.get(), kUTTypeRTF) || UTTypeConformsTo(cfType.get(), kUTTypeFlatRTFD))
+        return "text/html"_s;
+
+    if (UTTypeConformsTo(cfType.get(), kUTTypeURL))
+        return "text/uri-list"_s;
+
+    return nullptr;
+}
+
+static Vector<String> webSafeTypes(NSArray<NSString *> *platformTypes, Function<bool()>&& shouldAvoidExposingURLType)
+{
+    ListHashSet<String> domPasteboardTypes;
+    for (NSString *type in platformTypes) {
+        if ([type isEqualToString:@(PasteboardCustomData::cocoaType())])
+            continue;
+
+        if (Pasteboard::isSafeTypeForDOMToReadAndWrite(type)) {
+            domPasteboardTypes.add(type);
+            continue;
+        }
+
+        if (auto* coercedType = safeTypeForDOMToReadAndWriteForPlatformType(type)) {
+            auto domTypeAsString = String::fromUTF8(coercedType);
+            if (domTypeAsString == "text/uri-list"_s && ([platformTypes containsObject:(__bridge NSString *)kUTTypeFileURL] || shouldAvoidExposingURLType()))
+                continue;
+
+            domPasteboardTypes.add(WTFMove(domTypeAsString));
+        }
+    }
+    return copyToVector(domPasteboardTypes);
+}
+
 #if PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA
 
 static PasteboardItemPresentationStyle pasteboardItemPresentationStyle(UIPreferredPresentationStyle style)
@@ -116,6 +155,15 @@
 
 #endif // PASTEBOARD_SUPPORTS_PRESENTATION_STYLE_AND_TEAM_DATA
 
+static bool shouldTreatAtLeastOneTypeAsFile(NSArray <NSString *> *platformTypes)
+{
+    for (NSString *type in platformTypes) {
+        if (Pasteboard::shouldTreatCocoaTypeAsFile(type))
+            return true;
+    }
+    return false;
+}
+
 PasteboardItemInfo PlatformPasteboard::informationForItemAtIndex(size_t index)
 {
     if (index >= static_cast<NSUInteger>([m_pasteboard numberOfItems]))
@@ -183,6 +231,9 @@
     }
 
     info.changeCount = changeCount();
+    info.webSafeTypesByFidelity = webSafeTypes(registeredTypeIdentifiers, [&] {
+        return shouldTreatAtLeastOneTypeAsFile(registeredTypeIdentifiers) && !Pasteboard::canExposeURLToDOMWhenPasteboardContainsFiles(readString(index, kUTTypeURL));
+    });
 
     return info;
 }
@@ -202,12 +253,7 @@
     if ([pasteboard isKindOfClass:[WebItemProviderPasteboard class]])
         return false;
 #endif
-
-    for (NSString *type in pasteboard.pasteboardTypes) {
-        if (Pasteboard::shouldTreatCocoaTypeAsFile(type))
-            return true;
-    }
-    return false;
+    return shouldTreatAtLeastOneTypeAsFile(pasteboard.pasteboardTypes);
 }
 
 String PlatformPasteboard::stringForType(const String& type) const
@@ -480,22 +526,6 @@
     registerItemToPasteboard(representationsToRegister.get(), m_pasteboard.get());
 }
 
-static const char *safeTypeForDOMToReadAndWriteForPlatformType(const String& platformType)
-{
-    auto cfType = platformType.createCFString();
-    if (UTTypeConformsTo(cfType.get(), kUTTypePlainText))
-        return "text/plain"_s;
-
-    if (UTTypeConformsTo(cfType.get(), kUTTypeHTML) || UTTypeConformsTo(cfType.get(), (CFStringRef)WebArchivePboardType)
-        || UTTypeConformsTo(cfType.get(), kUTTypeRTF) || UTTypeConformsTo(cfType.get(), kUTTypeFlatRTFD))
-        return "text/html"_s;
-
-    if (UTTypeConformsTo(cfType.get(), kUTTypeURL))
-        return "text/uri-list"_s;
-
-    return nullptr;
-}
-
 static const char originKeyForTeamData[] = "com.apple.WebKit.drag-and-drop-team-data.origin";
 static const char customTypesKeyForTeamData[] = "com.apple.WebKit.drag-and-drop-team-data.custom-types";
 
@@ -534,28 +564,13 @@
         }
     }
 
-    for (NSString *type in [m_pasteboard pasteboardTypes]) {
-        if ([type isEqualToString:@(PasteboardCustomData::cocoaType())])
-            continue;
+    auto webSafePasteboardTypes = webSafeTypes([m_pasteboard pasteboardTypes], [&] {
+        BOOL ableToDetermineProtocolOfPasteboardURL = ![m_pasteboard isKindOfClass:[WebItemProviderPasteboard class]];
+        return ableToDetermineProtocolOfPasteboardURL && stringForType(kUTTypeURL).isEmpty();
+    });
 
-        if (Pasteboard::isSafeTypeForDOMToReadAndWrite(type)) {
-            domPasteboardTypes.add(type);
-            continue;
-        }
-
-        if (auto* coercedType = safeTypeForDOMToReadAndWriteForPlatformType(type)) {
-            auto domTypeAsString = String::fromUTF8(coercedType);
-            if (domTypeAsString == "text/uri-list") {
-                BOOL ableToDetermineProtocolOfPasteboardURL = ![m_pasteboard isKindOfClass:[WebItemProviderPasteboard class]];
-                if (ableToDetermineProtocolOfPasteboardURL && stringForType(kUTTypeURL).isEmpty())
-                    continue;
-
-                if ([[m_pasteboard pasteboardTypes] containsObject:(__bridge NSString *)kUTTypeFileURL])
-                    continue;
-            }
-            domPasteboardTypes.add(WTFMove(domTypeAsString));
-        }
-    }
+    for (auto& type : webSafePasteboardTypes)
+        domPasteboardTypes.add(type);
 
     return copyToVector(domPasteboardTypes);
 }