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