|
@@ -11,15 +11,16 @@
|
|
|
#include <AK/String.h>
|
|
|
#include <AK/StringBuilder.h>
|
|
|
#include <LibWeb/Fetch/Infrastructure/HTTP.h>
|
|
|
+#include <LibWeb/Infra/Strings.h>
|
|
|
#include <LibWeb/MimeSniff/MimeType.h>
|
|
|
|
|
|
namespace Web::MimeSniff {
|
|
|
|
|
|
// https://mimesniff.spec.whatwg.org/#javascript-mime-type-essence-match
|
|
|
-bool is_javascript_mime_type_essence_match(DeprecatedString const& string)
|
|
|
+bool is_javascript_mime_type_essence_match(StringView string)
|
|
|
{
|
|
|
// NOTE: The mime type parser automatically lowercases the essence.
|
|
|
- auto type = MimeType::parse(string);
|
|
|
+ auto type = MimeType::parse(string).release_value_but_fixme_should_propagate_errors();
|
|
|
if (!type.has_value())
|
|
|
return false;
|
|
|
return type->is_javascript();
|
|
@@ -38,7 +39,7 @@ static bool contains_only_http_quoted_string_token_code_points(StringView string
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-MimeType::MimeType(DeprecatedString type, DeprecatedString subtype)
|
|
|
+MimeType::MimeType(String type, String subtype)
|
|
|
: m_type(move(type))
|
|
|
, m_subtype(move(subtype))
|
|
|
{
|
|
@@ -63,8 +64,15 @@ static bool contains_only_http_token_code_points(StringView string)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ErrorOr<MimeType> MimeType::create(String type, String value)
|
|
|
+{
|
|
|
+ auto mime_type = MimeType { move(type), move(value) };
|
|
|
+ mime_type.m_cached_essence = TRY(String::formatted("{}/{}", mime_type.m_type, mime_type.m_subtype));
|
|
|
+ return mime_type;
|
|
|
+}
|
|
|
+
|
|
|
// https://mimesniff.spec.whatwg.org/#parse-a-mime-type
|
|
|
-Optional<MimeType> MimeType::parse(StringView string)
|
|
|
+ErrorOr<Optional<MimeType>> MimeType::parse(StringView string)
|
|
|
{
|
|
|
// 1. Remove any leading and trailing HTTP whitespace from input.
|
|
|
auto trimmed_string = string.trim(Fetch::Infrastructure::HTTP_WHITESPACE, TrimMode::Both);
|
|
@@ -77,11 +85,11 @@ Optional<MimeType> MimeType::parse(StringView string)
|
|
|
|
|
|
// 4. If type is the empty string or does not solely contain HTTP token code points, then return failure.
|
|
|
if (type.is_empty() || !contains_only_http_token_code_points(type))
|
|
|
- return {};
|
|
|
+ return OptionalNone {};
|
|
|
|
|
|
// 5. If position is past the end of input, then return failure.
|
|
|
if (lexer.is_eof())
|
|
|
- return {};
|
|
|
+ return OptionalNone {};
|
|
|
|
|
|
// 6. Advance position by 1. (This skips past U+002F (/).)
|
|
|
lexer.ignore(1);
|
|
@@ -94,10 +102,10 @@ Optional<MimeType> MimeType::parse(StringView string)
|
|
|
|
|
|
// 9. If subtype is the empty string or does not solely contain HTTP token code points, then return failure.
|
|
|
if (subtype.is_empty() || !contains_only_http_token_code_points(subtype))
|
|
|
- return {};
|
|
|
+ return OptionalNone {};
|
|
|
|
|
|
// 10. Let mimeType be a new MIME type record whose type is type, in ASCII lowercase, and subtype is subtype, in ASCII lowercase.
|
|
|
- auto mime_type = MimeType(type.to_lowercase_string(), subtype.to_lowercase_string());
|
|
|
+ auto mime_type = TRY(MimeType::create(TRY(Infra::to_ascii_lower_case(type)), TRY(Infra::to_ascii_lower_case(subtype))));
|
|
|
|
|
|
// 11. While position is not past the end of input:
|
|
|
while (!lexer.is_eof()) {
|
|
@@ -108,13 +116,12 @@ Optional<MimeType> MimeType::parse(StringView string)
|
|
|
lexer.ignore_while(is_any_of(Fetch::Infrastructure::HTTP_WHITESPACE));
|
|
|
|
|
|
// 3. Let parameterName be the result of collecting a sequence of code points that are not U+003B (;) or U+003D (=) from input, given position.
|
|
|
- auto parameter_name = lexer.consume_until([](char ch) {
|
|
|
+ auto parameter_name_view = lexer.consume_until([](char ch) {
|
|
|
return ch == ';' || ch == '=';
|
|
|
});
|
|
|
|
|
|
// 4. Set parameterName to parameterName, in ASCII lowercase.
|
|
|
- // NOTE: Reassigning to parameter_name here causes a UAF when trying to use parameter_name down the road.
|
|
|
- auto lowercase_parameter_name = parameter_name.to_lowercase_string();
|
|
|
+ auto parameter_name = TRY(Infra::to_ascii_lower_case(parameter_name_view));
|
|
|
|
|
|
// 5. If position is not past the end of input, then:
|
|
|
if (!lexer.is_eof()) {
|
|
@@ -132,12 +139,12 @@ Optional<MimeType> MimeType::parse(StringView string)
|
|
|
break;
|
|
|
|
|
|
// 7. Let parameterValue be null.
|
|
|
- DeprecatedString parameter_value;
|
|
|
+ String parameter_value;
|
|
|
|
|
|
// 8. If the code point at position within input is U+0022 ("), then:
|
|
|
if (lexer.peek() == '"') {
|
|
|
// 1. Set parameterValue to the result of collecting an HTTP quoted string from input, given position and the extract-value flag.
|
|
|
- parameter_value = Fetch::Infrastructure::collect_an_http_quoted_string(lexer, Fetch::Infrastructure::HttpQuotedStringExtractValue::Yes).release_value_but_fixme_should_propagate_errors().to_deprecated_string();
|
|
|
+ parameter_value = TRY(Fetch::Infrastructure::collect_an_http_quoted_string(lexer, Fetch::Infrastructure::HttpQuotedStringExtractValue::Yes));
|
|
|
|
|
|
// 2. Collect a sequence of code points that are not U+003B (;) from input, given position.
|
|
|
lexer.ignore_until(';');
|
|
@@ -146,10 +153,10 @@ Optional<MimeType> MimeType::parse(StringView string)
|
|
|
// 9. Otherwise:
|
|
|
else {
|
|
|
// 1. Set parameterValue to the result of collecting a sequence of code points that are not U+003B (;) from input, given position.
|
|
|
- parameter_value = lexer.consume_until(';');
|
|
|
+ parameter_value = TRY(String::from_utf8(lexer.consume_until(';')));
|
|
|
|
|
|
// 2. Remove any trailing HTTP whitespace from parameterValue.
|
|
|
- parameter_value = parameter_value.trim(Fetch::Infrastructure::HTTP_WHITESPACE, TrimMode::Right);
|
|
|
+ parameter_value = TRY(parameter_value.trim(Fetch::Infrastructure::HTTP_WHITESPACE, TrimMode::Right));
|
|
|
|
|
|
// 3. If parameterValue is the empty string, then continue.
|
|
|
if (parameter_value.is_empty())
|
|
@@ -157,77 +164,78 @@ Optional<MimeType> MimeType::parse(StringView string)
|
|
|
}
|
|
|
|
|
|
// 10. If all of the following are true
|
|
|
- // - parameterName is not the empty string
|
|
|
- // - parameterName solely contains HTTP token code points
|
|
|
- // - parameterValue solely contains HTTP quoted-string token code points
|
|
|
- // - mimeType’s parameters[parameterName] does not exist
|
|
|
- // then set mimeType’s parameters[parameterName] to parameterValue.
|
|
|
- if (!parameter_name.is_empty()
|
|
|
- && contains_only_http_token_code_points(lowercase_parameter_name)
|
|
|
+ if (
|
|
|
+ // - parameterName is not the empty string
|
|
|
+ !parameter_name.is_empty()
|
|
|
+ // - parameterName solely contains HTTP token code points
|
|
|
+ && contains_only_http_token_code_points(parameter_name)
|
|
|
+ // - parameterValue solely contains HTTP quoted-string token code points
|
|
|
&& contains_only_http_quoted_string_token_code_points(parameter_value)
|
|
|
- && !mime_type.m_parameters.contains(lowercase_parameter_name)) {
|
|
|
- mime_type.m_parameters.set(lowercase_parameter_name, parameter_value);
|
|
|
+ // - mimeType’s parameters[parameterName] does not exist
|
|
|
+ && !mime_type.m_parameters.contains(parameter_name)) {
|
|
|
+ // then set mimeType’s parameters[parameterName] to parameterValue.
|
|
|
+ TRY(mime_type.m_parameters.try_set(move(parameter_name), move(parameter_value)));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 12. Return mimeType.
|
|
|
- return Optional<MimeType> { move(mime_type) };
|
|
|
+ return mime_type;
|
|
|
}
|
|
|
|
|
|
// https://mimesniff.spec.whatwg.org/#mime-type-essence
|
|
|
-DeprecatedString MimeType::essence() const
|
|
|
+String const& MimeType::essence() const
|
|
|
{
|
|
|
// The essence of a MIME type mimeType is mimeType’s type, followed by U+002F (/), followed by mimeType’s subtype.
|
|
|
- // FIXME: I believe this can easily be cached as I don't think anything directly changes the type and subtype.
|
|
|
- return DeprecatedString::formatted("{}/{}", m_type, m_subtype);
|
|
|
+ return m_cached_essence;
|
|
|
}
|
|
|
|
|
|
// https://mimesniff.spec.whatwg.org/#serialize-a-mime-type
|
|
|
-DeprecatedString MimeType::serialized() const
|
|
|
+ErrorOr<String> MimeType::serialized() const
|
|
|
{
|
|
|
// 1. Let serialization be the concatenation of mimeType’s type, U+002F (/), and mimeType’s subtype.
|
|
|
StringBuilder serialization;
|
|
|
- serialization.append(m_type);
|
|
|
- serialization.append('/');
|
|
|
- serialization.append(m_subtype);
|
|
|
+ TRY(serialization.try_append(m_type));
|
|
|
+ TRY(serialization.try_append('/'));
|
|
|
+ TRY(serialization.try_append(m_subtype));
|
|
|
|
|
|
// 2. For each name → value of mimeType’s parameters:
|
|
|
for (auto [name, value] : m_parameters) {
|
|
|
// 1. Append U+003B (;) to serialization.
|
|
|
- serialization.append(';');
|
|
|
+ TRY(serialization.try_append(';'));
|
|
|
|
|
|
// 2. Append name to serialization.
|
|
|
- serialization.append(name);
|
|
|
+ TRY(serialization.try_append(name));
|
|
|
|
|
|
// 3. Append U+003D (=) to serialization.
|
|
|
- serialization.append('=');
|
|
|
+ TRY(serialization.try_append('='));
|
|
|
|
|
|
// 4. If value does not solely contain HTTP token code points or value is the empty string, then:
|
|
|
if (!contains_only_http_token_code_points(value) || value.is_empty()) {
|
|
|
// 1. Precede each occurrence of U+0022 (") or U+005C (\) in value with U+005C (\).
|
|
|
- value = value.replace("\\"sv, "\\\\"sv, ReplaceMode::All);
|
|
|
- value = value.replace("\""sv, "\\\""sv, ReplaceMode::All);
|
|
|
+ value = TRY(value.replace("\\"sv, "\\\\"sv, ReplaceMode::All));
|
|
|
+ value = TRY(value.replace("\""sv, "\\\""sv, ReplaceMode::All));
|
|
|
|
|
|
// 2. Prepend U+0022 (") to value.
|
|
|
// 3. Append U+0022 (") to value.
|
|
|
- value = DeprecatedString::formatted("\"{}\"", value);
|
|
|
+ value = TRY(String::formatted("\"{}\"", value));
|
|
|
}
|
|
|
|
|
|
// 5. Append value to serialization.
|
|
|
- serialization.append(value);
|
|
|
+ TRY(serialization.try_append(value));
|
|
|
}
|
|
|
|
|
|
// 3. Return serialization.
|
|
|
- return serialization.to_deprecated_string();
|
|
|
+ return serialization.to_string();
|
|
|
}
|
|
|
|
|
|
-void MimeType::set_parameter(DeprecatedString const& name, DeprecatedString const& value)
|
|
|
+ErrorOr<void> MimeType::set_parameter(String name, String value)
|
|
|
{
|
|
|
// https://mimesniff.spec.whatwg.org/#parameters
|
|
|
// A MIME type’s parameters is an ordered map whose keys are ASCII strings and values are strings limited to HTTP quoted-string token code points.
|
|
|
VERIFY(contains_only_http_quoted_string_token_code_points(name));
|
|
|
VERIFY(contains_only_http_quoted_string_token_code_points(value));
|
|
|
- m_parameters.set(name, value);
|
|
|
+ TRY(m_parameters.try_set(move(name), move(value)));
|
|
|
+ return {};
|
|
|
}
|
|
|
|
|
|
// https://mimesniff.spec.whatwg.org/#javascript-mime-type
|