script: complete resolve_module_specifier (#37552)

Implement whole spec of `resolve_module_specifier`. Servo can now
support script element with import map type!

Testing: `tests/wpt/tests/import-map`
Fixes: #37316 #36394

---------

Signed-off-by: Wu Yu Wei <yuweiwu@pm.me>
This commit is contained in:
Ngo Iok Ui (Wu Yu Wei) 2025-06-21 15:17:27 +09:00 committed by GitHub
parent d7269c0f3b
commit 927573de97
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 376 additions and 281 deletions

View file

@ -4,7 +4,7 @@
use std::cell::{Cell, OnceCell, Ref};
use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::collections::{HashMap, HashSet, VecDeque};
use std::ops::Index;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
@ -137,7 +137,7 @@ use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask};
use crate::network_listener::{NetworkListener, PreInvoke};
use crate::realms::{InRealm, enter_realm};
use crate::script_module::{
DynamicModuleList, ImportMap, ModuleScript, ModuleTree, ScriptFetchOptions,
DynamicModuleList, ImportMap, ModuleScript, ModuleTree, ResolvedModule, ScriptFetchOptions,
};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext, ThreadSafeJSContext};
use crate::script_thread::{ScriptThread, with_script_thread};
@ -385,6 +385,9 @@ pub(crate) struct GlobalScope {
///
/// <https://html.spec.whatwg.org/multipage/#import-maps>
import_map: DomRefCell<ImportMap>,
/// <https://html.spec.whatwg.org/multipage/#resolved-module-set>
resolved_module_set: DomRefCell<HashSet<ResolvedModule>>,
}
/// A wrapper for glue-code between the ipc router and the event-loop.
@ -789,6 +792,7 @@ impl GlobalScope {
count_queuing_strategy_size_function: OnceCell::new(),
notification_permission_request_callback_map: Default::default(),
import_map: Default::default(),
resolved_module_set: Default::default(),
}
}
@ -3487,8 +3491,40 @@ impl GlobalScope {
}
}
pub(crate) fn import_map(&self) -> &DomRefCell<ImportMap> {
&self.import_map
pub(crate) fn import_map(&self) -> Ref<'_, ImportMap> {
self.import_map.borrow()
}
pub(crate) fn import_map_mut(&self) -> RefMut<'_, ImportMap> {
self.import_map.borrow_mut()
}
pub(crate) fn resolved_module_set(&self) -> Ref<'_, HashSet<ResolvedModule>> {
self.resolved_module_set.borrow()
}
pub(crate) fn resolved_module_set_mut(&self) -> RefMut<'_, HashSet<ResolvedModule>> {
self.resolved_module_set.borrow_mut()
}
/// <https://html.spec.whatwg.org/multipage/#add-module-to-resolved-module-set>
pub(crate) fn add_module_to_resolved_module_set(
&self,
base_url: &str,
specifier: &str,
specifier_url: Option<ServoUrl>,
) {
// Step 1. Let global be settingsObject's global object.
// Step 2. If global does not implement Window, then return.
if self.is::<Window>() {
// Step 3. Let record be a new specifier resolution record, with serialized base URL
// set to serializedBaseURL, specifier set to normalizedSpecifier, and specifier as
// a URL set to asURL.
let record =
ResolvedModule::new(base_url.to_owned(), specifier.to_owned(), specifier_url);
// Step 4. Append record to global's resolved module set.
self.resolved_module_set.borrow_mut().insert(record);
}
}
}

View file

@ -856,7 +856,9 @@ impl HTMLScriptElement {
credentials_mode: module_credentials_mode,
};
// TODO: Step 30. Environment settings object.
// Step 30. Let settings object be el's node document's relevant settings object.
// This is done by passing ModuleOwner in step 31.11 and step 32.2.
// What we actually need is global's import map eventually.
let base_url = doc.base_url();
if let Some(src) = element.get_attribute(&ns!(), &local_name!("src")) {