mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-28 15:28:55 +00:00
LibWeb: Implement multiple import map support
This commit is contained in:
parent
0a216f9c14
commit
ac6fe2e211
Notes:
github-actions[bot]
2024-12-10 12:02:45 +00:00
Author: https://github.com/shannonbooth
Commit: ac6fe2e211
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2824
Reviewed-by: https://github.com/Lubrsi ✅
19 changed files with 294 additions and 100 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -10,7 +11,9 @@
|
|||
#include <LibWeb/HTML/Scripting/Fetching.h>
|
||||
#include <LibWeb/HTML/Scripting/ImportMap.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Infra/JSON.h>
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
|
@ -263,4 +266,134 @@ WebIDL::ExceptionOr<ModuleIntegrityMap> normalize_module_integrity_map(JS::Realm
|
|||
return normalised;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#merge-module-specifier-maps
|
||||
static ModuleSpecifierMap merge_module_specifier_maps(JS::Realm& realm, ModuleSpecifierMap const& new_map, ModuleSpecifierMap const& old_map)
|
||||
{
|
||||
// 1. Let mergedMap be a deep copy of oldMap.
|
||||
ModuleSpecifierMap merged_map = old_map;
|
||||
|
||||
// 2. For each specifier → url of newMap:
|
||||
for (auto const& [specifier, url] : new_map) {
|
||||
// 1. If specifier exists in oldMap, then:
|
||||
if (old_map.contains(specifier)) {
|
||||
// 1. The user agent may report a warning to the console indicating the ignored rule. They may choose to
|
||||
// avoid reporting if the rule is identical to an existing one.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
MUST(String::formatted("An import map rule for specifier '{}' was ignored as one was already present in the existing import map", specifier)));
|
||||
|
||||
// 2. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Set mergedMap[specifier] to url.
|
||||
merged_map.set(specifier, url);
|
||||
}
|
||||
|
||||
// 3. Return mergedMap.
|
||||
return merged_map;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#merge-existing-and-new-import-maps
|
||||
void merge_existing_and_new_import_maps(Window& global, ImportMap& new_import_map)
|
||||
{
|
||||
auto& realm = global.realm();
|
||||
|
||||
// 1. Let newImportMapScopes be a deep copy of newImportMap's scopes.
|
||||
auto new_import_map_scopes = new_import_map.scopes();
|
||||
|
||||
// Spec-Note: We're mutating these copies and removing items from them when they are used to ignore scope-specific
|
||||
// rules. This is true for newImportMapScopes, as well as to newImportMapImports below.
|
||||
|
||||
// 2. Let oldImportMap be global's import map.
|
||||
auto& old_import_map = global.import_map();
|
||||
|
||||
// 3. Let newImportMapImports be a deep copy of newImportMap's imports.
|
||||
auto new_import_map_imports = new_import_map.imports();
|
||||
|
||||
// 4. For each scopePrefix → scopeImports of newImportMapScopes:
|
||||
for (auto& [scope_prefix, scope_imports] : new_import_map_scopes) {
|
||||
// 1. For each record of global's resolved module set:
|
||||
for (auto const& record : global.resolved_module_set()) {
|
||||
// 1. If scopePrefix is record's serialized base URL, or if scopePrefix ends with U+002F (/) and scopePrefix is a code unit prefix of record's serialized base URL, then:
|
||||
if (scope_prefix == record.serialized_base_url || (scope_prefix.to_string().ends_with('/') && record.serialized_base_url.has_value() && Infra::is_code_unit_prefix(scope_prefix.to_string(), *record.serialized_base_url))) {
|
||||
// 1. For each specifierKey → resolutionResult of scopeImports:
|
||||
scope_imports.remove_all_matching([&](ByteString const& specifier_key, Optional<URL::URL> const&) {
|
||||
// 1. If specifierKey is record's specifier, or if all of the following conditions are true:
|
||||
// * specifierKey ends with U+002F (/);
|
||||
// * specifierKey is a code unit prefix of record's specifier;
|
||||
// * either record's specifier as a URL is null or is special,
|
||||
// then:
|
||||
if (specifier_key.view() == record.specifier
|
||||
|| (specifier_key.ends_with('/')
|
||||
&& Infra::is_code_unit_prefix(specifier_key, record.specifier)
|
||||
&& record.specifier_is_null_or_url_like_that_is_special)) {
|
||||
// 1. The user agent may report a warning to the console indicating the ignored rule. They
|
||||
// may choose to avoid reporting if the rule is identical to an existing one.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
MUST(String::formatted("An import map rule for specifier '{}' was ignored as one was already present in the existing import map", specifier_key)));
|
||||
|
||||
// 2. Remove scopeImports[specifierKey].
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If scopePrefix exists in oldImportMap's scopes, then set oldImportMap's scopes[scopePrefix] to the result
|
||||
// of merging module specifier maps, given scopeImports and oldImportMap's scopes[scopePrefix].
|
||||
if (auto it = old_import_map.scopes().find(scope_prefix); it != old_import_map.scopes().end()) {
|
||||
it->value = merge_module_specifier_maps(realm, scope_imports, it->value);
|
||||
}
|
||||
// 3. Otherwise, set oldImportMap's scopes[scopePrefix] to scopeImports.
|
||||
else {
|
||||
old_import_map.scopes().set(scope_prefix, scope_imports);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. For each url → integrity of newImportMap's integrity:
|
||||
for (auto const& [url, integrity] : new_import_map.integrity()) {
|
||||
// 1. If url exists in oldImportMap's integrity, then:
|
||||
if (old_import_map.integrity().contains(url)) {
|
||||
// 1. The user agent may report a warning to the console indicating the ignored rule. They may choose to
|
||||
// avoid reporting if the rule is identical to an existing one.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
MUST(String::formatted("An import map integrity rule for url '{}' was ignored as one was already present in the existing import map", url)));
|
||||
|
||||
// 2. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Set oldImportMap's integrity[url] to integrity.
|
||||
old_import_map.integrity().set(url, integrity);
|
||||
}
|
||||
|
||||
// 6. For each record of global's resolved module set:
|
||||
for (auto const& record : global.resolved_module_set()) {
|
||||
// 1. For each specifier → url of newImportMapImports:
|
||||
new_import_map_imports.remove_all_matching([&](ByteString const& specifier, Optional<URL::URL> const&) {
|
||||
// 1. If specifier starts with record's specifier, then:
|
||||
if (specifier.starts_with(record.specifier)) {
|
||||
// 1. The user agent may report a warning to the console indicating the ignored rule. They may choose to
|
||||
// avoid reporting if the rule is identical to an existing one.
|
||||
auto& console = realm.intrinsics().console_object()->console();
|
||||
console.output_debug_message(JS::Console::LogLevel::Warn,
|
||||
MUST(String::formatted("An import map rule for specifier '{}' was ignored as one was already present in the existing import map", specifier)));
|
||||
|
||||
// 2. Remove newImportMapImports[specifier].
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// 7. Set oldImportMap's imports to the result of merge module specifier maps, given newImportMapImports and oldImportMap's imports.
|
||||
old_import_map.set_imports(merge_module_specifier_maps(realm, new_import_map_imports, old_import_map.imports()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue