LibWeb: Implement WorkerGlobalScope.importScripts()

This method allows workers to synchronously import one or more scripts.
This commit is contained in:
Tim Ledbetter 2024-05-23 13:57:08 +01:00 committed by Andreas Kling
parent 975a067f58
commit bb923983fc
Notes: sideshowbarker 2024-07-17 03:19:14 +09:00
8 changed files with 134 additions and 12 deletions

View file

@ -1,9 +1,11 @@
/*
* Copyright (c) 2022-2023, networkException <networkexception@serenityos.org>
* Copyright (c) 2024, Tim Ledbetter <timledbetter@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/EventLoop.h>
#include <LibJS/Heap/HeapFunction.h>
#include <LibJS/Runtime/ModuleRequest.h>
#include <LibTextCodec/Decoder.h>
@ -423,6 +425,83 @@ WebIDL::ExceptionOr<void> fetch_classic_worker_script(URL::URL const& url, Envir
return {};
}
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-worker-imported-script
WebIDL::ExceptionOr<JS::NonnullGCPtr<ClassicScript>> fetch_a_classic_worker_imported_script(URL::URL const& url, HTML::EnvironmentSettingsObject& settings_object, PerformTheFetchHook perform_fetch)
{
auto& realm = settings_object.realm();
auto& vm = realm.vm();
// 1. Let response be null.
JS::GCPtr<Fetch::Infrastructure::Response> response = nullptr;
// 2. Let bodyBytes be null.
Fetch::Infrastructure::FetchAlgorithms::BodyBytes body_bytes;
// 3. Let request be a new request whose URL is url, client is settingsObject, destination is "script", initiator type is "other",
// parser metadata is "not parser-inserted", and whose use-URL-credentials flag is set.
auto request = Fetch::Infrastructure::Request::create(vm);
request->set_url(url);
request->set_client(&settings_object);
request->set_destination(Fetch::Infrastructure::Request::Destination::Script);
request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Other);
request->set_parser_metadata(Fetch::Infrastructure::Request::ParserMetadata::NotParserInserted);
request->set_use_url_credentials(true);
auto process_response_consume_body = [&response, &body_bytes](JS::NonnullGCPtr<Fetch::Infrastructure::Response> res, Fetch::Infrastructure::FetchAlgorithms::BodyBytes bb) {
// 1. Set bodyBytes to bb.
body_bytes = move(bb);
// 2. Set response to res.
response = res;
};
// 4. If performFetch was given, run performFetch with request, isTopLevel, and with processResponseConsumeBody as defined below.
if (perform_fetch) {
TRY(perform_fetch->function()(request, TopLevelModule::Yes, move(process_response_consume_body)));
}
// Otherwise, fetch request with processResponseConsumeBody set to processResponseConsumeBody as defined below.
else {
Fetch::Infrastructure::FetchAlgorithms::Input fetch_algorithms_input {};
fetch_algorithms_input.process_response_consume_body = move(process_response_consume_body);
TRY(Fetch::Fetching::fetch(realm, request, Fetch::Infrastructure::FetchAlgorithms::create(vm, move(fetch_algorithms_input))));
}
// 5. Pause until response is not null.
auto& event_loop = settings_object.responsible_event_loop();
event_loop.spin_until([&]() {
return response;
});
// 6. Set response to response's unsafe response.
response = response->unsafe_response();
// 7. If any of the following are true:
// - bodyBytes is null or failure;
// - response's status is not an ok status; or
// - the result of extracting a MIME type from response's header list is not a JavaScript MIME type,
// then throw a "NetworkError" DOMException.
if (body_bytes.template has<Empty>() || body_bytes.template has<Fetch::Infrastructure::FetchAlgorithms::ConsumeBodyFailureTag>()
|| !Fetch::Infrastructure::is_ok_status(response->status())
|| !response->header_list()->extract_mime_type().has_value() || !response->header_list()->extract_mime_type()->is_javascript()) {
return WebIDL::NetworkError::create(realm, "Network error"_fly_string);
}
// 8. Let sourceText be the result of UTF-8 decoding bodyBytes.
auto decoder = TextCodec::decoder_for("UTF-8"sv);
VERIFY(decoder.has_value());
auto source_text = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, body_bytes.get<ByteBuffer>()).release_value_but_fixme_should_propagate_errors();
// 9. Let mutedErrors 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;
// 10. Let script be the result of creating a classic script given sourceText, settingsObject, response's URL, the default classic script fetch options, and mutedErrors.
auto response_url = response->url().value_or({});
auto script = ClassicScript::create(response_url.to_byte_string(), source_text, settings_object, response_url, 1, muted_errors);
// 11. Return script.
return script;
}
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-module-worker-script-tree
WebIDL::ExceptionOr<void> fetch_module_worker_script_graph(URL::URL const& url, EnvironmentSettingsObject& fetch_client, Fetch::Infrastructure::Request::Destination destination, EnvironmentSettingsObject& settings_object, PerformTheFetchHook perform_fetch, OnFetchScriptComplete on_complete)
{