[Android WebView] Fix webview perf bot switchover to use org.chromium.webview_shell...
[chromium-blink-merge.git] / net / base / mime_util.cc
blobdee889230baeca1ad4946706125d963a8caafd02
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.
5 #include <algorithm>
6 #include <iterator>
7 #include <map>
8 #include <string>
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_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "build/build_config.h"
19 #include "net/base/mime_util.h"
20 #include "net/base/platform_mime_util.h"
21 #include "net/http/http_util.h"
23 using std::string;
25 namespace net {
27 // Singleton utility class for mime types.
28 class MimeUtil : public PlatformMimeUtil {
29 public:
30 bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
31 std::string* mime_type) const;
33 bool GetMimeTypeFromFile(const base::FilePath& file_path,
34 std::string* mime_type) const;
36 bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext,
37 std::string* mime_type) const;
39 bool MatchesMimeType(const std::string &mime_type_pattern,
40 const std::string &mime_type) const;
42 bool ParseMimeTypeWithoutParameter(const std::string& type_string,
43 std::string* top_level_type,
44 std::string* subtype) const;
46 bool IsValidTopLevelMimeType(const std::string& type_string) const;
48 private:
49 friend struct base::DefaultLazyInstanceTraits<MimeUtil>;
51 MimeUtil();
53 bool GetMimeTypeFromExtensionHelper(const base::FilePath::StringType& ext,
54 bool include_platform_types,
55 std::string* mime_type) const;
56 }; // class MimeUtil
58 // This variable is Leaky because we need to access it from WorkerPool threads.
59 static base::LazyInstance<MimeUtil>::Leaky g_mime_util =
60 LAZY_INSTANCE_INITIALIZER;
62 static const MimeInfo primary_mappings[] = {
63 { "text/html", "html,htm,shtml,shtm" },
64 { "text/css", "css" },
65 { "text/xml", "xml" },
66 { "image/gif", "gif" },
67 { "image/jpeg", "jpeg,jpg" },
68 { "image/webp", "webp" },
69 { "image/png", "png" },
70 { "video/mp4", "mp4,m4v" },
71 { "audio/x-m4a", "m4a" },
72 { "audio/mp3", "mp3" },
73 { "video/ogg", "ogv,ogm" },
74 { "audio/ogg", "ogg,oga,opus" },
75 { "video/webm", "webm" },
76 { "audio/webm", "webm" },
77 { "audio/wav", "wav" },
78 { "application/xhtml+xml", "xhtml,xht,xhtm" },
79 { "application/x-chrome-extension", "crx" },
80 { "multipart/related", "mhtml,mht" }
83 static const MimeInfo secondary_mappings[] = {
84 { "application/octet-stream", "exe,com,bin" },
85 { "application/gzip", "gz" },
86 { "application/pdf", "pdf" },
87 { "application/postscript", "ps,eps,ai" },
88 { "application/javascript", "js" },
89 { "application/font-woff", "woff" },
90 { "image/bmp", "bmp" },
91 { "image/x-icon", "ico" },
92 { "image/vnd.microsoft.icon", "ico" },
93 { "image/jpeg", "jfif,pjpeg,pjp" },
94 { "image/tiff", "tiff,tif" },
95 { "image/x-xbitmap", "xbm" },
96 { "image/svg+xml", "svg,svgz" },
97 { "image/x-png", "png"},
98 { "message/rfc822", "eml" },
99 { "text/plain", "txt,text" },
100 { "text/html", "ehtml" },
101 { "application/rss+xml", "rss" },
102 { "application/rdf+xml", "rdf" },
103 { "text/xml", "xsl,xbl,xslt" },
104 { "application/vnd.mozilla.xul+xml", "xul" },
105 { "application/x-shockwave-flash", "swf,swl" },
106 { "application/pkcs7-mime", "p7m,p7c,p7z" },
107 { "application/pkcs7-signature", "p7s" },
108 { "application/x-mpegurl", "m3u8" },
111 const char* FindMimeType(const MimeInfo* mappings,
112 size_t mappings_len,
113 const std::string& ext) {
114 for (size_t i = 0; i < mappings_len; ++i) {
115 const char* extensions = mappings[i].extensions;
116 for (;;) {
117 size_t end_pos = strcspn(extensions, ",");
118 if (end_pos == ext.size() &&
119 base::strncasecmp(extensions, ext.data(), ext.size()) == 0)
120 return mappings[i].mime_type;
121 extensions += end_pos;
122 if (!*extensions)
123 break;
124 extensions += 1; // skip over comma
127 return NULL;
130 bool MimeUtil::GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
131 string* result) const {
132 return GetMimeTypeFromExtensionHelper(ext, true, result);
135 bool MimeUtil::GetWellKnownMimeTypeFromExtension(
136 const base::FilePath::StringType& ext,
137 string* result) const {
138 return GetMimeTypeFromExtensionHelper(ext, false, result);
141 bool MimeUtil::GetMimeTypeFromFile(const base::FilePath& file_path,
142 string* result) const {
143 base::FilePath::StringType file_name_str = file_path.Extension();
144 if (file_name_str.empty())
145 return false;
146 return GetMimeTypeFromExtension(file_name_str.substr(1), result);
149 bool MimeUtil::GetMimeTypeFromExtensionHelper(
150 const base::FilePath::StringType& ext,
151 bool include_platform_types,
152 string* result) const {
153 // Avoids crash when unable to handle a long file path. See crbug.com/48733.
154 const unsigned kMaxFilePathSize = 65536;
155 if (ext.length() > kMaxFilePathSize)
156 return false;
158 // Reject a string which contains null character.
159 base::FilePath::StringType::size_type nul_pos =
160 ext.find(FILE_PATH_LITERAL('\0'));
161 if (nul_pos != base::FilePath::StringType::npos)
162 return false;
164 // We implement the same algorithm as Mozilla for mapping a file extension to
165 // a mime type. That is, we first check a hard-coded list (that cannot be
166 // overridden), and then if not found there, we defer to the system registry.
167 // Finally, we scan a secondary hard-coded list to catch types that we can
168 // deduce but that we also want to allow the OS to override.
170 base::FilePath path_ext(ext);
171 const string ext_narrow_str = path_ext.AsUTF8Unsafe();
172 const char* mime_type = FindMimeType(
173 primary_mappings, arraysize(primary_mappings), ext_narrow_str);
174 if (mime_type) {
175 *result = mime_type;
176 return true;
179 if (include_platform_types && GetPlatformMimeTypeFromExtension(ext, result))
180 return true;
182 mime_type = FindMimeType(secondary_mappings, arraysize(secondary_mappings),
183 ext_narrow_str);
184 if (mime_type) {
185 *result = mime_type;
186 return true;
189 return false;
192 MimeUtil::MimeUtil() {
195 // Tests for MIME parameter equality. Each parameter in the |mime_type_pattern|
196 // must be matched by a parameter in the |mime_type|. If there are no
197 // parameters in the pattern, the match is a success.
199 // According rfc2045 keys of parameters are case-insensitive, while values may
200 // or may not be case-sensitive, but they are usually case-sensitive. So, this
201 // function matches values in *case-sensitive* manner, however note that this
202 // may produce some false negatives.
203 bool MatchesMimeTypeParameters(const std::string& mime_type_pattern,
204 const std::string& mime_type) {
205 typedef std::map<std::string, std::string> StringPairMap;
207 const std::string::size_type semicolon = mime_type_pattern.find(';');
208 const std::string::size_type test_semicolon = mime_type.find(';');
209 if (semicolon != std::string::npos) {
210 if (test_semicolon == std::string::npos)
211 return false;
213 base::StringPairs pattern_parameters;
214 base::SplitStringIntoKeyValuePairs(mime_type_pattern.substr(semicolon + 1),
215 '=', ';', &pattern_parameters);
216 base::StringPairs test_parameters;
217 base::SplitStringIntoKeyValuePairs(mime_type.substr(test_semicolon + 1),
218 '=', ';', &test_parameters);
220 // Put the parameters to maps with the keys converted to lower case.
221 StringPairMap pattern_parameter_map;
222 for (const auto& pair : pattern_parameters) {
223 pattern_parameter_map[base::StringToLowerASCII(pair.first)] = pair.second;
226 StringPairMap test_parameter_map;
227 for (const auto& pair : test_parameters) {
228 test_parameter_map[base::StringToLowerASCII(pair.first)] = pair.second;
231 if (pattern_parameter_map.size() > test_parameter_map.size())
232 return false;
234 for (const auto& parameter_pair : pattern_parameter_map) {
235 const auto& test_parameter_pair_it =
236 test_parameter_map.find(parameter_pair.first);
237 if (test_parameter_pair_it == test_parameter_map.end())
238 return false;
239 if (parameter_pair.second != test_parameter_pair_it->second)
240 return false;
244 return true;
247 // This comparison handles absolute maching and also basic
248 // wildcards. The plugin mime types could be:
249 // application/x-foo
250 // application/*
251 // application/*+xml
252 // *
253 // Also tests mime parameters -- all parameters in the pattern must be present
254 // in the tested type for a match to succeed.
255 bool MimeUtil::MatchesMimeType(const std::string& mime_type_pattern,
256 const std::string& mime_type) const {
257 if (mime_type_pattern.empty())
258 return false;
260 std::string::size_type semicolon = mime_type_pattern.find(';');
261 const std::string base_pattern(mime_type_pattern.substr(0, semicolon));
262 semicolon = mime_type.find(';');
263 const std::string base_type(mime_type.substr(0, semicolon));
265 if (base_pattern == "*" || base_pattern == "*/*")
266 return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
268 const std::string::size_type star = base_pattern.find('*');
269 if (star == std::string::npos) {
270 if (base_pattern.size() == base_type.size() &&
271 base::strncasecmp(base_pattern.data(), base_type.data(),
272 base_pattern.size()) == 0) {
273 return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
274 } else {
275 return false;
279 // Test length to prevent overlap between |left| and |right|.
280 if (base_type.length() < base_pattern.length() - 1)
281 return false;
283 const std::string left(base_pattern.substr(0, star));
284 const std::string right(base_pattern.substr(star + 1));
286 if (!base::StartsWithASCII(base_type, left, false))
287 return false;
289 if (!right.empty() && !base::EndsWith(base_type, right, false))
290 return false;
292 return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
295 // See http://www.iana.org/assignments/media-types/media-types.xhtml
296 static const char* const legal_top_level_types[] = {
297 "application",
298 "audio",
299 "example",
300 "image",
301 "message",
302 "model",
303 "multipart",
304 "text",
305 "video",
308 bool MimeUtil::ParseMimeTypeWithoutParameter(
309 const std::string& type_string,
310 std::string* top_level_type,
311 std::string* subtype) const {
312 std::vector<std::string> components;
313 base::SplitString(type_string, '/', &components);
314 if (components.size() != 2 ||
315 !HttpUtil::IsToken(components[0]) ||
316 !HttpUtil::IsToken(components[1]))
317 return false;
319 if (top_level_type)
320 *top_level_type = components[0];
321 if (subtype)
322 *subtype = components[1];
323 return true;
326 bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const {
327 std::string lower_type = base::StringToLowerASCII(type_string);
328 for (size_t i = 0; i < arraysize(legal_top_level_types); ++i) {
329 if (lower_type.compare(legal_top_level_types[i]) == 0)
330 return true;
333 return type_string.size() > 2 &&
334 base::StartsWithASCII(type_string, "x-", false);
337 //----------------------------------------------------------------------------
338 // Wrappers for the singleton
339 //----------------------------------------------------------------------------
341 bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
342 std::string* mime_type) {
343 return g_mime_util.Get().GetMimeTypeFromExtension(ext, mime_type);
346 bool GetMimeTypeFromFile(const base::FilePath& file_path,
347 std::string* mime_type) {
348 return g_mime_util.Get().GetMimeTypeFromFile(file_path, mime_type);
351 bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext,
352 std::string* mime_type) {
353 return g_mime_util.Get().GetWellKnownMimeTypeFromExtension(ext, mime_type);
356 bool GetPreferredExtensionForMimeType(const std::string& mime_type,
357 base::FilePath::StringType* extension) {
358 return g_mime_util.Get().GetPreferredExtensionForMimeType(mime_type,
359 extension);
362 bool MatchesMimeType(const std::string& mime_type_pattern,
363 const std::string& mime_type) {
364 return g_mime_util.Get().MatchesMimeType(mime_type_pattern, mime_type);
367 bool ParseMimeTypeWithoutParameter(const std::string& type_string,
368 std::string* top_level_type,
369 std::string* subtype) {
370 return g_mime_util.Get().ParseMimeTypeWithoutParameter(
371 type_string, top_level_type, subtype);
374 bool IsValidTopLevelMimeType(const std::string& type_string) {
375 return g_mime_util.Get().IsValidTopLevelMimeType(type_string);
378 namespace {
380 // From http://www.w3schools.com/media/media_mimeref.asp and
381 // http://plugindoc.mozdev.org/winmime.php
382 static const char* const kStandardImageTypes[] = {
383 "image/bmp",
384 "image/cis-cod",
385 "image/gif",
386 "image/ief",
387 "image/jpeg",
388 "image/webp",
389 "image/pict",
390 "image/pipeg",
391 "image/png",
392 "image/svg+xml",
393 "image/tiff",
394 "image/vnd.microsoft.icon",
395 "image/x-cmu-raster",
396 "image/x-cmx",
397 "image/x-icon",
398 "image/x-portable-anymap",
399 "image/x-portable-bitmap",
400 "image/x-portable-graymap",
401 "image/x-portable-pixmap",
402 "image/x-rgb",
403 "image/x-xbitmap",
404 "image/x-xpixmap",
405 "image/x-xwindowdump"
407 static const char* const kStandardAudioTypes[] = {
408 "audio/aac",
409 "audio/aiff",
410 "audio/amr",
411 "audio/basic",
412 "audio/midi",
413 "audio/mp3",
414 "audio/mp4",
415 "audio/mpeg",
416 "audio/mpeg3",
417 "audio/ogg",
418 "audio/vorbis",
419 "audio/wav",
420 "audio/webm",
421 "audio/x-m4a",
422 "audio/x-ms-wma",
423 "audio/vnd.rn-realaudio",
424 "audio/vnd.wave"
426 static const char* const kStandardVideoTypes[] = {
427 "video/avi",
428 "video/divx",
429 "video/flc",
430 "video/mp4",
431 "video/mpeg",
432 "video/ogg",
433 "video/quicktime",
434 "video/sd-video",
435 "video/webm",
436 "video/x-dv",
437 "video/x-m4v",
438 "video/x-mpeg",
439 "video/x-ms-asf",
440 "video/x-ms-wmv"
443 struct StandardType {
444 const char* const leading_mime_type;
445 const char* const* standard_types;
446 size_t standard_types_len;
448 static const StandardType kStandardTypes[] = {
449 { "image/", kStandardImageTypes, arraysize(kStandardImageTypes) },
450 { "audio/", kStandardAudioTypes, arraysize(kStandardAudioTypes) },
451 { "video/", kStandardVideoTypes, arraysize(kStandardVideoTypes) },
452 { NULL, NULL, 0 }
455 void GetExtensionsFromHardCodedMappings(
456 const MimeInfo* mappings,
457 size_t mappings_len,
458 const std::string& leading_mime_type,
459 base::hash_set<base::FilePath::StringType>* extensions) {
460 for (size_t i = 0; i < mappings_len; ++i) {
461 if (base::StartsWithASCII(mappings[i].mime_type, leading_mime_type,
462 false)) {
463 std::vector<string> this_extensions;
464 base::SplitString(mappings[i].extensions, ',', &this_extensions);
465 for (size_t j = 0; j < this_extensions.size(); ++j) {
466 #if defined(OS_WIN)
467 base::FilePath::StringType extension(
468 base::UTF8ToWide(this_extensions[j]));
469 #else
470 base::FilePath::StringType extension(this_extensions[j]);
471 #endif
472 extensions->insert(extension);
478 void GetExtensionsHelper(
479 const char* const* standard_types,
480 size_t standard_types_len,
481 const std::string& leading_mime_type,
482 base::hash_set<base::FilePath::StringType>* extensions) {
483 for (size_t i = 0; i < standard_types_len; ++i) {
484 g_mime_util.Get().GetPlatformExtensionsForMimeType(standard_types[i],
485 extensions);
488 // Also look up the extensions from hard-coded mappings in case that some
489 // supported extensions are not registered in the system registry, like ogg.
490 GetExtensionsFromHardCodedMappings(primary_mappings,
491 arraysize(primary_mappings),
492 leading_mime_type,
493 extensions);
495 GetExtensionsFromHardCodedMappings(secondary_mappings,
496 arraysize(secondary_mappings),
497 leading_mime_type,
498 extensions);
501 // Note that the elements in the source set will be appended to the target
502 // vector.
503 template<class T>
504 void HashSetToVector(base::hash_set<T>* source, std::vector<T>* target) {
505 size_t old_target_size = target->size();
506 target->resize(old_target_size + source->size());
507 size_t i = 0;
508 for (typename base::hash_set<T>::iterator iter = source->begin();
509 iter != source->end(); ++iter, ++i)
510 (*target)[old_target_size + i] = *iter;
513 } // namespace
515 void GetExtensionsForMimeType(
516 const std::string& unsafe_mime_type,
517 std::vector<base::FilePath::StringType>* extensions) {
518 if (unsafe_mime_type == "*/*" || unsafe_mime_type == "*")
519 return;
521 const std::string mime_type = base::StringToLowerASCII(unsafe_mime_type);
522 base::hash_set<base::FilePath::StringType> unique_extensions;
524 if (base::EndsWith(mime_type, "/*", false)) {
525 std::string leading_mime_type = mime_type.substr(0, mime_type.length() - 1);
527 // Find the matching StandardType from within kStandardTypes, or fall
528 // through to the last (default) StandardType.
529 const StandardType* type = NULL;
530 for (size_t i = 0; i < arraysize(kStandardTypes); ++i) {
531 type = &(kStandardTypes[i]);
532 if (type->leading_mime_type &&
533 leading_mime_type == type->leading_mime_type)
534 break;
536 DCHECK(type);
537 GetExtensionsHelper(type->standard_types,
538 type->standard_types_len,
539 leading_mime_type,
540 &unique_extensions);
541 } else {
542 g_mime_util.Get().GetPlatformExtensionsForMimeType(mime_type,
543 &unique_extensions);
545 // Also look up the extensions from hard-coded mappings in case that some
546 // supported extensions are not registered in the system registry, like ogg.
547 GetExtensionsFromHardCodedMappings(primary_mappings,
548 arraysize(primary_mappings),
549 mime_type,
550 &unique_extensions);
552 GetExtensionsFromHardCodedMappings(secondary_mappings,
553 arraysize(secondary_mappings),
554 mime_type,
555 &unique_extensions);
558 HashSetToVector(&unique_extensions, extensions);
561 void AddMultipartValueForUpload(const std::string& value_name,
562 const std::string& value,
563 const std::string& mime_boundary,
564 const std::string& content_type,
565 std::string* post_data) {
566 DCHECK(post_data);
567 // First line is the boundary.
568 post_data->append("--" + mime_boundary + "\r\n");
569 // Next line is the Content-disposition.
570 post_data->append("Content-Disposition: form-data; name=\"" +
571 value_name + "\"\r\n");
572 if (!content_type.empty()) {
573 // If Content-type is specified, the next line is that.
574 post_data->append("Content-Type: " + content_type + "\r\n");
576 // Leave an empty line and append the value.
577 post_data->append("\r\n" + value + "\r\n");
580 void AddMultipartFinalDelimiterForUpload(const std::string& mime_boundary,
581 std::string* post_data) {
582 DCHECK(post_data);
583 post_data->append("--" + mime_boundary + "--\r\n");
586 } // namespace net