LibWeb: Make more MimeSniff::MimeType APIs infallible

This commit is contained in:
Andreas Kling 2024-10-14 11:06:43 +02:00 committed by Andreas Kling
parent 9a8db40a23
commit 88e7688940
Notes: github-actions[bot] 2024-10-14 18:48:50 +00:00
14 changed files with 55 additions and 56 deletions

View file

@ -18,7 +18,7 @@ TEST_CASE(data_url)
EXPECT_EQ(url.serialize(), "data:text/html,test");
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/html");
EXPECT_EQ(data_url.mime_type.serialized(), "text/html");
EXPECT_EQ(StringView(data_url.body.bytes()), "test"sv);
}
@ -31,7 +31,7 @@ TEST_CASE(data_url_default_mime_type)
EXPECT_EQ(url.serialize(), "data:,test");
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/plain;charset=US-ASCII");
EXPECT_EQ(data_url.mime_type.serialized(), "text/plain;charset=US-ASCII");
EXPECT_EQ(StringView(data_url.body.bytes()), "test"sv);
}
@ -44,7 +44,7 @@ TEST_CASE(data_url_encoded)
EXPECT_EQ(url.serialize(), "data:text/html,Hello%20friends%2C%0X%X0");
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/html");
EXPECT_EQ(data_url.mime_type.serialized(), "text/html");
EXPECT_EQ(StringView(data_url.body.bytes()), "Hello friends,%0X%X0"sv);
}
@ -57,7 +57,7 @@ TEST_CASE(data_url_base64_encoded)
EXPECT_EQ(url.serialize(), "data:text/html;base64,dGVzdA==");
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/html");
EXPECT_EQ(data_url.mime_type.serialized(), "text/html");
EXPECT_EQ(StringView(data_url.body.bytes()), "test"sv);
}
@ -70,7 +70,7 @@ TEST_CASE(data_url_base64_encoded_default_mime_type)
EXPECT_EQ(url.serialize(), "data:;base64,dGVzdA==");
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/plain;charset=US-ASCII");
EXPECT_EQ(data_url.mime_type.serialized(), "text/plain;charset=US-ASCII");
EXPECT_EQ(StringView(data_url.body.bytes()), "test"sv);
}
@ -83,7 +83,7 @@ TEST_CASE(data_url_base64_encoded_with_whitespace)
EXPECT_EQ(url.serialize(), "data: text/html ; bAsE64 , dGVz dA==");
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/html");
EXPECT_EQ(data_url.mime_type.serialized(), "text/html");
EXPECT_EQ(StringView(data_url.body.bytes()), "test");
}
@ -95,7 +95,7 @@ TEST_CASE(data_url_base64_encoded_with_inline_whitespace)
EXPECT(url.host().has<Empty>());
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/javascript");
EXPECT_EQ(data_url.mime_type.serialized(), "text/javascript");
EXPECT_EQ(StringView(data_url.body.bytes()), "d4 = 'four';"sv);
}
@ -108,6 +108,6 @@ TEST_CASE(data_url_completed_with_fragment)
EXPECT(url.host().has<Empty>());
auto data_url = TRY_OR_FAIL(Web::Fetch::Infrastructure::process_data_url(url));
EXPECT_EQ(TRY_OR_FAIL(data_url.mime_type.serialized()), "text/plain");
EXPECT_EQ(data_url.mime_type.serialized(), "text/plain");
EXPECT_EQ(StringView(data_url.body.bytes()), "test"sv);
}

View file

