Home | History | Annotate | Download | only in platform
      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 
     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 
    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 
    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 
    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 
    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 
    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 
    345 static void initializeSupportedMediaMIMETypes()
    346 {
    347     supportedMediaMIMETypes = new HashSet<String>;
    348 #if ENABLE(VIDEO)
    349     MediaPlayer::getSupportedTypes(*supportedMediaMIMETypes);
    350 #endif
    351 }
    352 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    443 HashSet<String>& MIMETypeRegistry::getSupportedImageMIMETypes()
    444 {
    445     if (!supportedImageMIMETypes)
    446         initializeMIMETypeRegistry();
    447     return *supportedImageMIMETypes;
    448 }
    449 
    450 HashSet<String>& MIMETypeRegistry::getSupportedImageResourceMIMETypes()
    451 {
    452     if (!supportedImageResourceMIMETypes)
    453         initializeMIMETypeRegistry();
    454     return *supportedImageResourceMIMETypes;
    455 }
    456 
    457 HashSet<String>& MIMETypeRegistry::getSupportedImageMIMETypesForEncoding()
    458 {
    459     if (!supportedImageMIMETypesForEncoding)
    460         initializeSupportedImageMIMETypesForEncoding();
    461     return *supportedImageMIMETypesForEncoding;
    462 }
    463 
    464 HashSet<String>& MIMETypeRegistry::getSupportedNonImageMIMETypes()
    465 {
    466     if (!supportedNonImageMIMETypes)
    467         initializeMIMETypeRegistry();
    468     return *supportedNonImageMIMETypes;
    469 }
    470 
    471 HashSet<String>& MIMETypeRegistry::getSupportedMediaMIMETypes()
    472 {
    473     if (!supportedMediaMIMETypes)
    474         initializeSupportedMediaMIMETypes();
    475     return *supportedMediaMIMETypes;
    476 }
    477 
    478 const String& defaultMIMEType()
    479 {
    480     DEFINE_STATIC_LOCAL(const String, defaultMIMEType, ("application/octet-stream"));
    481     return defaultMIMEType;
    482 }
    483 
    484 } // namespace WebCore
    485