Web Automation: elements larger than the viewport have incorrect in-view center point
[WebKit-https.git] / Source / WebCore / platform / MIMETypeRegistry.cpp
1 /*
2  * Copyright (C) 2006-2019 Apple Inc. All rights reserved.
3  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "MIMETypeRegistry.h"
29
30 #include "MediaPlayer.h"
31 #include <wtf/HashMap.h>
32 #include <wtf/MainThread.h>
33 #include <wtf/NeverDestroyed.h>
34 #include <wtf/StdLibExtras.h>
35
36 #if USE(CG)
37 #include "ImageSourceCG.h"
38 #include "UTIRegistry.h"
39 #include <ImageIO/ImageIO.h>
40 #include <wtf/RetainPtr.h>
41 #endif
42
43 #if USE(CG) && PLATFORM(COCOA)
44 #include "UTIUtilities.h"
45 #endif
46
47 #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
48 #include "ArchiveFactory.h"
49 #endif
50
51 #if HAVE(AVASSETREADER)
52 #include "ContentType.h"
53 #include "ImageDecoderAVFObjC.h"
54 #endif
55
56 #if USE(QUICK_LOOK)
57 #include "PreviewConverter.h"
58 #endif
59
60 #if USE(APPLE_INTERNAL_SDK)
61 #include <WebKitAdditions/AdditionalSystemPreviewTypes.h>
62 #else
63 #define ADDITIONAL_SYSTEM_PREVIEW_TYPES
64 #endif
65
66 namespace WebCore {
67
68 const HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::supportedImageMIMETypes()
69 {
70     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> supportedImageMIMETypes = std::initializer_list<String> {
71 #if USE(CG)
72         // This represents the subset of allowed image UTIs for which CoreServices has a corresponding MIME type.
73         "image/tiff"_s,
74         "image/gif"_s,
75         "image/jpeg"_s,
76         "image/vnd.microsoft.icon"_s,
77         "image/jp2"_s,
78         "image/png"_s,
79         "image/bmp"_s,
80
81         "image/x-icon"_s, // Favicons don't have a MIME type in the registry either.
82         "image/pjpeg"_s, //  We only get one MIME type per UTI, hence our need to add these manually
83
84 #if PLATFORM(IOS_FAMILY)
85         // Add malformed image mimetype for compatibility with Mail and to handle malformed mimetypes from the net
86         // These were removed for <rdar://problem/6564538> Re-enable UTI code in WebCore now that MobileCoreServices exists
87         // But Mail relies on at least image/tif reported as being supported (should be image/tiff).
88         // This can be removed when Mail addresses:
89         // <rdar://problem/7879510> Mail should use standard image mimetypes
90         // and we fix sniffing so that it corrects items such as image/jpg -> image/jpeg.
91
92         // JPEG (image/jpeg)
93         "image/jpg"_s,
94         "image/jp_"_s,
95         "image/jpe_"_s,
96         "application/jpg"_s,
97         "application/x-jpg"_s,
98         "image/pipeg"_s,
99         "image/vnd.switfview-jpeg"_s,
100         "image/x-xbitmap"_s,
101
102         // GIF (image/gif)
103         "image/gi_"_s,
104
105         // PNG (image/png)
106         "application/png"_s,
107         "application/x-png"_s,
108
109         // TIFF (image/tiff)
110         "image/x-tif"_s,
111         "image/tif"_s,
112         "image/x-tiff"_s,
113         "application/tif"_s,
114         "application/x-tif"_s,
115         "application/tiff"_s,
116         "application/x-tiff"_s,
117
118         // BMP (image/bmp, image/x-bitmap)
119         "image/x-bmp"_s,
120         "image/x-win-bitmap"_s,
121         "image/x-windows-bmp"_s,
122         "image/ms-bmp"_s,
123         "image/x-ms-bmp"_s,
124         "application/bmp"_s,
125         "application/x-bmp"_s,
126         "application/x-win-bitmap"_s,
127 #endif
128 #else
129         // assume that all implementations at least support the following standard
130         // image types:
131         "image/jpeg"_s,
132         "image/png"_s,
133         "image/gif"_s,
134         "image/bmp"_s,
135         "image/vnd.microsoft.icon"_s, // ico
136         "image/x-icon"_s, // ico
137         "image/x-xbitmap"_s, // xbm
138 #if USE(OPENJPEG)
139         "image/jp2"_s,
140         "image/jpeg2000"_s,
141 #endif
142 #if USE(WEBP)
143         "image/webp"_s,
144 #endif
145 #endif
146     };
147
148 #if USE(CG)
149 #ifndef NDEBUG
150     // Esnure supportedImageMIMETypes() is in sync with defaultSupportedImageTypes().
151     static std::once_flag onceFlag;
152     std::call_once(onceFlag, [] {
153         for (auto& imageType : defaultSupportedImageTypes()) {
154             auto mimeType = MIMETypeForImageType(imageType);
155             ASSERT_IMPLIES(!mimeType.isEmpty(), supportedImageMIMETypes.get().contains(mimeType));
156         }
157     });
158 #endif
159 #endif
160     return supportedImageMIMETypes;
161 }
162
163 HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::additionalSupportedImageMIMETypes()
164 {
165     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> additionalSupportedImageMIMETypes;
166     return additionalSupportedImageMIMETypes;
167 }
168
169 static const HashSet<String, ASCIICaseInsensitiveHash>& supportedImageMIMETypesForEncoding()
170 {
171 #if PLATFORM(COCOA)
172     static const auto supportedImageMIMETypesForEncoding = makeNeverDestroyed([] {
173         RetainPtr<CFArrayRef> supportedTypes = adoptCF(CGImageDestinationCopyTypeIdentifiers());
174         HashSet<String, ASCIICaseInsensitiveHash> supportedImageMIMETypesForEncoding;
175         CFIndex count = CFArrayGetCount(supportedTypes.get());
176         for (CFIndex i = 0; i < count; i++) {
177             CFStringRef supportedType = reinterpret_cast<CFStringRef>(CFArrayGetValueAtIndex(supportedTypes.get(), i));
178             String mimeType = MIMETypeForImageType(supportedType);
179             if (!mimeType.isEmpty())
180                 supportedImageMIMETypesForEncoding.add(mimeType);
181         }
182         return supportedImageMIMETypesForEncoding;
183     }());
184 #else
185     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> supportedImageMIMETypesForEncoding =std::initializer_list<String> {
186 #if USE(CG)
187         // FIXME: Add Windows support for all the supported UTI's when a way to convert from MIMEType to UTI reliably is found.
188         // For now, only support PNG, JPEG and GIF. See <rdar://problem/6095286>.
189         "image/png"_s,
190         "image/jpeg"_s,
191         "image/gif"_s,
192 #elif PLATFORM(GTK)
193         "image/png"_s,
194         "image/jpeg"_s,
195         "image/tiff"_s,
196         "image/bmp"_s,
197         "image/ico"_s,
198 #elif USE(CAIRO)
199         "image/png"_s,
200 #endif
201     };
202 #endif
203     return supportedImageMIMETypesForEncoding;
204 }
205
206 static const HashSet<String, ASCIICaseInsensitiveHash>& supportedJavaScriptMIMETypes()
207 {
208     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> supportedJavaScriptMIMETypes = std::initializer_list<String> {
209         // https://html.spec.whatwg.org/multipage/scripting.html#javascript-mime-type
210         "text/javascript"_s,
211         "text/ecmascript"_s,
212         "application/javascript"_s,
213         "application/ecmascript"_s,
214         "application/x-javascript"_s,
215         "application/x-ecmascript"_s,
216         "text/javascript1.0"_s,
217         "text/javascript1.1"_s,
218         "text/javascript1.2"_s,
219         "text/javascript1.3"_s,
220         "text/javascript1.4"_s,
221         "text/javascript1.5"_s,
222         "text/jscript"_s,
223         "text/livescript"_s,
224         "text/x-javascript"_s,
225         "text/x-ecmascript"_s,
226     };
227     return supportedJavaScriptMIMETypes;
228 }
229
230 HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::supportedNonImageMIMETypes()
231 {
232     static auto supportedNonImageMIMETypes = makeNeverDestroyed([] {
233         HashSet<String, ASCIICaseInsensitiveHash> supportedNonImageMIMETypes = std::initializer_list<String> {
234             "text/html"_s,
235             "text/xml"_s,
236             "text/xsl"_s,
237             "text/plain"_s,
238             "text/"_s,
239             "application/xml"_s,
240             "application/xhtml+xml"_s,
241 #if !PLATFORM(IOS_FAMILY)
242             "application/vnd.wap.xhtml+xml"_s,
243             "application/rss+xml"_s,
244             "application/atom+xml"_s,
245 #endif
246             "application/json"_s,
247             "image/svg+xml"_s,
248 #if ENABLE(FTPDIR)
249             "application/x-ftp-directory"_s,
250 #endif
251             "multipart/x-mixed-replace"_s,
252         // Note: Adding a new type here will probably render it as HTML.
253         // This can result in cross-site scripting vulnerabilities.
254         };
255         supportedNonImageMIMETypes.add(supportedJavaScriptMIMETypes().begin(), supportedJavaScriptMIMETypes().end());
256 #if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
257         ArchiveFactory::registerKnownArchiveMIMETypes(supportedNonImageMIMETypes);
258 #endif
259         return supportedNonImageMIMETypes;
260     }());
261     return supportedNonImageMIMETypes;
262 }
263
264 const HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::supportedMediaMIMETypes()
265 {
266     static const auto supportedMediaMIMETypes = makeNeverDestroyed([] {
267         HashSet<String, ASCIICaseInsensitiveHash> supportedMediaMIMETypes;
268 #if ENABLE(VIDEO)
269         MediaPlayer::getSupportedTypes(supportedMediaMIMETypes);
270 #endif
271         return supportedMediaMIMETypes;
272     }());
273     return supportedMediaMIMETypes;
274 }
275
276 const HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::pdfMIMETypes()
277 {
278     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> pdfMIMETypes = std::initializer_list<String> {
279         "application/pdf"_s,
280         "text/pdf"_s,
281     };
282     return pdfMIMETypes;
283 }
284
285 const HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::unsupportedTextMIMETypes()
286 {
287     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> unsupportedTextMIMETypes = std::initializer_list<String> {
288         "text/calendar"_s,
289         "text/x-calendar"_s,
290         "text/x-vcalendar"_s,
291         "text/vcalendar"_s,
292         "text/vcard"_s,
293         "text/x-vcard"_s,
294         "text/directory"_s,
295         "text/ldif"_s,
296         "text/qif"_s,
297         "text/x-qif"_s,
298         "text/x-csv"_s,
299         "text/x-vcf"_s,
300 #if !PLATFORM(IOS_FAMILY)
301         "text/rtf"_s,
302 #else
303         "text/vnd.sun.j2me.app-descriptor"_s,
304 #endif
305     };
306     return unsupportedTextMIMETypes;
307 }
308
309 static const Vector<String>* typesForCommonExtension(const String& extension)
310 {
311     static const auto map = makeNeverDestroyed([] {
312         struct TypeExtensionPair {
313             ASCIILiteral type;
314             ASCIILiteral extension;
315         };
316
317         // A table of common media MIME types and file extentions used when a platform's
318         // specific MIME type lookup doesn't have a match for a media file extension.
319         static const TypeExtensionPair commonMediaTypes[] = {
320             // Ogg
321             { "application/ogg"_s, "ogx"_s },
322             { "audio/ogg"_s, "ogg"_s },
323             { "audio/ogg"_s, "oga"_s },
324             { "video/ogg"_s, "ogv"_s },
325
326             // Annodex
327             { "application/annodex"_s, "anx"_s },
328             { "audio/annodex"_s, "axa"_s },
329             { "video/annodex"_s, "axv"_s },
330             { "audio/speex"_s, "spx"_s },
331
332             // WebM
333             { "video/webm"_s, "webm"_s },
334             { "audio/webm"_s, "webm"_s },
335
336             // MPEG
337             { "audio/mpeg"_s, "m1a"_s },
338             { "audio/mpeg"_s, "m2a"_s },
339             { "audio/mpeg"_s, "m1s"_s },
340             { "audio/mpeg"_s, "mpa"_s },
341             { "video/mpeg"_s, "mpg"_s },
342             { "video/mpeg"_s, "m15"_s },
343             { "video/mpeg"_s, "m1s"_s },
344             { "video/mpeg"_s, "m1v"_s },
345             { "video/mpeg"_s, "m75"_s },
346             { "video/mpeg"_s, "mpa"_s },
347             { "video/mpeg"_s, "mpeg"_s },
348             { "video/mpeg"_s, "mpm"_s },
349             { "video/mpeg"_s, "mpv"_s },
350
351             // MPEG playlist
352             { "application/vnd.apple.mpegurl"_s, "m3u8"_s },
353             { "application/mpegurl"_s, "m3u8"_s },
354             { "application/x-mpegurl"_s, "m3u8"_s },
355             { "audio/mpegurl"_s, "m3url"_s },
356             { "audio/x-mpegurl"_s, "m3url"_s },
357             { "audio/mpegurl"_s, "m3u"_s },
358             { "audio/x-mpegurl"_s, "m3u"_s },
359
360             // MPEG-4
361             { "video/x-m4v"_s, "m4v"_s },
362             { "audio/x-m4a"_s, "m4a"_s },
363             { "audio/x-m4b"_s, "m4b"_s },
364             { "audio/x-m4p"_s, "m4p"_s },
365             { "audio/mp4"_s, "m4a"_s },
366
367             // MP3
368             { "audio/mp3"_s, "mp3"_s },
369             { "audio/x-mp3"_s, "mp3"_s },
370             { "audio/x-mpeg"_s, "mp3"_s },
371
372             // MPEG-2
373             { "video/x-mpeg2"_s, "mp2"_s },
374             { "video/mpeg2"_s, "vob"_s },
375             { "video/mpeg2"_s, "mod"_s },
376             { "video/m2ts"_s, "m2ts"_s },
377             { "video/x-m2ts"_s, "m2t"_s },
378             { "video/x-m2ts"_s, "ts"_s },
379
380             // 3GP/3GP2
381             { "audio/3gpp"_s, "3gpp"_s },
382             { "audio/3gpp2"_s, "3g2"_s },
383             { "application/x-mpeg"_s, "amc"_s },
384
385             // AAC
386             { "audio/aac"_s, "aac"_s },
387             { "audio/aac"_s, "adts"_s },
388             { "audio/x-aac"_s, "m4r"_s },
389
390             // CoreAudio File
391             { "audio/x-caf"_s, "caf"_s },
392             { "audio/x-gsm"_s, "gsm"_s },
393
394             // ADPCM
395             { "audio/x-wav"_s, "wav"_s },
396             { "audio/vnd.wave"_s, "wav"_s },
397         };
398
399         HashMap<String, Vector<String>, ASCIICaseInsensitiveHash> map;
400         for (auto& pair : commonMediaTypes) {
401             ASCIILiteral type = pair.type;
402             ASCIILiteral extension = pair.extension;
403             map.ensure(extension, [type, extension] {
404                 // First type in the vector must always be the one from getMIMETypeForExtension,
405                 // so we can use the map without also calling getMIMETypeForExtension each time.
406                 Vector<String> synonyms;
407                 String systemType = MIMETypeRegistry::getMIMETypeForExtension(extension);
408                 if (!systemType.isEmpty() && type != systemType)
409                     synonyms.append(systemType);
410                 return synonyms;
411             }).iterator->value.append(type);
412         }
413         return map;
414     }());
415     auto mapEntry = map.get().find(extension);
416     if (mapEntry == map.get().end())
417         return nullptr;
418     return &mapEntry->value;
419 }
420
421 String MIMETypeRegistry::getMediaMIMETypeForExtension(const String& extension)
422 {
423     auto* vector = typesForCommonExtension(extension);
424     if (vector)
425         return (*vector)[0];
426     return getMIMETypeForExtension(extension);
427 }
428
429 Vector<String> MIMETypeRegistry::getMediaMIMETypesForExtension(const String& extension)
430 {
431     auto* vector = typesForCommonExtension(extension);
432     if (vector)
433         return *vector;
434     String type = getMIMETypeForExtension(extension);
435     if (!type.isNull())
436         return { { type } };
437     return { };
438 }
439
440 String MIMETypeRegistry::getMIMETypeForPath(const String& path)
441 {
442     size_t pos = path.reverseFind('.');
443     if (pos != notFound) {
444         String extension = path.substring(pos + 1);
445         String result = getMIMETypeForExtension(extension);
446         if (result.length())
447             return result;
448     }
449     return defaultMIMEType();
450 }
451
452 bool MIMETypeRegistry::isSupportedImageMIMEType(const String& mimeType)
453 {
454     if (mimeType.isEmpty())
455         return false;
456     String normalizedMIMEType = getNormalizedMIMEType(mimeType);
457     return supportedImageMIMETypes().contains(normalizedMIMEType) || additionalSupportedImageMIMETypes().contains(normalizedMIMEType);
458 }
459
460 bool MIMETypeRegistry::isSupportedImageVideoOrSVGMIMEType(const String& mimeType)
461 {
462     if (isSupportedImageMIMEType(mimeType) || equalLettersIgnoringASCIICase(mimeType, "image/svg+xml"))
463         return true;
464
465 #if HAVE(AVASSETREADER)
466     if (ImageDecoderAVFObjC::supportsContentType(ContentType(mimeType)))
467         return true;
468 #endif
469
470     return false;
471 }
472
473 bool MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(const String& mimeType)
474 {
475     ASSERT(isMainThread());
476
477     if (mimeType.isEmpty())
478         return false;
479     return supportedImageMIMETypesForEncoding().contains(mimeType);
480 }
481
482 bool MIMETypeRegistry::isSupportedJavaScriptMIMEType(const String& mimeType)
483 {
484     if (mimeType.isEmpty())
485         return false;
486
487     if (!isMainThread()) {
488         bool isSupported = false;
489         callOnMainThreadAndWait([&isSupported, mimeType = mimeType.isolatedCopy()] {
490             isSupported = isSupportedJavaScriptMIMEType(mimeType);
491         });
492         return isSupported;
493     }
494
495     return supportedJavaScriptMIMETypes().contains(mimeType);
496 }
497
498 bool MIMETypeRegistry::isSupportedStyleSheetMIMEType(const String& mimeType)
499 {
500     return equalLettersIgnoringASCIICase(mimeType, "text/css");
501 }
502
503 bool MIMETypeRegistry::isSupportedFontMIMEType(const String& mimeType)
504 {
505     static const unsigned fontLength = 5;
506     if (!startsWithLettersIgnoringASCIICase(mimeType, "font/"))
507         return false;
508     auto subtype = StringView { mimeType }.substring(fontLength);
509     return equalLettersIgnoringASCIICase(subtype, "woff")
510         || equalLettersIgnoringASCIICase(subtype, "woff2")
511         || equalLettersIgnoringASCIICase(subtype, "otf")
512         || equalLettersIgnoringASCIICase(subtype, "ttf")
513         || equalLettersIgnoringASCIICase(subtype, "sfnt");
514 }
515
516 bool MIMETypeRegistry::isTextMediaPlaylistMIMEType(const String& mimeType)
517 {
518     if (startsWithLettersIgnoringASCIICase(mimeType, "application/")) {
519         static const unsigned applicationLength = 12;
520         auto subtype = StringView { mimeType }.substring(applicationLength);
521         return equalLettersIgnoringASCIICase(subtype, "vnd.apple.mpegurl")
522             || equalLettersIgnoringASCIICase(subtype, "mpegurl")
523             || equalLettersIgnoringASCIICase(subtype, "x-mpegurl");
524     }
525
526     if (startsWithLettersIgnoringASCIICase(mimeType, "audio/")) {
527         static const unsigned audioLength = 6;
528         auto subtype = StringView { mimeType }.substring(audioLength);
529         return equalLettersIgnoringASCIICase(subtype, "mpegurl")
530             || equalLettersIgnoringASCIICase(subtype, "x-mpegurl");
531     }
532
533     return false;
534 }
535
536 bool MIMETypeRegistry::isSupportedJSONMIMEType(const String& mimeType)
537 {
538     if (mimeType.isEmpty())
539         return false;
540
541     if (equalLettersIgnoringASCIICase(mimeType, "application/json"))
542         return true;
543
544     // When detecting +json ensure there is a non-empty type / subtype preceeding the suffix.
545     if (mimeType.endsWithIgnoringASCIICase("+json") && mimeType.length() >= 8) {
546         size_t slashPosition = mimeType.find('/');
547         if (slashPosition != notFound && slashPosition > 0 && slashPosition <= mimeType.length() - 6)
548             return true;
549     }
550
551     return false;
552 }
553
554 bool MIMETypeRegistry::isSupportedNonImageMIMEType(const String& mimeType)
555 {
556     if (mimeType.isEmpty())
557         return false;
558     return supportedNonImageMIMETypes().contains(mimeType);
559 }
560
561 bool MIMETypeRegistry::isSupportedMediaMIMEType(const String& mimeType)
562 {
563     if (mimeType.isEmpty())
564         return false;
565     return supportedMediaMIMETypes().contains(mimeType);
566 }
567
568 bool MIMETypeRegistry::isSupportedTextTrackMIMEType(const String& mimeType)
569 {
570     return equalLettersIgnoringASCIICase(mimeType, "text/vtt");
571 }
572
573 bool MIMETypeRegistry::isUnsupportedTextMIMEType(const String& mimeType)
574 {
575     if (mimeType.isEmpty())
576         return false;
577     return unsupportedTextMIMETypes().contains(mimeType);
578 }
579
580 bool MIMETypeRegistry::isTextMIMEType(const String& mimeType)
581 {
582     return isSupportedJavaScriptMIMEType(mimeType)
583         || isSupportedJSONMIMEType(mimeType) // Render JSON as text/plain.
584         || (startsWithLettersIgnoringASCIICase(mimeType, "text/")
585             && !equalLettersIgnoringASCIICase(mimeType, "text/html")
586             && !equalLettersIgnoringASCIICase(mimeType, "text/xml")
587             && !equalLettersIgnoringASCIICase(mimeType, "text/xsl"));
588 }
589
590 static inline bool isValidXMLMIMETypeChar(UChar c)
591 {
592     // Valid characters per RFCs 3023 and 2045: 0-9a-zA-Z_-+~!$^{}|.%'`#&*
593     return isASCIIAlphanumeric(c) || c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' || c == '+'
594         || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '{' || c == '|' || c == '}' || c == '~';
595 }
596
597 bool MIMETypeRegistry::isXMLMIMEType(const String& mimeType)
598 {
599     if (equalLettersIgnoringASCIICase(mimeType, "text/xml") || equalLettersIgnoringASCIICase(mimeType, "application/xml") || equalLettersIgnoringASCIICase(mimeType, "text/xsl"))
600         return true;
601
602     if (!mimeType.endsWithIgnoringASCIICase("+xml"))
603         return false;
604
605     size_t slashPosition = mimeType.find('/');
606     // Take into account the '+xml' ending of mimeType.
607     if (slashPosition == notFound || !slashPosition || slashPosition == mimeType.length() - 5)
608         return false;
609
610     // Again, mimeType ends with '+xml', no need to check the validity of that substring.
611     size_t mimeLength = mimeType.length();
612     for (size_t i = 0; i < mimeLength - 4; ++i) {
613         if (!isValidXMLMIMETypeChar(mimeType[i]) && i != slashPosition)
614             return false;
615     }
616
617     return true;
618 }
619
620 bool MIMETypeRegistry::isJavaAppletMIMEType(const String& mimeType)
621 {
622     // Since this set is very limited and is likely to remain so we won't bother with the overhead
623     // of using a hash set.
624     // Any of the MIME types below may be followed by any number of specific versions of the JVM,
625     // which is why we use startsWith()
626     return startsWithLettersIgnoringASCIICase(mimeType, "application/x-java-applet")
627         || startsWithLettersIgnoringASCIICase(mimeType, "application/x-java-bean")
628         || startsWithLettersIgnoringASCIICase(mimeType, "application/x-java-vm");
629 }
630
631 bool MIMETypeRegistry::isPDFMIMEType(const String& mimeType)
632 {
633     if (mimeType.isEmpty())
634         return false;
635     return pdfMIMETypes().contains(mimeType);
636 }
637
638 bool MIMETypeRegistry::isPostScriptMIMEType(const String& mimeType)
639 {
640     return mimeType == "application/postscript";
641 }
642
643 bool MIMETypeRegistry::isPDFOrPostScriptMIMEType(const String& mimeType)
644 {
645     return isPDFMIMEType(mimeType) || isPostScriptMIMEType(mimeType);
646 }
647
648 bool MIMETypeRegistry::canShowMIMEType(const String& mimeType)
649 {
650     if (isSupportedImageMIMEType(mimeType) || isSupportedNonImageMIMEType(mimeType) || isSupportedMediaMIMEType(mimeType))
651         return true;
652
653     if (isSupportedJavaScriptMIMEType(mimeType) || isSupportedJSONMIMEType(mimeType))
654         return true;
655
656 #if USE(QUICK_LOOK)
657     if (PreviewConverter::supportsMIMEType(mimeType))
658         return true;
659 #endif
660
661     if (startsWithLettersIgnoringASCIICase(mimeType, "text/"))
662         return !isUnsupportedTextMIMEType(mimeType);
663
664     return false;
665 }
666
667 const String& defaultMIMEType()
668 {
669     static NeverDestroyed<const String> defaultMIMEType(MAKE_STATIC_STRING_IMPL("application/octet-stream"));
670     return defaultMIMEType;
671 }
672
673 const HashSet<String, ASCIICaseInsensitiveHash>& MIMETypeRegistry::systemPreviewMIMETypes()
674 {
675     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> systemPreviewMIMETypes = std::initializer_list<String> {
676         // The official type: https://www.iana.org/assignments/media-types/model/vnd.usdz+zip
677         "model/vnd.usdz+zip",
678         // Unofficial, but supported because we documented them.
679         "model/usd",
680         "model/vnd.pixar.usd",
681         ADDITIONAL_SYSTEM_PREVIEW_TYPES
682     };
683     return systemPreviewMIMETypes;
684 }
685
686 bool MIMETypeRegistry::isSystemPreviewMIMEType(const String& mimeType)
687 {
688     if (mimeType.isEmpty())
689         return false;
690     return systemPreviewMIMETypes().contains(mimeType);
691 }
692
693 #if !USE(CURL)
694
695 // FIXME: Not sure why it makes sense to have a cross-platform function when only CURL has the concept
696 // of a "normalized" MIME type.
697 String MIMETypeRegistry::getNormalizedMIMEType(const String& mimeType)
698 {
699     return mimeType;
700 }
701
702 #else
703
704 String MIMETypeRegistry::getNormalizedMIMEType(const String& mimeType)
705 {
706     static const auto mimeTypeAssociationMap = makeNeverDestroyed([] {
707         static const std::pair<ASCIILiteral, ASCIILiteral> mimeTypeAssociations[] = {
708             { "image/x-ms-bmp"_s, "image/bmp"_s },
709             { "image/x-windows-bmp"_s, "image/bmp"_s },
710             { "image/x-bmp"_s, "image/bmp"_s },
711             { "image/x-bitmap"_s, "image/bmp"_s },
712             { "image/x-ms-bitmap"_s, "image/bmp"_s },
713             { "image/jpg"_s, "image/jpeg"_s },
714             { "image/pjpeg"_s, "image/jpeg"_s },
715             { "image/x-png"_s, "image/png"_s },
716             { "image/vnd.rim.png"_s, "image/png"_s },
717             { "image/ico"_s, "image/vnd.microsoft.icon"_s },
718             { "image/icon"_s, "image/vnd.microsoft.icon"_s },
719             { "text/ico"_s, "image/vnd.microsoft.icon"_s },
720             { "application/ico"_s, "image/vnd.microsoft.icon"_s },
721             { "image/x-icon"_s, "image/vnd.microsoft.icon"_s },
722             { "audio/vnd.qcelp"_s, "audio/qcelp"_s },
723             { "audio/qcp"_s, "audio/qcelp"_s },
724             { "audio/vnd.qcp"_s, "audio/qcelp"_s },
725             { "audio/wav"_s, "audio/x-wav"_s },
726             { "audio/vnd.wave"_s, "audio/x-wav"_s },
727             { "audio/mid"_s, "audio/midi"_s },
728             { "audio/sp-midi"_s, "audio/midi"_s },
729             { "audio/x-mid"_s, "audio/midi"_s },
730             { "audio/x-midi"_s, "audio/midi"_s },
731             { "audio/x-mpeg"_s, "audio/mpeg"_s },
732             { "audio/mp3"_s, "audio/mpeg"_s },
733             { "audio/x-mp3"_s, "audio/mpeg"_s },
734             { "audio/mpeg3"_s, "audio/mpeg"_s },
735             { "audio/x-mpeg3"_s, "audio/mpeg"_s },
736             { "audio/mpg3"_s, "audio/mpeg"_s },
737             { "audio/mpg"_s, "audio/mpeg"_s },
738             { "audio/x-mpg"_s, "audio/mpeg"_s },
739             { "audio/m4a"_s, "audio/mp4"_s },
740             { "audio/x-m4a"_s, "audio/mp4"_s },
741             { "audio/x-mp4"_s, "audio/mp4"_s },
742             { "audio/x-aac"_s, "audio/aac"_s },
743             { "audio/x-amr"_s, "audio/amr"_s },
744             { "audio/mpegurl"_s, "audio/x-mpegurl"_s },
745             { "audio/flac"_s, "audio/x-flac"_s },
746             { "video/3gp"_s, "video/3gpp"_s },
747             { "video/avi"_s, "video/x-msvideo"_s },
748             { "video/x-m4v"_s, "video/mp4"_s },
749             { "video/x-quicktime"_s, "video/quicktime"_s },
750             { "application/java"_s, "application/java-archive"_s },
751             { "application/x-java-archive"_s, "application/java-archive"_s },
752             { "application/x-zip-compressed"_s, "application/zip"_s },
753             { "text/cache-manifest"_s, "text/plain"_s },
754         };
755
756         HashMap<String, String, ASCIICaseInsensitiveHash> map;
757         for (auto& pair : mimeTypeAssociations)
758             map.add(pair.first, pair.second);
759         return map;
760     }());
761
762     auto it = mimeTypeAssociationMap.get().find(mimeType);
763     if (it != mimeTypeAssociationMap.get().end())
764         return it->value;
765     return mimeType;
766 }
767
768 #endif
769
770 String MIMETypeRegistry::appendFileExtensionIfNecessary(const String& filename, const String& mimeType)
771 {
772     if (filename.isEmpty())
773         return emptyString();
774
775     if (filename.reverseFind('.') != notFound)
776         return filename;
777
778     String preferredExtension = getPreferredExtensionForMIMEType(mimeType);
779     if (preferredExtension.isEmpty())
780         return filename;
781
782     return filename + "." + preferredExtension;
783 }
784
785 } // namespace WebCore