@ -15,7 +15,7 @@ TEST_CASE(determine_computed_mime_type_given_no_sniff_is_set)
auto mime_type = Web::MimeSniff::MimeType::create("text"_string, "html"_string);
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff("\x00"sv.bytes(), Web::MimeSniff::SniffingConfiguration { .supplied_type = mime_type, .no_sniff = true }));
EXPECT_EQ("text/html"sv, MUST(computed_mime_type.serialized()));
EXPECT_EQ("text/html"sv, computed_mime_type.serialized());
// Cover the edge case in the context-specific sniffing algorithm.
computed_mime_type = MUST(Web::MimeSniff::Resource::sniff("\x00"sv.bytes(), Web::MimeSniff::SniffingConfiguration {
@ -24,7 +24,7 @@ TEST_CASE(determine_computed_mime_type_given_no_sniff_is_set)
.no_sniff = true,
}));
EXPECT_EQ("text/html"sv, MUST(computed_mime_type.serialized()));
EXPECT_EQ("text/html"sv, computed_mime_type.serialized());
}
TEST_CASE(determine_computed_mime_type_given_no_sniff_is_unset)
@ -32,7 +32,7 @@ TEST_CASE(determine_computed_mime_type_given_no_sniff_is_unset)
auto supplied_type = Web::MimeSniff::MimeType::create("application"_string, "x-this-is-a-test"_string);
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff("\x00"sv.bytes(), Web::MimeSniff::SniffingConfiguration { .supplied_type = supplied_type }));
EXPECT_EQ("application/x-this-is-a-test"sv, MUST(computed_mime_type.serialized()));
EXPECT_EQ("application/x-this-is-a-test"sv, computed_mime_type.serialized());
}
TEST_CASE(determine_computed_mime_type_given_xml_mime_type_as_supplied_type)
@ -41,7 +41,7 @@ TEST_CASE(determine_computed_mime_type_given_xml_mime_type_as_supplied_type)
auto supplied_type = MUST(Web::MimeSniff::MimeType::parse(xml_mime_type)).release_value();
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff("\x00"sv.bytes(), Web::MimeSniff::SniffingConfiguration { .supplied_type = supplied_type }));
EXPECT_EQ(xml_mime_type, MUST(computed_mime_type.serialized()));
EXPECT_EQ(xml_mime_type, computed_mime_type.serialized());
}
static void set_image_type_mappings(HashMap<StringView, Vector<StringView>>& mime_type_to_headers_map)
@ -89,7 +89,7 @@ TEST_CASE(determine_computed_mime_type_given_supplied_type_that_is_an_apache_bug
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff("Hello world!"sv.bytes(),
Web::MimeSniff::SniffingConfiguration { .scheme = "http"sv, .supplied_type = supplied_type }));
EXPECT_EQ("text/plain"sv, MUST(computed_mime_type.serialized()));
EXPECT_EQ("text/plain"sv, computed_mime_type.serialized());
}
// Cover all code paths in "rules for distinguishing if a resource is text or binary".
@ -106,7 +106,7 @@ TEST_CASE(determine_computed_mime_type_given_supplied_type_that_is_an_apache_bug
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(header.bytes(),
Web::MimeSniff::SniffingConfiguration { .scheme = "http"sv, .supplied_type = supplied_type }));
EXPECT_EQ(mime_type, MUST(computed_mime_type.serialized()));
EXPECT_EQ(mime_type, computed_mime_type.serialized());
}
}
}
@ -116,12 +116,12 @@ TEST_CASE(determine_computed_mime_type_given_xml_or_html_supplied_type)
// With HTML supplied type.
auto config = Web::MimeSniff::SniffingConfiguration { .supplied_type = Web::MimeSniff::MimeType::create("text"_string, "html"_string) };
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(""sv.bytes(), config));
EXPECT_EQ("text/html"sv, MUST(computed_mime_type.serialized()));
EXPECT_EQ("text/html"sv, computed_mime_type.serialized());
// With XML supplied type.
config = Web::MimeSniff::SniffingConfiguration { .supplied_type = Web::MimeSniff::MimeType::create("text"_string, "xml"_string) };
computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(""sv.bytes(), config));
EXPECT_EQ("text/xml"sv, MUST(computed_mime_type.serialized()));
EXPECT_EQ("text/xml"sv, computed_mime_type.serialized());
}
TEST_CASE(determine_computed_mime_type_in_both_none_and_browsing_sniffing_context)
@ -198,7 +198,7 @@ TEST_CASE(determine_computed_mime_type_in_image_sniffing_context)
auto supplied_type = MUST(Web::MimeSniff::MimeType::parse(mime_type)).release_value();
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(""sv.bytes(), Web::MimeSniff::SniffingConfiguration { .sniffing_context = Web::MimeSniff::SniffingContext::Image, .supplied_type = supplied_type }));
EXPECT_EQ(mime_type, MUST(computed_mime_type.serialized()));
EXPECT_EQ(mime_type, computed_mime_type.serialized());
HashMap<StringView, Vector<StringView>> mime_type_to_headers_map;
@ -234,7 +234,7 @@ TEST_CASE(determine_computed_mime_type_in_audio_or_video_sniffing_context)
.supplied_type = supplied_type,
}));
EXPECT_EQ(mime_type, MUST(computed_mime_type.serialized()));
EXPECT_EQ(mime_type, computed_mime_type.serialized());
HashMap<StringView, Vector<StringView>> mime_type_to_headers_map;
set_audio_or_video_type_mappings(mime_type_to_headers_map);
@ -290,7 +290,7 @@ TEST_CASE(determine_computed_mime_type_when_trying_to_match_mp4_signature)
for (auto const& header : mime_type_to_headers.value) {
auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(header.bytes(), Web::MimeSniff::SniffingConfiguration { .sniffing_context = Web::MimeSniff::SniffingContext::AudioOrVideo }));
EXPECT_EQ(mime_type, MUST(computed_mime_type.serialized()));
EXPECT_EQ(mime_type, computed_mime_type.serialized());
}
}
}
@ -305,7 +305,7 @@ TEST_CASE(determine_computed_mime_type_in_a_font_context)
.supplied_type = supplied_type,
}));
EXPECT_EQ(mime_type, MUST(computed_mime_type.serialized()));
EXPECT_EQ(mime_type, computed_mime_type.serialized());
HashMap<StringView, Vector<StringView>> mime_type_to_headers_map;
mime_type_to_headers_map.set("application/octet-stream"sv, { "\x00"sv });
@ -343,7 +343,7 @@ TEST_CASE(determine_computed_mime_type_given_text_or_binary_context)
.sniffing_context = Web::MimeSniff::SniffingContext::TextOrBinary,
.supplied_type = supplied_type,
}));
EXPECT_EQ("application/octet-stream"sv, MUST(computed_mime_type.serialized()));
EXPECT_EQ("application/octet-stream"sv, computed_mime_type.serialized());
}
TEST_CASE(determine_minimised_mime_type)

