2f02da13dc4ffabf81b4118d704b54007c406363
[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", "" },
464   { "audio/mp3", "" },
465   { "audio/x-mp3", "" }
466 };
467
468 MimeUtil::MimeUtil() {
469   InitializeMimeTypeMaps();
470 }
471
472 // static
473 bool MimeUtil::AreSupportedCodecs(const MimeMappings& supported_codecs,
474                                   const std::vector<std::string>& codecs) {
475   if (supported_codecs.empty())
476     return codecs.empty();
477
478   for (size_t i = 0; i < codecs.size(); ++i) {
479     if (supported_codecs.find(codecs[i]) == supported_codecs.end())
480       return false;
481   }
482   return !codecs.empty();
483 }
484
485 void MimeUtil::InitializeMimeTypeMaps() {
486   for (size_t i = 0; i < arraysize(supported_image_types); ++i)
487     image_map_.insert(supported_image_types[i]);
488
489   // Initialize the supported non-image types.
490   for (size_t i = 0; i < arraysize(supported_non_image_types); ++i)
491     non_image_map_.insert(supported_non_image_types[i]);
492   for (size_t i = 0; i < arraysize(supported_certificate_types); ++i)
493     non_image_map_.insert(supported_certificate_types[i].mime_type);
494   for (size_t i = 0; i < arraysize(unsupported_text_types); ++i)
495     unsupported_text_map_.insert(unsupported_text_types[i]);
496   for (size_t i = 0; i < arraysize(supported_javascript_types); ++i)
497     non_image_map_.insert(supported_javascript_types[i]);
498   for (size_t i = 0; i < arraysize(common_media_types); ++i) {
499 #if defined(OS_ANDROID)
500     if (!IsMimeTypeSupportedOnAndroid(common_media_types[i]))
501       continue;
502 #endif
503     non_image_map_.insert(common_media_types[i]);
504   }
505 #if defined(USE_PROPRIETARY_CODECS)
506   for (size_t i = 0; i < arraysize(proprietary_media_types); ++i)
507     non_image_map_.insert(proprietary_media_types[i]);
508 #endif
509
510   // Initialize the supported media types.
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     media_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     media_map_.insert(proprietary_media_types[i]);
521 #endif
522
523   for (size_t i = 0; i < arraysize(supported_javascript_types); ++i)
524     javascript_map_.insert(supported_javascript_types[i]);
525
526   for (size_t i = 0; i < arraysize(common_media_codecs); ++i) {
527 #if defined(OS_ANDROID)
528     if (!IsCodecSupportedOnAndroid(common_media_codecs[i]))
529       continue;
530 #endif
531     codecs_map_.insert(common_media_codecs[i]);
532   }
533 #if defined(USE_PROPRIETARY_CODECS)
534   for (size_t i = 0; i < arraysize(proprietary_media_codecs); ++i)
535     codecs_map_.insert(proprietary_media_codecs[i]);
536 #endif
537
538   // Initialize the strict supported media types.
539   for (size_t i = 0; i < arraysize(format_codec_mappings); ++i) {
540     std::vector<std::string> mime_type_codecs;
541     ParseCodecString(format_codec_mappings[i].codecs_list,
542                      &mime_type_codecs,
543                      false);
544
545     MimeMappings codecs;
546     for (size_t j = 0; j < mime_type_codecs.size(); ++j) {
547 #if defined(OS_ANDROID)
548       if (!IsCodecSupportedOnAndroid(mime_type_codecs[j]))
549         continue;
550 #endif
551       codecs.insert(mime_type_codecs[j]);
552     }
553     strict_format_map_[format_codec_mappings[i].mime_type] = codecs;
554   }
555 }
556
557 bool MimeUtil::IsSupportedImageMimeType(const std::string& mime_type) const {
558   return image_map_.find(mime_type) != image_map_.end();
559 }
560
561 bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const {
562   return media_map_.find(mime_type) != media_map_.end();
563 }
564
565 bool MimeUtil::IsSupportedNonImageMimeType(const std::string& mime_type) const {
566   return non_image_map_.find(mime_type) != non_image_map_.end() ||
567       (mime_type.compare(0, 5, "text/") == 0 &&
568        !IsUnsupportedTextMimeType(mime_type)) ||
569       (mime_type.compare(0, 12, "application/") == 0 &&
570        MatchesMimeType("application/*+json", mime_type));
571 }
572
573 bool MimeUtil::IsUnsupportedTextMimeType(const std::string& mime_type) const {
574   return unsupported_text_map_.find(mime_type) != unsupported_text_map_.end();
575 }
576
577 bool MimeUtil::IsSupportedJavascriptMimeType(
578     const std::string& mime_type) const {
579   return javascript_map_.find(mime_type) != javascript_map_.end();
580 }
581
582 // Mirrors WebViewImpl::CanShowMIMEType()
583 bool MimeUtil::IsSupportedMimeType(const std::string& mime_type) const {
584   return (mime_type.compare(0, 6, "image/") == 0 &&
585           IsSupportedImageMimeType(mime_type)) ||
586          IsSupportedNonImageMimeType(mime_type);
587 }
588
589 // Tests for MIME parameter equality. Each parameter in the |mime_type_pattern|
590 // must be matched by a parameter in the |mime_type|. If there are no
591 // parameters in the pattern, the match is a success.
592 bool MatchesMimeTypeParameters(const std::string& mime_type_pattern,
593                                const std::string& mime_type) {
594   const std::string::size_type semicolon = mime_type_pattern.find(';');
595   const std::string::size_type test_semicolon = mime_type.find(';');
596   if (semicolon != std::string::npos) {
597     if (test_semicolon == std::string::npos)
598       return false;
599
600     std::vector<std::string> pattern_parameters;
601     base::SplitString(mime_type_pattern.substr(semicolon + 1),
602                       ';', &pattern_parameters);
603
604     std::vector<std::string> test_parameters;
605     base::SplitString(mime_type.substr(test_semicolon + 1),
606                       ';', &test_parameters);
607
608     sort(pattern_parameters.begin(), pattern_parameters.end());
609     sort(test_parameters.begin(), test_parameters.end());
610     std::vector<std::string> difference =
611       base::STLSetDifference<std::vector<std::string> >(pattern_parameters,
612                                                         test_parameters);
613     return difference.size() == 0;
614   }
615   return true;
616 }
617
618 // This comparison handles absolute maching and also basic
619 // wildcards.  The plugin mime types could be:
620 //      application/x-foo
621 //      application/*
622 //      application/*+xml
623 //      *
624 // Also tests mime parameters -- all parameters in the pattern must be present
625 // in the tested type for a match to succeed.
626 bool MimeUtil::MatchesMimeType(const std::string& mime_type_pattern,
627                                const std::string& mime_type) const {
628   // Verify caller is passing lowercase strings.
629   DCHECK_EQ(StringToLowerASCII(mime_type_pattern), mime_type_pattern);
630   DCHECK_EQ(StringToLowerASCII(mime_type), mime_type);
631
632   if (mime_type_pattern.empty())
633     return false;
634
635   std::string::size_type semicolon = mime_type_pattern.find(';');
636   const std::string base_pattern(mime_type_pattern.substr(0, semicolon));
637   semicolon = mime_type.find(';');
638   const std::string base_type(mime_type.substr(0, semicolon));
639
640   if (base_pattern == "*" || base_pattern == "*/*")
641     return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
642
643   const std::string::size_type star = base_pattern.find('*');
644   if (star == std::string::npos) {
645     if (base_pattern == base_type)
646       return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
647     else
648       return false;
649   }
650
651   // Test length to prevent overlap between |left| and |right|.
652   if (base_type.length() < base_pattern.length() - 1)
653     return false;
654
655   const std::string left(base_pattern.substr(0, star));
656   const std::string right(base_pattern.substr(star + 1));
657
658   if (base_type.find(left) != 0)
659     return false;
660
661   if (!right.empty() &&
662       base_type.rfind(right) != base_type.length() - right.length())
663     return false;
664
665   return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
666 }
667
668 // See http://www.iana.org/assignments/media-types/media-types.xhtml
669 static const char* legal_top_level_types[] = {
670   "application",
671   "audio",
672   "example",
673   "image",
674   "message",
675   "model",
676   "multipart",
677   "text",
678   "video",
679 };
680
681 bool MimeUtil::ParseMimeTypeWithoutParameter(
682     const std::string& type_string,
683     std::string* top_level_type,
684     std::string* subtype) const {
685   std::vector<std::string> components;
686   base::SplitString(type_string, '/', &components);
687   if (components.size() != 2 ||
688       !HttpUtil::IsToken(components[0]) ||
689       !HttpUtil::IsToken(components[1]))
690     return false;
691
692   if (top_level_type)
693     *top_level_type = components[0];
694   if (subtype)
695     *subtype = components[1];
696   return true;
697 }
698
699 bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const {
700   std::string lower_type = StringToLowerASCII(type_string);
701   for (size_t i = 0; i < arraysize(legal_top_level_types); ++i) {
702     if (lower_type.compare(legal_top_level_types[i]) == 0)
703       return true;
704   }
705
706   return type_string.size() > 2 && StartsWithASCII(type_string, "x-", false);
707 }
708
709 bool MimeUtil::AreSupportedMediaCodecs(
710     const std::vector<std::string>& codecs) const {
711   return AreSupportedCodecs(codecs_map_, codecs);
712 }
713
714 void MimeUtil::ParseCodecString(const std::string& codecs,
715                                 std::vector<std::string>* codecs_out,
716                                 bool strip) {
717   std::string no_quote_codecs;
718   base::TrimString(codecs, "\"", &no_quote_codecs);
719   base::SplitString(no_quote_codecs, ',', codecs_out);
720
721   if (!strip)
722     return;
723
724   // Strip everything past the first '.'
725   for (std::vector<std::string>::iterator it = codecs_out->begin();
726        it != codecs_out->end();
727        ++it) {
728     size_t found = it->find_first_of('.');
729     if (found != std::string::npos)
730       it->resize(found);
731   }
732 }
733
734 bool MimeUtil::IsStrictMediaMimeType(const std::string& mime_type) const {
735   if (strict_format_map_.find(mime_type) == strict_format_map_.end())
736     return false;
737   return true;
738 }
739
740 bool MimeUtil::IsSupportedStrictMediaMimeType(
741     const std::string& mime_type,
742     const std::vector<std::string>& codecs) const {
743   StrictMappings::const_iterator it = strict_format_map_.find(mime_type);
744   return (it != strict_format_map_.end()) &&
745       AreSupportedCodecs(it->second, codecs);
746 }
747
748 void MimeUtil::RemoveProprietaryMediaTypesAndCodecsForTests() {
749   for (size_t i = 0; i < arraysize(proprietary_media_types); ++i) {
750     non_image_map_.erase(proprietary_media_types[i]);
751     media_map_.erase(proprietary_media_types[i]);
752   }
753   for (size_t i = 0; i < arraysize(proprietary_media_codecs); ++i)
754     codecs_map_.erase(proprietary_media_codecs[i]);
755 }
756
757 //----------------------------------------------------------------------------
758 // Wrappers for the singleton
759 //----------------------------------------------------------------------------
760
761 bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
762                               std::string* mime_type) {
763   return g_mime_util.Get().GetMimeTypeFromExtension(ext, mime_type);
764 }
765
766 bool GetMimeTypeFromFile(const base::FilePath& file_path,
767                          std::string* mime_type) {
768   return g_mime_util.Get().GetMimeTypeFromFile(file_path, mime_type);
769 }
770
771 bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext,
772                                        std::string* mime_type) {
773   return g_mime_util.Get().GetWellKnownMimeTypeFromExtension(ext, mime_type);
774 }
775
776 bool GetPreferredExtensionForMimeType(const std::string& mime_type,
777                                       base::FilePath::StringType* extension) {
778   return g_mime_util.Get().GetPreferredExtensionForMimeType(mime_type,
779                                                             extension);
780 }
781
782 bool IsSupportedImageMimeType(const std::string& mime_type) {
783   return g_mime_util.Get().IsSupportedImageMimeType(mime_type);
784 }
785
786 bool IsSupportedMediaMimeType(const std::string& mime_type) {
787   return g_mime_util.Get().IsSupportedMediaMimeType(mime_type);
788 }
789
790 bool IsSupportedNonImageMimeType(const std::string& mime_type) {
791   return g_mime_util.Get().IsSupportedNonImageMimeType(mime_type);
792 }
793
794 bool IsUnsupportedTextMimeType(const std::string& mime_type) {
795   return g_mime_util.Get().IsUnsupportedTextMimeType(mime_type);
796 }
797
798 bool IsSupportedJavascriptMimeType(const std::string& mime_type) {
799   return g_mime_util.Get().IsSupportedJavascriptMimeType(mime_type);
800 }
801
802 bool IsSupportedMimeType(const std::string& mime_type) {
803   return g_mime_util.Get().IsSupportedMimeType(mime_type);
804 }
805
806 bool MatchesMimeType(const std::string& mime_type_pattern,
807                      const std::string& mime_type) {
808   return g_mime_util.Get().MatchesMimeType(mime_type_pattern, mime_type);
809 }
810
811 bool ParseMimeTypeWithoutParameter(const std::string& type_string,
812                                    std::string* top_level_type,
813                                    std::string* subtype) {
814   return g_mime_util.Get().ParseMimeTypeWithoutParameter(
815       type_string, top_level_type, subtype);
816 }
817
818 bool IsValidTopLevelMimeType(const std::string& type_string) {
819   return g_mime_util.Get().IsValidTopLevelMimeType(type_string);
820 }
821
822 bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) {
823   return g_mime_util.Get().AreSupportedMediaCodecs(codecs);
824 }
825
826 bool IsStrictMediaMimeType(const std::string& mime_type) {
827   return g_mime_util.Get().IsStrictMediaMimeType(mime_type);
828 }
829
830 bool IsSupportedStrictMediaMimeType(const std::string& mime_type,
831                                     const std::vector<std::string>& codecs) {
832   return g_mime_util.Get().IsSupportedStrictMediaMimeType(mime_type, codecs);
833 }
834
835 void ParseCodecString(const std::string& codecs,
836                       std::vector<std::string>* codecs_out,
837                       const bool strip) {
838   g_mime_util.Get().ParseCodecString(codecs, codecs_out, strip);
839 }
840
841 namespace {
842
843 // From http://www.w3schools.com/media/media_mimeref.asp and
844 // http://plugindoc.mozdev.org/winmime.php
845 static const char* const kStandardImageTypes[] = {
846   "image/bmp",
847   "image/cis-cod",
848   "image/gif",
849   "image/ief",
850   "image/jpeg",
851   "image/webp",
852   "image/pict",
853   "image/pipeg",
854   "image/png",
855   "image/svg+xml",
856   "image/tiff",
857   "image/vnd.microsoft.icon",
858   "image/x-cmu-raster",
859   "image/x-cmx",
860   "image/x-icon",
861   "image/x-portable-anymap",
862   "image/x-portable-bitmap",
863   "image/x-portable-graymap",
864   "image/x-portable-pixmap",
865   "image/x-rgb",
866   "image/x-xbitmap",
867   "image/x-xpixmap",
868   "image/x-xwindowdump"
869 };
870 static const char* const kStandardAudioTypes[] = {
871   "audio/aac",
872   "audio/aiff",
873   "audio/amr",
874   "audio/basic",
875   "audio/midi",
876   "audio/mp3",
877   "audio/mp4",
878   "audio/mpeg",
879   "audio/mpeg3",
880   "audio/ogg",
881   "audio/vorbis",
882   "audio/wav",
883   "audio/webm",
884   "audio/x-m4a",
885   "audio/x-ms-wma",
886   "audio/vnd.rn-realaudio",
887   "audio/vnd.wave"
888 };
889 static const char* const kStandardVideoTypes[] = {
890   "video/avi",
891   "video/divx",
892   "video/flc",
893   "video/mp4",
894   "video/mpeg",
895   "video/ogg",
896   "video/quicktime",
897   "video/sd-video",
898   "video/webm",
899   "video/x-dv",
900   "video/x-m4v",
901   "video/x-mpeg",
902   "video/x-ms-asf",
903   "video/x-ms-wmv"
904 };
905
906 struct StandardType {
907   const char* leading_mime_type;
908   const char* const* standard_types;
909   size_t standard_types_len;
910 };
911 static const StandardType kStandardTypes[] = {
912   { "image/", kStandardImageTypes, arraysize(kStandardImageTypes) },
913   { "audio/", kStandardAudioTypes, arraysize(kStandardAudioTypes) },
914   { "video/", kStandardVideoTypes, arraysize(kStandardVideoTypes) },
915   { NULL, NULL, 0 }
916 };
917
918 void GetExtensionsFromHardCodedMappings(
919     const MimeInfo* mappings,
920     size_t mappings_len,
921     const std::string& leading_mime_type,
922     base::hash_set<base::FilePath::StringType>* extensions) {
923   base::FilePath::StringType extension;
924   for (size_t i = 0; i < mappings_len; ++i) {
925     if (StartsWithASCII(mappings[i].mime_type, leading_mime_type, false)) {
926       std::vector<string> this_extensions;
927       base::SplitString(mappings[i].extensions, ',', &this_extensions);
928       for (size_t j = 0; j < this_extensions.size(); ++j) {
929 #if defined(OS_WIN)
930         base::FilePath::StringType extension(
931             base::UTF8ToWide(this_extensions[j]));
932 #else
933         base::FilePath::StringType extension(this_extensions[j]);
934 #endif
935         extensions->insert(extension);
936       }
937     }
938   }
939 }
940
941 void GetExtensionsHelper(
942     const char* const* standard_types,
943     size_t standard_types_len,
944     const std::string& leading_mime_type,
945     base::hash_set<base::FilePath::StringType>* extensions) {
946   for (size_t i = 0; i < standard_types_len; ++i) {
947     g_mime_util.Get().GetPlatformExtensionsForMimeType(standard_types[i],
948                                                        extensions);
949   }
950
951   // Also look up the extensions from hard-coded mappings in case that some
952   // supported extensions are not registered in the system registry, like ogg.
953   GetExtensionsFromHardCodedMappings(primary_mappings,
954                                      arraysize(primary_mappings),
955                                      leading_mime_type,
956                                      extensions);
957
958   GetExtensionsFromHardCodedMappings(secondary_mappings,
959                                      arraysize(secondary_mappings),
960                                      leading_mime_type,
961                                      extensions);
962 }
963
964 // Note that the elements in the source set will be appended to the target
965 // vector.
966 template<class T>
967 void HashSetToVector(base::hash_set<T>* source, std::vector<T>* target) {
968   size_t old_target_size = target->size();
969   target->resize(old_target_size + source->size());
970   size_t i = 0;
971   for (typename base::hash_set<T>::iterator iter = source->begin();
972        iter != source->end(); ++iter, ++i)
973     (*target)[old_target_size + i] = *iter;
974 }
975 }
976
977 void GetExtensionsForMimeType(
978     const std::string& unsafe_mime_type,
979     std::vector<base::FilePath::StringType>* extensions) {
980   if (unsafe_mime_type == "*/*" || unsafe_mime_type == "*")
981     return;
982
983   const std::string mime_type = StringToLowerASCII(unsafe_mime_type);
984   base::hash_set<base::FilePath::StringType> unique_extensions;
985
986   if (EndsWith(mime_type, "/*", true)) {
987     std::string leading_mime_type = mime_type.substr(0, mime_type.length() - 1);
988
989     // Find the matching StandardType from within kStandardTypes, or fall
990     // through to the last (default) StandardType.
991     const StandardType* type = NULL;
992     for (size_t i = 0; i < arraysize(kStandardTypes); ++i) {
993       type = &(kStandardTypes[i]);
994       if (type->leading_mime_type &&
995           leading_mime_type == type->leading_mime_type)
996         break;
997     }
998     DCHECK(type);
999     GetExtensionsHelper(type->standard_types,
1000                         type->standard_types_len,
1001                         leading_mime_type,
1002                         &unique_extensions);
1003   } else {
1004     g_mime_util.Get().GetPlatformExtensionsForMimeType(mime_type,
1005                                                        &unique_extensions);
1006
1007     // Also look up the extensions from hard-coded mappings in case that some
1008     // supported extensions are not registered in the system registry, like ogg.
1009     GetExtensionsFromHardCodedMappings(primary_mappings,
1010                                        arraysize(primary_mappings),
1011                                        mime_type,
1012                                        &unique_extensions);
1013
1014     GetExtensionsFromHardCodedMappings(secondary_mappings,
1015                                        arraysize(secondary_mappings),
1016                                        mime_type,
1017                                        &unique_extensions);
1018   }
1019
1020   HashSetToVector(&unique_extensions, extensions);
1021 }
1022
1023 void RemoveProprietaryMediaTypesAndCodecsForTests() {
1024   g_mime_util.Get().RemoveProprietaryMediaTypesAndCodecsForTests();
1025 }
1026
1027 const std::string GetIANAMediaType(const std::string& mime_type) {
1028   for (size_t i = 0; i < arraysize(kIanaMediaTypes); ++i) {
1029     if (StartsWithASCII(mime_type, kIanaMediaTypes[i].matcher, true)) {
1030       return kIanaMediaTypes[i].name;
1031     }
1032   }
1033   return std::string();
1034 }
1035
1036 CertificateMimeType GetCertificateMimeTypeForMimeType(
1037     const std::string& mime_type) {
1038   // Don't create a map, there is only one entry in the table,
1039   // except on Android.
1040   for (size_t i = 0; i < arraysize(supported_certificate_types); ++i) {
1041     if (mime_type == net::supported_certificate_types[i].mime_type)
1042       return net::supported_certificate_types[i].cert_type;
1043   }
1044   return CERTIFICATE_MIME_TYPE_UNKNOWN;
1045 }
1046
1047 bool IsSupportedCertificateMimeType(const std::string& mime_type) {
1048   CertificateMimeType file_type =
1049       GetCertificateMimeTypeForMimeType(mime_type);
1050   return file_type != CERTIFICATE_MIME_TYPE_UNKNOWN;
1051 }
1052
1053 void AddMultipartValueForUpload(const std::string& value_name,
1054                                 const std::string& value,
1055                                 const std::string& mime_boundary,
1056                                 const std::string& content_type,
1057                                 std::string* post_data) {
1058   DCHECK(post_data);
1059   // First line is the boundary.
1060   post_data->append("--" + mime_boundary + "\r\n");
1061   // Next line is the Content-disposition.
1062   post_data->append("Content-Disposition: form-data; name=\"" +
1063                     value_name + "\"\r\n");
1064   if (!content_type.empty()) {
1065     // If Content-type is specified, the next line is that.
1066     post_data->append("Content-Type: " + content_type + "\r\n");
1067   }
1068   // Leave an empty line and append the value.
1069   post_data->append("\r\n" + value + "\r\n");
1070 }
1071
1072 void AddMultipartFinalDelimiterForUpload(const std::string& mime_boundary,
1073                                          std::string* post_data) {
1074   DCHECK(post_data);
1075   post_data->append("--" + mime_boundary + "--\r\n");
1076 }
1077
1078 }  // namespace net