From 3dce6766a39859302b536f5c69cc05fbf4c7bfe2 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 26 Nov 2025 13:32:39 -0500 Subject: [PATCH] LibWeb: Extract some CORS and MIME Fetch helpers to their own files An upcoming commit will migrate the contents of Headers.h/cpp to LibHTTP for use outside of LibWeb. These CORS and MIME helpers depend on other LibWeb facilities, however, so they cannot be moved. --- Libraries/LibWeb/CMakeLists.txt | 2 + Libraries/LibWeb/CSS/CSSImportRule.cpp | 3 +- Libraries/LibWeb/CSS/StyleComputer.cpp | 3 +- Libraries/LibWeb/DOM/DocumentLoading.cpp | 9 +- Libraries/LibWeb/Fetch/Fetching/Fetching.cpp | 4 +- Libraries/LibWeb/Fetch/Headers.cpp | 1 + .../LibWeb/Fetch/Infrastructure/HTTP/CORS.cpp | 197 +++++++++++++ .../LibWeb/Fetch/Infrastructure/HTTP/CORS.h | 25 ++ .../Fetch/Infrastructure/HTTP/Headers.cpp | 263 ------------------ .../Fetch/Infrastructure/HTTP/Headers.h | 13 - .../LibWeb/Fetch/Infrastructure/HTTP/MIME.cpp | 93 +++++++ .../LibWeb/Fetch/Infrastructure/HTTP/MIME.h | 18 ++ .../Fetch/Infrastructure/HTTP/Responses.cpp | 1 + .../Fetch/Infrastructure/MimeTypeBlocking.cpp | 3 +- .../Fetch/Infrastructure/NoSniffBlocking.cpp | 3 +- Libraries/LibWeb/Fetch/Request.cpp | 3 +- Libraries/LibWeb/Fetch/Response.cpp | 3 +- Libraries/LibWeb/HTML/EventSource.cpp | 3 +- Libraries/LibWeb/HTML/HTMLLinkElement.cpp | 3 +- Libraries/LibWeb/HTML/HTMLObjectElement.cpp | 3 +- Libraries/LibWeb/HTML/NavigatorBeacon.cpp | 1 + .../HTML/Parser/HTMLEncodingDetection.cpp | 3 +- .../HTML/Parser/HTMLEncodingDetection.h | 1 + Libraries/LibWeb/HTML/Scripting/Fetching.cpp | 9 +- .../LibWeb/HTML/SharedResourceRequest.cpp | 3 +- .../MediaCapabilities.cpp | 1 + Libraries/LibWeb/SVG/SVGScriptElement.cpp | 1 + Libraries/LibWeb/ServiceWorker/Job.cpp | 3 +- Libraries/LibWeb/WebAssembly/WebAssembly.cpp | 3 +- Libraries/LibWeb/XHR/XMLHttpRequest.cpp | 3 +- 30 files changed, 382 insertions(+), 299 deletions(-) create mode 100644 Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.cpp create mode 100644 Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.h create mode 100644 Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.cpp create mode 100644 Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.h diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 92eb11181d2..8790cb8a26f 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -379,8 +379,10 @@ set(SOURCES Fetch/Infrastructure/FetchTimingInfo.cpp Fetch/Infrastructure/HTTP.cpp Fetch/Infrastructure/HTTP/Bodies.cpp + Fetch/Infrastructure/HTTP/CORS.cpp Fetch/Infrastructure/HTTP/Headers.cpp Fetch/Infrastructure/HTTP/Methods.cpp + Fetch/Infrastructure/HTTP/MIME.cpp Fetch/Infrastructure/HTTP/Requests.cpp Fetch/Infrastructure/HTTP/Responses.cpp Fetch/Infrastructure/HTTP/Statuses.cpp diff --git a/Libraries/LibWeb/CSS/CSSImportRule.cpp b/Libraries/LibWeb/CSS/CSSImportRule.cpp index 903d22718cc..ffed330ec07 100644 --- a/Libraries/LibWeb/CSS/CSSImportRule.cpp +++ b/Libraries/LibWeb/CSS/CSSImportRule.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -153,7 +154,7 @@ void CSSImportRule::fetch() // 4. Let importedStylesheet be the result of parsing byteStream given parsedUrl. // FIXME: Tidy up our parsing API. For now, do the decoding here. Optional mime_type_charset; - if (auto extracted_mime_type = response->header_list()->extract_mime_type(); extracted_mime_type.has_value()) { + if (auto extracted_mime_type = Fetch::Infrastructure::extract_mime_type(response->header_list()); extracted_mime_type.has_value()) { if (auto charset = extracted_mime_type->parameters().get("charset"sv); charset.has_value()) mime_type_charset = charset.value(); } diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index 500fa97fc3c..8cdbe0edee4 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include @@ -310,7 +311,7 @@ void FontLoader::font_did_load_or_fail(RefPtr typeface) ErrorOr> FontLoader::try_load_font(Fetch::Infrastructure::Response const& response, ByteBuffer const& bytes) { // FIXME: This could maybe use the format() provided in @font-face as well, since often the mime type is just application/octet-stream and we have to try every format - auto mime_type = response.header_list()->extract_mime_type(); + auto mime_type = Fetch::Infrastructure::extract_mime_type(response.header_list()); if (!mime_type.has_value() || !mime_type->is_font()) { mime_type = MimeSniff::Resource::sniff(bytes, MimeSniff::SniffingConfiguration { .sniffing_context = MimeSniff::SniffingContext::Font }); } diff --git a/Libraries/LibWeb/DOM/DocumentLoading.cpp b/Libraries/LibWeb/DOM/DocumentLoading.cpp index 0278d5ffb15..68898628734 100644 --- a/Libraries/LibWeb/DOM/DocumentLoading.cpp +++ b/Libraries/LibWeb/DOM/DocumentLoading.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -93,8 +94,8 @@ static WebIDL::ExceptionOr> load_html_document(HTML::Navi // causes a load event to be fired. else { // FIXME: Parse as we receive the document data, instead of waiting for the whole document to be fetched first. - auto process_body = GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing, url = navigation_params.response->url().value(), mime_type = navigation_params.response->header_list()->extract_mime_type()](ByteBuffer data) { - Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(document->heap(), [signal_to_continue_session_history_processing, document = document, data = move(data), url = url, mime_type] { + auto process_body = GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing, url = navigation_params.response->url().value(), mime_type = Fetch::Infrastructure::extract_mime_type(navigation_params.response->header_list())](ByteBuffer data) mutable { + Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(document->heap(), [signal_to_continue_session_history_processing, document = document, data = move(data), url = url, mime_type = move(mime_type)] { // NB: If document is part of a session history entry's traversal, resolve the signal_to_continue_session_history_processing. signal_to_continue_session_history_processing->resolve({}); auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data, mime_type); @@ -362,7 +363,7 @@ static WebIDL::ExceptionOr> load_media_document(HTML::Nav auto& realm = document->realm(); navigation_params.response->body()->fully_read( realm, - GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing](ByteBuffer) { + GC::create_function(document->heap(), [document, signal_to_continue_session_history_processing](ByteBuffer) { // NB: If document is part of session history traversal, resolve the signal_to_continue_session_history_processing. signal_to_continue_session_history_processing->resolve({}); HTML::HTMLParser::the_end(document); }), @@ -418,7 +419,7 @@ GC::Ptr load_document(HTML::NavigationParams const& navigation_pa // NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry. // 1. Let type be the computed type of navigationParams's response. - auto supplied_type = navigation_params.response->header_list()->extract_mime_type(); + auto supplied_type = Fetch::Infrastructure::extract_mime_type(navigation_params.response->header_list()); auto type = MimeSniff::Resource::sniff( navigation_params.response->body()->source().visit( [](Empty) { return ReadonlyBytes {}; }, diff --git a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index 7f2e8d71da0..20894588cde 100644 --- a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -713,7 +715,7 @@ void fetch_response_handover(JS::Realm& realm, Infrastructure::FetchParams const response_status = response.status(); // 2. Let mimeType be the result of extracting a MIME type from response’s header list. - auto mime_type = response.header_list()->extract_mime_type(); + auto mime_type = Infrastructure::extract_mime_type(response.header_list()); // 3. If mimeType is non-null, then set bodyInfo’s content type to the result of minimizing a supported MIME type given mimeType. if (mime_type.has_value()) diff --git a/Libraries/LibWeb/Fetch/Headers.cpp b/Libraries/LibWeb/Fetch/Headers.cpp index e7236fe51a1..9d7ea2485ee 100644 --- a/Libraries/LibWeb/Fetch/Headers.cpp +++ b/Libraries/LibWeb/Fetch/Headers.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace Web::Fetch { diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.cpp b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.cpp new file mode 100644 index 00000000000..9e327456bfc --- /dev/null +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2022-2023, Linus Groh + * Copyright (c) 2022, Kenneth Myhra + * Copyright (c) 2022, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Web::Fetch::Infrastructure { + +// https://fetch.spec.whatwg.org/#cors-safelisted-request-header +bool is_cors_safelisted_request_header(Header const& header) +{ + auto const& [name, value] = header; + + // 1. If value’s length is greater than 128, then return false. + if (value.length() > 128) + return false; + + // 2. Byte-lowercase name and switch on the result: + // `accept` + if (name.equals_ignoring_ascii_case("accept"sv)) { + // If value contains a CORS-unsafe request-header byte, then return false. + if (any_of(value, is_cors_unsafe_request_header_byte)) + return false; + } + // `accept-language` + // `content-language` + else if (name.is_one_of_ignoring_ascii_case("accept-language"sv, "content-language"sv)) { + // If value contains a byte that is not in the range 0x30 (0) to 0x39 (9), inclusive, is not in the range 0x41 (A) to 0x5A (Z), inclusive, is not in the range 0x61 (a) to 0x7A (z), inclusive, and is not 0x20 (SP), 0x2A (*), 0x2C (,), 0x2D (-), 0x2E (.), 0x3B (;), or 0x3D (=), then return false. + if (any_of(value, [](auto byte) { + return !(is_ascii_digit(byte) || is_ascii_alpha(byte) || " *,-.;="sv.contains(byte)); + })) + return false; + } + // `content-type` + else if (name.equals_ignoring_ascii_case("content-type"sv)) { + // 1. If value contains a CORS-unsafe request-header byte, then return false. + if (any_of(value, is_cors_unsafe_request_header_byte)) + return false; + + // 2. Let mimeType be the result of parsing the result of isomorphic decoding value. + auto decoded = TextCodec::isomorphic_decode(value); + auto mime_type = MimeSniff::MimeType::parse(decoded); + + // 3. If mimeType is failure, then return false. + if (!mime_type.has_value()) + return false; + + // 4. If mimeType’s essence is not "application/x-www-form-urlencoded", "multipart/form-data", or "text/plain", then return false. + if (!mime_type->essence().is_one_of("application/x-www-form-urlencoded"sv, "multipart/form-data"sv, "text/plain"sv)) + return false; + } + // `range` + else if (name.equals_ignoring_ascii_case("range"sv)) { + // 1. Let rangeValue be the result of parsing a single range header value given value and false. + auto range_value = parse_single_range_header_value(value, false); + + // 2. If rangeValue is failure, then return false. + if (!range_value.has_value()) + return false; + + // 3. If rangeValue[0] is null, then return false. + // NOTE: As web browsers have historically not emitted ranges such as `bytes=-500` this algorithm does not safelist them. + if (!range_value->start.has_value()) + return false; + } + // Otherwise + else { + // Return false. + return false; + } + + // 3. Return true. + return true; +} + +// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte +bool is_cors_unsafe_request_header_byte(u8 byte) +{ + // A CORS-unsafe request-header byte is a byte byte for which one of the following is true: + // - byte is less than 0x20 and is not 0x09 HT + // - byte is 0x22 ("), 0x28 (left parenthesis), 0x29 (right parenthesis), 0x3A (:), 0x3C (<), 0x3E (>), 0x3F (?), 0x40 (@), 0x5B ([), 0x5C (\), 0x5D (]), 0x7B ({), 0x7D (}), or 0x7F DEL. + return (byte < 0x20 && byte != 0x09) + || (Array { 0x22, 0x28, 0x29, 0x3A, 0x3C, 0x3E, 0x3F, 0x40, 0x5B, 0x5C, 0x5D, 0x7B, 0x7D, 0x7F }.contains_slow(byte)); +} + +// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-names +Vector get_cors_unsafe_header_names(HeaderList const& headers) +{ + // 1. Let unsafeNames be a new list. + Vector unsafe_names; + + // 2. Let potentiallyUnsafeNames be a new list. + Vector potentially_unsafe_names; + + // 3. Let safelistValueSize be 0. + Checked safelist_value_size = 0; + + // 4. For each header of headers: + for (auto const& header : headers) { + // 1. If header is not a CORS-safelisted request-header, then append header’s name to unsafeNames. + if (!is_cors_safelisted_request_header(header)) { + unsafe_names.append(header.name); + } + // 2. Otherwise, append header’s name to potentiallyUnsafeNames and increase safelistValueSize by header’s + // value’s length. + else { + potentially_unsafe_names.append(header.name); + safelist_value_size += header.value.length(); + } + } + + // 5. If safelistValueSize is greater than 1024, then for each name of potentiallyUnsafeNames, append name to + // unsafeNames. + if (safelist_value_size.has_overflow() || safelist_value_size.value() > 1024) + unsafe_names.extend(move(potentially_unsafe_names)); + + // 6. Return the result of convert header names to a sorted-lowercase set with unsafeNames. + return convert_header_names_to_a_sorted_lowercase_set(unsafe_names.span()); +} + +// https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name +bool is_cors_non_wildcard_request_header_name(StringView header_name) +{ + // A CORS non-wildcard request-header name is a header name that is a byte-case-insensitive match for `Authorization`. + return header_name.equals_ignoring_ascii_case("Authorization"sv); +} + +// https://fetch.spec.whatwg.org/#privileged-no-cors-request-header-name +bool is_privileged_no_cors_request_header_name(StringView header_name) +{ + // A privileged no-CORS request-header name is a header name that is a byte-case-insensitive match for one of + // - `Range`. + return header_name.equals_ignoring_ascii_case("Range"sv); +} + +// https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name +bool is_cors_safelisted_response_header_name(StringView header_name, ReadonlySpan list) +{ + // A CORS-safelisted response-header name, given a list of header names list, is a header name that is a byte-case-insensitive match for one of + // - `Cache-Control` + // - `Content-Language` + // - `Content-Length` + // - `Content-Type` + // - `Expires` + // - `Last-Modified` + // - `Pragma` + // - Any item in list that is not a forbidden response-header name. + return header_name.is_one_of_ignoring_ascii_case( + "Cache-Control"sv, + "Content-Language"sv, + "Content-Length"sv, + "Content-Type"sv, + "Expires"sv, + "Last-Modified"sv, + "Pragma"sv) + || any_of(list, [&](auto list_header_name) { + return header_name.equals_ignoring_ascii_case(list_header_name) + && !is_forbidden_response_header_name(list_header_name); + }); +} + +// https://fetch.spec.whatwg.org/#no-cors-safelisted-request-header-name +bool is_no_cors_safelisted_request_header_name(StringView header_name) +{ + // A no-CORS-safelisted request-header name is a header name that is a byte-case-insensitive match for one of + // - `Accept` + // - `Accept-Language` + // - `Content-Language` + // - `Content-Type` + return header_name.is_one_of_ignoring_ascii_case( + "Accept"sv, + "Accept-Language"sv, + "Content-Language"sv, + "Content-Type"sv); +} + +// https://fetch.spec.whatwg.org/#no-cors-safelisted-request-header +bool is_no_cors_safelisted_request_header(Header const& header) +{ + // 1. If name is not a no-CORS-safelisted request-header name, then return false. + if (!is_no_cors_safelisted_request_header_name(header.name)) + return false; + + // 2. Return whether (name, value) is a CORS-safelisted request-header. + return is_cors_safelisted_request_header(header); +} + +} diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.h b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.h new file mode 100644 index 00000000000..56172e2526b --- /dev/null +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/CORS.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022-2023, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::Fetch::Infrastructure { + +[[nodiscard]] bool is_cors_safelisted_request_header(Header const&); +[[nodiscard]] bool is_cors_unsafe_request_header_byte(u8); +[[nodiscard]] Vector get_cors_unsafe_header_names(HeaderList const&); +[[nodiscard]] bool is_cors_non_wildcard_request_header_name(StringView); +[[nodiscard]] bool is_privileged_no_cors_request_header_name(StringView); +[[nodiscard]] bool is_cors_safelisted_response_header_name(StringView, ReadonlySpan); +[[nodiscard]] bool is_no_cors_safelisted_request_header_name(StringView); +[[nodiscard]] bool is_no_cors_safelisted_request_header(Header const&); + +} diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp index a5469654494..1488a97db8e 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp @@ -19,7 +19,6 @@ #include #include #include -#include namespace Web::Fetch::Infrastructure { @@ -311,61 +310,6 @@ Variant HeaderList::extract_length return *result; } -// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type -Optional HeaderList::extract_mime_type() const -{ - // 1. Let charset be null. - Optional charset; - - // 2. Let essence be null. - Optional essence; - - // 3. Let mimeType be null. - Optional mime_type; - - // 4. Let values be the result of getting, decoding, and splitting `Content-Type` from headers. - auto values = get_decode_and_split("Content-Type"sv); - - // 5. If values is null, then return failure. - if (!values.has_value()) - return {}; - - // 6. For each value of values: - for (auto const& value : *values) { - // 1. Let temporaryMimeType be the result of parsing value. - auto temporary_mime_type = MimeSniff::MimeType::parse(value); - - // 2. If temporaryMimeType is failure or its essence is "*/*", then continue. - if (!temporary_mime_type.has_value() || temporary_mime_type->essence() == "*/*"sv) - continue; - - // 3. Set mimeType to temporaryMimeType. - mime_type = temporary_mime_type; - - // 4. If mimeType’s essence is not essence, then: - if (!essence.has_value() || (mime_type->essence() != essence->bytes_as_string_view())) { - // 1. Set charset to null. - charset = {}; - - // 2. If mimeType’s parameters["charset"] exists, then set charset to mimeType’s parameters["charset"]. - auto it = mime_type->parameters().find("charset"sv); - if (it != mime_type->parameters().end()) - charset = it->value; - - // 3. Set essence to mimeType’s essence. - essence = mime_type->essence(); - } - // 5. Otherwise, if mimeType’s parameters["charset"] does not exist, and charset is non-null, set mimeType’s parameters["charset"] to charset. - else if (!mime_type->parameters().contains("charset"sv) && charset.has_value()) { - mime_type->set_parameter("charset"_string, charset.release_value()); - } - } - - // 7. If mimeType is null, then return failure. - // 8. Return mimeType. - return mime_type; -} - // Non-standard Vector HeaderList::unique_names() const { @@ -494,29 +438,6 @@ bool is_forbidden_response_header_name(StringView header_name) "Set-Cookie2"sv); } -// https://fetch.spec.whatwg.org/#legacy-extract-an-encoding -StringView legacy_extract_an_encoding(Optional const& mime_type, StringView fallback_encoding) -{ - // 1. If mimeType is failure, then return fallbackEncoding. - if (!mime_type.has_value()) - return fallback_encoding; - - // 2. If mimeType["charset"] does not exist, then return fallbackEncoding. - auto charset = mime_type->parameters().get("charset"sv); - if (!charset.has_value()) - return fallback_encoding; - - // 3. Let tentativeEncoding be the result of getting an encoding from mimeType["charset"]. - auto tentative_encoding = TextCodec::get_standardized_encoding(*charset); - - // 4. If tentativeEncoding is failure, then return fallbackEncoding. - if (!tentative_encoding.has_value()) - return fallback_encoding; - - // 5. Return tentativeEncoding. - return *tentative_encoding; -} - // https://fetch.spec.whatwg.org/#header-value-get-decode-and-split Vector get_decode_and_split_header_value(StringView value) { @@ -676,190 +597,6 @@ Optional parse_single_range_header_value(StringView const valu return RangeHeaderValue { move(range_start_value), move(range_end_value) }; } -// https://fetch.spec.whatwg.org/#cors-safelisted-request-header -bool is_cors_safelisted_request_header(Header const& header) -{ - // To determine whether a header (name, value) is a CORS-safelisted request-header, run these steps: - auto const& [name, value] = header; - - // 1. If value’s length is greater than 128, then return false. - if (value.length() > 128) - return false; - - // 2. Byte-lowercase name and switch on the result: - // `accept` - if (name.equals_ignoring_ascii_case("accept"sv)) { - // If value contains a CORS-unsafe request-header byte, then return false. - if (any_of(value, is_cors_unsafe_request_header_byte)) - return false; - } - // `accept-language` - // `content-language` - else if (name.is_one_of_ignoring_ascii_case("accept-language"sv, "content-language"sv)) { - // If value contains a byte that is not in the range 0x30 (0) to 0x39 (9), inclusive, is not in the range 0x41 (A) to 0x5A (Z), inclusive, is not in the range 0x61 (a) to 0x7A (z), inclusive, and is not 0x20 (SP), 0x2A (*), 0x2C (,), 0x2D (-), 0x2E (.), 0x3B (;), or 0x3D (=), then return false. - if (any_of(value, [](auto byte) { - return !(is_ascii_digit(byte) || is_ascii_alpha(byte) || " *,-.;="sv.contains(byte)); - })) - return false; - } - // `content-type` - else if (name.equals_ignoring_ascii_case("content-type"sv)) { - // 1. If value contains a CORS-unsafe request-header byte, then return false. - if (any_of(value, is_cors_unsafe_request_header_byte)) - return false; - - // 2. Let mimeType be the result of parsing the result of isomorphic decoding value. - auto decoded = TextCodec::isomorphic_decode(value); - auto mime_type = MimeSniff::MimeType::parse(decoded); - - // 3. If mimeType is failure, then return false. - if (!mime_type.has_value()) - return false; - - // 4. If mimeType’s essence is not "application/x-www-form-urlencoded", "multipart/form-data", or "text/plain", then return false. - if (!mime_type->essence().is_one_of("application/x-www-form-urlencoded"sv, "multipart/form-data"sv, "text/plain"sv)) - return false; - } - // `range` - else if (name.equals_ignoring_ascii_case("range"sv)) { - // 1. Let rangeValue be the result of parsing a single range header value given value and false. - auto range_value = parse_single_range_header_value(value, false); - - // 2. If rangeValue is failure, then return false. - if (!range_value.has_value()) - return false; - - // 3. If rangeValue[0] is null, then return false. - // NOTE: As web browsers have historically not emitted ranges such as `bytes=-500` this algorithm does not safelist them. - if (!range_value->start.has_value()) - return false; - } - // Otherwise - else { - // Return false. - return false; - } - - // 3. Return true. - return true; -} - -// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte -bool is_cors_unsafe_request_header_byte(u8 byte) -{ - // A CORS-unsafe request-header byte is a byte byte for which one of the following is true: - // - byte is less than 0x20 and is not 0x09 HT - // - byte is 0x22 ("), 0x28 (left parenthesis), 0x29 (right parenthesis), 0x3A (:), 0x3C (<), 0x3E (>), 0x3F (?), 0x40 (@), 0x5B ([), 0x5C (\), 0x5D (]), 0x7B ({), 0x7D (}), or 0x7F DEL. - return (byte < 0x20 && byte != 0x09) - || (Array { 0x22, 0x28, 0x29, 0x3A, 0x3C, 0x3E, 0x3F, 0x40, 0x5B, 0x5C, 0x5D, 0x7B, 0x7D, 0x7F }.span().contains_slow(byte)); -} - -// https://fetch.spec.whatwg.org/#cors-unsafe-request-header-names -Vector get_cors_unsafe_header_names(HeaderList const& headers) -{ - // The CORS-unsafe request-header names, given a header list headers, are determined as follows: - - // 1. Let unsafeNames be a new list. - Vector unsafe_names; - - // 2. Let potentiallyUnsafeNames be a new list. - Vector potentially_unsafe_names; - - // 3. Let safelistValueSize be 0. - Checked safelist_value_size = 0; - - // 4. For each header of headers: - for (auto const& header : headers) { - // 1. If header is not a CORS-safelisted request-header, then append header’s name to unsafeNames. - if (!is_cors_safelisted_request_header(header)) { - unsafe_names.append(header.name); - } - // 2. Otherwise, append header’s name to potentiallyUnsafeNames and increase safelistValueSize by header’s - // value’s length. - else { - potentially_unsafe_names.append(header.name); - safelist_value_size += header.value.length(); - } - } - - // 5. If safelistValueSize is greater than 1024, then for each name of potentiallyUnsafeNames, append name to - // unsafeNames. - if (safelist_value_size.has_overflow() || safelist_value_size.value() > 1024) - unsafe_names.extend(move(potentially_unsafe_names)); - - // 6. Return the result of convert header names to a sorted-lowercase set with unsafeNames. - return convert_header_names_to_a_sorted_lowercase_set(unsafe_names); -} - -// https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name -bool is_cors_non_wildcard_request_header_name(StringView header_name) -{ - // A CORS non-wildcard request-header name is a header name that is a byte-case-insensitive match for `Authorization`. - return header_name.equals_ignoring_ascii_case("Authorization"sv); -} - -// https://fetch.spec.whatwg.org/#privileged-no-cors-request-header-name -bool is_privileged_no_cors_request_header_name(StringView header_name) -{ - // A privileged no-CORS request-header name is a header name that is a byte-case-insensitive match for one of - // - `Range`. - return header_name.equals_ignoring_ascii_case("Range"sv); -} - -// https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name -bool is_cors_safelisted_response_header_name(StringView header_name, ReadonlySpan list) -{ - // A CORS-safelisted response-header name, given a list of header names list, is a header name that is a byte-case-insensitive match for one of - // - `Cache-Control` - // - `Content-Language` - // - `Content-Length` - // - `Content-Type` - // - `Expires` - // - `Last-Modified` - // - `Pragma` - // - Any item in list that is not a forbidden response-header name. - return header_name.is_one_of_ignoring_ascii_case( - "Cache-Control"sv, - "Content-Language"sv, - "Content-Length"sv, - "Content-Type"sv, - "Expires"sv, - "Last-Modified"sv, - "Pragma"sv) - || any_of(list, [&](auto list_header_name) { - return header_name.equals_ignoring_ascii_case(list_header_name) - && !is_forbidden_response_header_name(list_header_name); - }); -} - -// https://fetch.spec.whatwg.org/#no-cors-safelisted-request-header-name -bool is_no_cors_safelisted_request_header_name(StringView header_name) -{ - // A no-CORS-safelisted request-header name is a header name that is a byte-case-insensitive match for one of - // - `Accept` - // - `Accept-Language` - // - `Content-Language` - // - `Content-Type` - return header_name.is_one_of_ignoring_ascii_case( - "Accept"sv, - "Accept-Language"sv, - "Content-Language"sv, - "Content-Type"sv); -} - -// https://fetch.spec.whatwg.org/#no-cors-safelisted-request-header -bool is_no_cors_safelisted_request_header(Header const& header) -{ - // To determine whether a header (name, value) is a no-CORS-safelisted request-header, run these steps: - - // 1. If name is not a no-CORS-safelisted request-header name, then return false. - if (!is_no_cors_safelisted_request_header_name(header.name)) - return false; - - // 2. Return whether (name, value) is a CORS-safelisted request-header. - return is_cors_safelisted_request_header(header); -} - // https://fetch.spec.whatwg.org/#default-user-agent-value ByteString const& default_user_agent_value() { diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h index 9f0385a6397..8fb652e8c6d 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h @@ -15,7 +15,6 @@ #include #include #include -#include namespace Web::Fetch::Infrastructure { @@ -61,8 +60,6 @@ public: struct ExtractLengthFailure { }; [[nodiscard]] Variant extract_length() const; - [[nodiscard]] Optional extract_mime_type() const; - [[nodiscard]] Vector unique_names() const; }; @@ -78,22 +75,12 @@ struct RangeHeaderValue { [[nodiscard]] bool is_forbidden_request_header(Header const&); [[nodiscard]] bool is_forbidden_response_header_name(StringView); -[[nodiscard]] WEB_API StringView legacy_extract_an_encoding(Optional const& mime_type, StringView fallback_encoding); [[nodiscard]] Vector get_decode_and_split_header_value(StringView); [[nodiscard]] Vector convert_header_names_to_a_sorted_lowercase_set(ReadonlySpan); [[nodiscard]] WEB_API ByteString build_content_range(u64 range_start, u64 range_end, u64 full_length); [[nodiscard]] WEB_API Optional parse_single_range_header_value(StringView, bool); -[[nodiscard]] bool is_cors_safelisted_request_header(Header const&); -[[nodiscard]] bool is_cors_unsafe_request_header_byte(u8); -[[nodiscard]] WEB_API Vector get_cors_unsafe_header_names(HeaderList const&); -[[nodiscard]] WEB_API bool is_cors_non_wildcard_request_header_name(StringView); -[[nodiscard]] bool is_privileged_no_cors_request_header_name(StringView); -[[nodiscard]] bool is_cors_safelisted_response_header_name(StringView, ReadonlySpan); -[[nodiscard]] bool is_no_cors_safelisted_request_header_name(StringView); -[[nodiscard]] bool is_no_cors_safelisted_request_header(Header const&); - [[nodiscard]] WEB_API ByteString const& default_user_agent_value(); } diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.cpp b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.cpp new file mode 100644 index 00000000000..8412d891f22 --- /dev/null +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022-2023, Linus Groh + * Copyright (c) 2022, Kenneth Myhra + * Copyright (c) 2022, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Web::Fetch::Infrastructure { + +// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type +Optional extract_mime_type(HeaderList const& headers) +{ + // 1. Let charset be null. + Optional charset; + + // 2. Let essence be null. + Optional essence; + + // 3. Let mimeType be null. + Optional mime_type; + + // 4. Let values be the result of getting, decoding, and splitting `Content-Type` from headers. + auto values = headers.get_decode_and_split("Content-Type"sv); + + // 5. If values is null, then return failure. + if (!values.has_value()) + return {}; + + // 6. For each value of values: + for (auto const& value : *values) { + // 1. Let temporaryMimeType be the result of parsing value. + auto temporary_mime_type = MimeSniff::MimeType::parse(value); + + // 2. If temporaryMimeType is failure or its essence is "*/*", then continue. + if (!temporary_mime_type.has_value() || temporary_mime_type->essence() == "*/*"sv) + continue; + + // 3. Set mimeType to temporaryMimeType. + mime_type = temporary_mime_type; + + // 4. If mimeType’s essence is not essence, then: + if (!essence.has_value() || (mime_type->essence() != *essence)) { + // 1. Set charset to null. + charset = {}; + + // 2. If mimeType’s parameters["charset"] exists, then set charset to mimeType’s parameters["charset"]. + auto it = mime_type->parameters().find("charset"sv); + if (it != mime_type->parameters().end()) + charset = it->value; + + // 3. Set essence to mimeType’s essence. + essence = mime_type->essence(); + } + // 5. Otherwise, if mimeType’s parameters["charset"] does not exist, and charset is non-null, set mimeType’s parameters["charset"] to charset. + else if (!mime_type->parameters().contains("charset"sv) && charset.has_value()) { + mime_type->set_parameter("charset"_string, charset.release_value()); + } + } + + // 7. If mimeType is null, then return failure. + // 8. Return mimeType. + return mime_type; +} + +// https://fetch.spec.whatwg.org/#legacy-extract-an-encoding +StringView legacy_extract_an_encoding(Optional const& mime_type, StringView fallback_encoding) +{ + // 1. If mimeType is failure, then return fallbackEncoding. + if (!mime_type.has_value()) + return fallback_encoding; + + // 2. If mimeType["charset"] does not exist, then return fallbackEncoding. + auto charset = mime_type->parameters().get("charset"sv); + if (!charset.has_value()) + return fallback_encoding; + + // 3. Let tentativeEncoding be the result of getting an encoding from mimeType["charset"]. + auto tentative_encoding = TextCodec::get_standardized_encoding(*charset); + + // 4. If tentativeEncoding is failure, then return fallbackEncoding. + if (!tentative_encoding.has_value()) + return fallback_encoding; + + // 5. Return tentativeEncoding. + return *tentative_encoding; +} + +} diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.h b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.h new file mode 100644 index 00000000000..70b82a7388c --- /dev/null +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/MIME.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2022-2023, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::Fetch::Infrastructure { + +Optional extract_mime_type(HeaderList const&); +StringView legacy_extract_an_encoding(Optional const& mime_type, StringView fallback_encoding); + +} diff --git a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.cpp b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.cpp index 65ac73a8592..7bfc5ea3c6a 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.cpp +++ b/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace Web::Fetch::Infrastructure { diff --git a/Libraries/LibWeb/Fetch/Infrastructure/MimeTypeBlocking.cpp b/Libraries/LibWeb/Fetch/Infrastructure/MimeTypeBlocking.cpp index dd64426250a..6880f93f201 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/MimeTypeBlocking.cpp +++ b/Libraries/LibWeb/Fetch/Infrastructure/MimeTypeBlocking.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -14,7 +15,7 @@ namespace Web::Fetch::Infrastructure { RequestOrResponseBlocking should_response_to_request_be_blocked_due_to_its_mime_type(Response const& response, Request const& request) { // 1. Let mimeType be the result of extracting a MIME type from response’s header list. - auto mime_type = response.header_list()->extract_mime_type(); + auto mime_type = Infrastructure::extract_mime_type(response.header_list()); // 2. If mimeType is failure, then return allowed. if (!mime_type.has_value()) diff --git a/Libraries/LibWeb/Fetch/Infrastructure/NoSniffBlocking.cpp b/Libraries/LibWeb/Fetch/Infrastructure/NoSniffBlocking.cpp index 158f2b884ef..abadab6f4c9 100644 --- a/Libraries/LibWeb/Fetch/Infrastructure/NoSniffBlocking.cpp +++ b/Libraries/LibWeb/Fetch/Infrastructure/NoSniffBlocking.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -38,7 +39,7 @@ RequestOrResponseBlocking should_response_to_request_be_blocked_due_to_nosniff(R return RequestOrResponseBlocking::Allowed; // 2. Let mimeType be the result of extracting a MIME type from response’s header list. - auto mime_type = response.header_list()->extract_mime_type(); + auto mime_type = Infrastructure::extract_mime_type(response.header_list()); // 3. Let destination be request’s destination. auto const& destination = request.destination(); diff --git a/Libraries/LibWeb/Fetch/Request.cpp b/Libraries/LibWeb/Fetch/Request.cpp index fb6b57e0362..bf8cd7d2024 100644 --- a/Libraries/LibWeb/Fetch/Request.cpp +++ b/Libraries/LibWeb/Fetch/Request.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +52,7 @@ Optional Request::mime_type_impl() const { // Objects including the Body interface mixin need to define an associated MIME type algorithm which takes no arguments and returns failure or a MIME type. // A Request object’s MIME type is to return the result of extracting a MIME type from its request’s header list. - return m_request->header_list()->extract_mime_type(); + return Infrastructure::extract_mime_type(m_request->header_list()); } // https://fetch.spec.whatwg.org/#concept-body-body diff --git a/Libraries/LibWeb/Fetch/Response.cpp b/Libraries/LibWeb/Fetch/Response.cpp index df5dfe83fae..db19c3032c0 100644 --- a/Libraries/LibWeb/Fetch/Response.cpp +++ b/Libraries/LibWeb/Fetch/Response.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,7 @@ Optional Response::mime_type_impl() const { // Objects including the Body interface mixin need to define an associated MIME type algorithm which takes no arguments and returns failure or a MIME type. // A Response object’s MIME type is to return the result of extracting a MIME type from its response’s header list. - return m_response->header_list()->extract_mime_type(); + return Infrastructure::extract_mime_type(m_response->header_list()); } // https://fetch.spec.whatwg.org/#concept-body-body diff --git a/Libraries/LibWeb/HTML/EventSource.cpp b/Libraries/LibWeb/HTML/EventSource.cpp index 0b42a708b62..62f25a9d8c4 100644 --- a/Libraries/LibWeb/HTML/EventSource.cpp +++ b/Libraries/LibWeb/HTML/EventSource.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -100,7 +101,7 @@ WebIDL::ExceptionOr> EventSource::construct_impl(JS::Realm& response = response->unsafe_response(); auto content_type_is_text_event_stream = [&]() { - auto content_type = response->header_list()->extract_mime_type(); + auto content_type = Fetch::Infrastructure::extract_mime_type(response->header_list()); if (!content_type.has_value()) return false; diff --git a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp index 867884288e0..cf911487c70 100644 --- a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -778,7 +779,7 @@ void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastru // 1. If the resource's Content-Type metadata is not text/css, then set success to false. auto mime_type_string = m_mime_type; Optional mime_type_charset; - auto extracted_mime_type = response.header_list()->extract_mime_type(); + auto extracted_mime_type = Fetch::Infrastructure::extract_mime_type(response.header_list()); if (extracted_mime_type.has_value()) { if (!mime_type_string.has_value()) mime_type_string = extracted_mime_type->essence(); diff --git a/Libraries/LibWeb/HTML/HTMLObjectElement.cpp b/Libraries/LibWeb/HTML/HTMLObjectElement.cpp index d4a3b13f2b6..4d001b3a585 100644 --- a/Libraries/LibWeb/HTML/HTMLObjectElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLObjectElement.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -361,7 +362,7 @@ void HTMLObjectElement::resource_did_load(Fetch::Infrastructure::Response const& // 3. Run the appropriate set of steps from the following list: // -> If the resource has associated Content-Type metadata - if (auto content_type = response.header_list()->extract_mime_type(); content_type.has_value()) { + if (auto content_type = Fetch::Infrastructure::extract_mime_type(response.header_list()); content_type.has_value()) { // 1. Let binary be false. bool binary = false; diff --git a/Libraries/LibWeb/HTML/NavigatorBeacon.cpp b/Libraries/LibWeb/HTML/NavigatorBeacon.cpp index 73a7a78a490..ca58a63a7dd 100644 --- a/Libraries/LibWeb/HTML/NavigatorBeacon.cpp +++ b/Libraries/LibWeb/HTML/NavigatorBeacon.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.cpp b/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.cpp index 3fc728472ca..3c19dae648f 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.cpp +++ b/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -444,7 +445,7 @@ Optional run_bom_sniff(ReadonlyBytes input) // Byte order mark Encoding // 0xEF 0xBB 0xBF UTF-8 // 0xFE 0xFF UTF-16BE - // 0xFF 0xFE UTF-16LE + // 0xFF 0xFE UTF-16LE if (input[0] == 0xEF && input[1] == 0xBB && input[2] == 0xBF) { return "UTF-8"; } diff --git a/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.h b/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.h index 75528315d84..8b7910192fd 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.h +++ b/Libraries/LibWeb/HTML/Parser/HTMLEncodingDetection.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace Web::HTML { diff --git a/Libraries/LibWeb/HTML/Scripting/Fetching.cpp b/Libraries/LibWeb/HTML/Scripting/Fetching.cpp index f7e82ec1dfa..8782fc66a44 100644 --- a/Libraries/LibWeb/HTML/Scripting/Fetching.cpp +++ b/Libraries/LibWeb/HTML/Scripting/Fetching.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -360,7 +361,7 @@ void fetch_classic_script(GC::Ref element, URL::URL const& ur } // 3. Let potentialMIMETypeForEncoding be the result of extracting a MIME type given response's header list. - auto potential_mime_type_for_encoding = response->header_list()->extract_mime_type(); + auto potential_mime_type_for_encoding = Fetch::Infrastructure::extract_mime_type(response->header_list()); // 4. Set character encoding to the result of legacy extracting an encoding given potentialMIMETypeForEncoding // and character encoding. @@ -427,7 +428,7 @@ WebIDL::ExceptionOr fetch_classic_worker_script(URL::URL const& url, Envir // 3. If all of the following are true: // - response's URL's scheme is an HTTP(S) scheme; and // - the result of extracting a MIME type from response's header list is not a JavaScript MIME type, - auto maybe_mime_type = response->header_list()->extract_mime_type(); + auto maybe_mime_type = Fetch::Infrastructure::extract_mime_type(response->header_list()); 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) { @@ -527,7 +528,7 @@ WebIDL::ExceptionOr> fetch_a_classic_worker_imported_scri // then throw a "NetworkError" DOMException. if (body_bytes.template has() || body_bytes.template has() || !Fetch::Infrastructure::is_ok_status(response->status()) - || !response->header_list()->extract_mime_type().has_value() || !response->header_list()->extract_mime_type()->is_javascript()) { + || !Fetch::Infrastructure::extract_mime_type(response->header_list()).has_value() || !Fetch::Infrastructure::extract_mime_type(response->header_list())->is_javascript()) { return WebIDL::NetworkError::create(realm, "Network error"_utf16); } @@ -702,7 +703,7 @@ void fetch_single_module_script(JS::Realm& realm, auto source_text = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, body_bytes.get()).release_value_but_fixme_should_propagate_errors(); // 3. Let mimeType be the result of extracting a MIME type from response's header list. - auto mime_type = response->header_list()->extract_mime_type(); + auto mime_type = Fetch::Infrastructure::extract_mime_type(response->header_list()); // 4. Let moduleScript be null. GC::Ptr module_script; diff --git a/Libraries/LibWeb/HTML/SharedResourceRequest.cpp b/Libraries/LibWeb/HTML/SharedResourceRequest.cpp index 68623ce19ca..afcc25d1bd5 100644 --- a/Libraries/LibWeb/HTML/SharedResourceRequest.cpp +++ b/Libraries/LibWeb/HTML/SharedResourceRequest.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,7 @@ void SharedResourceRequest::fetch_resource(JS::Realm& realm, GC::Refunsafe_response(); auto process_body = GC::create_function(heap(), [this, request, response](ByteBuffer data) { - auto extracted_mime_type = response->header_list()->extract_mime_type(); + auto extracted_mime_type = Fetch::Infrastructure::extract_mime_type(response->header_list()); auto mime_type = extracted_mime_type.has_value() ? extracted_mime_type.value().essence().bytes_as_string_view() : StringView {}; handle_successful_fetch(request->url(), mime_type, move(data)); }); diff --git a/Libraries/LibWeb/MediaCapabilitiesAPI/MediaCapabilities.cpp b/Libraries/LibWeb/MediaCapabilitiesAPI/MediaCapabilities.cpp index dcda6fc3ee5..5e84ba3156b 100644 --- a/Libraries/LibWeb/MediaCapabilitiesAPI/MediaCapabilities.cpp +++ b/Libraries/LibWeb/MediaCapabilitiesAPI/MediaCapabilities.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace Web::MediaCapabilitiesAPI { diff --git a/Libraries/LibWeb/SVG/SVGScriptElement.cpp b/Libraries/LibWeb/SVG/SVGScriptElement.cpp index 9c38e19e3f3..f70fa8ea329 100644 --- a/Libraries/LibWeb/SVG/SVGScriptElement.cpp +++ b/Libraries/LibWeb/SVG/SVGScriptElement.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/Libraries/LibWeb/ServiceWorker/Job.cpp b/Libraries/LibWeb/ServiceWorker/Job.cpp index e757c98d60b..8340c710d65 100644 --- a/Libraries/LibWeb/ServiceWorker/Job.cpp +++ b/Libraries/LibWeb/ServiceWorker/Job.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -254,7 +255,7 @@ static void update(JS::VM& vm, GC::Ref job) fetch_algorithms_input.process_response = [request, job, state, newest_worker, &realm, ®istration, &process_response_completion_result](GC::Ref response) mutable -> void { // 7. Extract a MIME type from the response’s header list. If s MIME type (ignoring parameters) is not a JavaScript MIME type, then: - auto mime_type = response->header_list()->extract_mime_type(); + auto mime_type = Fetch::Infrastructure::extract_mime_type(response->header_list()); if (!mime_type.has_value() || !mime_type->is_javascript()) { // 1. Invoke Reject Job Promise with job and "SecurityError" DOMException. reject_job_promise(job, "Service Worker script response is not a JavaScript MIME type"_utf16); diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index 6539fc7799a..a3d6298fcd3 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -891,7 +892,7 @@ GC::Ref compile_potential_webassembly_response(JS::VM& vm, GC:: // 5. If mimeType is not a byte-case-insensitive match for `application/wasm`, reject returnValue with a TypeError and abort these substeps. // Note: extra parameters are not allowed, including the empty `application/wasm;`. // FIXME: Validate these extra constraints that are not checked by extract_mime_type() - if (auto mime = response->header_list()->extract_mime_type(); !mime.has_value() || mime.value().essence() != "application/wasm"sv) { + if (auto mime = Fetch::Infrastructure::extract_mime_type(response->header_list()); !mime.has_value() || mime.value().essence() != "application/wasm"sv) { WebIDL::reject_promise(realm, return_value, vm.throw_completion("Response does not match the application/wasm MIME type"sv).value()); return JS::js_undefined(); } diff --git a/Libraries/LibWeb/XHR/XMLHttpRequest.cpp b/Libraries/LibWeb/XHR/XMLHttpRequest.cpp index 8ae53e42421..2cd7d2fd9c3 100644 --- a/Libraries/LibWeb/XHR/XMLHttpRequest.cpp +++ b/Libraries/LibWeb/XHR/XMLHttpRequest.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -366,7 +367,7 @@ MimeSniff::MimeType XMLHttpRequest::get_final_mime_type() const MimeSniff::MimeType XMLHttpRequest::get_response_mime_type() const { // 1. Let mimeType be the result of extracting a MIME type from xhr’s response’s header list. - auto mime_type = m_response->header_list()->extract_mime_type(); + auto mime_type = Fetch::Infrastructure::extract_mime_type(m_response->header_list()); // 2. If mimeType is failure, then set mimeType to text/xml. if (!mime_type.has_value())