1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:58:12 +00:00

LibWeb/MimeSniff: Port MimeType to new String

This commit is contained in:
Linus Groh 2023-03-03 09:27:51 +00:00
parent fabea2a6a7
commit 2d7ce38ee2
7 changed files with 86 additions and 71 deletions

View file

@ -108,7 +108,7 @@ WebIDL::ExceptionOr<JS::Value> package_data(JS::Realm& realm, ByteBuffer bytes,
case PackageDataType::Blob: { case PackageDataType::Blob: {
// Return a Blob whose contents are bytes and type attribute is mimeType. // Return a Blob whose contents are bytes and type attribute is mimeType.
// NOTE: If extracting the mime type returns failure, other browsers set it to an empty string - not sure if that's spec'd. // NOTE: If extracting the mime type returns failure, other browsers set it to an empty string - not sure if that's spec'd.
auto mime_type_string = mime_type.has_value() ? TRY_OR_THROW_OOM(vm, String::from_deprecated_string(mime_type->serialized())) : String {}; auto mime_type_string = mime_type.has_value() ? TRY_OR_THROW_OOM(vm, mime_type->serialized()) : String {};
return TRY(FileAPI::Blob::create(realm, move(bytes), move(mime_type_string))); return TRY(FileAPI::Blob::create(realm, move(bytes), move(mime_type_string)));
} }
case PackageDataType::FormData: case PackageDataType::FormData:

View file

@ -322,7 +322,7 @@ Optional<MimeSniff::MimeType> HeaderList::extract_mime_type() const
// 6. For each value of values: // 6. For each value of values:
for (auto const& value : *values) { for (auto const& value : *values) {
// 1. Let temporaryMimeType be the result of parsing value. // 1. Let temporaryMimeType be the result of parsing value.
auto temporary_mime_type = MimeSniff::MimeType::parse(value); auto temporary_mime_type = MimeSniff::MimeType::parse(value).release_value_but_fixme_should_propagate_errors();
// 2. If temporaryMimeType is failure or its essence is "*/*", then continue. // 2. If temporaryMimeType is failure or its essence is "*/*", then continue.
if (!temporary_mime_type.has_value() || temporary_mime_type->essence() == "*/*"sv) if (!temporary_mime_type.has_value() || temporary_mime_type->essence() == "*/*"sv)
@ -339,14 +339,14 @@ Optional<MimeSniff::MimeType> HeaderList::extract_mime_type() const
// 2. If mimeTypes parameters["charset"] exists, then set charset to mimeTypes parameters["charset"]. // 2. If mimeTypes parameters["charset"] exists, then set charset to mimeTypes parameters["charset"].
auto it = mime_type->parameters().find("charset"sv); auto it = mime_type->parameters().find("charset"sv);
if (it != mime_type->parameters().end()) if (it != mime_type->parameters().end())
charset = String::from_deprecated_string(it->value).release_value_but_fixme_should_propagate_errors(); charset = it->value;
// 3. Set essence to mimeTypes essence. // 3. Set essence to mimeTypes essence.
essence = String::from_deprecated_string(mime_type->essence()).release_value_but_fixme_should_propagate_errors(); essence = mime_type->essence();
} }
// 5. Otherwise, if mimeTypes parameters["charset"] does not exist, and charset is non-null, set mimeTypes parameters["charset"] to charset. // 5. Otherwise, if mimeTypes parameters["charset"] does not exist, and charset is non-null, set mimeTypes parameters["charset"] to charset.
else if (!mime_type->parameters().contains("charset"sv) && charset.has_value()) { else if (!mime_type->parameters().contains("charset"sv) && charset.has_value()) {
mime_type->set_parameter("charset"sv, charset->to_deprecated_string()); mime_type->set_parameter("charset"_string.release_value_but_fixme_should_propagate_errors(), charset.release_value()).release_value_but_fixme_should_propagate_errors();
} }
} }
@ -457,7 +457,7 @@ bool is_cors_safelisted_request_header(Header const& header)
return false; return false;
// 2. Let mimeType be the result of parsing the result of isomorphic decoding value. // 2. Let mimeType be the result of parsing the result of isomorphic decoding value.
auto mime_type = MimeSniff::MimeType::parse(StringView { value }); auto mime_type = MimeSniff::MimeType::parse(StringView { value }).release_value_but_fixme_should_propagate_errors();
// 3. If mimeType is failure, then return false. // 3. If mimeType is failure, then return false.
if (!mime_type.has_value()) if (!mime_type.has_value())

View file

