MIMETypeRegistry::getExtensionsForMIMEType() needs to handle wildcard MIME types
https://bugs.webkit.org/show_bug.cgi?id=213826

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2020-07-01
Reviewed by Darin Adler.

Source/WebCore:

Working towards webkit.org/b/213347, it needs to be possible for WebCore
to get the file extensions for wildcard MIME types, e.g. "image/*" or "video/*".

For Cocoa platforms, we will enumerate the UTIs of the system. Get the
MIMEType and the extensions of each UTI. Add the following pairs to a
singleton HashMap:

    { MIMEType, extension }
    { Type(MIMEType)/*, extension }

Change MIMETypeRegistry::getExtensionsForMIMEType() such that it calls
extensionsForWildcardMIMEType() if the MIMEType ends with "*".

* platform/MIMETypeRegistry.h:
* platform/cocoa/MIMETypeRegistryCocoa.mm:
(WebCore::extensionsForMIMETypeMap):
(WebCore::extensionsForWildcardMIMEType):
(WebCore::MIMETypeRegistry::getExtensionsForMIMEType):
* platform/playstation/MIMETypeRegistryPlayStation.cpp:
(WebCore::MIMETypeRegistry::getExtensionsForMIMEType):
* platform/win/MIMETypeRegistryWin.cpp:
(WebCore::MIMETypeRegistry::getExtensionsForMIMEType):
* platform/xdg/MIMETypeRegistryXdg.cpp:
(WebCore::MIMETypeRegistry::getExtensionsForMIMEType):

Source/WebKit:

Replace extensionsForMIMEType() with MIMETypeRegistry::getExtensionsForMIMEType().

* UIProcess/API/Cocoa/WKOpenPanelParameters.mm:
(-[WKOpenPanelParameters _allowedFileExtensions]):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@263832 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index a1f5145..9145fb8 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,5 +1,37 @@
 2020-07-01  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
+        MIMETypeRegistry::getExtensionsForMIMEType() needs to handle wildcard MIME types
+        https://bugs.webkit.org/show_bug.cgi?id=213826
+
+        Reviewed by Darin Adler.
+
+        Working towards webkit.org/b/213347, it needs to be possible for WebCore
+        to get the file extensions for wildcard MIME types, e.g. "image/*" or "video/*".
+
+        For Cocoa platforms, we will enumerate the UTIs of the system. Get the
+        MIMEType and the extensions of each UTI. Add the following pairs to a
+        singleton HashMap:
+
+            { MIMEType, extension }
+            { Type(MIMEType)/*, extension }
+
+        Change MIMETypeRegistry::getExtensionsForMIMEType() such that it calls
+        extensionsForWildcardMIMEType() if the MIMEType ends with "*".
+
+        * platform/MIMETypeRegistry.h:
+        * platform/cocoa/MIMETypeRegistryCocoa.mm:
+        (WebCore::extensionsForMIMETypeMap):
+        (WebCore::extensionsForWildcardMIMEType):
+        (WebCore::MIMETypeRegistry::getExtensionsForMIMEType):
+        * platform/playstation/MIMETypeRegistryPlayStation.cpp:
+        (WebCore::MIMETypeRegistry::getExtensionsForMIMEType):
+        * platform/win/MIMETypeRegistryWin.cpp:
+        (WebCore::MIMETypeRegistry::getExtensionsForMIMEType):
+        * platform/xdg/MIMETypeRegistryXdg.cpp:
+        (WebCore::MIMETypeRegistry::getExtensionsForMIMEType):
+
+2020-07-01  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
         Allow the File object to be created with a replacement file
         https://bugs.webkit.org/show_bug.cgi?id=213825
 
diff --git a/Source/WebCore/platform/MIMETypeRegistry.h b/Source/WebCore/platform/MIMETypeRegistry.h
index 996ee78..e5e7aa1 100644
--- a/Source/WebCore/platform/MIMETypeRegistry.h
+++ b/Source/WebCore/platform/MIMETypeRegistry.h
@@ -57,7 +57,7 @@
     WEBCORE_EXPORT static String getMIMETypeForExtension(const String& extension);
 
     // FIXME: WebKit coding style says we should not have the word "get" in the names of these functions.
-    static Vector<String> getExtensionsForMIMEType(const String& type);
+    WEBCORE_EXPORT static Vector<String> getExtensionsForMIMEType(const String& type);
     WEBCORE_EXPORT static String getPreferredExtensionForMIMEType(const String& type);
     WEBCORE_EXPORT static String getMediaMIMETypeForExtension(const String& extension);
     static Vector<String> getMediaMIMETypesForExtension(const String& extension);
diff --git a/Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm b/Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm
index 2b3fe05..b3fc626 100644
--- a/Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm
+++ b/Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm
@@ -27,11 +27,67 @@
 #import "config.h"
 #import "MIMETypeRegistry.h"
 
+#import <pal/spi/cocoa/CoreServicesSPI.h>
 #import <pal/spi/cocoa/NSURLFileTypeMappingsSPI.h>
 #import <wtf/cocoa/VectorCocoa.h>
 
 namespace WebCore {
 
+static HashMap<String, HashSet<String>>& extensionsForMIMETypeMap()
+{
+    static auto extensionsForMIMETypeMap = makeNeverDestroyed([] {
+        HashMap<String, HashSet<String>> map;
+
+        auto addExtension = [&](const String& type, const String& extension) {
+            map.add(type, HashSet<String>()).iterator->value.add(extension);
+        };
+
+        auto addExtensions = [&](const String& type, NSArray<NSString *> *extensions) {
+            size_t pos = type.reverseFind('/');
+
+            ASSERT(pos != notFound);
+            auto wildcardMIMEType = makeString(type.left(pos), "/*"_s);
+
+            for (NSString *extension in extensions) {
+                if (!extension)
+                    continue;
+
+                // Add extension to wildcardMIMEType, for example add "png" to "image/*"
+                addExtension(wildcardMIMEType, extension);
+                // Add extension to its mimeType, for example add "png" to "image/png"
+                addExtension(type, extension);
+            }
+        };
+
+        auto allUTIs = adoptNS((__bridge NSArray<NSString *> *)_UTCopyDeclaredTypeIdentifiers());
+
+        for (NSString *uti in allUTIs.get()) {
+            auto type = adoptCF(UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uti, kUTTagClassMIMEType));
+            if (!type)
+                continue;
+            auto extensions = adoptCF(UTTypeCopyAllTagsWithClass((__bridge CFStringRef)uti, kUTTagClassFilenameExtension));
+            if (!extensions || !CFArrayGetCount(extensions.get()))
+                continue;
+            addExtensions(type.get(), (__bridge NSArray<NSString *> *)extensions.get());
+        }
+
+        return map;
+    }());
+
+    return extensionsForMIMETypeMap;
+}
+
+static Vector<String> extensionsForWildcardMIMEType(const String& type)
+{
+    Vector<String> extensions;
+
+    auto iterator = extensionsForMIMETypeMap().find(type);
+    if (iterator != extensionsForMIMETypeMap().end())
+        extensions.appendRange(iterator->value.begin(), iterator->value.end());
+
+    return extensions;
+}
+
 String MIMETypeRegistry::getMIMETypeForExtension(const String& extension)
 {
     return [[NSURLFileTypeMappings sharedMappings] MIMETypeForExtension:(NSString *)extension];
@@ -39,6 +95,8 @@
 
 Vector<String> MIMETypeRegistry::getExtensionsForMIMEType(const String& type)
 {
+    if (type.endsWith('*'))
+        return extensionsForWildcardMIMEType(type);
     return makeVector<String>([[NSURLFileTypeMappings sharedMappings] extensionsForMIMEType:type]);
 }
 
diff --git a/Source/WebCore/platform/playstation/MIMETypeRegistryPlayStation.cpp b/Source/WebCore/platform/playstation/MIMETypeRegistryPlayStation.cpp
index f372ce30..fb9831f 100644
--- a/Source/WebCore/platform/playstation/MIMETypeRegistryPlayStation.cpp
+++ b/Source/WebCore/platform/playstation/MIMETypeRegistryPlayStation.cpp
@@ -80,4 +80,10 @@
     return emptyString();
 }
 
+Vector<String> MIMETypeRegistry::getExtensionsForMIMEType(const String&)
+{
+    ASSERT_NOT_IMPLEMENTED_YET();
+    return { };
+}
+
 } // namespace WebCore
diff --git a/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp b/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp
index a044982..d38d1ca 100644
--- a/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp
+++ b/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp
@@ -114,4 +114,10 @@
     return false;
 }
 
+Vector<String> MIMETypeRegistry::getExtensionsForMIMEType(const String&)
+{
+    ASSERT_NOT_IMPLEMENTED_YET();
+    return { };
+}
+
 }
diff --git a/Source/WebCore/platform/xdg/MIMETypeRegistryXdg.cpp b/Source/WebCore/platform/xdg/MIMETypeRegistryXdg.cpp
index fc91190b..f3b4837 100644
--- a/Source/WebCore/platform/xdg/MIMETypeRegistryXdg.cpp
+++ b/Source/WebCore/platform/xdg/MIMETypeRegistryXdg.cpp
@@ -66,4 +66,10 @@
     return returnValue;
 }
 
+Vector<String> MIMETypeRegistry::getExtensionsForMIMEType(const String&)
+{
+    ASSERT_NOT_IMPLEMENTED_YET();
+    return { };
+}
+
 }
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index 6de9f20..6359e10 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,15 @@
+2020-07-01  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        MIMETypeRegistry::getExtensionsForMIMEType() needs to handle wildcard MIME types
+        https://bugs.webkit.org/show_bug.cgi?id=213826
+
+        Reviewed by Darin Adler.
+
+        Replace extensionsForMIMEType() with MIMETypeRegistry::getExtensionsForMIMEType().
+
+        * UIProcess/API/Cocoa/WKOpenPanelParameters.mm:
+        (-[WKOpenPanelParameters _allowedFileExtensions]):
+
 2020-07-01  Lauro Moura  <lmoura@igalia.com>
 
         [SOUP] Build fix after r263797 for older soup versions.
diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKOpenPanelParameters.mm b/Source/WebKit/UIProcess/API/Cocoa/WKOpenPanelParameters.mm
index d900f9e..9f23350 100644
--- a/Source/WebKit/UIProcess/API/Cocoa/WKOpenPanelParameters.mm
+++ b/Source/WebKit/UIProcess/API/Cocoa/WKOpenPanelParameters.mm
@@ -25,60 +25,12 @@
 
 #import "config.h"
 #import "WKOpenPanelParametersInternal.h"
-#import <pal/spi/cocoa/CoreServicesSPI.h>
+#import <WebCore/MIMETypeRegistry.h>
 
 #if PLATFORM(MAC)
 
 #import "WKNSArray.h"
 
-static NSDictionary<NSString *, NSSet<NSString *> *> *extensionsForMIMETypeMap()
-{
-    static auto extensionsForMIMETypeMap = makeNeverDestroyed([] {
-        auto extensionsForMIMETypeMap = adoptNS([[NSMutableDictionary alloc] init]);
-        auto allUTIs = adoptCF(_UTCopyDeclaredTypeIdentifiers());
-
-        auto addExtensionForMIMEType = ^(NSString *mimeType, NSString *extension) {
-            if (!extensionsForMIMETypeMap.get()[mimeType])
-                extensionsForMIMETypeMap.get()[mimeType] = [NSMutableSet set];
-            [extensionsForMIMETypeMap.get()[mimeType] addObject:extension];
-        };
-
-        auto addExtensionsForMIMEType = ^(NSString *mimeType, NSArray<NSString *> *extensions) {
-            auto wildcardMIMEType = [[mimeType componentsSeparatedByString:@"/"][0] stringByAppendingString:@"/*"];
-
-            for (NSString *extension in extensions) {
-                if (!extension)
-                    continue;
-                // Add extension to wildcardMIMEType, for example add "png" to "image/*"
-                addExtensionForMIMEType(wildcardMIMEType, extension);
-                // Add extension to itsmimeType, for example add "png" to "image/png"
-                addExtensionForMIMEType(mimeType, extension);
-            }
-        };
-
-        for (CFIndex i = 0, count = CFArrayGetCount(allUTIs.get()); i < count; ++i) {
-            auto uti = static_cast<CFStringRef>(CFArrayGetValueAtIndex(allUTIs.get(), i));
-            auto mimeType = adoptCF(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
-            if (!mimeType)
-                continue;
-            auto extensions = adoptCF(UTTypeCopyAllTagsWithClass(uti, kUTTagClassFilenameExtension));
-            addExtensionsForMIMEType((__bridge NSString *)mimeType.get(), (__bridge NSArray<NSString *> *)extensions.get());
-        }
-
-        // Add additional mime types which _UTCopyDeclaredTypeIdentifiers() may not return.
-        addExtensionForMIMEType(@"image/webp", @"webp");
-
-        return extensionsForMIMETypeMap;
-    }());
-
-    return extensionsForMIMETypeMap.get().get();
-}
-
-static NSSet<NSString *> *extensionsForMIMEType(NSString *mimetype)
-{
-    return [extensionsForMIMETypeMap() objectForKey:mimetype];
-}
-
 @implementation WKOpenPanelParameters
 
 - (BOOL)allowsMultipleSelection
@@ -122,7 +74,8 @@
 
     [acceptedMIMETypes enumerateObjectsUsingBlock:^(NSString *mimeType, NSUInteger index, BOOL* stop) {
         ASSERT([mimeType containsString:@"/"]);
-        [allowedFileExtensions unionSet:extensionsForMIMEType(mimeType)];
+        auto extensions = API::Array::createStringArray(WebCore::MIMETypeRegistry::getExtensionsForMIMEType(mimeType));
+        [allowedFileExtensions addObjectsFromArray:wrapper(extensions)];
     }];
 
     auto additionalAllowedFileExtensions = adoptNS([[NSMutableArray alloc] init]);