View file

@ -126,7 +126,7 @@ WebIDL::ExceptionOr<JS::Value> package_data(JS::Realm& realm, ByteBuffer bytes,
case PackageDataType::Blob: {
// 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.
auto mime_type_string = mime_type.has_value() ? MUST(mime_type->serialized()) : String {};
auto mime_type_string = mime_type.has_value() ? mime_type->serialized() : String {};
return FileAPI::Blob::create(realm, move(bytes), move(mime_type_string));
}
case PackageDataType::Uint8Array: {

View file

@ -901,7 +901,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> scheme_fetch(JS::Realm& r
return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Failed to process 'data:' URL"sv));
// 3. Let mimeType be dataURLStructs MIME type, serialized.
auto const& mime_type = MUST(data_url_struct.value().mime_type.serialized());
auto const& mime_type = data_url_struct.value().mime_type.serialized();
// 4. Return a new response whose status message is `OK`, header list is « (`Content-Type`, mimeType) », and
// body is dataURLStructs body as a body.

View file

@ -389,7 +389,7 @@ Optional<MimeSniff::MimeType> HeaderList::extract_mime_type() const
}
// 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()) {
MUST(mime_type->set_parameter("charset"_string, charset.release_value()));
mime_type->set_parameter("charset"_string, charset.release_value());
}
}

View file

@ -102,7 +102,7 @@ ErrorOr<DataURL> process_data_url(URL::URL const& data_url)
// 14. If mimeTypeRecord is failure, then set mimeTypeRecord to text/plain;charset=US-ASCII.
if (!mime_type_record.has_value()) {
mime_type_record = MimeSniff::MimeType::create("text"_string, "plain"_string);
TRY(mime_type_record->set_parameter("charset"_string, "US-ASCII"_string));
mime_type_record->set_parameter("charset"_string, "US-ASCII"_string);
}
// 15. Return a new data: URL struct whose MIME type is mimeTypeRecord and body is body.

View file

@ -207,7 +207,7 @@ JS::NonnullGCPtr<Blob> Blob::create(JS::Realm& realm, Optional<Vector<BlobPart>>
auto maybe_parsed_type = MUST(Web::MimeSniff::MimeType::parse(options->type));
if (maybe_parsed_type.has_value())
type = MUST(maybe_parsed_type->serialized());
type = maybe_parsed_type->serialized();
}
}

View file

@ -65,7 +65,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<File>> File::create(JS::Realm& realm, Vecto
auto maybe_parsed_type = MUST(Web::MimeSniff::MimeType::parse(options->type));
if (maybe_parsed_type.has_value())
type = MUST(maybe_parsed_type->serialized());
type = maybe_parsed_type->serialized();
// 3. If the lastModified member is provided, let d be set to the lastModified dictionary member. If it is not provided, set d to the current date and time represented as the number of milliseconds since the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).
// Note: Since ECMA-262 Date objects convert to long long values representing the number of milliseconds since the Unix Epoch, the lastModified member could be a Date object [ECMA-262].

View file

@ -424,7 +424,7 @@ WebIDL::ExceptionOr<void> fetch_classic_worker_script(URL::URL const& url, Envir
auto mime_type_is_javascript = maybe_mime_type.has_value() && maybe_mime_type->is_javascript();
if (response->url().has_value() && Fetch::Infrastructure::is_http_or_https_scheme(response->url()->scheme()) && !mime_type_is_javascript) {
auto mime_type_serialized = maybe_mime_type.has_value() ? MUST(maybe_mime_type->serialized()) : "unknown"_string;
auto mime_type_serialized = maybe_mime_type.has_value() ? maybe_mime_type->serialized() : "unknown"_string;
dbgln("Invalid non-javascript mime type \"{}\" for worker script at {}", mime_type_serialized, response->url().value());
// then run onComplete given null, and abort these steps.

View file

@ -294,11 +294,11 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback
auto data_url = data_url_or_error.release_value();
dbgln_if(SPAM_DEBUG, "ResourceLoader loading a data URL with mime-type: '{}', payload='{}'",
MUST(data_url.mime_type.serialized()),
data_url.mime_type.serialized(),
StringView(data_url.body.bytes()));
HTTP::HeaderMap response_headers;
response_headers.set("Content-Type", MUST(data_url.mime_type.serialized()).to_byte_string());
response_headers.set("Content-Type", data_url.mime_type.serialized().to_byte_string());
log_success(request);

View file

@ -199,52 +199,51 @@ String const& MimeType::essence() const
}
// https://mimesniff.spec.whatwg.org/#serialize-a-mime-type
ErrorOr<String> MimeType::serialized() const
String MimeType::serialized() const
{
// 1. Let serialization be the concatenation of mimeTypes type, U+002F (/), and mimeTypes subtype.
StringBuilder serialization;
TRY(serialization.try_append(m_type));
TRY(serialization.try_append('/'));
TRY(serialization.try_append(m_subtype));
serialization.append(m_type);
serialization.append('/');
serialization.append(m_subtype);
// 2. For each name → value of mimeTypes parameters:
for (auto [name, value] : m_parameters) {
// 1. Append U+003B (;) to serialization.
TRY(serialization.try_append(';'));
serialization.append(';');
// 2. Append name to serialization.
TRY(serialization.try_append(name));
serialization.append(name);
// 3. Append U+003D (=) to serialization.
TRY(serialization.try_append('='));
serialization.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 = TRY(value.replace("\\"sv, "\\\\"sv, ReplaceMode::All));
value = TRY(value.replace("\""sv, "\\\""sv, ReplaceMode::All));
value = MUST(value.replace("\\"sv, "\\\\"sv, ReplaceMode::All));
value = MUST(value.replace("\""sv, "\\\""sv, ReplaceMode::All));
// 2. Prepend U+0022 (") to value.
// 3. Append U+0022 (") to value.
value = TRY(String::formatted("\"{}\"", value));
value = MUST(String::formatted("\"{}\"", value));
}
// 5. Append value to serialization.
TRY(serialization.try_append(value));
serialization.append(value);
}
// 3. Return serialization.
return serialization.to_string();
return serialization.to_string_without_validation();
}
ErrorOr<void> MimeType::set_parameter(String name, String value)
void MimeType::set_parameter(String name, String value)
{
// 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.
VERIFY(contains_only_http_quoted_string_token_code_points(name));
VERIFY(contains_only_http_quoted_string_token_code_points(value));
TRY(m_parameters.try_set(move(name), move(value)));
return {};
m_parameters.set(move(name), move(value));
}
// https://mimesniff.spec.whatwg.org/#image-mime-type

View file

@ -43,10 +43,10 @@ public:
bool is_javascript() const;
bool is_json() const;
ErrorOr<void> set_parameter(String name, String value);
void set_parameter(String name, String value);
String const& essence() const;
ErrorOr<String> serialized() const;
[[nodiscard]] String serialized() const;
private:
MimeType(String type, String subtype);

View file

@ -505,7 +505,7 @@ ErrorOr<void> Resource::supplied_mime_type_detection_algorithm(StringView scheme
"text/plain;charset=UTF-8"sv
};
auto serialized_supplied_type = TRY(supplied_type->serialized());
auto serialized_supplied_type = supplied_type->serialized();
for (auto apache_bug_mime_type : apache_bug_mime_types) {
if (serialized_supplied_type == apache_bug_mime_type) {
m_check_for_apache_bug_flag = true;

View file

@ -215,7 +215,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.
else if (m_response_type == Bindings::XMLHttpRequestResponseType::Blob) {
auto mime_type_as_string = TRY_OR_THROW_OOM(vm, TRY_OR_THROW_OOM(vm, get_final_mime_type()).serialized());
auto mime_type_as_string = TRY_OR_THROW_OOM(vm, get_final_mime_type()).serialized();
auto blob_part = FileAPI::Blob::create(realm(), m_received_bytes, move(mime_type_as_string));
auto blob = FileAPI::Blob::create(realm(), Vector<FileAPI::BlobPart> { JS::make_handle(*blob_part) });
m_response_object = JS::NonnullGCPtr<JS::Object> { blob };
@ -287,20 +287,20 @@ void XMLHttpRequest::set_document_response()
return;
// 2. Let finalMIME be the result of get a final MIME type for xhr.
auto final_mine = MUST(get_final_mime_type());
auto final_mime = MUST(get_final_mime_type());
// 3. If finalMIME is not an HTML MIME type or an XML MIME type, then return.
if (!final_mine.is_html() && !final_mine.is_xml())
if (!final_mime.is_html() && !final_mime.is_xml())
return;
// 4. If xhrs response type is the empty string and finalMIME is an HTML MIME type, then return.
if (m_response_type == Bindings::XMLHttpRequestResponseType::Empty && final_mine.is_html())
if (m_response_type == Bindings::XMLHttpRequestResponseType::Empty && final_mime.is_html())
return;
// 5. If finalMIME is an HTML MIME type, then:
Optional<String> charset;
JS::GCPtr<DOM::Document> document;
if (final_mine.is_html()) {
if (final_mime.is_html()) {
// 5.1. Let charset be the result of get a final encoding for xhr.
if (auto final_encoding = MUST(get_final_encoding()); final_encoding.has_value())
charset = MUST(String::from_utf8(*final_encoding));
@ -340,7 +340,7 @@ void XMLHttpRequest::set_document_response()
document->set_encoding(move(charset));
// 9. Set documents content type to finalMIME.
document->set_content_type(MUST(final_mine.serialized()));
document->set_content_type(final_mime.serialized());
// 10. Set documents URL to xhrs responses URL.
document->set_url(m_response->url().value_or({}));
@ -601,10 +601,10 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
auto charset_parameter_iterator = content_type_record->parameters().find("charset"sv);
if (charset_parameter_iterator != content_type_record->parameters().end() && !Infra::is_ascii_case_insensitive_match(charset_parameter_iterator->value, "UTF-8"sv)) {
// 1. Set contentTypeRecords parameters["charset"] to "UTF-8".
TRY_OR_THROW_OOM(vm, content_type_record->set_parameter("charset"_string, "UTF-8"_string));
content_type_record->set_parameter("charset"_string, "UTF-8"_string);
// 2. Let newContentTypeSerialized be the result of serializing contentTypeRecord.
auto new_content_type_serialized = TRY_OR_THROW_OOM(vm, content_type_record->serialized());
auto new_content_type_serialized = content_type_record->serialized();
// 3. Set (`Content-Type`, newContentTypeSerialized) in thiss author request headers.
auto header = Fetch::Infrastructure::Header::from_string_pair("Content-Type"sv, new_content_type_serialized);