@ -24,7 +24,7 @@ RequestOrResponseBlocking should_response_to_request_be_blocked_due_to_its_mime_
// 4. If destination is script-like and one of the following is true, then return blocked: // 4. If destination is script-like and one of the following is true, then return blocked:
if (request.destination_is_script_like() && ( if (request.destination_is_script_like() && (
// - mimeTypes essence starts with "audio/", "image/", or "video/". // - mimeTypes essence starts with "audio/", "image/", or "video/".
any_of(Array { "audio/"sv, "image/"sv, "video/"sv }, [&](auto prefix) { return mime_type->essence().starts_with(prefix); }) any_of(Array { "audio/"sv, "image/"sv, "video/"sv }, [&](auto prefix) { return mime_type->essence().starts_with_bytes(prefix); })
// - mimeTypes essence is "text/csv". // - mimeTypes essence is "text/csv".
|| mime_type->essence() == "text/csv"sv)) { || mime_type->essence() == "text/csv"sv)) {
return RequestOrResponseBlocking::Blocked; return RequestOrResponseBlocking::Blocked;

View file

@ -218,12 +218,12 @@ void HTMLObjectElement::resource_did_load()
static bool is_xml_mime_type(StringView resource_type) static bool is_xml_mime_type(StringView resource_type)
{ {
auto mime_type = MimeSniff::MimeType::parse(resource_type); auto mime_type = MimeSniff::MimeType::parse(resource_type).release_value_but_fixme_should_propagate_errors();
if (!mime_type.has_value()) if (!mime_type.has_value())
return false; return false;
// An XML MIME type is any MIME type whose subtype ends in "+xml" or whose essence is "text/xml" or "application/xml". [RFC7303] // An XML MIME type is any MIME type whose subtype ends in "+xml" or whose essence is "text/xml" or "application/xml". [RFC7303]
if (mime_type->subtype().ends_with("+xml"sv)) if (mime_type->subtype().ends_with_bytes("+xml"sv))
return true; return true;
return mime_type->essence().is_one_of("text/xml"sv, "application/xml"sv); return mime_type->essence().is_one_of("text/xml"sv, "application/xml"sv);

View file

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

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org> * Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org> * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -12,39 +12,44 @@
namespace Web::MimeSniff { namespace Web::MimeSniff {
bool is_javascript_mime_type_essence_match(DeprecatedString const&); bool is_javascript_mime_type_essence_match(StringView);
// https://mimesniff.spec.whatwg.org/#mime-type // https://mimesniff.spec.whatwg.org/#mime-type
class MimeType { class MimeType {
public: public:
static Optional<MimeType> parse(StringView); static ErrorOr<MimeType> create(String type, String subtype);
static ErrorOr<Optional<MimeType>> parse(StringView);
MimeType(DeprecatedString type, DeprecatedString subtype);
~MimeType(); ~MimeType();
DeprecatedString const& type() const { return m_type; } String const& type() const { return m_type; }
DeprecatedString const& subtype() const { return m_subtype; } String const& subtype() const { return m_subtype; }
OrderedHashMap<DeprecatedString, DeprecatedString> const& parameters() const { return m_parameters; } OrderedHashMap<String, String> const& parameters() const { return m_parameters; }
bool is_javascript() const; bool is_javascript() const;
void set_parameter(DeprecatedString const& name, DeprecatedString const& value); ErrorOr<void> set_parameter(String name, String value);
DeprecatedString essence() const; String const& essence() const;
DeprecatedString serialized() const; ErrorOr<String> serialized() const;
private: private:
MimeType(String type, String subtype);
// https://mimesniff.spec.whatwg.org/#type // https://mimesniff.spec.whatwg.org/#type
// A MIME types type is a non-empty ASCII string. // A MIME types type is a non-empty ASCII string.
DeprecatedString m_type; String m_type;
// https://mimesniff.spec.whatwg.org/#subtype // https://mimesniff.spec.whatwg.org/#subtype
// A MIME types subtype is a non-empty ASCII string. // A MIME types subtype is a non-empty ASCII string.
DeprecatedString m_subtype; String m_subtype;
// https://mimesniff.spec.whatwg.org/#parameters // https://mimesniff.spec.whatwg.org/#parameters
// A MIME types parameters is an ordered map whose keys are ASCII strings and values are strings limited to HTTP quoted-string token code points. It is initially empty. // A MIME types parameters is an ordered map whose keys are ASCII strings and values are strings limited to HTTP quoted-string token code points. It is initially empty.
OrderedHashMap<DeprecatedString, DeprecatedString> m_parameters; OrderedHashMap<String, String> m_parameters;
// Non-standard, but computed once upfront.
String m_cached_essence;
}; };
} }

View file

@ -161,7 +161,7 @@ WebIDL::ExceptionOr<JS::Value> XMLHttpRequest::response()
} }
// 6. Otherwise, if thiss response type is "blob", set thiss response object to a new Blob object representing thiss received bytes with type set to the result of get a final MIME type for this. // 6. Otherwise, if thiss response type is "blob", set thiss response object to a new Blob object representing thiss received bytes with type set to the result of get a final MIME type for this.
else if (m_response_type == Bindings::XMLHttpRequestResponseType::Blob) { else if (m_response_type == Bindings::XMLHttpRequestResponseType::Blob) {
auto mime_type_as_string = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(get_final_mime_type().serialized())); auto mime_type_as_string = TRY_OR_THROW_OOM(vm, get_final_mime_type().serialized());
auto blob_part = TRY(FileAPI::Blob::create(realm(), m_received_bytes, move(mime_type_as_string))); auto blob_part = TRY(FileAPI::Blob::create(realm(), m_received_bytes, move(mime_type_as_string)));
auto blob = TRY(FileAPI::Blob::create(realm(), Vector<FileAPI::BlobPart> { JS::make_handle(*blob_part) })); auto blob = TRY(FileAPI::Blob::create(realm(), Vector<FileAPI::BlobPart> { JS::make_handle(*blob_part) }));
m_response_object = JS::Value(blob.ptr()); m_response_object = JS::Value(blob.ptr());
@ -207,7 +207,7 @@ DeprecatedString XMLHttpRequest::get_text_response() const
if (mime_type.essence().is_one_of("text/xml"sv, "application/xml"sv)) if (mime_type.essence().is_one_of("text/xml"sv, "application/xml"sv))
return true; return true;
return mime_type.subtype().ends_with("+xml"sv); return mime_type.subtype().ends_with_bytes("+xml"sv);
}; };
// 3. If xhrs response type is the empty string, charset is null, and the result of get a final MIME type for xhr is an XML MIME type, // 3. If xhrs response type is the empty string, charset is null, and the result of get a final MIME type for xhr is an XML MIME type,
@ -256,7 +256,7 @@ MimeSniff::MimeType XMLHttpRequest::get_response_mime_type() const
// 2. If mimeType is failure, then set mimeType to text/xml. // 2. If mimeType is failure, then set mimeType to text/xml.
if (!mime_type.has_value()) if (!mime_type.has_value())
return MimeSniff::MimeType("text"sv, "xml"sv); return MimeSniff::MimeType::create("text"_string.release_value_but_fixme_should_propagate_errors(), "xml"_short_string).release_value_but_fixme_should_propagate_errors();
// 3. Return mimeType. // 3. Return mimeType.
return mime_type.release_value(); return mime_type.release_value();
@ -274,13 +274,13 @@ Optional<StringView> XMLHttpRequest::get_final_encoding() const
// 3. If responseMIMEs parameters["charset"] exists, then set label to it. // 3. If responseMIMEs parameters["charset"] exists, then set label to it.
auto response_mime_charset_it = response_mime.parameters().find("charset"sv); auto response_mime_charset_it = response_mime.parameters().find("charset"sv);
if (response_mime_charset_it != response_mime.parameters().end()) if (response_mime_charset_it != response_mime.parameters().end())
label = response_mime_charset_it->value; label = response_mime_charset_it->value.to_deprecated_string();
// 4. If xhrs override MIME types parameters["charset"] exists, then set label to it. // 4. If xhrs override MIME types parameters["charset"] exists, then set label to it.
if (m_override_mime_type.has_value()) { if (m_override_mime_type.has_value()) {
auto override_mime_charset_it = m_override_mime_type->parameters().find("charset"sv); auto override_mime_charset_it = m_override_mime_type->parameters().find("charset"sv);
if (override_mime_charset_it != m_override_mime_type->parameters().end()) if (override_mime_charset_it != m_override_mime_type->parameters().end())
label = override_mime_charset_it->value; label = override_mime_charset_it->value.to_deprecated_string();
} }
// 5. If label is null, then return null. // 5. If label is null, then return null.
@ -605,16 +605,18 @@ DeprecatedString XMLHttpRequest::get_all_response_headers() const
// https://xhr.spec.whatwg.org/#dom-xmlhttprequest-overridemimetype // https://xhr.spec.whatwg.org/#dom-xmlhttprequest-overridemimetype
WebIDL::ExceptionOr<void> XMLHttpRequest::override_mime_type(DeprecatedString const& mime) WebIDL::ExceptionOr<void> XMLHttpRequest::override_mime_type(DeprecatedString const& mime)
{ {
auto& vm = this->vm();
// 1. If thiss state is loading or done, then throw an "InvalidStateError" DOMException. // 1. If thiss state is loading or done, then throw an "InvalidStateError" DOMException.
if (m_state == State::Loading || m_state == State::Done) if (m_state == State::Loading || m_state == State::Done)
return WebIDL::InvalidStateError::create(realm(), "Cannot override MIME type when state is Loading or Done."); return WebIDL::InvalidStateError::create(realm(), "Cannot override MIME type when state is Loading or Done.");
// 2. Set thiss override MIME type to the result of parsing mime. // 2. Set thiss override MIME type to the result of parsing mime.
m_override_mime_type = MimeSniff::MimeType::parse(mime); m_override_mime_type = TRY_OR_THROW_OOM(vm, MimeSniff::MimeType::parse(mime));
// 3. If thiss override MIME type is failure, then set thiss override MIME type to application/octet-stream. // 3. If thiss override MIME type is failure, then set thiss override MIME type to application/octet-stream.
if (!m_override_mime_type.has_value()) if (!m_override_mime_type.has_value())
m_override_mime_type = MimeSniff::MimeType("application"sv, "octet-stream"sv); m_override_mime_type = TRY_OR_THROW_OOM(vm, MimeSniff::MimeType::create(TRY_OR_THROW_OOM(vm, "application"_string), TRY_OR_THROW_OOM(vm, "octet-stream"_string)));
return {}; return {};
} }