mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-26 22:38:51 +00:00
LibWeb: Implement the fetch a classic script AO
Note that this unfortunately requires the same workaround as <link> elements to handle CORS cross-origin responses.
This commit is contained in:
parent
00fa23237a
commit
12976b74ca
Notes:
sideshowbarker
2024-07-17 03:27:40 +09:00
Author: https://github.com/trflynn89
Commit: 12976b74ca
Pull-request: https://github.com/SerenityOS/serenity/pull/18772
Issue: https://github.com/SerenityOS/serenity/issues/18432
2 changed files with 158 additions and 0 deletions
|
@ -6,6 +6,16 @@
|
|||
|
||||
#include <AK/URLParser.h>
|
||||
#include <LibJS/Runtime/ModuleRequest.h>
|
||||
#include <LibTextCodec/Decoder.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/Fetch/Fetching/Fetching.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
|
||||
#include <LibWeb/HTML/HTMLScriptElement.h>
|
||||
#include <LibWeb/HTML/PotentialCORSRequest.h>
|
||||
#include <LibWeb/HTML/Scripting/ClassicScript.h>
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/Scripting/Fetching.h>
|
||||
#include <LibWeb/HTML/Scripting/ModuleScript.h>
|
||||
|
@ -210,6 +220,126 @@ Optional<AK::URL> resolve_url_like_module_specifier(DeprecatedString const& spec
|
|||
return url;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#set-up-the-classic-script-request
|
||||
static void set_up_classic_script_request(Fetch::Infrastructure::Request& request, ScriptFetchOptions const& options)
|
||||
{
|
||||
// Set request's cryptographic nonce metadata to options's cryptographic nonce, its integrity metadata to options's
|
||||
// integrity metadata, its parser metadata to options's parser metadata, its referrer policy to options's referrer
|
||||
// policy, its render-blocking to options's render-blocking, and its priority to options's fetch priority.
|
||||
request.set_cryptographic_nonce_metadata(options.cryptographic_nonce);
|
||||
request.set_integrity_metadata(options.integrity_metadata);
|
||||
request.set_parser_metadata(options.parser_metadata);
|
||||
request.set_referrer_policy(options.referrer_policy);
|
||||
request.set_render_blocking(options.render_blocking);
|
||||
request.set_priority(options.fetch_priority);
|
||||
}
|
||||
|
||||
class ClassicScriptResponseHandler final : public RefCounted<ClassicScriptResponseHandler> {
|
||||
public:
|
||||
ClassicScriptResponseHandler(JS::NonnullGCPtr<HTMLScriptElement> element, EnvironmentSettingsObject& settings_object, ScriptFetchOptions options, String character_encoding, OnFetchScriptComplete on_complete)
|
||||
: m_element(element)
|
||||
, m_settings_object(settings_object)
|
||||
, m_options(move(options))
|
||||
, m_character_encoding(move(character_encoding))
|
||||
, m_on_complete(move(on_complete))
|
||||
{
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetching-scripts:concept-fetch-4
|
||||
void process_response(JS::NonnullGCPtr<Fetch::Infrastructure::Response> response, Fetch::Infrastructure::FetchAlgorithms::BodyBytes body_bytes)
|
||||
{
|
||||
// 1. Set response to response's unsafe response.
|
||||
response = response->unsafe_response();
|
||||
|
||||
// 2. If either of the following conditions are met:
|
||||
// - bodyBytes is null or failure; or
|
||||
// - response's status is not an ok status,
|
||||
if (body_bytes.template has<Empty>() || body_bytes.template has<Fetch::Infrastructure::FetchAlgorithms::ConsumeBodyFailureTag>() || !Fetch::Infrastructure::is_ok_status(response->status())) {
|
||||
// then run onComplete given null, and abort these steps.
|
||||
m_on_complete(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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().release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
// 4. Set character encoding to the result of legacy extracting an encoding given potentialMIMETypeForEncoding
|
||||
// and character encoding.
|
||||
auto character_encoding = Fetch::Infrastructure::legacy_extract_an_encoding(potential_mime_type_for_encoding, m_character_encoding);
|
||||
|
||||
// 5. Let source text be the result of decoding bodyBytes to Unicode, using character encoding as the fallback
|
||||
// encoding.
|
||||
auto fallback_decoder = TextCodec::decoder_for(character_encoding);
|
||||
VERIFY(fallback_decoder.has_value());
|
||||
|
||||
auto source_text = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*fallback_decoder, body_bytes.template get<ByteBuffer>()).release_value_but_fixme_should_propagate_errors();
|
||||
|
||||
// 6. Let muted errors be true if response was CORS-cross-origin, and false otherwise.
|
||||
auto muted_errors = response->is_cors_cross_origin() ? ClassicScript::MutedErrors::Yes : ClassicScript::MutedErrors::No;
|
||||
|
||||
// 7. Let script be the result of creating a classic script given source text, settings object, response's URL,
|
||||
// options, and muted errors.
|
||||
// FIXME: Pass options.
|
||||
auto script = ClassicScript::create(m_element->document().url().to_deprecated_string(), source_text, *m_settings_object, response->url().value_or({}), 1, muted_errors);
|
||||
|
||||
// 8. Run onComplete given script.
|
||||
m_on_complete(script);
|
||||
}
|
||||
|
||||
private:
|
||||
JS::Handle<HTMLScriptElement> m_element;
|
||||
JS::Handle<EnvironmentSettingsObject> m_settings_object;
|
||||
ScriptFetchOptions m_options;
|
||||
String m_character_encoding;
|
||||
OnFetchScriptComplete m_on_complete;
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
|
||||
WebIDL::ExceptionOr<void> fetch_classic_script(JS::NonnullGCPtr<HTMLScriptElement> element, AK::URL const& url, EnvironmentSettingsObject& settings_object, ScriptFetchOptions options, CORSSettingAttribute cors_setting, String character_encoding, OnFetchScriptComplete on_complete)
|
||||
{
|
||||
auto& realm = element->realm();
|
||||
auto& vm = realm.vm();
|
||||
|
||||
// 1. Let request be the result of creating a potential-CORS request given url, "script", and CORS setting.
|
||||
auto request = create_potential_CORS_request(vm, url, Fetch::Infrastructure::Request::Destination::Script, cors_setting);
|
||||
|
||||
// 2. Set request's client to settings object.
|
||||
request->set_client(&settings_object);
|
||||
|
||||
// 3. Set request's initiator type to "script".
|
||||
request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Script);
|
||||
|
||||
// 4. Set up the classic script request given request and options.
|
||||
set_up_classic_script_request(*request, options);
|
||||
|
||||
// 5. Fetch request with the following processResponseConsumeBody steps given response response and null, failure,
|
||||
// or a byte sequence bodyBytes:
|
||||
auto response_handler = make_ref_counted<ClassicScriptResponseHandler>(element, settings_object, move(options), move(character_encoding), move(on_complete));
|
||||
|
||||
Fetch::Infrastructure::FetchAlgorithms::Input fetch_algorithms_input {};
|
||||
fetch_algorithms_input.process_response_consume_body = [&realm, response_handler = move(response_handler)](auto response, auto body_bytes) {
|
||||
// FIXME: See HTMLLinkElement::default_fetch_and_process_linked_resource for thorough notes on the workaround
|
||||
// added here for CORS cross-origin responses. The gist is that all cross-origin responses will have a
|
||||
// null bodyBytes. So we must read the actual body from the unsafe response.
|
||||
// https://github.com/whatwg/html/issues/9066
|
||||
if (response->is_cors_cross_origin() && body_bytes.template has<Empty>() && response->unsafe_response()->body().has_value()) {
|
||||
auto process_body = [response, response_handler](auto bytes) {
|
||||
response_handler->process_response(response, move(bytes));
|
||||
};
|
||||
auto process_body_error = [response, response_handler](auto&) {
|
||||
response_handler->process_response(response, Fetch::Infrastructure::FetchAlgorithms::ConsumeBodyFailureTag {});
|
||||
};
|
||||
|
||||
response->unsafe_response()->body()->fully_read(realm, move(process_body), move(process_body_error), JS::NonnullGCPtr { realm.global_object() }).release_value_but_fixme_should_propagate_errors();
|
||||
} else {
|
||||
response_handler->process_response(response, move(body_bytes));
|
||||
}
|
||||
};
|
||||
|
||||
TRY(Fetch::Fetching::fetch(element->realm(), request, Fetch::Infrastructure::FetchAlgorithms::create(vm, move(fetch_algorithms_input))));
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
|
||||
void fetch_internal_module_script_graph(JS::ModuleRequest const& module_request, EnvironmentSettingsObject& fetch_client_settings_object, StringView destination, Script& referring_script, HashTable<ModuleLocationTuple> const& visited_set, OnFetchScriptComplete on_complete)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue