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