Home | History | Annotate | Download | only in base
      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