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