mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-11 17:00:37 +00:00
LibWeb: Support "importmap" scripts
This commit is contained in:
parent
ccb363c443
commit
e487f70bbf
Notes:
sideshowbarker
2024-07-18 04:46:35 +09:00
Author: https://github.com/jamierocks Commit: https://github.com/SerenityOS/serenity/commit/e487f70bbf Pull-request: https://github.com/SerenityOS/serenity/pull/23954 Issue: https://github.com/SerenityOS/serenity/issues/23117
13 changed files with 402 additions and 9 deletions
|
@ -0,0 +1 @@
|
|||
true
|
1
Tests/LibWeb/Text/expected/HTML/import-maps.txt
Normal file
1
Tests/LibWeb/Text/expected/HTML/import-maps.txt
Normal file
|
@ -0,0 +1 @@
|
|||
hello, friends!
|
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
println(HTMLScriptElement.supports("importmap"));
|
||||
});
|
||||
</script>
|
15
Tests/LibWeb/Text/input/HTML/import-maps.html
Normal file
15
Tests/LibWeb/Text/input/HTML/import-maps.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"application": "./import-maps.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
import * as Application from 'application';
|
||||
test(() => {
|
||||
Application.main();
|
||||
});
|
||||
</script>
|
3
Tests/LibWeb/Text/input/HTML/import-maps.js
Normal file
3
Tests/LibWeb/Text/input/HTML/import-maps.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function main() {
|
||||
println("hello, friends!");
|
||||
}
|
|
@ -399,6 +399,8 @@ set(SOURCES
|
|||
HTML/Scripting/EnvironmentSettingsSnapshot.cpp
|
||||
HTML/Scripting/ExceptionReporter.cpp
|
||||
HTML/Scripting/Fetching.cpp
|
||||
HTML/Scripting/ImportMap.cpp
|
||||
HTML/Scripting/ImportMapParseResult.cpp
|
||||
HTML/Scripting/ModuleMap.cpp
|
||||
HTML/Scripting/ModuleScript.cpp
|
||||
HTML/Scripting/Script.cpp
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <LibWeb/HTML/HTMLScriptElement.h>
|
||||
#include <LibWeb/HTML/Scripting/ClassicScript.h>
|
||||
#include <LibWeb/HTML/Scripting/Fetching.h>
|
||||
#include <LibWeb/HTML/Scripting/ImportMapParseResult.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Infra/CharacterTypes.h>
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
#include <LibWeb/MimeSniff/MimeType.h>
|
||||
|
@ -127,9 +129,11 @@ void HTMLScriptElement::execute_script()
|
|||
|
||||
// 2. Run the module script given by el's result.
|
||||
(void)verify_cast<JavaScriptModuleScript>(*m_result.get<JS::NonnullGCPtr<Script>>()).run();
|
||||
} else if (m_script_type == ScriptType::ImportMap) {
|
||||
// FIXME: 1. Register an import map given el's relevant global object and el's result.
|
||||
dbgln("FIXME: HTMLScriptElement import map support");
|
||||
}
|
||||
// -> "importmap"
|
||||
else if (m_script_type == ScriptType::ImportMap) {
|
||||
// 1. Register an import map given el's relevant global object and el's result.
|
||||
m_result.get<JS::NonnullGCPtr<ImportMapParseResult>>()->register_import_map(verify_cast<Window>(relevant_global_object(*this)));
|
||||
}
|
||||
|
||||
// 7. Decrement the ignore-destructive-writes counter of document, if it was incremented in the earlier step.
|
||||
|
@ -433,13 +437,26 @@ void HTMLScriptElement::prepare_script()
|
|||
}
|
||||
// -> "importmap"
|
||||
else if (m_script_type == ScriptType::ImportMap) {
|
||||
// FIXME: 1. If el's relevant global object's import maps allowed is false, then queue an element task on the DOM manipulation task source given el to fire an event named error at el, and return.
|
||||
// FIXME: need to check if relevant global object is a Window - is this correct?
|
||||
auto& global = relevant_global_object(*this);
|
||||
|
||||
// FIXME: 2. Set el's relevant global object's import maps allowed to false.
|
||||
// 1. If el's relevant global object's import maps allowed is false, then queue an element task on the DOM manipulation task source given el to fire an event named error at el, and return.
|
||||
if (is<Window>(global) && !verify_cast<Window>(global).import_maps_allowed()) {
|
||||
queue_an_element_task(HTML::Task::Source::DOMManipulation, [this] {
|
||||
dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: 3. Let result be the result of creating an import map parse result given source text and base URL.
|
||||
// 2. Set el's relevant global object's import maps allowed to false.
|
||||
if (is<Window>(global))
|
||||
verify_cast<Window>(global).set_import_maps_allowed(false);
|
||||
|
||||
// FIXME: 4. Mark as ready el given result.
|
||||
// 3. Let result be the result of creating an import map parse result given source text and base URL.
|
||||
auto result = ImportMapParseResult::create(realm(), source_text.to_byte_string(), base_url);
|
||||
|
||||
// 4. Mark as ready el given result.
|
||||
mark_as_ready(Result(move(result)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
|
||||
#include <LibWeb/HTML/CORSSettingAttribute.h>
|
||||
#include <LibWeb/HTML/HTMLElement.h>
|
||||
#include <LibWeb/HTML/Scripting/ImportMapParseResult.h>
|
||||
#include <LibWeb/HTML/Scripting/Script.h>
|
||||
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
|
||||
|
||||
|
@ -47,7 +48,7 @@ public:
|
|||
// https://html.spec.whatwg.org/multipage/scripting.html#dom-script-supports
|
||||
static bool supports(JS::VM&, StringView type)
|
||||
{
|
||||
return type.is_one_of("classic"sv, "module"sv);
|
||||
return type.is_one_of("classic"sv, "module"sv, "importmap"sv);
|
||||
}
|
||||
|
||||
void set_source_line_number(Badge<HTMLParser>, size_t source_line_number) { m_source_line_number = source_line_number; }
|
||||
|
@ -81,7 +82,7 @@ private:
|
|||
struct Null { };
|
||||
};
|
||||
|
||||
using Result = Variant<ResultState::Uninitialized, ResultState::Null, JS::NonnullGCPtr<HTML::Script>>;
|
||||
using Result = Variant<ResultState::Uninitialized, ResultState::Null, JS::NonnullGCPtr<HTML::Script>, JS::NonnullGCPtr<HTML::ImportMapParseResult>>;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#mark-as-ready
|
||||
void mark_as_ready(Result);
|
||||
|
|
211
Userland/Libraries/LibWeb/HTML/Scripting/ImportMap.cpp
Normal file
211
Userland/Libraries/LibWeb/HTML/Scripting/ImportMap.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Console.h>
|
||||
#include <LibJS/Runtime/ConsoleObject.h>
|
||||
#include <LibWeb/Bindings/HostDefined.h>
|
||||
#include <LibWeb/DOMURL/DOMURL.h>
|
||||
#include <LibWeb/HTML/Scripting/Fetching.h>
|
||||
#include <LibWeb/HTML/Scripting/ImportMap.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/Infra/JSON.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#parse-an-import-map-string
|
||||
WebIDL::ExceptionOr<ImportMap> parse_import_map_string(JS::Realm& realm, ByteString const& input, URL::URL base_url)
|
||||
{
|
||||
HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm) };
|
||||
|
||||
// 1. Let parsed be the result of parsing a JSON string to an Infra value given input.
|
||||
auto parsed = TRY(Infra::parse_json_string_to_javascript_value(realm, input));
|
||||
|
||||
// 2. If parsed is not an ordered map, then throw a TypeError indicating that the top-level value needs to be a JSON object.
|
||||
if (!parsed.is_object())
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, String::formatted("The top-level value of an importmap needs to be a JSON object.").release_value_but_fixme_should_propagate_errors() };
|
||||
auto& parsed_object = parsed.as_object();
|
||||
|
||||
// 3. Let sortedAndNormalizedImports be an empty ordered map.
|
||||
ModuleSpecifierMap sorted_and_normalised_imports;
|
||||
|
||||
// 4. If parsed["imports"] exists, then:
|
||||
if (TRY(parsed_object.has_property("imports"))) {
|
||||
auto imports = TRY(parsed_object.get("imports"));
|
||||
|
||||
// If parsed["imports"] is not an ordered map, then throw a TypeError indicating that the value for the "imports" top-level key needs to be a JSON object.
|
||||
if (!imports.is_object())
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, String::formatted("The 'imports' top-level value of an importmap needs to be a JSON object.").release_value_but_fixme_should_propagate_errors() };
|
||||
|
||||
// Set sortedAndNormalizedImports to the result of sorting and normalizing a module specifier map given parsed["imports"] and baseURL.
|
||||
sorted_and_normalised_imports = TRY(sort_and_normalise_module_specifier_map(realm, imports.as_object(), base_url));
|
||||
}
|
||||
|
||||
// 5. Let sortedAndNormalizedScopes be an empty ordered map.
|
||||
HashMap<URL::URL, ModuleSpecifierMap> sorted_and_normalised_scopes;
|
||||
|
||||
// 6. If parsed["scopes"] exists, then:
|
||||
if (TRY(parsed_object.has_property("scopes"))) {
|
||||
auto scopes = TRY(parsed_object.get("scopes"));
|
||||
|
||||
// If parsed["scopes"] is not an ordered map, then throw a TypeError indicating that the value for the "scopes" top-level key needs to be a JSON object.
|
||||
if (!scopes.is_object())
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, String::formatted("The 'scopes' top-level value of an importmap needs to be a JSON object.").release_value_but_fixme_should_propagate_errors() };
|
||||
|
||||
// Set sortedAndNormalizedScopes to the result of sorting and normalizing scopes given parsed["scopes"] and baseURL.
|
||||
sorted_and_normalised_scopes = TRY(sort_and_normalise_scopes(realm, scopes.as_object(), base_url));
|
||||
}
|
||||
|
||||
// 7. If parsed's keys contains any items besides "imports" or "scopes", then the user agent should report a warning to the console indicating that an invalid top-level key was present in the import map.
|
||||
for (auto& key : parsed_object.shape().property_table().keys()) {
|
||||
if (key.as_string() == "imports" || key.as_string() == "imports")
|
||||
continue;
|
||||
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
TRY_OR_THROW_OOM(realm.vm(), String::formatted("An invalid top-level key ({}) was present in the import map", key.as_string())));
|
||||
}
|
||||
|
||||
// 8. Return an import map whose imports are sortedAndNormalizedImports and whose scopes are sortedAndNormalizedScopes.
|
||||
ImportMap import_map;
|
||||
import_map.set_imports(sorted_and_normalised_imports);
|
||||
return import_map;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#normalizing-a-specifier-key
|
||||
WebIDL::ExceptionOr<Optional<DeprecatedFlyString>> normalise_specifier_key(JS::Realm& realm, DeprecatedFlyString specifier_key, URL::URL base_url)
|
||||
{
|
||||
// 1. If specifierKey is the empty string, then:
|
||||
if (specifier_key.is_empty()) {
|
||||
// 1. The user agent may report a warning to the console indicating that specifier keys may not be the empty string.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
TRY_OR_THROW_OOM(realm.vm(), String::formatted("Specifier keys may not be empty")));
|
||||
|
||||
// 2. Return null.
|
||||
return Optional<DeprecatedFlyString> {};
|
||||
}
|
||||
|
||||
// 2. Let url be the result of resolving a URL-like module specifier, given specifierKey and baseURL.
|
||||
auto url = resolve_url_like_module_specifier(specifier_key, base_url);
|
||||
|
||||
// 3. If url is not null, then return the serialization of url.
|
||||
if (url.has_value())
|
||||
return url->serialize();
|
||||
|
||||
// 4. Return specifierKey.
|
||||
return specifier_key;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#sorting-and-normalizing-a-module-specifier-map
|
||||
WebIDL::ExceptionOr<ModuleSpecifierMap> sort_and_normalise_module_specifier_map(JS::Realm& realm, JS::Object& original_map, URL::URL base_url)
|
||||
{
|
||||
// 1. Let normalized be an empty ordered map.
|
||||
ModuleSpecifierMap normalised;
|
||||
|
||||
// 2. For each specifierKey → value of originalMap:
|
||||
for (auto& specifier_key : original_map.shape().property_table().keys()) {
|
||||
auto value = TRY(original_map.get(specifier_key.as_string()));
|
||||
|
||||
// 1. Let normalizedSpecifierKey be the result of normalizing a specifier key given specifierKey and baseURL.
|
||||
auto normalised_specifier_key = TRY(normalise_specifier_key(realm, specifier_key.as_string(), base_url));
|
||||
|
||||
// 2. If normalizedSpecifierKey is null, then continue.
|
||||
if (!normalised_specifier_key.has_value())
|
||||
continue;
|
||||
|
||||
// 3. If value is not a string, then:
|
||||
if (!value.is_string()) {
|
||||
// 1. The user agent may report a warning to the console indicating that addresses need to be strings.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
TRY_OR_THROW_OOM(realm.vm(), String::formatted("Addresses need to be strings")));
|
||||
|
||||
// 2. Set normalized[normalizedSpecifierKey] to null.
|
||||
normalised.set(normalised_specifier_key.value(), {});
|
||||
|
||||
// 3. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4. Let addressURL be the result of resolving a URL-like module specifier given value and baseURL.
|
||||
auto address_url = resolve_url_like_module_specifier(value.as_string().byte_string(), base_url);
|
||||
|
||||
// 5. If addressURL is null, then:
|
||||
if (!address_url.has_value()) {
|
||||
// 1. The user agent may report a warning to the console indicating that the address was invalid.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
TRY_OR_THROW_OOM(realm.vm(), String::formatted("Address was invalid")));
|
||||
|
||||
// 2. Set normalized[normalizedSpecifierKey] to null.
|
||||
normalised.set(normalised_specifier_key.value(), {});
|
||||
|
||||
// 3. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 6. If specifierKey ends with U+002F (/), and the serialization of addressURL does not end with U+002F (/), then:
|
||||
if (specifier_key.as_string().ends_with("/"sv) && !address_url->serialize().ends_with("/"sv)) {
|
||||
// 1. The user agent may report a warning to the console indicating that an invalid address was given for the specifier key specifierKey; since specifierKey ends with a slash, the address needs to as well.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
TRY_OR_THROW_OOM(realm.vm(), String::formatted("An invalid address was given for the specifier key ({}); since specifierKey ends with a slash, the address needs to as well", specifier_key.as_string())));
|
||||
|
||||
// 2. Set normalized[normalizedSpecifierKey] to null.
|
||||
normalised.set(normalised_specifier_key.value(), {});
|
||||
|
||||
// 3. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 7. Set normalized[normalizedSpecifierKey] to addressURL.
|
||||
normalised.set(normalised_specifier_key.value(), address_url.value());
|
||||
}
|
||||
|
||||
// 3. Return the result of sorting in descending order normalized, with an entry a being less than an entry b if a's key is code unit less than b's key.
|
||||
return normalised;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#sorting-and-normalizing-scopes
|
||||
WebIDL::ExceptionOr<HashMap<URL::URL, ModuleSpecifierMap>> sort_and_normalise_scopes(JS::Realm& realm, JS::Object& original_map, URL::URL base_url)
|
||||
{
|
||||
// 1. Let normalized be an empty ordered map.
|
||||
HashMap<URL::URL, ModuleSpecifierMap> normalised;
|
||||
|
||||
// 2. For each scopePrefix → potentialSpecifierMap of originalMap:
|
||||
for (auto& scope_prefix : original_map.shape().property_table().keys()) {
|
||||
auto potential_specifier_map = TRY(original_map.get(scope_prefix.as_string()));
|
||||
|
||||
// 1. If potentialSpecifierMap is not an ordered map, then throw a TypeError indicating that the value of the scope with prefix scopePrefix needs to be a JSON object.
|
||||
if (!potential_specifier_map.is_object())
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, String::formatted("The value of the scope with the prefix '{}' needs to be a JSON object.", scope_prefix.as_string()).release_value_but_fixme_should_propagate_errors() };
|
||||
|
||||
// 2. Let scopePrefixURL be the result of URL parsing scopePrefix with baseURL.
|
||||
auto scope_prefix_url = DOMURL::parse(scope_prefix.as_string(), base_url);
|
||||
|
||||
// 3. If scopePrefixURL is failure, then:
|
||||
if (!scope_prefix_url.is_valid()) {
|
||||
// 1. The user agent may report a warning to the console that the scope prefix URL was not parseable.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
TRY_OR_THROW_OOM(realm.vm(), String::formatted("The scope prefix URL ({}) was not parseable", scope_prefix.as_string())));
|
||||
|
||||
// 2. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4. Let normalizedScopePrefix be the serialization of scopePrefixURL.
|
||||
auto normalised_scope_prefix = scope_prefix_url.serialize();
|
||||
|
||||
// 5. Set normalized[normalizedScopePrefix] to the result of sorting and normalizing a module specifier map given potentialSpecifierMap and baseURL.
|
||||
normalised.set(normalised_scope_prefix, TRY(sort_and_normalise_module_specifier_map(realm, potential_specifier_map.as_object(), base_url)));
|
||||
}
|
||||
|
||||
// 3. Return the result of sorting in descending order normalized, with an entry a being less than an entry b if a's key is code unit less than b's key.
|
||||
return normalised;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022, networkException <networkexception@serenityos.org>
|
||||
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -7,6 +8,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
|
@ -19,13 +22,20 @@ public:
|
|||
|
||||
ModuleSpecifierMap const& imports() const { return m_imports; }
|
||||
ModuleSpecifierMap& imports() { return m_imports; }
|
||||
void set_imports(ModuleSpecifierMap const& imports) { m_imports = imports; }
|
||||
|
||||
HashMap<URL::URL, ModuleSpecifierMap> const& scopes() const { return m_scopes; }
|
||||
HashMap<URL::URL, ModuleSpecifierMap>& scopes() { return m_scopes; }
|
||||
void set_scopes(HashMap<URL::URL, ModuleSpecifierMap> const& scopes) { m_scopes = scopes; }
|
||||
|
||||
private:
|
||||
ModuleSpecifierMap m_imports;
|
||||
HashMap<URL::URL, ModuleSpecifierMap> m_scopes;
|
||||
};
|
||||
|
||||
WebIDL::ExceptionOr<ImportMap> parse_import_map_string(JS::Realm& realm, ByteString const& input, URL::URL base_url);
|
||||
WebIDL::ExceptionOr<Optional<DeprecatedFlyString>> normalise_specifier_key(JS::Realm& realm, DeprecatedFlyString specifier_key, URL::URL base_url);
|
||||
WebIDL::ExceptionOr<ModuleSpecifierMap> sort_and_normalise_module_specifier_map(JS::Realm& realm, JS::Object& original_map, URL::URL base_url);
|
||||
WebIDL::ExceptionOr<HashMap<URL::URL, ModuleSpecifierMap>> sort_and_normalise_scopes(JS::Realm& realm, JS::Object& original_map, URL::URL base_url);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/ModuleRequest.h>
|
||||
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
|
||||
#include <LibWeb/HTML/Scripting/ImportMapParseResult.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/WebIDL/DOMException.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(ImportMapParseResult);
|
||||
|
||||
ImportMapParseResult::ImportMapParseResult() = default;
|
||||
|
||||
ImportMapParseResult::~ImportMapParseResult() = default;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#create-an-import-map-parse-result
|
||||
JS::NonnullGCPtr<ImportMapParseResult> ImportMapParseResult::create(JS::Realm& realm, ByteString const& input, URL::URL base_url)
|
||||
{
|
||||
// 1. Let result be an import map parse result whose import map is null and whose error to rethrow is null.
|
||||
auto result = realm.heap().allocate<ImportMapParseResult>(realm);
|
||||
result->set_error_to_rethrow(JS::js_null());
|
||||
|
||||
// 2. Parse an import map string given input and baseURL, catching any exceptions.
|
||||
auto import_map = parse_import_map_string(realm, input, base_url);
|
||||
|
||||
// 2.1. If this threw an exception, then set result's error to rethrow to that exception.
|
||||
// FIXME: rethrow the original exception
|
||||
if (import_map.is_exception())
|
||||
result->set_error_to_rethrow(JS::Error::create(realm, "Failed to parse import map string"sv));
|
||||
|
||||
// 2.2. Otherwise, set result's import map to the return value.
|
||||
else
|
||||
result->set_import_map(import_map.release_value());
|
||||
|
||||
// 3. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
void ImportMapParseResult::visit_host_defined_self(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(*this);
|
||||
}
|
||||
|
||||
void ImportMapParseResult::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_error_to_rethrow);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
|
||||
void ImportMapParseResult::register_import_map(Window& global)
|
||||
{
|
||||
// 1. If result's error to rethrow is not null, then report the exception given by result's error to rethrow and return.
|
||||
if (!m_error_to_rethrow.is_null()) {
|
||||
HTML::report_exception(m_error_to_rethrow, global.realm());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Assert: global's import map is an empty import map.
|
||||
VERIFY(global.import_map().imports().is_empty() && global.import_map().scopes().is_empty());
|
||||
|
||||
// 3. Set global's import map to result's import map.
|
||||
VERIFY(m_import_map.has_value());
|
||||
global.set_import_map(m_import_map.value());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibJS/Script.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/Scripting/ImportMap.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#import-map-parse-result
|
||||
class ImportMapParseResult
|
||||
: public JS::Cell
|
||||
, public JS::Script::HostDefined {
|
||||
JS_CELL(Script, JS::Cell);
|
||||
JS_DECLARE_ALLOCATOR(ImportMapParseResult);
|
||||
|
||||
public:
|
||||
virtual ~ImportMapParseResult() override;
|
||||
|
||||
static JS::NonnullGCPtr<ImportMapParseResult> create(JS::Realm& realm, ByteString const& input, URL::URL base_url);
|
||||
|
||||
[[nodiscard]] Optional<ImportMap> const& import_map() const { return m_import_map; }
|
||||
void set_import_map(ImportMap const& value) { m_import_map = value; }
|
||||
|
||||
[[nodiscard]] JS::Value error_to_rethrow() const { return m_error_to_rethrow; }
|
||||
void set_error_to_rethrow(JS::Value value) { m_error_to_rethrow = value; }
|
||||
|
||||
void register_import_map(Window& global);
|
||||
|
||||
protected:
|
||||
ImportMapParseResult();
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
private:
|
||||
virtual void visit_host_defined_self(JS::Cell::Visitor&) override;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#impr-import-map
|
||||
Optional<ImportMap> m_import_map;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#impr-error-to-rethrow
|
||||
JS::Value m_error_to_rethrow;
|
||||
};
|
||||
|
||||
}
|
|
@ -93,6 +93,7 @@ public:
|
|||
JS::GCPtr<Navigable> navigable() const;
|
||||
|
||||
ImportMap const& import_map() const { return m_import_map; }
|
||||
void set_import_map(ImportMap const& import_map) { m_import_map = import_map; }
|
||||
|
||||
bool import_maps_allowed() const { return m_import_maps_allowed; }
|
||||
void set_import_maps_allowed(bool import_maps_allowed) { m_import_maps_allowed = import_maps_allowed; }
|
||||
|
|
Loading…
Reference in a new issue