mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-30 12:49:19 +00:00
LibWeb: Implement WorkerGlobalScope.importScripts()
This method allows workers to synchronously import one or more scripts.
This commit is contained in:
parent
975a067f58
commit
bb923983fc
Notes:
sideshowbarker
2024-07-17 03:19:14 +09:00
Author: https://github.com/tcl3
Commit: bb923983fc
Pull-request: https://github.com/SerenityOS/serenity/pull/24423
Reviewed-by: https://github.com/alimpfard
8 changed files with 134 additions and 12 deletions
|
@ -0,0 +1 @@
|
|||
importScripts() works as expected: YES
|
10
Tests/LibWeb/Text/input/Worker/Worker-importScripts.html
Normal file
10
Tests/LibWeb/Text/input/Worker/Worker-importScripts.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<script src="../include.js"></script>
|
||||
<script>
|
||||
asyncTest((done) => {
|
||||
const work = new Worker("worker-importScripts.js");
|
||||
work.onmessage = (evt) => {
|
||||
println(`importScripts() works as expected: ${evt.data}`);
|
||||
done();
|
||||
};
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,3 @@
|
|||
function importedFunction() {
|
||||
return "YES";
|
||||
}
|
3
Tests/LibWeb/Text/input/Worker/worker-importScripts.js
Normal file
3
Tests/LibWeb/Text/input/Worker/worker-importScripts.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
importScripts("worker-importScripts-scriptToImport.js");
|
||||
const fromImportedScript = importedFunction();
|
||||
self.postMessage(fromImportedScript);
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -89,6 +89,7 @@ Optional<URL::URL> resolve_url_like_module_specifier(ByteString const& specifier
|
|||
|
||||
WebIDL::ExceptionOr<void> fetch_classic_script(JS::NonnullGCPtr<HTMLScriptElement>, URL::URL const&, EnvironmentSettingsObject& settings_object, ScriptFetchOptions options, CORSSettingAttribute cors_setting, String character_encoding, OnFetchScriptComplete on_complete);
|
||||
WebIDL::ExceptionOr<void> fetch_classic_worker_script(URL::URL const&, EnvironmentSettingsObject& fetch_client, Fetch::Infrastructure::Request::Destination, EnvironmentSettingsObject& settings_object, PerformTheFetchHook, OnFetchScriptComplete);
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<ClassicScript>> fetch_a_classic_worker_imported_script(URL::URL const&, HTML::EnvironmentSettingsObject&, PerformTheFetchHook = nullptr);
|
||||
WebIDL::ExceptionOr<void> fetch_module_worker_script_graph(URL::URL const&, EnvironmentSettingsObject& fetch_client, Fetch::Infrastructure::Request::Destination, EnvironmentSettingsObject& settings_object, PerformTheFetchHook, OnFetchScriptComplete);
|
||||
WebIDL::ExceptionOr<void> fetch_worklet_module_worker_script_graph(URL::URL const&, EnvironmentSettingsObject& fetch_client, Fetch::Infrastructure::Request::Destination, EnvironmentSettingsObject& settings_object, PerformTheFetchHook, OnFetchScriptComplete);
|
||||
void fetch_internal_module_script_graph(JS::Realm&, JS::ModuleRequest const& module_request, EnvironmentSettingsObject& fetch_client_settings_object, Fetch::Infrastructure::Request::Destination, ScriptFetchOptions const&, Script& referring_script, HashTable<ModuleLocationTuple> const& visited_set, PerformTheFetchHook, OnFetchScriptComplete on_complete);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <LibWeb/HTML/EventHandler.h>
|
||||
#include <LibWeb/HTML/EventNames.h>
|
||||
#include <LibWeb/HTML/MessageEvent.h>
|
||||
#include <LibWeb/HTML/Scripting/ClassicScript.h>
|
||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||
#include <LibWeb/HTML/WorkerGlobalScope.h>
|
||||
#include <LibWeb/HTML/WorkerLocation.h>
|
||||
|
@ -70,28 +71,51 @@ void WorkerGlobalScope::set_internal_port(JS::NonnullGCPtr<MessagePort> port)
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/workers.html#importing-scripts-and-libraries
|
||||
WebIDL::ExceptionOr<void> WorkerGlobalScope::import_scripts(Vector<String> urls)
|
||||
WebIDL::ExceptionOr<void> WorkerGlobalScope::import_scripts(Vector<String> const& urls, PerformTheFetchHook perform_fetch)
|
||||
{
|
||||
// The algorithm may optionally be customized by supplying custom perform the fetch hooks,
|
||||
// which if provided will be used when invoking fetch a classic worker-imported script.
|
||||
// NOTE: Service Workers is an example of a specification that runs this algorithm with its own options for the perform the fetch hook.
|
||||
|
||||
// FIXME: 1. If worker global scope's type is "module", throw a TypeError exception.
|
||||
// FIXME: 2. Let settings object be the current settings object.
|
||||
|
||||
// 2. Let settings object be the current settings object.
|
||||
auto& settings_object = HTML::current_settings_object();
|
||||
|
||||
// 3. If urls is empty, return.
|
||||
if (urls.is_empty())
|
||||
return {};
|
||||
|
||||
// FIXME: 4. Parse each value in urls relative to settings object. If any fail, throw a "SyntaxError" DOMException.
|
||||
// FIXME: 5. For each url in the resulting URL records, run these substeps:
|
||||
// 1. Fetch a classic worker-imported script given url and settings object, passing along any custom perform the fetch steps provided.
|
||||
// If this succeeds, let script be the result. Otherwise, rethrow the exception.
|
||||
// 2. Run the classic script script, with the rethrow errors argument set to true.
|
||||
// NOTE: script will run until it either returns, fails to parse, fails to catch an exception,
|
||||
// or gets prematurely aborted by the terminate a worker algorithm defined above.
|
||||
// If an exception was thrown or if the script was prematurely aborted, then abort all these steps,
|
||||
// letting the exception or aborting continue to be processed by the calling script.
|
||||
// 4. Let urlRecords be « ».
|
||||
Vector<URL::URL> url_records;
|
||||
url_records.ensure_capacity(urls.size());
|
||||
|
||||
// 5. For each url of urls:
|
||||
for (auto const& url : urls) {
|
||||
// 1. Let urlRecord be the result of encoding-parsing a URL given url, relative to settings object.
|
||||
auto url_record = settings_object.parse_url(url);
|
||||
|
||||
// 2. If urlRecord is failure, then throw a "SyntaxError" DOMException.
|
||||
if (!url_record.is_valid())
|
||||
return WebIDL::SyntaxError::create(realm(), "Invalid URL"_fly_string);
|
||||
|
||||
// 3. Append urlRecord to urlRecords.
|
||||
url_records.unchecked_append(url_record);
|
||||
}
|
||||
|
||||
// 6. For each urlRecord of urlRecords:
|
||||
for (auto const& url_record : url_records) {
|
||||
// 1. Fetch a classic worker-imported script given urlRecord and settings object, passing along performFetch if provided.
|
||||
// If this succeeds, let script be the result. Otherwise, rethrow the exception.
|
||||
auto classic_script = TRY(HTML::fetch_a_classic_worker_imported_script(url_record, settings_object, perform_fetch));
|
||||
|
||||
// 2. Run the classic script script, with the rethrow errors argument set to true.
|
||||
// NOTE: script will run until it either returns, fails to parse, fails to catch an exception,
|
||||
// or gets prematurely aborted by the terminate a worker algorithm defined above.
|
||||
// If an exception was thrown or if the script was prematurely aborted, then abort all these steps,
|
||||
// letting the exception or aborting continue to be processed by the calling script.
|
||||
TRY(classic_script->run(ClassicScript::RethrowErrors::Yes));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/Scripting/Fetching.h>
|
||||
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
|
||||
#include <LibWeb/HTML/WorkerLocation.h>
|
||||
#include <LibWeb/HTML/WorkerNavigator.h>
|
||||
|
@ -66,7 +67,7 @@ public:
|
|||
|
||||
JS::NonnullGCPtr<WorkerLocation> location() const;
|
||||
JS::NonnullGCPtr<WorkerNavigator> navigator() const;
|
||||
WebIDL::ExceptionOr<void> import_scripts(Vector<String> urls);
|
||||
WebIDL::ExceptionOr<void> import_scripts(Vector<String> const& urls, PerformTheFetchHook = nullptr);
|
||||
|
||||
#undef __ENUMERATE
|
||||
#define __ENUMERATE(attribute_name, event_name) \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue