• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2008, 2009 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 COMPUTER, 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 COMPUTER, 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 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
31 #include "ArchiveFactory.h"
32 #endif
33 #include "MediaPlayer.h"
34 #include "StringHash.h"
35 #include <wtf/HashMap.h>
36 #include <wtf/HashSet.h>
37 #include <wtf/StdLibExtras.h>
38 
39 #if PLATFORM(CG)
40 #include "ImageSourceCG.h"
41 #include <ApplicationServices/ApplicationServices.h>
42 #include <wtf/RetainPtr.h>
43 #endif
44 #if PLATFORM(QT)
45 #include <qimagereader.h>
46 #include <qimagewriter.h>
47 #endif
48 
49 namespace WebCore {
50 
51 static HashSet<String>* supportedImageResourceMIMETypes;
52 static HashSet<String>* supportedImageMIMETypes;
53 static HashSet<String>* supportedImageMIMETypesForEncoding;
54 static HashSet<String>* supportedJavaScriptMIMETypes;
55 static HashSet<String>* supportedNonImageMIMETypes;
56 static HashSet<String>* supportedMediaMIMETypes;
57 static HashMap<String, String, CaseFoldingHash>* mediaMIMETypeForExtensionMap;
58 
initializeSupportedImageMIMETypes()59 static void initializeSupportedImageMIMETypes()
60 {
61 #if PLATFORM(CG)
62     RetainPtr<CFArrayRef> supportedTypes(AdoptCF, CGImageSourceCopyTypeIdentifiers());
63     CFIndex count = CFArrayGetCount(supportedTypes.get());
64     for (CFIndex i = 0; i < count; i++) {
65         RetainPtr<CFStringRef> supportedType(AdoptCF, reinterpret_cast<CFStringRef>(CFArrayGetValueAtIndex(supportedTypes.get(), i)));
66         String mimeType = MIMETypeForImageSourceType(supportedType.get());
67         if (!mimeType.isEmpty()) {
68             supportedImageMIMETypes->add(mimeType);
69             supportedImageResourceMIMETypes->add(mimeType);
70         }
71     }
72 
73     // On Tiger and Leopard, com.microsoft.bmp doesn't have a MIME type in the registry.
74     supportedImageMIMETypes->add("image/bmp");
75     supportedImageResourceMIMETypes->add("image/bmp");
76 
77     // Favicons don't have a MIME type in the registry either.
78     supportedImageMIMETypes->add("image/vnd.microsoft.icon");
79     supportedImageMIMETypes->add("image/x-icon");
80     supportedImageResourceMIMETypes->add("image/vnd.microsoft.icon");
81     supportedImageResourceMIMETypes->add("image/x-icon");
82 
83     //  We only get one MIME type per UTI, hence our need to add these manually
84     supportedImageMIMETypes->add("image/pjpeg");
85     supportedImageResourceMIMETypes->add("image/pjpeg");
86 
87     //  We don't want to try to treat all binary data as an image
88     supportedImageMIMETypes->remove("application/octet-stream");
89     supportedImageResourceMIMETypes->remove("application/octet-stream");
90 
91     //  Don't treat pdf/postscript as images directly
92     supportedImageMIMETypes->remove("application/pdf");
93     supportedImageMIMETypes->remove("application/postscript");
94 
95 #elif PLATFORM(QT)
96     QList<QByteArray> formats = QImageReader::supportedImageFormats();
97     for (size_t i = 0; i < static_cast<size_t>(formats.size()); ++i) {
98 #if ENABLE(SVG)
99         /*
100          * Qt has support for SVG, but we want to use KSVG2
101          */
102         if (formats.at(i).toLower().startsWith("svg"))
103             continue;
104 #endif
105         String mimeType = MIMETypeRegistry::getMIMETypeForExtension(formats.at(i).constData());
106         supportedImageMIMETypes->add(mimeType);
107         supportedImageResourceMIMETypes->add(mimeType);
108     }
109 
110     supportedImageMIMETypes->remove("application/octet-stream");
111     supportedImageResourceMIMETypes->remove("application/octet-stream");
112 #elif PLATFORM(ANDROID)
113     static const char* types[] = {
114         "image/jpeg",
115         "image/png",
116         "image/gif",
117         "image/bmp",
118         "image/x-icon",    // ico
119         "image/ico",
120         "image/x-xbitmap"  // xbm
121     };
122     for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
123         supportedImageMIMETypes->add(types[i]);
124         supportedImageResourceMIMETypes->add(types[i]);
125     }
126     // Checked Safari impl, it seems that the HTTP stack returns
127     // multiple responses, the initial response, and then one for
128     // multipart segment. Each response is sent to the same ResourceLoader
129     // so for us to support this we would need to do the same.
130     supportedNonImageMIMETypes->remove("multipart/x-mixed-replace");
131 #if !ENABLE(XSLT)
132     supportedNonImageMIMETypes->remove("text/xsl");
133 #endif
134 #else
135     // assume that all implementations at least support the following standard
136     // image types:
137     static const char* types[] = {
138         "image/jpeg",
139         "image/png",
140         "image/gif",
141         "image/bmp",
142         "image/vnd.microsoft.icon",    // ico
143         "image/x-icon",    // ico
144         "image/x-xbitmap"  // xbm
145     };
146     for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
147         supportedImageMIMETypes->add(types[i]);
148         supportedImageResourceMIMETypes->add(types[i]);
149     }
150 #endif
151 }
152 
initializeSupportedImageMIMETypesForEncoding()153 static void initializeSupportedImageMIMETypesForEncoding()
154 {
155     supportedImageMIMETypesForEncoding = new HashSet<String>;
156 
157 #if PLATFORM(CG)
158 #if PLATFORM(MAC)
159     RetainPtr<CFArrayRef> supportedTypes(AdoptCF, CGImageDestinationCopyTypeIdentifiers());
160     CFIndex count = CFArrayGetCount(supportedTypes.get());
161     for (CFIndex i = 0; i < count; i++) {
162         RetainPtr<CFStringRef> supportedType(AdoptCF, reinterpret_cast<CFStringRef>(CFArrayGetValueAtIndex(supportedTypes.get(), i)));
163         String mimeType = MIMETypeForImageSourceType(supportedType.get());
164         if (!mimeType.isEmpty())
165             supportedImageMIMETypesForEncoding->add(mimeType);
166     }
167 #else
168     // FIXME: Add Windows support for all the supported UTI's when a way to convert from MIMEType to UTI reliably is found.
169     // For now, only support PNG, JPEG and GIF.  See <rdar://problem/6095286>.
170     supportedImageMIMETypesForEncoding->add("image/png");
171     supportedImageMIMETypesForEncoding->add("image/jpeg");
172     supportedImageMIMETypesForEncoding->add("image/gif");
173 #endif
174 #elif PLATFORM(QT)
175     QList<QByteArray> formats = QImageWriter::supportedImageFormats();
176     for (int i = 0; i < formats.size(); ++i) {
177         String mimeType = MIMETypeRegistry::getMIMETypeForExtension(formats.at(i).constData());
178         supportedImageMIMETypesForEncoding->add(mimeType);
179     }
180 
181     supportedImageMIMETypesForEncoding->remove("application/octet-stream");
182 #elif PLATFORM(CAIRO)
183     supportedImageMIMETypesForEncoding->add("image/png");
184 #endif
185 }
186 
initializeSupportedJavaScriptMIMETypes()187 static void initializeSupportedJavaScriptMIMETypes()
188 {
189     /*
190         Mozilla 1.8 and WinIE 7 both accept text/javascript and text/ecmascript.
191         Mozilla 1.8 accepts application/javascript, application/ecmascript, and application/x-javascript, but WinIE 7 doesn't.
192         WinIE 7 accepts text/javascript1.1 - text/javascript1.3, text/jscript, and text/livescript, but Mozilla 1.8 doesn't.
193         Mozilla 1.8 allows leading and trailing whitespace, but WinIE 7 doesn't.
194         Mozilla 1.8 and WinIE 7 both accept the empty string, but neither accept a whitespace-only string.
195         We want to accept all the values that either of these browsers accept, but not other values.
196      */
197     static const char* types[] = {
198         "text/javascript",
199         "text/ecmascript",
200         "application/javascript",
201         "application/ecmascript",
202         "application/x-javascript",
203         "text/javascript1.1",
204         "text/javascript1.2",
205         "text/javascript1.3",
206         "text/jscript",
207         "text/livescript",
208     };
209     for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i)
210       supportedJavaScriptMIMETypes->add(types[i]);
211 }
212 
initializeSupportedNonImageMimeTypes()213 static void initializeSupportedNonImageMimeTypes()
214 {
215     static const char* types[] = {
216 #if ENABLE(WML)
217         "text/vnd.wap.wml",
218         "application/vnd.wap.wmlc",
219 #endif
220         "text/html",
221         "text/xml",
222         "text/xsl",
223         "text/plain",
224         "text/",
225         "application/xml",
226         "application/xhtml+xml",
227 #if ENABLE(XHTMLMP)
228         "application/vnd.wap.xhtml+xml",
229 #endif
230         "application/rss+xml",
231         "application/atom+xml",
232 #if ENABLE(SVG)
233         "image/svg+xml",
234 #endif
235 #if ENABLE(FTPDIR)
236         "application/x-ftp-directory",
237 #endif
238         "multipart/x-mixed-replace"
239     };
240     for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
241         supportedNonImageMIMETypes->add(types[i]);
242 
243 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
244     ArchiveFactory::registerKnownArchiveMIMETypes();
245 #endif
246 }
247 
initializeMediaTypeMaps()248 static void initializeMediaTypeMaps()
249 {
250     struct TypeExtensionPair {
251         const char* type;
252         const char* extension;
253     };
254 
255     // A table of common media MIME types and file extenstions used when a platform's
256     // specific MIME type lookup doens't have a match for a media file extension. While some
257     // file extensions are claimed by multiple MIME types, this table only includes one
258     // for each because it is currently only used by getMediaMIMETypeForExtension. If we
259     // ever add a MIME type -> file extension mapping, the alternate MIME types will need
260     // to be added.
261     static const TypeExtensionPair pairs[] = {
262 
263         // Ogg
264         { "application/ogg", "ogx" },
265         { "audio/ogg", "ogg" },
266         { "audio/ogg", "oga" },
267         { "video/ogg", "ogv" },
268 
269         // Annodex
270         { "application/annodex", "anx" },
271         { "audio/annodex", "axa" },
272         { "video/annodex", "axv" },
273         { "audio/speex", "spx" },
274 
275         // MPEG
276         { "audio/mpeg", "m1a" },
277         { "audio/mpeg", "m2a" },
278         { "audio/mpeg", "m1s" },
279         { "audio/mpeg", "mpa" },
280         { "video/mpeg", "mpg" },
281         { "video/mpeg", "m15" },
282         { "video/mpeg", "m1s" },
283         { "video/mpeg", "m1v" },
284         { "video/mpeg", "m75" },
285         { "video/mpeg", "mpa" },
286         { "video/mpeg", "mpeg" },
287         { "video/mpeg", "mpm" },
288         { "video/mpeg", "mpv" },
289 
290         // MPEG playlist
291         { "audio/x-mpegurl", "m3url" },
292         { "application/x-mpegurl", "m3u8" },
293 
294         // MPEG-4
295         { "video/x-m4v", "m4v" },
296         { "audio/x-m4a", "m4a" },
297         { "audio/x-m4b", "m4b" },
298         { "audio/x-m4p", "m4p" },
299 
300         // MP3
301         { "audio/mp3", "mp3" },
302 
303         // MPEG-2
304         { "video/x-mpeg2", "mp2" },
305         { "video/mpeg2", "vob" },
306         { "video/mpeg2", "mod" },
307         { "video/m2ts", "m2ts" },
308         { "video/x-m2ts", "m2t" },
309         { "video/x-m2ts", "ts" },
310 
311         // 3GP/3GP2
312         { "audio/3gpp", "3gpp" },
313         { "audio/3gpp2", "3g2" },
314         { "application/x-mpeg", "amc" },
315 
316         // AAC
317         { "audio/aac", "aac" },
318         { "audio/aac", "adts" },
319         { "audio/x-aac", "m4r" },
320 
321         // CoreAudio File
322         { "audio/x-caf", "caf" },
323         { "audio/x-gsm", "gsm" }
324     };
325 
326     mediaMIMETypeForExtensionMap = new HashMap<String, String, CaseFoldingHash>;
327     const unsigned numPairs = sizeof(pairs) / sizeof(pairs[0]);
328     for (unsigned ndx = 0; ndx < numPairs; ++ndx)
329         mediaMIMETypeForExtensionMap->set(pairs[ndx].extension, pairs[ndx].type);
330 }
331 
getMediaMIMETypeForExtension(const String & ext)332 String MIMETypeRegistry::getMediaMIMETypeForExtension(const String& ext)
333 {
334     // Check with system specific implementation first.
335     String mimeType = getMIMETypeForExtension(ext);
336     if (!mimeType.isEmpty())
337         return mimeType;
338 
339     // No match, look in the static mapping.
340     if (!mediaMIMETypeForExtensionMap)
341         initializeMediaTypeMaps();
342     return mediaMIMETypeForExtensionMap->get(ext);
343 }
344 
initializeSupportedMediaMIMETypes()345 static void initializeSupportedMediaMIMETypes()
346 {
347     supportedMediaMIMETypes = new HashSet<String>;
348 #if ENABLE(VIDEO)
349     MediaPlayer::getSupportedTypes(*supportedMediaMIMETypes);
350 #endif
351 }
352 
initializeMIMETypeRegistry()353 static void initializeMIMETypeRegistry()
354 {
355     supportedJavaScriptMIMETypes = new HashSet<String>;
356     initializeSupportedJavaScriptMIMETypes();
357 
358     supportedNonImageMIMETypes = new HashSet<String>(*supportedJavaScriptMIMETypes);
359     initializeSupportedNonImageMimeTypes();
360 
361     supportedImageResourceMIMETypes = new HashSet<String>;
362     supportedImageMIMETypes = new HashSet<String>;
363     initializeSupportedImageMIMETypes();
364 }
365 
getMIMETypeForPath(const String & path)366 String MIMETypeRegistry::getMIMETypeForPath(const String& path)
367 {
368     int pos = path.reverseFind('.');
369     if (pos >= 0) {
370         String extension = path.substring(pos + 1);
371         String result = getMIMETypeForExtension(extension);
372         if (result.length())
373             return result;
374     }
375     return "application/octet-stream";
376 }
377 
isSupportedImageMIMEType(const String & mimeType)378 bool MIMETypeRegistry::isSupportedImageMIMEType(const String& mimeType)
379 {
380     if (mimeType.isEmpty())
381         return false;
382     if (!supportedImageMIMETypes)
383         initializeMIMETypeRegistry();
384     return supportedImageMIMETypes->contains(mimeType);
385 }
386 
isSupportedImageResourceMIMEType(const String & mimeType)387 bool MIMETypeRegistry::isSupportedImageResourceMIMEType(const String& mimeType)
388 {
389     if (mimeType.isEmpty())
390         return false;
391     if (!supportedImageResourceMIMETypes)
392         initializeMIMETypeRegistry();
393     return supportedImageResourceMIMETypes->contains(mimeType);
394 }
395 
isSupportedImageMIMETypeForEncoding(const String & mimeType)396 bool MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(const String& mimeType)
397 {
398     if (mimeType.isEmpty())
399         return false;
400     if (!supportedImageMIMETypesForEncoding)
401         initializeSupportedImageMIMETypesForEncoding();
402     return supportedImageMIMETypesForEncoding->contains(mimeType);
403 }
404 
isSupportedJavaScriptMIMEType(const String & mimeType)405 bool MIMETypeRegistry::isSupportedJavaScriptMIMEType(const String& mimeType)
406 {
407     if (mimeType.isEmpty())
408         return false;
409     if (!supportedJavaScriptMIMETypes)
410         initializeMIMETypeRegistry();
411     return supportedJavaScriptMIMETypes->contains(mimeType);
412 }
413 
isSupportedNonImageMIMEType(const String & mimeType)414 bool MIMETypeRegistry::isSupportedNonImageMIMEType(const String& mimeType)
415 {
416     if (mimeType.isEmpty())
417         return false;
418     if (!supportedNonImageMIMETypes)
419         initializeMIMETypeRegistry();
420     return supportedNonImageMIMETypes->contains(mimeType);
421 }
422 
isSupportedMediaMIMEType(const String & mimeType)423 bool MIMETypeRegistry::isSupportedMediaMIMEType(const String& mimeType)
424 {
425     if (mimeType.isEmpty())
426         return false;
427     if (!supportedMediaMIMETypes)
428         initializeSupportedMediaMIMETypes();
429     return supportedMediaMIMETypes->contains(mimeType);
430 }
431 
isJavaAppletMIMEType(const String & mimeType)432 bool MIMETypeRegistry::isJavaAppletMIMEType(const String& mimeType)
433 {
434     // Since this set is very limited and is likely to remain so we won't bother with the overhead
435     // of using a hash set.
436     // Any of the MIME types below may be followed by any number of specific versions of the JVM,
437     // which is why we use startsWith()
438     return mimeType.startsWith("application/x-java-applet", false)
439         || mimeType.startsWith("application/x-java-bean", false)
440         || mimeType.startsWith("application/x-java-vm", false);
441 }
442 
getSupportedImageMIMETypes()443 HashSet<String>& MIMETypeRegistry::getSupportedImageMIMETypes()
444 {
445     if (!supportedImageMIMETypes)
446         initializeMIMETypeRegistry();
447     return *supportedImageMIMETypes;
448 }
449 
getSupportedImageResourceMIMETypes()450 HashSet<String>& MIMETypeRegistry::getSupportedImageResourceMIMETypes()
451 {
452     if (!supportedImageResourceMIMETypes)
453         initializeMIMETypeRegistry();
454     return *supportedImageResourceMIMETypes;
455 }
456 
getSupportedImageMIMETypesForEncoding()457 HashSet<String>& MIMETypeRegistry::getSupportedImageMIMETypesForEncoding()
458 {
459     if (!supportedImageMIMETypesForEncoding)
460         initializeSupportedImageMIMETypesForEncoding();
461     return *supportedImageMIMETypesForEncoding;
462 }
463 
getSupportedNonImageMIMETypes()464 HashSet<String>& MIMETypeRegistry::getSupportedNonImageMIMETypes()
465 {
466     if (!supportedNonImageMIMETypes)
467         initializeMIMETypeRegistry();
468     return *supportedNonImageMIMETypes;
469 }
470 
getSupportedMediaMIMETypes()471 HashSet<String>& MIMETypeRegistry::getSupportedMediaMIMETypes()
472 {
473     if (!supportedMediaMIMETypes)
474         initializeSupportedMediaMIMETypes();
475     return *supportedMediaMIMETypes;
476 }
477 
defaultMIMEType()478 const String& defaultMIMEType()
479 {
480     DEFINE_STATIC_LOCAL(const String, defaultMIMEType, ("application/octet-stream"));
481     return defaultMIMEType;
482 }
483 
484 } // namespace WebCore
485