1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <algorithm> 6 #include <iterator> 7 #include <map> 8 #include <string> 9 10 #include "base/containers/hash_tables.h" 11 #include "base/lazy_instance.h" 12 #include "base/logging.h" 13 #include "base/stl_util.h" 14 #include "base/strings/string_split.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "net/base/mime_util.h" 18 #include "net/base/platform_mime_util.h" 19 #include "net/http/http_util.h" 20 21 #if defined(OS_ANDROID) 22 #include "base/android/build_info.h" 23 #endif 24 25 using std::string; 26 27 namespace { 28 29 struct MediaType { 30 const char name[12]; 31 const char matcher[13]; 32 }; 33 34 static const MediaType kIanaMediaTypes[] = { 35 { "application", "application/" }, 36 { "audio", "audio/" }, 37 { "example", "example/" }, 38 { "image", "image/" }, 39 { "message", "message/" }, 40 { "model", "model/" }, 41 { "multipart", "multipart/" }, 42 { "text", "text/" }, 43 { "video", "video/" }, 44 }; 45 46 } // namespace 47 48 namespace net { 49 50 // Singleton utility class for mime types. 51 class MimeUtil : public PlatformMimeUtil { 52 public: 53 bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, 54 std::string* mime_type) const; 55 56 bool GetMimeTypeFromFile(const base::FilePath& file_path, 57 std::string* mime_type) const; 58 59 bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext, 60 std::string* mime_type) const; 61 62 bool IsSupportedImageMimeType(const std::string& mime_type) const; 63 bool IsSupportedMediaMimeType(const std::string& mime_type) const; 64 bool IsSupportedNonImageMimeType(const std::string& mime_type) const; 65 bool IsUnsupportedTextMimeType(const std::string& mime_type) const; 66 bool IsSupportedJavascriptMimeType(const std::string& mime_type) const; 67 68 bool IsSupportedMimeType(const std::string& mime_type) const; 69 70 bool MatchesMimeType(const std::string &mime_type_pattern, 71 const std::string &mime_type) const; 72 73 bool ParseMimeTypeWithoutParameter(const std::string& type_string, 74 std::string* top_level_type, 75 std::string* subtype) const; 76 77 bool IsValidTopLevelMimeType(const std::string& type_string) const; 78 79 bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) const; 80 81 void ParseCodecString(const std::string& codecs, 82 std::vector<std::string>* codecs_out, 83 bool strip); 84 85 bool IsStrictMediaMimeType(const std::string& mime_type) const; 86 bool IsSupportedStrictMediaMimeType( 87 const std::string& mime_type, 88 const std::vector<std::string>& codecs) const; 89 90 void RemoveProprietaryMediaTypesAndCodecsForTests(); 91 92 private: 93 friend struct base::DefaultLazyInstanceTraits<MimeUtil>; 94 95 typedef base::hash_set<std::string> MimeMappings; 96 typedef std::map<std::string, MimeMappings> StrictMappings; 97 98 MimeUtil(); 99 100 // Returns true if |codecs| is nonempty and all the items in it are present in 101 // |supported_codecs|. 102 static bool AreSupportedCodecs(const MimeMappings& supported_codecs, 103 const std::vector<std::string>& codecs); 104 105 // For faster lookup, keep hash sets. 106 void InitializeMimeTypeMaps(); 107 108 bool GetMimeTypeFromExtensionHelper(const base::FilePath::StringType& ext, 109 bool include_platform_types, 110 std::string* mime_type) const; 111 112 MimeMappings image_map_; 113 MimeMappings media_map_; 114 MimeMappings non_image_map_; 115 MimeMappings unsupported_text_map_; 116 MimeMappings javascript_map_; 117 MimeMappings codecs_map_; 118 119 StrictMappings strict_format_map_; 120 }; // class MimeUtil 121 122 // This variable is Leaky because we need to access it from WorkerPool threads. 123 static base::LazyInstance<MimeUtil>::Leaky g_mime_util = 124 LAZY_INSTANCE_INITIALIZER; 125 126 struct MimeInfo { 127 const char* mime_type; 128 const char* extensions; // comma separated list 129 }; 130 131 static const MimeInfo primary_mappings[] = { 132 { "text/html", "html,htm,shtml,shtm" }, 133 { "text/css", "css" }, 134 { "text/xml", "xml" }, 135 { "image/gif", "gif" }, 136 { "image/jpeg", "jpeg,jpg" }, 137 { "image/webp", "webp" }, 138 { "image/png", "png" }, 139 { "video/mp4", "mp4,m4v" }, 140 { "audio/x-m4a", "m4a" }, 141 { "audio/mp3", "mp3" }, 142 { "video/ogg", "ogv,ogm" }, 143 { "audio/ogg", "ogg,oga,opus" }, 144 { "video/webm", "webm" }, 145 { "audio/webm", "webm" }, 146 { "audio/wav", "wav" }, 147 { "application/xhtml+xml", "xhtml,xht,xhtm" }, 148 { "application/x-chrome-extension", "crx" }, 149 { "multipart/related", "mhtml,mht" } 150 }; 151 152 static const MimeInfo secondary_mappings[] = { 153 { "application/octet-stream", "exe,com,bin" }, 154 { "application/gzip", "gz" }, 155 { "application/pdf", "pdf" }, 156 { "application/postscript", "ps,eps,ai" }, 157 { "application/javascript", "js" }, 158 { "application/font-woff", "woff" }, 159 { "image/bmp", "bmp" }, 160 { "image/x-icon", "ico" }, 161 { "image/vnd.microsoft.icon", "ico" }, 162 { "image/jpeg", "jfif,pjpeg,pjp" }, 163 { "image/tiff", "tiff,tif" }, 164 { "image/x-xbitmap", "xbm" }, 165 { "image/svg+xml", "svg,svgz" }, 166 { "image/x-png", "png"}, 167 { "message/rfc822", "eml" }, 168 { "text/plain", "txt,text" }, 169 { "text/html", "ehtml" }, 170 { "application/rss+xml", "rss" }, 171 { "application/rdf+xml", "rdf" }, 172 { "text/xml", "xsl,xbl,xslt" }, 173 { "application/vnd.mozilla.xul+xml", "xul" }, 174 { "application/x-shockwave-flash", "swf,swl" }, 175 { "application/pkcs7-mime", "p7m,p7c,p7z" }, 176 { "application/pkcs7-signature", "p7s" } 177 }; 178 179 static const char* FindMimeType(const MimeInfo* mappings, 180 size_t mappings_len, 181 const char* ext) { 182 size_t ext_len = strlen(ext); 183 184 for (size_t i = 0; i < mappings_len; ++i) { 185 const char* extensions = mappings[i].extensions; 186 for (;;) { 187 size_t end_pos = strcspn(extensions, ","); 188 if (end_pos == ext_len && 189 base::strncasecmp(extensions, ext, ext_len) == 0) 190 return mappings[i].mime_type; 191 extensions += end_pos; 192 if (!*extensions) 193 break; 194 extensions += 1; // skip over comma 195 } 196 } 197 return NULL; 198 } 199 200 bool MimeUtil::GetMimeTypeFromExtension(const base::FilePath::StringType& ext, 201 string* result) const { 202 return GetMimeTypeFromExtensionHelper(ext, true, result); 203 } 204 205 bool MimeUtil::GetWellKnownMimeTypeFromExtension( 206 const base::FilePath::StringType& ext, 207 string* result) const { 208 return GetMimeTypeFromExtensionHelper(ext, false, result); 209 } 210 211 bool MimeUtil::GetMimeTypeFromFile(const base::FilePath& file_path, 212 string* result) const { 213 base::FilePath::StringType file_name_str = file_path.Extension(); 214 if (file_name_str.empty()) 215 return false; 216 return GetMimeTypeFromExtension(file_name_str.substr(1), result); 217 } 218 219 bool MimeUtil::GetMimeTypeFromExtensionHelper( 220 const base::FilePath::StringType& ext, 221 bool include_platform_types, 222 string* result) const { 223 // Avoids crash when unable to handle a long file path. See crbug.com/48733. 224 const unsigned kMaxFilePathSize = 65536; 225 if (ext.length() > kMaxFilePathSize) 226 return false; 227 228 // We implement the same algorithm as Mozilla for mapping a file extension to 229 // a mime type. That is, we first check a hard-coded list (that cannot be 230 // overridden), and then if not found there, we defer to the system registry. 231 // Finally, we scan a secondary hard-coded list to catch types that we can 232 // deduce but that we also want to allow the OS to override. 233 234 base::FilePath path_ext(ext); 235 const string ext_narrow_str = path_ext.AsUTF8Unsafe(); 236 const char* mime_type = FindMimeType(primary_mappings, 237 arraysize(primary_mappings), 238 ext_narrow_str.c_str()); 239 if (mime_type) { 240 *result = mime_type; 241 return true; 242 } 243 244 if (include_platform_types && GetPlatformMimeTypeFromExtension(ext, result)) 245 return true; 246 247 mime_type = FindMimeType(secondary_mappings, arraysize(secondary_mappings), 248 ext_narrow_str.c_str()); 249 if (mime_type) { 250 *result = mime_type; 251 return true; 252 } 253 254 return false; 255 } 256 257 // From WebKit's WebCore/platform/MIMETypeRegistry.cpp: 258 259 static const char* const supported_image_types[] = { 260 "image/jpeg", 261 "image/pjpeg", 262 "image/jpg", 263 "image/webp", 264 "image/png", 265 "image/gif", 266 "image/bmp", 267 "image/vnd.microsoft.icon", // ico 268 "image/x-icon", // ico 269 "image/x-xbitmap", // xbm 270 "image/x-png" 271 }; 272 273 // A list of media types: http://en.wikipedia.org/wiki/Internet_media_type 274 // A comprehensive mime type list: http://plugindoc.mozdev.org/winmime.php 275 // This set of codecs is supported by all variations of Chromium. 276 static const char* const common_media_types[] = { 277 // Ogg. 278 "audio/ogg", 279 "application/ogg", 280 #if !defined(OS_ANDROID) // Android doesn't support Ogg Theora. 281 "video/ogg", 282 #endif 283 284 // WebM. 285 "video/webm", 286 "audio/webm", 287 288 // Wav. 289 "audio/wav", 290 "audio/x-wav", 291 292 #if defined(OS_ANDROID) 293 // HLS. Supported by Android ICS and above. 294 "application/vnd.apple.mpegurl", 295 "application/x-mpegurl", 296 #endif 297 }; 298 299 // List of proprietary types only supported by Google Chrome. 300 static const char* const proprietary_media_types[] = { 301 // MPEG-4. 302 "video/mp4", 303 "video/x-m4v", 304 "audio/mp4", 305 "audio/x-m4a", 306 307 // MP3. 308 "audio/mp3", 309 "audio/x-mp3", 310 "audio/mpeg", 311 }; 312 313 // List of supported codecs when passed in with <source type="...">. 314 // This set of codecs is supported by all variations of Chromium. 315 // 316 // Refer to http://wiki.whatwg.org/wiki/Video_type_parameters#Browser_Support 317 // for more information. 318 // 319 // The codecs for WAV are integers as defined in Appendix A of RFC2361: 320 // http://tools.ietf.org/html/rfc2361 321 static const char* const common_media_codecs[] = { 322 #if !defined(OS_ANDROID) // Android doesn't support Ogg Theora. 323 "theora", 324 #endif 325 "opus", 326 "vorbis", 327 "vp8", 328 "vp9", 329 "1" // WAVE_FORMAT_PCM. 330 }; 331 332 // List of proprietary codecs only supported by Google Chrome. 333 static const char* const proprietary_media_codecs[] = { 334 "avc1", 335 "avc3", 336 "mp4a" 337 }; 338 339 // Note: 340 // - does not include javascript types list (see supported_javascript_types) 341 // - does not include types starting with "text/" (see 342 // IsSupportedNonImageMimeType()) 343 static const char* const supported_non_image_types[] = { 344 "image/svg+xml", // SVG is text-based XML, even though it has an image/ type 345 "application/xml", 346 "application/atom+xml", 347 "application/rss+xml", 348 "application/xhtml+xml", 349 "application/json", 350 "multipart/related", // For MHTML support. 351 "multipart/x-mixed-replace" 352 // Note: ADDING a new type here will probably render it AS HTML. This can 353 // result in cross site scripting. 354 }; 355 356 // Dictionary of cryptographic file mime types. 357 struct CertificateMimeTypeInfo { 358 const char* mime_type; 359 CertificateMimeType cert_type; 360 }; 361 362 static const CertificateMimeTypeInfo supported_certificate_types[] = { 363 { "application/x-x509-user-cert", 364 CERTIFICATE_MIME_TYPE_X509_USER_CERT }, 365 #if defined(OS_ANDROID) 366 { "application/x-x509-ca-cert", CERTIFICATE_MIME_TYPE_X509_CA_CERT }, 367 { "application/x-pkcs12", CERTIFICATE_MIME_TYPE_PKCS12_ARCHIVE }, 368 #endif 369 }; 370 371 // These types are excluded from the logic that allows all text/ types because 372 // while they are technically text, it's very unlikely that a user expects to 373 // see them rendered in text form. 374 static const char* const unsupported_text_types[] = { 375 "text/calendar", 376 "text/x-calendar", 377 "text/x-vcalendar", 378 "text/vcalendar", 379 "text/vcard", 380 "text/x-vcard", 381 "text/directory", 382 "text/ldif", 383 "text/qif", 384 "text/x-qif", 385 "text/x-csv", 386 "text/x-vcf", 387 "text/rtf", 388 "text/comma-separated-values", 389 "text/csv", 390 "text/tab-separated-values", 391 "text/tsv", 392 "text/ofx", // http://crbug.com/162238 393 "text/vnd.sun.j2me.app-descriptor" // http://crbug.com/176450 394 }; 395 396 // Mozilla 1.8 and WinIE 7 both accept text/javascript and text/ecmascript. 397 // Mozilla 1.8 accepts application/javascript, application/ecmascript, and 398 // application/x-javascript, but WinIE 7 doesn't. 399 // WinIE 7 accepts text/javascript1.1 - text/javascript1.3, text/jscript, and 400 // text/livescript, but Mozilla 1.8 doesn't. 401 // Mozilla 1.8 allows leading and trailing whitespace, but WinIE 7 doesn't. 402 // Mozilla 1.8 and WinIE 7 both accept the empty string, but neither accept a 403 // whitespace-only string. 404 // We want to accept all the values that either of these browsers accept, but 405 // not other values. 406 static const char* const supported_javascript_types[] = { 407 "text/javascript", 408 "text/ecmascript", 409 "application/javascript", 410 "application/ecmascript", 411 "application/x-javascript", 412 "text/javascript1.1", 413 "text/javascript1.2", 414 "text/javascript1.3", 415 "text/jscript", 416 "text/livescript" 417 }; 418 419 #if defined(OS_ANDROID) 420 static bool IsCodecSupportedOnAndroid(const std::string& codec) { 421 // Theora is not supported in Android 422 if (!codec.compare("theora")) 423 return false; 424 425 // VP9 is supported only in KitKat+ (API Level 19). 426 if ((!codec.compare("vp9") || !codec.compare("vp9.0")) && 427 base::android::BuildInfo::GetInstance()->sdk_int() < 19) { 428 return false; 429 } 430 431 // TODO(vigneshv): Change this similar to the VP9 check once Opus is 432 // supported on Android (http://crbug.com/318436). 433 if (!codec.compare("opus")) { 434 return false; 435 } 436 return true; 437 } 438 439 static bool IsMimeTypeSupportedOnAndroid(const std::string& mimeType) { 440 // HLS codecs are supported in ICS and above (API level 14) 441 if ((!mimeType.compare("application/vnd.apple.mpegurl") || 442 !mimeType.compare("application/x-mpegurl")) && 443 base::android::BuildInfo::GetInstance()->sdk_int() < 14) { 444 return false; 445 } 446 return true; 447 } 448 #endif 449 450 struct MediaFormatStrict { 451 const char* mime_type; 452 const char* codecs_list; 453 }; 454 455 static const MediaFormatStrict format_codec_mappings[] = { 456 { "video/webm", "opus,vorbis,vp8,vp8.0,vp9,vp9.0" }, 457 { "audio/webm", "opus,vorbis" }, 458 { "audio/wav", "1" }, 459 { "audio/x-wav", "1" }, 460 { "video/ogg", "opus,theora,vorbis" }, 461 { "audio/ogg", "opus,vorbis" }, 462 { "application/ogg", "opus,theora,vorbis" }, 463 { "audio/mpeg", ",mp3" }, // Note: The comma before the 'mp3'results in an 464 // empty string codec ID and indicates 465 // a missing codecs= parameter is also valid. 466 // The presense of 'mp3' is not RFC compliant, 467 // but is common in the wild so it is a defacto 468 // standard. 469 { "audio/mp3", "" }, 470 { "audio/x-mp3", "" } 471 }; 472 473 MimeUtil::MimeUtil() { 474 InitializeMimeTypeMaps(); 475 } 476 477 // static 478 bool MimeUtil::AreSupportedCodecs(const MimeMappings& supported_codecs, 479 const std::vector<std::string>& codecs) { 480 if (supported_codecs.empty()) 481 return codecs.empty(); 482 483 // If no codecs are specified in the mimetype, check to see if a missing 484 // codecs parameter is allowed. 485 if (codecs.empty()) 486 return supported_codecs.find(std::string()) != supported_codecs.end(); 487 488 for (size_t i = 0; i < codecs.size(); ++i) { 489 if (codecs[i].empty() || 490 supported_codecs.find(codecs[i]) == supported_codecs.end()) { 491 return false; 492 } 493 } 494 495 return true; 496 } 497 498 void MimeUtil::InitializeMimeTypeMaps() { 499 for (size_t i = 0; i < arraysize(supported_image_types); ++i) 500 image_map_.insert(supported_image_types[i]); 501 502 // Initialize the supported non-image types. 503 for (size_t i = 0; i < arraysize(supported_non_image_types); ++i) 504 non_image_map_.insert(supported_non_image_types[i]); 505 for (size_t i = 0; i < arraysize(supported_certificate_types); ++i) 506 non_image_map_.insert(supported_certificate_types[i].mime_type); 507 for (size_t i = 0; i < arraysize(unsupported_text_types); ++i) 508 unsupported_text_map_.insert(unsupported_text_types[i]); 509 for (size_t i = 0; i < arraysize(supported_javascript_types); ++i) 510 non_image_map_.insert(supported_javascript_types[i]); 511 for (size_t i = 0; i < arraysize(common_media_types); ++i) { 512 #if defined(OS_ANDROID) 513 if (!IsMimeTypeSupportedOnAndroid(common_media_types[i])) 514 continue; 515 #endif 516 non_image_map_.insert(common_media_types[i]); 517 } 518 #if defined(USE_PROPRIETARY_CODECS) 519 for (size_t i = 0; i < arraysize(proprietary_media_types); ++i) 520 non_image_map_.insert(proprietary_media_types[i]); 521 #endif 522 523 // Initialize the supported media types. 524 for (size_t i = 0; i < arraysize(common_media_types); ++i) { 525 #if defined(OS_ANDROID) 526 if (!IsMimeTypeSupportedOnAndroid(common_media_types[i])) 527 continue; 528 #endif 529 media_map_.insert(common_media_types[i]); 530 } 531 #if defined(USE_PROPRIETARY_CODECS) 532 for (size_t i = 0; i < arraysize(proprietary_media_types); ++i) 533 media_map_.insert(proprietary_media_types[i]); 534 #endif 535 536 for (size_t i = 0; i < arraysize(supported_javascript_types); ++i) 537 javascript_map_.insert(supported_javascript_types[i]); 538 539 for (size_t i = 0; i < arraysize(common_media_codecs); ++i) { 540 #if defined(OS_ANDROID) 541 if (!IsCodecSupportedOnAndroid(common_media_codecs[i])) 542 continue; 543 #endif 544 codecs_map_.insert(common_media_codecs[i]); 545 } 546 #if defined(USE_PROPRIETARY_CODECS) 547 for (size_t i = 0; i < arraysize(proprietary_media_codecs); ++i) 548 codecs_map_.insert(proprietary_media_codecs[i]); 549 #endif 550 551 // Initialize the strict supported media types. 552 for (size_t i = 0; i < arraysize(format_codec_mappings); ++i) { 553 std::vector<std::string> mime_type_codecs; 554 ParseCodecString(format_codec_mappings[i].codecs_list, 555 &mime_type_codecs, 556 false); 557 558 MimeMappings codecs; 559 for (size_t j = 0; j < mime_type_codecs.size(); ++j) { 560 #if defined(OS_ANDROID) 561 if (!IsCodecSupportedOnAndroid(mime_type_codecs[j])) 562 continue; 563 #endif 564 codecs.insert(mime_type_codecs[j]); 565 } 566 strict_format_map_[format_codec_mappings[i].mime_type] = codecs; 567 } 568 } 569 570 bool MimeUtil::IsSupportedImageMimeType(const std::string& mime_type) const { 571 return image_map_.find(mime_type) != image_map_.end(); 572 } 573 574 bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const { 575 return media_map_.find(mime_type) != media_map_.end(); 576 } 577 578 bool MimeUtil::IsSupportedNonImageMimeType(const std::string& mime_type) const { 579 return non_image_map_.find(mime_type) != non_image_map_.end() || 580 (mime_type.compare(0, 5, "text/") == 0 && 581 !IsUnsupportedTextMimeType(mime_type)) || 582 (mime_type.compare(0, 12, "application/") == 0 && 583 MatchesMimeType("application/*+json", mime_type)); 584 } 585 586 bool MimeUtil::IsUnsupportedTextMimeType(const std::string& mime_type) const { 587 return unsupported_text_map_.find(mime_type) != unsupported_text_map_.end(); 588 } 589 590 bool MimeUtil::IsSupportedJavascriptMimeType( 591 const std::string& mime_type) const { 592 return javascript_map_.find(mime_type) != javascript_map_.end(); 593 } 594 595 // Mirrors WebViewImpl::CanShowMIMEType() 596 bool MimeUtil::IsSupportedMimeType(const std::string& mime_type) const { 597 return (mime_type.compare(0, 6, "image/") == 0 && 598 IsSupportedImageMimeType(mime_type)) || 599 IsSupportedNonImageMimeType(mime_type); 600 } 601 602 // Tests for MIME parameter equality. Each parameter in the |mime_type_pattern| 603 // must be matched by a parameter in the |mime_type|. If there are no 604 // parameters in the pattern, the match is a success. 605 bool MatchesMimeTypeParameters(const std::string& mime_type_pattern, 606 const std::string& mime_type) { 607 const std::string::size_type semicolon = mime_type_pattern.find(';'); 608 const std::string::size_type test_semicolon = mime_type.find(';'); 609 if (semicolon != std::string::npos) { 610 if (test_semicolon == std::string::npos) 611 return false; 612 613 std::vector<std::string> pattern_parameters; 614 base::SplitString(mime_type_pattern.substr(semicolon + 1), 615 ';', &pattern_parameters); 616 617 std::vector<std::string> test_parameters; 618 base::SplitString(mime_type.substr(test_semicolon + 1), 619 ';', &test_parameters); 620 621 sort(pattern_parameters.begin(), pattern_parameters.end()); 622 sort(test_parameters.begin(), test_parameters.end()); 623 std::vector<std::string> difference = 624 base::STLSetDifference<std::vector<std::string> >(pattern_parameters, 625 test_parameters); 626 return difference.size() == 0; 627 } 628 return true; 629 } 630 631 // This comparison handles absolute maching and also basic 632 // wildcards. The plugin mime types could be: 633 // application/x-foo 634 // application/* 635 // application/*+xml 636 // * 637 // Also tests mime parameters -- all parameters in the pattern must be present 638 // in the tested type for a match to succeed. 639 bool MimeUtil::MatchesMimeType(const std::string& mime_type_pattern, 640 const std::string& mime_type) const { 641 // Verify caller is passing lowercase strings. 642 DCHECK_EQ(StringToLowerASCII(mime_type_pattern), mime_type_pattern); 643 DCHECK_EQ(StringToLowerASCII(mime_type), mime_type); 644 645 if (mime_type_pattern.empty()) 646 return false; 647 648 std::string::size_type semicolon = mime_type_pattern.find(';'); 649 const std::string base_pattern(mime_type_pattern.substr(0, semicolon)); 650 semicolon = mime_type.find(';'); 651 const std::string base_type(mime_type.substr(0, semicolon)); 652 653 if (base_pattern == "*" || base_pattern == "*/*") 654 return MatchesMimeTypeParameters(mime_type_pattern, mime_type); 655 656 const std::string::size_type star = base_pattern.find('*'); 657 if (star == std::string::npos) { 658 if (base_pattern == base_type) 659 return MatchesMimeTypeParameters(mime_type_pattern, mime_type); 660 else 661 return false; 662 } 663 664 // Test length to prevent overlap between |left| and |right|. 665 if (base_type.length() < base_pattern.length() - 1) 666 return false; 667 668 const std::string left(base_pattern.substr(0, star)); 669 const std::string right(base_pattern.substr(star + 1)); 670 671 if (base_type.find(left) != 0) 672 return false; 673 674 if (!right.empty() && 675 base_type.rfind(right) != base_type.length() - right.length()) 676 return false; 677 678 return MatchesMimeTypeParameters(mime_type_pattern, mime_type); 679 } 680 681 // See http://www.iana.org/assignments/media-types/media-types.xhtml 682 static const char* legal_top_level_types[] = { 683 "application", 684 "audio", 685 "example", 686 "image", 687 "message", 688 "model", 689 "multipart", 690 "text", 691 "video", 692 }; 693 694 bool MimeUtil::ParseMimeTypeWithoutParameter( 695 const std::string& type_string, 696 std::string* top_level_type, 697 std::string* subtype) const { 698 std::vector<std::string> components; 699 base::SplitString(type_string, '/', &components); 700 if (components.size() != 2 || 701 !HttpUtil::IsToken(components[0]) || 702 !HttpUtil::IsToken(components[1])) 703 return false; 704 705 if (top_level_type) 706 *top_level_type = components[0]; 707 if (subtype) 708 *subtype = components[1]; 709 return true; 710 } 711 712 bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const { 713 std::string lower_type = StringToLowerASCII(type_string); 714 for (size_t i = 0; i < arraysize(legal_top_level_types); ++i) { 715 if (lower_type.compare(legal_top_level_types[i]) == 0) 716 return true; 717 } 718 719 return type_string.size() > 2 && StartsWithASCII(type_string, "x-", false); 720 } 721 722 bool MimeUtil::AreSupportedMediaCodecs( 723 const std::vector<std::string>& codecs) const { 724 return AreSupportedCodecs(codecs_map_, codecs); 725 } 726 727 void MimeUtil::ParseCodecString(const std::string& codecs, 728 std::vector<std::string>* codecs_out, 729 bool strip) { 730 std::string no_quote_codecs; 731 base::TrimString(codecs, "\"", &no_quote_codecs); 732 base::SplitString(no_quote_codecs, ',', codecs_out); 733 734 if (!strip) 735 return; 736 737 // Strip everything past the first '.' 738 for (std::vector<std::string>::iterator it = codecs_out->begin(); 739 it != codecs_out->end(); 740 ++it) { 741 size_t found = it->find_first_of('.'); 742 if (found != std::string::npos) 743 it->resize(found); 744 } 745 } 746 747 bool MimeUtil::IsStrictMediaMimeType(const std::string& mime_type) const { 748 if (strict_format_map_.find(mime_type) == strict_format_map_.end()) 749 return false; 750 return true; 751 } 752 753 bool MimeUtil::IsSupportedStrictMediaMimeType( 754 const std::string& mime_type, 755 const std::vector<std::string>& codecs) const { 756 StrictMappings::const_iterator it = strict_format_map_.find(mime_type); 757 return (it != strict_format_map_.end()) && 758 AreSupportedCodecs(it->second, codecs); 759 } 760 761 void MimeUtil::RemoveProprietaryMediaTypesAndCodecsForTests() { 762 for (size_t i = 0; i < arraysize(proprietary_media_types); ++i) { 763 non_image_map_.erase(proprietary_media_types[i]); 764 media_map_.erase(proprietary_media_types[i]); 765 } 766 for (size_t i = 0; i < arraysize(proprietary_media_codecs); ++i) 767 codecs_map_.erase(proprietary_media_codecs[i]); 768 } 769 770 //---------------------------------------------------------------------------- 771 // Wrappers for the singleton 772 //---------------------------------------------------------------------------- 773 774 bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, 775 std::string* mime_type) { 776 return g_mime_util.Get().GetMimeTypeFromExtension(ext, mime_type); 777 } 778 779 bool GetMimeTypeFromFile(const base::FilePath& file_path, 780 std::string* mime_type) { 781 return g_mime_util.Get().GetMimeTypeFromFile(file_path, mime_type); 782 } 783 784 bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext, 785 std::string* mime_type) { 786 return g_mime_util.Get().GetWellKnownMimeTypeFromExtension(ext, mime_type); 787 } 788 789 bool GetPreferredExtensionForMimeType(const std::string& mime_type, 790 base::FilePath::StringType* extension) { 791 return g_mime_util.Get().GetPreferredExtensionForMimeType(mime_type, 792 extension); 793 } 794 795 bool IsSupportedImageMimeType(const std::string& mime_type) { 796 return g_mime_util.Get().IsSupportedImageMimeType(mime_type); 797 } 798 799 bool IsSupportedMediaMimeType(const std::string& mime_type) { 800 return g_mime_util.Get().IsSupportedMediaMimeType(mime_type); 801 } 802 803 bool IsSupportedNonImageMimeType(const std::string& mime_type) { 804 return g_mime_util.Get().IsSupportedNonImageMimeType(mime_type); 805 } 806 807 bool IsUnsupportedTextMimeType(const std::string& mime_type) { 808 return g_mime_util.Get().IsUnsupportedTextMimeType(mime_type); 809 } 810 811 bool IsSupportedJavascriptMimeType(const std::string& mime_type) { 812 return g_mime_util.Get().IsSupportedJavascriptMimeType(mime_type); 813 } 814 815 bool IsSupportedMimeType(const std::string& mime_type) { 816 return g_mime_util.Get().IsSupportedMimeType(mime_type); 817 } 818 819 bool MatchesMimeType(const std::string& mime_type_pattern, 820 const std::string& mime_type) { 821 return g_mime_util.Get().MatchesMimeType(mime_type_pattern, mime_type); 822 } 823 824 bool ParseMimeTypeWithoutParameter(const std::string& type_string, 825 std::string* top_level_type, 826 std::string* subtype) { 827 return g_mime_util.Get().ParseMimeTypeWithoutParameter( 828 type_string, top_level_type, subtype); 829 } 830 831 bool IsValidTopLevelMimeType(const std::string& type_string) { 832 return g_mime_util.Get().IsValidTopLevelMimeType(type_string); 833 } 834 835 bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) { 836 return g_mime_util.Get().AreSupportedMediaCodecs(codecs); 837 } 838 839 bool IsStrictMediaMimeType(const std::string& mime_type) { 840 return g_mime_util.Get().IsStrictMediaMimeType(mime_type); 841 } 842 843 bool IsSupportedStrictMediaMimeType(const std::string& mime_type, 844 const std::vector<std::string>& codecs) { 845 return g_mime_util.Get().IsSupportedStrictMediaMimeType(mime_type, codecs); 846 } 847 848 void ParseCodecString(const std::string& codecs, 849 std::vector<std::string>* codecs_out, 850 const bool strip) { 851 g_mime_util.Get().ParseCodecString(codecs, codecs_out, strip); 852 } 853 854 namespace { 855 856 // From http://www.w3schools.com/media/media_mimeref.asp and 857 // http://plugindoc.mozdev.org/winmime.php 858 static const char* const kStandardImageTypes[] = { 859 "image/bmp", 860 "image/cis-cod", 861 "image/gif", 862 "image/ief", 863 "image/jpeg", 864 "image/webp", 865 "image/pict", 866 "image/pipeg", 867 "image/png", 868 "image/svg+xml", 869 "image/tiff", 870 "image/vnd.microsoft.icon", 871 "image/x-cmu-raster", 872 "image/x-cmx", 873 "image/x-icon", 874 "image/x-portable-anymap", 875 "image/x-portable-bitmap", 876 "image/x-portable-graymap", 877 "image/x-portable-pixmap", 878 "image/x-rgb", 879 "image/x-xbitmap", 880 "image/x-xpixmap", 881 "image/x-xwindowdump" 882 }; 883 static const char* const kStandardAudioTypes[] = { 884 "audio/aac", 885 "audio/aiff", 886 "audio/amr", 887 "audio/basic", 888 "audio/midi", 889 "audio/mp3", 890 "audio/mp4", 891 "audio/mpeg", 892 "audio/mpeg3", 893 "audio/ogg", 894 "audio/vorbis", 895 "audio/wav", 896 "audio/webm", 897 "audio/x-m4a", 898 "audio/x-ms-wma", 899 "audio/vnd.rn-realaudio", 900 "audio/vnd.wave" 901 }; 902 static const char* const kStandardVideoTypes[] = { 903 "video/avi", 904 "video/divx", 905 "video/flc", 906 "video/mp4", 907 "video/mpeg", 908 "video/ogg", 909 "video/quicktime", 910 "video/sd-video", 911 "video/webm", 912 "video/x-dv", 913 "video/x-m4v", 914 "video/x-mpeg", 915 "video/x-ms-asf", 916 "video/x-ms-wmv" 917 }; 918 919 struct StandardType { 920 const char* leading_mime_type; 921 const char* const* standard_types; 922 size_t standard_types_len; 923 }; 924 static const StandardType kStandardTypes[] = { 925 { "image/", kStandardImageTypes, arraysize(kStandardImageTypes) }, 926 { "audio/", kStandardAudioTypes, arraysize(kStandardAudioTypes) }, 927 { "video/", kStandardVideoTypes, arraysize(kStandardVideoTypes) }, 928 { NULL, NULL, 0 } 929 }; 930 931 void GetExtensionsFromHardCodedMappings( 932 const MimeInfo* mappings, 933 size_t mappings_len, 934 const std::string& leading_mime_type, 935 base::hash_set<base::FilePath::StringType>* extensions) { 936 base::FilePath::StringType extension; 937 for (size_t i = 0; i < mappings_len; ++i) { 938 if (StartsWithASCII(mappings[i].mime_type, leading_mime_type, false)) { 939 std::vector<string> this_extensions; 940 base::SplitString(mappings[i].extensions, ',', &this_extensions); 941 for (size_t j = 0; j < this_extensions.size(); ++j) { 942 #if defined(OS_WIN) 943 base::FilePath::StringType extension( 944 base::UTF8ToWide(this_extensions[j])); 945 #else 946 base::FilePath::StringType extension(this_extensions[j]); 947 #endif 948 extensions->insert(extension); 949 } 950 } 951 } 952 } 953 954 void GetExtensionsHelper( 955 const char* const* standard_types, 956 size_t standard_types_len, 957 const std::string& leading_mime_type, 958 base::hash_set<base::FilePath::StringType>* extensions) { 959 for (size_t i = 0; i < standard_types_len; ++i) { 960 g_mime_util.Get().GetPlatformExtensionsForMimeType(standard_types[i], 961 extensions); 962 } 963 964 // Also look up the extensions from hard-coded mappings in case that some 965 // supported extensions are not registered in the system registry, like ogg. 966 GetExtensionsFromHardCodedMappings(primary_mappings, 967 arraysize(primary_mappings), 968 leading_mime_type, 969 extensions); 970 971 GetExtensionsFromHardCodedMappings(secondary_mappings, 972 arraysize(secondary_mappings), 973 leading_mime_type, 974 extensions); 975 } 976 977 // Note that the elements in the source set will be appended to the target 978 // vector. 979 template<class T> 980 void HashSetToVector(base::hash_set<T>* source, std::vector<T>* target) { 981 size_t old_target_size = target->size(); 982 target->resize(old_target_size + source->size()); 983 size_t i = 0; 984 for (typename base::hash_set<T>::iterator iter = source->begin(); 985 iter != source->end(); ++iter, ++i) 986 (*target)[old_target_size + i] = *iter; 987 } 988 } 989 990 void GetExtensionsForMimeType( 991 const std::string& unsafe_mime_type, 992 std::vector<base::FilePath::StringType>* extensions) { 993 if (unsafe_mime_type == "*/*" || unsafe_mime_type == "*") 994 return; 995 996 const std::string mime_type = StringToLowerASCII(unsafe_mime_type); 997 base::hash_set<base::FilePath::StringType> unique_extensions; 998 999 if (EndsWith(mime_type, "/*", true)) { 1000 std::string leading_mime_type = mime_type.substr(0, mime_type.length() - 1); 1001 1002 // Find the matching StandardType from within kStandardTypes, or fall 1003 // through to the last (default) StandardType. 1004 const StandardType* type = NULL; 1005 for (size_t i = 0; i < arraysize(kStandardTypes); ++i) { 1006 type = &(kStandardTypes[i]); 1007 if (type->leading_mime_type && 1008 leading_mime_type == type->leading_mime_type) 1009 break; 1010 } 1011 DCHECK(type); 1012 GetExtensionsHelper(type->standard_types, 1013 type->standard_types_len, 1014 leading_mime_type, 1015 &unique_extensions); 1016 } else { 1017 g_mime_util.Get().GetPlatformExtensionsForMimeType(mime_type, 1018 &unique_extensions); 1019 1020 // Also look up the extensions from hard-coded mappings in case that some 1021 // supported extensions are not registered in the system registry, like ogg. 1022 GetExtensionsFromHardCodedMappings(primary_mappings, 1023 arraysize(primary_mappings), 1024 mime_type, 1025 &unique_extensions); 1026 1027 GetExtensionsFromHardCodedMappings(secondary_mappings, 1028 arraysize(secondary_mappings), 1029 mime_type, 1030 &unique_extensions); 1031 } 1032 1033 HashSetToVector(&unique_extensions, extensions); 1034 } 1035 1036 void RemoveProprietaryMediaTypesAndCodecsForTests() { 1037 g_mime_util.Get().RemoveProprietaryMediaTypesAndCodecsForTests(); 1038 } 1039 1040 const std::string GetIANAMediaType(const std::string& mime_type) { 1041 for (size_t i = 0; i < arraysize(kIanaMediaTypes); ++i) { 1042 if (StartsWithASCII(mime_type, kIanaMediaTypes[i].matcher, true)) { 1043 return kIanaMediaTypes[i].name; 1044 } 1045 } 1046 return std::string(); 1047 } 1048 1049 CertificateMimeType GetCertificateMimeTypeForMimeType( 1050 const std::string& mime_type) { 1051 // Don't create a map, there is only one entry in the table, 1052 // except on Android. 1053 for (size_t i = 0; i < arraysize(supported_certificate_types); ++i) { 1054 if (mime_type == net::supported_certificate_types[i].mime_type) 1055 return net::supported_certificate_types[i].cert_type; 1056 } 1057 return CERTIFICATE_MIME_TYPE_UNKNOWN; 1058 } 1059 1060 bool IsSupportedCertificateMimeType(const std::string& mime_type) { 1061 CertificateMimeType file_type = 1062 GetCertificateMimeTypeForMimeType(mime_type); 1063 return file_type != CERTIFICATE_MIME_TYPE_UNKNOWN; 1064 } 1065 1066 void AddMultipartValueForUpload(const std::string& value_name, 1067 const std::string& value, 1068 const std::string& mime_boundary, 1069 const std::string& content_type, 1070 std::string* post_data) { 1071 DCHECK(post_data); 1072 // First line is the boundary. 1073 post_data->append("--" + mime_boundary + "\r\n"); 1074 // Next line is the Content-disposition. 1075 post_data->append("Content-Disposition: form-data; name=\"" + 1076 value_name + "\"\r\n"); 1077 if (!content_type.empty()) { 1078 // If Content-type is specified, the next line is that. 1079 post_data->append("Content-Type: " + content_type + "\r\n"); 1080 } 1081 // Leave an empty line and append the value. 1082 post_data->append("\r\n" + value + "\r\n"); 1083 } 1084 1085 void AddMultipartFinalDelimiterForUpload(const std::string& mime_boundary, 1086 std::string* post_data) { 1087 DCHECK(post_data); 1088 post_data->append("--" + mime_boundary + "--\r\n"); 1089 } 1090 1091 } // namespace net 1092