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