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
24using std::string;
25
26namespace {
27
28struct MediaType {
29  const char name[12];
30  const char matcher[13];
31};
32
33static 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
47namespace net {
48
49// Singleton utility class for mime types.
50class 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.
118static base::LazyInstance<MimeUtil>::Leaky g_mime_util =
119    LAZY_INSTANCE_INITIALIZER;
120
121struct MimeInfo {
122  const char* mime_type;
123  const char* extensions;  // comma separated list
124};
125
126static 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
147static 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
173static 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
194bool MimeUtil::GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
195                                        string* result) const {
196  return GetMimeTypeFromExtensionHelper(ext, true, result);
197}
198
199bool MimeUtil::GetWellKnownMimeTypeFromExtension(
200    const base::FilePath::StringType& ext,
201    string* result) const {
202  return GetMimeTypeFromExtensionHelper(ext, false, result);
203}
204
205bool 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
213bool 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
253static 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.
269static 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.
287static 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
308static 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.
320static 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())
330static 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.
344struct CertificateMimeTypeInfo {
345  const char* mime_type;
346  CertificateMimeType cert_type;
347};
348
349static 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.
361static 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.
393static 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)
407static 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
423struct MediaFormatStrict {
424  const char* mime_type;
425  const char* codecs_list;
426};
427
428static 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
434MimeUtil::MimeUtil() {
435  InitializeMimeTypeMaps();
436}
437
438// static
439bool 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
448void 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
510bool MimeUtil::IsSupportedImageMimeType(const std::string& mime_type) const {
511  return image_map_.find(mime_type) != image_map_.end();
512}
513
514bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const {
515  return media_map_.find(mime_type) != media_map_.end();
516}
517
518bool 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
524bool MimeUtil::IsUnsupportedTextMimeType(const std::string& mime_type) const {
525  return unsupported_text_map_.find(mime_type) != unsupported_text_map_.end();
526}
527
528bool 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()
534bool 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.
543bool 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.
577bool 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
620static 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
632bool 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
662bool MimeUtil::AreSupportedMediaCodecs(
663    const std::vector<std::string>& codecs) const {
664  return AreSupportedCodecs(codecs_map_, codecs);
665}
666
667void 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
687bool 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
693bool 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
701void 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
714bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
715                              std::string* mime_type) {
716  return g_mime_util.Get().GetMimeTypeFromExtension(ext, mime_type);
717}
718
719bool GetMimeTypeFromFile(const base::FilePath& file_path,
720                         std::string* mime_type) {
721  return g_mime_util.Get().GetMimeTypeFromFile(file_path, mime_type);
722}
723
724bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext,
725                                       std::string* mime_type) {
726  return g_mime_util.Get().GetWellKnownMimeTypeFromExtension(ext, mime_type);
727}
728
729bool GetPreferredExtensionForMimeType(const std::string& mime_type,
730                                      base::FilePath::StringType* extension) {
731  return g_mime_util.Get().GetPreferredExtensionForMimeType(mime_type,
732                                                            extension);
733}
734
735bool IsSupportedImageMimeType(const std::string& mime_type) {
736  return g_mime_util.Get().IsSupportedImageMimeType(mime_type);
737}
738
739bool IsSupportedMediaMimeType(const std::string& mime_type) {
740  return g_mime_util.Get().IsSupportedMediaMimeType(mime_type);
741}
742
743bool IsSupportedNonImageMimeType(const std::string& mime_type) {
744  return g_mime_util.Get().IsSupportedNonImageMimeType(mime_type);
745}
746
747bool IsUnsupportedTextMimeType(const std::string& mime_type) {
748  return g_mime_util.Get().IsUnsupportedTextMimeType(mime_type);
749}
750
751bool IsSupportedJavascriptMimeType(const std::string& mime_type) {
752  return g_mime_util.Get().IsSupportedJavascriptMimeType(mime_type);
753}
754
755bool IsSupportedMimeType(const std::string& mime_type) {
756  return g_mime_util.Get().IsSupportedMimeType(mime_type);
757}
758
759bool 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
764bool IsMimeType(const std::string& type_string) {
765  return g_mime_util.Get().IsMimeType(type_string);
766}
767
768bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) {
769  return g_mime_util.Get().AreSupportedMediaCodecs(codecs);
770}
771
772bool IsStrictMediaMimeType(const std::string& mime_type) {
773  return g_mime_util.Get().IsStrictMediaMimeType(mime_type);
774}
775
776bool 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
781void 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
787namespace {
788
789// From http://www.w3schools.com/media/media_mimeref.asp and
790// http://plugindoc.mozdev.org/winmime.php
791static 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};
816static 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};
835static 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
852struct StandardType {
853  const char* leading_mime_type;
854  const char* const* standard_types;
855  size_t standard_types_len;
856};
857static const StandardType kStandardTypes[] = {
858  { "image/", kStandardImageTypes, arraysize(kStandardImageTypes) },
859  { "audio/", kStandardAudioTypes, arraysize(kStandardAudioTypes) },
860  { "video/", kStandardVideoTypes, arraysize(kStandardVideoTypes) },
861  { NULL, NULL, 0 }
862};
863
864void 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
887void 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.
912template<class T>
913void 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
923void 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
969void RemoveProprietaryMediaTypesAndCodecsForTests() {
970  g_mime_util.Get().RemoveProprietaryMediaTypesAndCodecsForTests();
971}
972
973const 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
982CertificateMimeType 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
993bool IsSupportedCertificateMimeType(const std::string& mime_type) {
994  CertificateMimeType file_type =
995      GetCertificateMimeTypeForMimeType(mime_type);
996  return file_type != CERTIFICATE_MIME_TYPE_UNKNOWN;
997}
998
999void 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
1018void 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