1use std::collections::{HashMap, HashSet};
9use std::ffi::CStr;
10use std::rc::Rc;
11use std::str::FromStr;
12use std::sync::{Arc, Mutex};
13use std::{mem, ptr};
14
15use encoding_rs::UTF_8;
16use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
17use html5ever::local_name;
18use hyper_serde::Serde;
19use indexmap::{IndexMap, IndexSet};
20use js::conversions::jsstr_to_string;
21use js::jsapi::{
22 CompileModule1, ExceptionStackBehavior, FinishDynamicModuleImport, GetModuleRequestSpecifier,
23 GetModuleResolveHook, GetRequestedModuleSpecifier, GetRequestedModulesCount,
24 Handle as RawHandle, HandleObject, HandleValue as RawHandleValue, Heap,
25 JS_ClearPendingException, JS_DefineProperty4, JS_IsExceptionPending, JS_NewStringCopyN,
26 JSAutoRealm, JSContext, JSObject, JSPROP_ENUMERATE, JSRuntime, ModuleErrorBehaviour,
27 ModuleEvaluate, ModuleLink, MutableHandleValue, SetModuleDynamicImportHook,
28 SetModuleMetadataHook, SetModulePrivate, SetModuleResolveHook, SetScriptPrivateReferenceHooks,
29 ThrowOnModuleEvaluationFailure, Value,
30};
31use js::jsval::{JSVal, PrivateValue, UndefinedValue};
32use js::rust::wrappers::{JS_GetModulePrivate, JS_GetPendingException, JS_SetPendingException};
33use js::rust::{
34 CompileOptionsWrapper, Handle, HandleObject as RustHandleObject, HandleValue, IntoHandle,
35 MutableHandleObject as RustMutableHandleObject, transform_str_to_source_text,
36};
37use mime::Mime;
38use net_traits::http_status::HttpStatus;
39use net_traits::request::{
40 CredentialsMode, Destination, ParserMetadata, Referrer, RequestBuilder, RequestId, RequestMode,
41};
42use net_traits::{
43 FetchMetadata, FetchResponseListener, Metadata, NetworkError, ReferrerPolicy,
44 ResourceFetchTiming, ResourceTimingType,
45};
46use script_bindings::domstring::BytesView;
47use script_bindings::error::Fallible;
48use serde_json::{Map as JsonMap, Value as JsonValue};
49use servo_url::ServoUrl;
50use uuid::Uuid;
51
52use crate::document_loader::LoadType;
53use crate::dom::bindings::cell::DomRefCell;
54use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
55use crate::dom::bindings::error::{
56 Error, ErrorToJsval, report_pending_exception, throw_dom_exception,
57};
58use crate::dom::bindings::inheritance::Castable;
59use crate::dom::bindings::refcounted::Trusted;
60use crate::dom::bindings::reflector::{DomGlobal, DomObject};
61use crate::dom::bindings::root::DomRoot;
62use crate::dom::bindings::settings_stack::AutoIncumbentScript;
63use crate::dom::bindings::str::DOMString;
64use crate::dom::bindings::trace::RootedTraceableBox;
65use crate::dom::csp::{GlobalCspReporting, Violation};
66use crate::dom::document::Document;
67use crate::dom::dynamicmoduleowner::{DynamicModuleId, DynamicModuleOwner};
68use crate::dom::element::Element;
69use crate::dom::globalscope::GlobalScope;
70use crate::dom::html::htmlscriptelement::{
71 HTMLScriptElement, SCRIPT_JS_MIMES, ScriptId, ScriptOrigin, ScriptType,
72};
73use crate::dom::node::NodeTraits;
74use crate::dom::performanceresourcetiming::InitiatorType;
75use crate::dom::promise::Promise;
76use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
77use crate::dom::types::Console;
78use crate::dom::window::Window;
79use crate::dom::worker::TrustedWorkerAddress;
80use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
81use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
82use crate::script_runtime::{CanGc, IntroductionType, JSContext as SafeJSContext};
83use crate::task::TaskBox;
84
85fn gen_type_error(global: &GlobalScope, string: String, can_gc: CanGc) -> RethrowError {
86 rooted!(in(*GlobalScope::get_cx()) let mut thrown = UndefinedValue());
87 Error::Type(string).to_jsval(GlobalScope::get_cx(), global, thrown.handle_mut(), can_gc);
88
89 RethrowError(RootedTraceableBox::from_box(Heap::boxed(thrown.get())))
90}
91
92#[derive(JSTraceable)]
93pub(crate) struct ModuleObject(RootedTraceableBox<Heap<*mut JSObject>>);
94
95impl ModuleObject {
96 fn new(obj: RustHandleObject) -> ModuleObject {
97 ModuleObject(RootedTraceableBox::from_box(Heap::boxed(obj.get())))
98 }
99
100 pub(crate) fn handle(&self) -> HandleObject {
101 self.0.handle().into()
102 }
103}
104
105#[derive(JSTraceable)]
106pub(crate) struct RethrowError(RootedTraceableBox<Heap<JSVal>>);
107
108impl RethrowError {
109 fn handle(&self) -> Handle<'_, JSVal> {
110 self.0.handle()
111 }
112}
113
114impl Clone for RethrowError {
115 fn clone(&self) -> Self {
116 Self(RootedTraceableBox::from_box(Heap::boxed(self.0.get())))
117 }
118}
119
120pub(crate) struct ModuleScript {
121 base_url: ServoUrl,
122 options: ScriptFetchOptions,
123 owner: Option<ModuleOwner>,
124}
125
126impl ModuleScript {
127 pub(crate) fn new(
128 base_url: ServoUrl,
129 options: ScriptFetchOptions,
130 owner: Option<ModuleOwner>,
131 ) -> Self {
132 ModuleScript {
133 base_url,
134 options,
135 owner,
136 }
137 }
138}
139
140#[derive(Clone, Debug, Eq, Hash, JSTraceable, PartialEq)]
149pub(crate) enum ModuleIdentity {
150 ScriptId(ScriptId),
151 ModuleUrl(#[no_trace] ServoUrl),
152}
153
154impl ModuleIdentity {
155 pub(crate) fn get_module_tree(&self, global: &GlobalScope) -> Rc<ModuleTree> {
156 match self {
157 ModuleIdentity::ModuleUrl(url) => {
158 let module_map = global.get_module_map().borrow();
159 module_map.get(&url.clone()).unwrap().clone()
160 },
161 ModuleIdentity::ScriptId(script_id) => {
162 let inline_module_map = global.get_inline_module_map().borrow();
163 inline_module_map.get(script_id).unwrap().clone()
164 },
165 }
166 }
167}
168
169#[derive(JSTraceable)]
170pub(crate) struct ModuleTree {
171 #[no_trace]
172 url: ServoUrl,
173 text: DomRefCell<Rc<DOMString>>,
174 record: DomRefCell<Option<ModuleObject>>,
175 status: DomRefCell<ModuleStatus>,
176 #[custom_trace]
185 parent_identities: DomRefCell<IndexSet<ModuleIdentity>>,
186 #[no_trace]
187 descendant_urls: DomRefCell<IndexSet<ServoUrl>>,
188 #[no_trace]
190 incomplete_fetch_urls: DomRefCell<IndexSet<ServoUrl>>,
191 #[no_trace]
192 visited_urls: DomRefCell<HashSet<ServoUrl>>,
193 rethrow_error: DomRefCell<Option<RethrowError>>,
194 #[no_trace]
195 network_error: DomRefCell<Option<NetworkError>>,
196 promise: DomRefCell<Option<Rc<Promise>>>,
199 external: bool,
200}
201
202impl ModuleTree {
203 pub(crate) fn new(url: ServoUrl, external: bool, visited_urls: HashSet<ServoUrl>) -> Self {
204 ModuleTree {
205 url,
206 text: DomRefCell::new(Rc::new(DOMString::new())),
207 record: DomRefCell::new(None),
208 status: DomRefCell::new(ModuleStatus::Initial),
209 parent_identities: DomRefCell::new(IndexSet::new()),
210 descendant_urls: DomRefCell::new(IndexSet::new()),
211 incomplete_fetch_urls: DomRefCell::new(IndexSet::new()),
212 visited_urls: DomRefCell::new(visited_urls),
213 rethrow_error: DomRefCell::new(None),
214 network_error: DomRefCell::new(None),
215 promise: DomRefCell::new(None),
216 external,
217 }
218 }
219
220 pub(crate) fn get_status(&self) -> ModuleStatus {
221 *self.status.borrow()
222 }
223
224 pub(crate) fn set_status(&self, status: ModuleStatus) {
225 *self.status.borrow_mut() = status;
226 }
227
228 pub(crate) fn get_record(&self) -> &DomRefCell<Option<ModuleObject>> {
229 &self.record
230 }
231
232 pub(crate) fn set_record(&self, record: ModuleObject) {
233 *self.record.borrow_mut() = Some(record);
234 }
235
236 pub(crate) fn get_rethrow_error(&self) -> &DomRefCell<Option<RethrowError>> {
237 &self.rethrow_error
238 }
239
240 pub(crate) fn set_rethrow_error(&self, rethrow_error: RethrowError) {
241 *self.rethrow_error.borrow_mut() = Some(rethrow_error);
242 }
243
244 pub(crate) fn get_network_error(&self) -> &DomRefCell<Option<NetworkError>> {
245 &self.network_error
246 }
247
248 pub(crate) fn set_network_error(&self, network_error: NetworkError) {
249 *self.network_error.borrow_mut() = Some(network_error);
250 }
251
252 pub(crate) fn get_text(&self) -> &DomRefCell<Rc<DOMString>> {
253 &self.text
254 }
255
256 pub(crate) fn set_text(&self, module_text: Rc<DOMString>) {
257 *self.text.borrow_mut() = module_text;
258 }
259
260 pub(crate) fn get_incomplete_fetch_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
261 &self.incomplete_fetch_urls
262 }
263
264 pub(crate) fn get_descendant_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
265 &self.descendant_urls
266 }
267
268 pub(crate) fn get_parent_urls(&self) -> IndexSet<ServoUrl> {
269 let parent_identities = self.parent_identities.borrow();
270
271 parent_identities
272 .iter()
273 .filter_map(|parent_identity| match parent_identity {
274 ModuleIdentity::ScriptId(_) => None,
275 ModuleIdentity::ModuleUrl(url) => Some(url.clone()),
276 })
277 .collect()
278 }
279
280 pub(crate) fn insert_parent_identity(&self, parent_identity: ModuleIdentity) {
281 self.parent_identities.borrow_mut().insert(parent_identity);
282 }
283
284 pub(crate) fn insert_incomplete_fetch_url(&self, dependency: &ServoUrl) {
285 self.incomplete_fetch_urls
286 .borrow_mut()
287 .insert(dependency.clone());
288 }
289
290 pub(crate) fn remove_incomplete_fetch_url(&self, dependency: &ServoUrl) {
291 self.incomplete_fetch_urls
292 .borrow_mut()
293 .shift_remove(dependency);
294 }
295
296 fn recursive_check_descendants(
299 module_tree: &ModuleTree,
300 module_map: &HashMap<ServoUrl, Rc<ModuleTree>>,
301 discovered_urls: &mut HashSet<ServoUrl>,
302 ) -> bool {
303 discovered_urls.insert(module_tree.url.clone());
304
305 let descendant_urls = module_tree.descendant_urls.borrow();
306
307 for descendant_url in descendant_urls.iter() {
308 match module_map.get(&descendant_url.clone()) {
309 None => return false,
310 Some(descendant_module) => {
311 if discovered_urls.contains(&descendant_module.url) {
312 continue;
313 }
314
315 let descendant_status = descendant_module.get_status();
316 if descendant_status < ModuleStatus::FetchingDescendants {
317 return false;
318 }
319
320 let all_ready_descendants = ModuleTree::recursive_check_descendants(
321 descendant_module,
322 module_map,
323 discovered_urls,
324 );
325
326 if !all_ready_descendants {
327 return false;
328 }
329 },
330 }
331 }
332
333 true
334 }
335
336 fn has_all_ready_descendants(&self, global: &GlobalScope) -> bool {
337 let module_map = global.get_module_map().borrow();
338 let mut discovered_urls = HashSet::new();
339
340 ModuleTree::recursive_check_descendants(self, &module_map.0, &mut discovered_urls)
341 }
342
343 fn append_handler(
346 &self,
347 owner: ModuleOwner,
348 module_identity: ModuleIdentity,
349 fetch_options: ScriptFetchOptions,
350 can_gc: CanGc,
351 ) {
352 let this = owner.clone();
353 let identity = module_identity.clone();
354 let options = fetch_options.clone();
355
356 let handler = PromiseNativeHandler::new(
357 &owner.global(),
358 Some(ModuleHandler::new_boxed(Box::new(
359 task!(fetched_resolve: move || {
360 this.notify_owner_to_finish(identity, options, CanGc::note());
361 }),
362 ))),
363 None,
364 can_gc,
365 );
366
367 let realm = enter_realm(&*owner.global());
368 let comp = InRealm::Entered(&realm);
369 let _ais = AutoIncumbentScript::new(&owner.global());
370
371 if let Some(promise) = self.promise.borrow().as_ref() {
372 promise.append_native_handler(&handler, comp, can_gc);
373 return;
374 }
375
376 let new_promise = Promise::new_in_current_realm(comp, can_gc);
377 new_promise.append_native_handler(&handler, comp, can_gc);
378 *self.promise.borrow_mut() = Some(new_promise);
379 }
380
381 fn append_dynamic_module_handler(
382 &self,
383 owner: ModuleOwner,
384 module_identity: ModuleIdentity,
385 dynamic_module: RootedTraceableBox<DynamicModule>,
386 can_gc: CanGc,
387 ) {
388 let this = owner.clone();
389 let identity = module_identity.clone();
390
391 let module_id = owner.global().dynamic_module_list().push(dynamic_module);
392
393 let handler = PromiseNativeHandler::new(
394 &owner.global(),
395 Some(ModuleHandler::new_boxed(Box::new(
396 task!(fetched_resolve: move || {
397 this.finish_dynamic_module(identity, module_id, CanGc::note());
398 }),
399 ))),
400 None,
401 can_gc,
402 );
403
404 let realm = enter_realm(&*owner.global());
405 let comp = InRealm::Entered(&realm);
406 let _ais = AutoIncumbentScript::new(&owner.global());
407
408 if let Some(promise) = self.promise.borrow().as_ref() {
409 promise.append_native_handler(&handler, comp, can_gc);
410 return;
411 }
412
413 let new_promise = Promise::new_in_current_realm(comp, can_gc);
414 new_promise.append_native_handler(&handler, comp, can_gc);
415 *self.promise.borrow_mut() = Some(new_promise);
416 }
417}
418
419#[derive(Clone, Copy, Debug, JSTraceable, PartialEq, PartialOrd)]
420pub(crate) enum ModuleStatus {
421 Initial,
422 Fetching,
423 FetchingDescendants,
424 Finished,
425}
426
427struct ModuleSource {
428 source: Rc<DOMString>,
429 unminified_dir: Option<String>,
430 external: bool,
431 url: ServoUrl,
432}
433
434impl crate::unminify::ScriptSource for ModuleSource {
435 fn unminified_dir(&self) -> Option<String> {
436 self.unminified_dir.clone()
437 }
438
439 fn extract_bytes(&self) -> BytesView<'_> {
440 self.source.as_bytes()
441 }
442
443 fn rewrite_source(&mut self, source: Rc<DOMString>) {
444 self.source = source;
445 }
446
447 fn url(&self) -> ServoUrl {
448 self.url.clone()
449 }
450
451 fn is_external(&self) -> bool {
452 self.external
453 }
454}
455
456impl ModuleTree {
457 #[allow(unsafe_code, clippy::too_many_arguments)]
458 fn compile_module_script(
463 &self,
464 global: &GlobalScope,
465 owner: ModuleOwner,
466 module_script_text: Rc<DOMString>,
467 url: &ServoUrl,
468 options: ScriptFetchOptions,
469 mut module_script: RustMutableHandleObject,
470 inline: bool,
471 line_number: u64,
472 introduction_type: Option<&'static CStr>,
473 can_gc: CanGc,
474 ) -> Result<(), RethrowError> {
475 let cx = GlobalScope::get_cx();
476 let _ac = JSAutoRealm::new(*cx, *global.reflector().get_jsobject());
477
478 let mut compile_options =
479 unsafe { CompileOptionsWrapper::new(*cx, url.as_str(), line_number as u32) };
480 if let Some(introduction_type) = introduction_type {
481 compile_options.set_introduction_type(introduction_type);
482 }
483 let mut module_source = ModuleSource {
484 source: module_script_text,
485 unminified_dir: global.unminified_js_dir(),
486 external: !inline,
487 url: url.clone(),
488 };
489 crate::unminify::unminify_js(&mut module_source);
490
491 unsafe {
492 module_script.set(CompileModule1(
493 *cx,
494 compile_options.ptr,
495 &mut transform_str_to_source_text(&module_source.source.str()),
496 ));
497
498 if module_script.is_null() {
499 warn!("fail to compile module script of {}", url);
500
501 rooted!(in(*cx) let mut exception = UndefinedValue());
502 assert!(JS_GetPendingException(*cx, exception.handle_mut()));
503 JS_ClearPendingException(*cx);
504
505 return Err(RethrowError(RootedTraceableBox::from_box(Heap::boxed(
506 exception.get(),
507 ))));
508 }
509
510 let module_script_data = Rc::new(ModuleScript::new(url.clone(), options, Some(owner)));
511
512 SetModulePrivate(
513 module_script.get(),
514 &PrivateValue(Rc::into_raw(module_script_data) as *const _),
515 );
516
517 debug!("module script of {} compile done", url);
518
519 self.resolve_requested_module_specifiers(
520 global,
521 module_script.handle().into_handle(),
522 can_gc,
523 )
524 .map(|_| ())
525 }
526 }
527
528 #[allow(unsafe_code)]
529 pub(crate) fn instantiate_module_tree(
532 &self,
533 global: &GlobalScope,
534 module_record: HandleObject,
535 ) -> Result<(), RethrowError> {
536 let cx = GlobalScope::get_cx();
537 let _ac = JSAutoRealm::new(*cx, *global.reflector().get_jsobject());
538
539 unsafe {
540 if !ModuleLink(*cx, module_record) {
541 warn!("fail to link & instantiate module");
542
543 rooted!(in(*cx) let mut exception = UndefinedValue());
544 assert!(JS_GetPendingException(*cx, exception.handle_mut()));
545 JS_ClearPendingException(*cx);
546
547 Err(RethrowError(RootedTraceableBox::from_box(Heap::boxed(
548 exception.get(),
549 ))))
550 } else {
551 debug!("module instantiated successfully");
552
553 Ok(())
554 }
555 }
556 }
557
558 #[allow(unsafe_code)]
562 pub(crate) fn execute_module(
563 &self,
564 global: &GlobalScope,
565 module_record: HandleObject,
566 eval_result: MutableHandleValue,
567 _can_gc: CanGc,
568 ) -> Result<(), RethrowError> {
569 let cx = GlobalScope::get_cx();
570 let _ac = JSAutoRealm::new(*cx, *global.reflector().get_jsobject());
571
572 unsafe {
573 let ok = ModuleEvaluate(*cx, module_record, eval_result);
574 assert!(ok, "module evaluation failed");
575
576 rooted!(in(*cx) let mut evaluation_promise = ptr::null_mut::<JSObject>());
577 if eval_result.is_object() {
578 evaluation_promise.set(eval_result.to_object());
579 }
580
581 let throw_result = ThrowOnModuleEvaluationFailure(
582 *cx,
583 evaluation_promise.handle().into(),
584 ModuleErrorBehaviour::ThrowModuleErrorsSync,
585 );
586 if !throw_result {
587 warn!("fail to evaluate module");
588
589 rooted!(in(*cx) let mut exception = UndefinedValue());
590 assert!(JS_GetPendingException(*cx, exception.handle_mut()));
591 JS_ClearPendingException(*cx);
592
593 Err(RethrowError(RootedTraceableBox::from_box(Heap::boxed(
594 exception.get(),
595 ))))
596 } else {
597 debug!("module evaluated successfully");
598 Ok(())
599 }
600 }
601 }
602
603 #[allow(unsafe_code)]
604 pub(crate) fn report_error(&self, global: &GlobalScope, can_gc: CanGc) {
605 let module_error = self.rethrow_error.borrow();
606
607 if let Some(exception) = &*module_error {
608 let ar = enter_realm(global);
609 unsafe {
610 JS_SetPendingException(
611 *GlobalScope::get_cx(),
612 exception.handle(),
613 ExceptionStackBehavior::Capture,
614 );
615 }
616 report_pending_exception(GlobalScope::get_cx(), true, InRealm::Entered(&ar), can_gc);
617 }
618 }
619
620 #[allow(unsafe_code)]
621 fn resolve_requested_module_specifiers(
622 &self,
623 global: &GlobalScope,
624 module_object: HandleObject,
625 can_gc: CanGc,
626 ) -> Result<IndexSet<ServoUrl>, RethrowError> {
627 let cx = GlobalScope::get_cx();
628 let _ac = JSAutoRealm::new(*cx, *global.reflector().get_jsobject());
629
630 let mut specifier_urls = IndexSet::new();
631
632 unsafe {
633 let length = GetRequestedModulesCount(*cx, module_object);
634
635 for index in 0..length {
636 let jsstr =
637 std::ptr::NonNull::new(GetRequestedModuleSpecifier(*cx, module_object, index))
638 .unwrap();
639 let specifier = DOMString::from_string(jsstr_to_string(*cx, jsstr));
640
641 rooted!(in(*cx) let mut private = UndefinedValue());
642 JS_GetModulePrivate(module_object.get(), private.handle_mut());
643 let private = private.handle().into_handle();
644 let script = module_script_from_reference_private(&private);
645 let url = ModuleTree::resolve_module_specifier(global, script, specifier, can_gc);
646
647 if url.is_err() {
648 let specifier_error =
649 gen_type_error(global, "Wrong module specifier".to_owned(), can_gc);
650
651 return Err(specifier_error);
652 }
653
654 specifier_urls.insert(url.unwrap());
655 }
656 }
657
658 Ok(specifier_urls)
659 }
660
661 #[allow(unsafe_code)]
663 fn resolve_module_specifier(
664 global: &GlobalScope,
665 script: Option<&ModuleScript>,
666 specifier: DOMString,
667 can_gc: CanGc,
668 ) -> Fallible<ServoUrl> {
669 let script_global = script.and_then(|s| s.owner.as_ref().map(|o| o.global()));
671 let (global, base_url): (&GlobalScope, &ServoUrl) = match script {
673 Some(s) => (script_global.as_ref().map_or(global, |g| g), &s.base_url),
677 None => (global, &global.api_base_url()),
682 };
683
684 let import_map = if global.is::<Window>() {
688 Some(global.import_map())
689 } else {
690 None
691 };
692
693 let serialized_base_url = base_url.as_str();
695 let as_url = Self::resolve_url_like_module_specifier(&specifier, base_url);
697 let specifier = specifier.str();
700 let normalized_specifier = match &as_url {
701 Some(url) => url.as_str(),
702 None => &specifier,
703 };
704
705 let mut result = None;
707 if let Some(map) = import_map {
708 for (prefix, imports) in &map.scopes {
710 let prefix = prefix.as_str();
713 if prefix == serialized_base_url ||
714 (serialized_base_url.starts_with(prefix) && prefix.ends_with('\u{002f}'))
715 {
716 result = resolve_imports_match(
721 normalized_specifier,
722 as_url.as_ref(),
723 imports,
724 can_gc,
725 )?;
726 break;
727 }
728 }
729
730 if result.is_none() {
733 result = resolve_imports_match(
734 normalized_specifier,
735 as_url.as_ref(),
736 &map.imports,
737 can_gc,
738 )?;
739 }
740 }
741
742 if result.is_none() {
744 result = as_url.clone();
745 }
746
747 match result {
749 Some(result) => {
750 global.add_module_to_resolved_module_set(
753 serialized_base_url,
754 normalized_specifier,
755 as_url.clone(),
756 );
757 Ok(result)
759 },
760 None => Err(Error::Type(
763 "Specifier was a bare specifier, but was not remapped to anything by importMap."
764 .to_owned(),
765 )),
766 }
767 }
768
769 fn resolve_url_like_module_specifier(
771 specifier: &DOMString,
772 base_url: &ServoUrl,
773 ) -> Option<ServoUrl> {
774 if specifier.starts_with('/') ||
776 specifier.starts_with_str("./") ||
777 specifier.starts_with_str("../")
778 {
779 return ServoUrl::parse_with_base(Some(base_url), &specifier.str()).ok();
781 }
782 ServoUrl::parse(&specifier.str()).ok()
784 }
785
786 fn find_first_parse_error(
788 &self,
789 global: &GlobalScope,
790 discovered_urls: &mut HashSet<ServoUrl>,
791 ) -> (Option<NetworkError>, Option<RethrowError>) {
792 discovered_urls.insert(self.url.clone());
794
795 let record = self.get_record().borrow();
797 if record.is_none() {
798 return (
799 self.network_error.borrow().clone(),
800 self.rethrow_error.borrow().clone(),
801 );
802 }
803
804 let module_map = global.get_module_map().borrow();
805 let mut parse_error: Option<RethrowError> = None;
806
807 let descendant_urls = self.descendant_urls.borrow();
809 for descendant_module in descendant_urls
810 .iter()
811 .filter_map(|url| module_map.get(&url.clone()))
813 {
814 if discovered_urls.contains(&descendant_module.url) {
816 continue;
817 }
818
819 let (child_network_error, child_parse_error) =
821 descendant_module.find_first_parse_error(global, discovered_urls);
822
823 if child_network_error.is_some() {
826 return (child_network_error, None);
827 }
828
829 if child_parse_error.is_some() && parse_error.is_none() {
835 parse_error = child_parse_error;
836 }
837 }
838
839 (None, parse_error)
841 }
842
843 #[allow(unsafe_code)]
844 fn fetch_module_descendants(
847 &self,
848 owner: &ModuleOwner,
849 destination: Destination,
850 options: &ScriptFetchOptions,
851 parent_identity: ModuleIdentity,
852 can_gc: CanGc,
853 ) {
854 debug!("Start to load dependencies of {}", self.url);
855
856 let global = owner.global();
857
858 self.set_status(ModuleStatus::FetchingDescendants);
859
860 let specifier_urls = {
861 let raw_record = self.record.borrow();
862 match raw_record.as_ref() {
863 None => {
865 self.set_status(ModuleStatus::Finished);
866 debug!(
867 "Module {} doesn't have module record but tried to load descendants.",
868 self.url
869 );
870 return;
871 },
872 Some(raw_record) => {
874 self.resolve_requested_module_specifiers(&global, raw_record.handle(), can_gc)
875 },
876 }
877 };
878
879 match specifier_urls {
880 Ok(valid_specifier_urls) if valid_specifier_urls.is_empty() => {
882 debug!("Module {} doesn't have any dependencies.", self.url);
883 self.advance_finished_and_link(&global, can_gc);
884 },
885 Ok(valid_specifier_urls) => {
886 self.descendant_urls
887 .borrow_mut()
888 .extend(valid_specifier_urls.clone());
889
890 let urls_to_fetch = {
891 let mut urls = IndexSet::new();
892 let mut visited_urls = self.visited_urls.borrow_mut();
893
894 for parsed_url in &valid_specifier_urls {
895 if !visited_urls.contains(parsed_url) {
897 urls.insert(parsed_url.clone());
899 visited_urls.insert(parsed_url.clone());
901
902 self.insert_incomplete_fetch_url(parsed_url);
903 }
904 }
905 urls
906 };
907
908 if urls_to_fetch.is_empty() {
910 debug!(
911 "After checking with visited urls, module {} doesn't have dependencies to load.",
912 &self.url
913 );
914 self.advance_finished_and_link(&global, can_gc);
915 return;
916 }
917
918 let visited_urls = self.visited_urls.borrow().clone();
921 let options = options.descendant_fetch_options();
922
923 for url in urls_to_fetch {
924 assert!(self.visited_urls.borrow().contains(&url));
927
928 fetch_single_module_script(
930 owner.clone(),
931 url,
932 visited_urls.clone(),
933 destination,
934 options.clone(),
935 Some(parent_identity.clone()),
936 false,
937 None,
938 Some(IntroductionType::IMPORTED_MODULE),
940 can_gc,
941 );
942 }
943 },
944 Err(error) => {
945 self.set_rethrow_error(error);
946 self.advance_finished_and_link(&global, can_gc);
947 },
948 }
949 }
950
951 fn advance_finished_and_link(&self, global: &GlobalScope, can_gc: CanGc) {
954 {
955 if !self.has_all_ready_descendants(global) {
956 return;
957 }
958 }
959
960 self.set_status(ModuleStatus::Finished);
961
962 debug!("Going to advance and finish for: {}", self.url);
963
964 {
965 let parent_identities = self.parent_identities.borrow();
970 for parent_identity in parent_identities.iter() {
971 let parent_tree = parent_identity.get_module_tree(global);
972
973 let incomplete_count_before_remove = {
974 let incomplete_urls = parent_tree.get_incomplete_fetch_urls().borrow();
975 incomplete_urls.len()
976 };
977
978 if incomplete_count_before_remove > 0 {
979 parent_tree.remove_incomplete_fetch_url(&self.url);
980 parent_tree.advance_finished_and_link(global, can_gc);
981 }
982 }
983 }
984
985 let mut discovered_urls: HashSet<ServoUrl> = HashSet::new();
986 let (network_error, rethrow_error) =
987 self.find_first_parse_error(global, &mut discovered_urls);
988
989 match (network_error, rethrow_error) {
990 (Some(network_error), _) => {
991 self.set_network_error(network_error);
992 },
993 (None, None) => {
994 let module_record = self.get_record().borrow();
995 if let Some(record) = &*module_record {
996 let instantiated = self.instantiate_module_tree(global, record.handle());
997
998 if let Err(exception) = instantiated {
999 self.set_rethrow_error(exception);
1000 }
1001 }
1002 },
1003 (None, Some(error)) => {
1004 self.set_rethrow_error(error);
1005 },
1006 }
1007
1008 let promise = self.promise.borrow();
1009 if let Some(promise) = promise.as_ref() {
1010 promise.resolve_native(&(), can_gc);
1011 }
1012 }
1013}
1014
1015#[derive(JSTraceable, MallocSizeOf)]
1016struct ModuleHandler {
1017 #[ignore_malloc_size_of = "Measuring trait objects is hard"]
1018 task: DomRefCell<Option<Box<dyn TaskBox>>>,
1019}
1020
1021impl ModuleHandler {
1022 pub(crate) fn new_boxed(task: Box<dyn TaskBox>) -> Box<dyn Callback> {
1023 Box::new(Self {
1024 task: DomRefCell::new(Some(task)),
1025 })
1026 }
1027}
1028
1029impl Callback for ModuleHandler {
1030 fn callback(&self, _cx: SafeJSContext, _v: HandleValue, _realm: InRealm, _can_gc: CanGc) {
1031 let task = self.task.borrow_mut().take().unwrap();
1032 task.run_box();
1033 }
1034}
1035
1036#[derive(Clone)]
1039pub(crate) enum ModuleOwner {
1040 #[allow(dead_code)]
1041 Worker(TrustedWorkerAddress),
1042 Window(Trusted<HTMLScriptElement>),
1043 DynamicModule(Trusted<DynamicModuleOwner>),
1044}
1045
1046impl ModuleOwner {
1047 pub(crate) fn global(&self) -> DomRoot<GlobalScope> {
1048 match &self {
1049 ModuleOwner::Worker(worker) => (*worker.root().clone()).global(),
1050 ModuleOwner::Window(script) => (*script.root()).global(),
1051 ModuleOwner::DynamicModule(dynamic_module) => (*dynamic_module.root()).global(),
1052 }
1053 }
1054
1055 pub(crate) fn notify_owner_to_finish(
1056 &self,
1057 module_identity: ModuleIdentity,
1058 fetch_options: ScriptFetchOptions,
1059 can_gc: CanGc,
1060 ) {
1061 match &self {
1062 ModuleOwner::Worker(_) => unimplemented!(),
1063 ModuleOwner::DynamicModule(_) => unimplemented!(),
1064 ModuleOwner::Window(script) => {
1065 let global = self.global();
1066
1067 let document = script.root().owner_document();
1068 let load = {
1069 let module_tree = module_identity.get_module_tree(&global);
1070
1071 let network_error = module_tree.get_network_error().borrow();
1072 match network_error.as_ref() {
1073 Some(network_error) => Err(network_error.clone().into()),
1074 None => match module_identity {
1075 ModuleIdentity::ModuleUrl(script_src) => Ok(ScriptOrigin::external(
1076 Rc::clone(&module_tree.get_text().borrow()),
1077 script_src.clone(),
1078 fetch_options,
1079 ScriptType::Module,
1080 global.unminified_js_dir(),
1081 )),
1082 ModuleIdentity::ScriptId(_) => Ok(ScriptOrigin::internal(
1083 Rc::clone(&module_tree.get_text().borrow()),
1084 document.base_url().clone(),
1085 fetch_options,
1086 ScriptType::Module,
1087 global.unminified_js_dir(),
1088 Err(Error::NotFound(None)),
1089 )),
1090 },
1091 }
1092 };
1093
1094 let asynch = script
1095 .root()
1096 .upcast::<Element>()
1097 .has_attribute(&local_name!("async"));
1098
1099 if !asynch && (*script.root()).get_parser_inserted() {
1100 document.deferred_script_loaded(&script.root(), load, can_gc);
1101 } else if !asynch && !(*script.root()).get_non_blocking() {
1102 document.asap_in_order_script_loaded(&script.root(), load, can_gc);
1103 } else {
1104 document.asap_script_loaded(&script.root(), load, can_gc);
1105 };
1106 },
1107 }
1108 }
1109
1110 #[allow(unsafe_code)]
1111 fn finish_dynamic_module(
1114 &self,
1115 module_identity: ModuleIdentity,
1116 dynamic_module_id: DynamicModuleId,
1117 can_gc: CanGc,
1118 ) {
1119 let global = self.global();
1120
1121 let module = global.dynamic_module_list().remove(dynamic_module_id);
1122
1123 let cx = GlobalScope::get_cx();
1124 let module_tree = module_identity.get_module_tree(&global);
1125
1126 let network_error = module_tree.get_network_error().borrow().as_ref().cloned();
1131 let existing_rethrow_error = module_tree.get_rethrow_error().borrow().as_ref().cloned();
1132
1133 rooted!(in(*cx) let mut rval = UndefinedValue());
1134 if network_error.is_none() && existing_rethrow_error.is_none() {
1135 let record = module_tree
1136 .get_record()
1137 .borrow()
1138 .as_ref()
1139 .map(|record| record.handle());
1140
1141 if let Some(record) = record {
1142 let evaluated = module_tree
1143 .execute_module(&global, record, rval.handle_mut().into(), can_gc)
1144 .err();
1145
1146 if let Some(exception) = evaluated.clone() {
1147 module_tree.set_rethrow_error(exception);
1148 }
1149 }
1150 }
1151
1152 match (network_error, existing_rethrow_error) {
1154 (Some(_), _) => unsafe {
1155 let err = gen_type_error(&global, "Dynamic import failed".to_owned(), can_gc);
1156 JS_SetPendingException(*cx, err.handle(), ExceptionStackBehavior::Capture);
1157 },
1158 (None, Some(rethrow_error)) => unsafe {
1159 JS_SetPendingException(
1160 *cx,
1161 rethrow_error.handle(),
1162 ExceptionStackBehavior::Capture,
1163 );
1164 },
1165 (None, None) => {},
1167 };
1168
1169 debug!("Finishing dynamic import for {:?}", module_identity);
1170
1171 rooted!(in(*cx) let mut evaluation_promise = ptr::null_mut::<JSObject>());
1172 if rval.is_object() {
1173 evaluation_promise.set(rval.to_object());
1174 }
1175
1176 unsafe {
1177 let ok = FinishDynamicModuleImport(
1178 *cx,
1179 evaluation_promise.handle().into(),
1180 module.referencing_private.handle(),
1181 module.specifier.handle(),
1182 module.promise.reflector().get_jsobject().into_handle(),
1183 );
1184 if ok {
1185 assert!(!JS_IsExceptionPending(*cx));
1186 } else {
1187 warn!("failed to finish dynamic module import");
1188 }
1189 }
1190 }
1191}
1192
1193struct ModuleContext {
1195 owner: ModuleOwner,
1197 data: Vec<u8>,
1199 metadata: Option<Metadata>,
1201 url: ServoUrl,
1203 destination: Destination,
1205 options: ScriptFetchOptions,
1207 status: Result<(), NetworkError>,
1209 resource_timing: ResourceFetchTiming,
1211 introduction_type: Option<&'static CStr>,
1213}
1214
1215impl FetchResponseListener for ModuleContext {
1216 fn process_request_body(&mut self, _: RequestId) {}
1218
1219 fn process_request_eof(&mut self, _: RequestId) {}
1221
1222 fn process_response(&mut self, _: RequestId, metadata: Result<FetchMetadata, NetworkError>) {
1223 self.metadata = metadata.ok().map(|meta| match meta {
1224 FetchMetadata::Unfiltered(m) => m,
1225 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
1226 });
1227
1228 let status = self
1229 .metadata
1230 .as_ref()
1231 .map(|m| m.status.clone())
1232 .unwrap_or_else(HttpStatus::new_error);
1233
1234 self.status = {
1235 if status.is_error() {
1236 Err(NetworkError::Internal(
1237 "No http status code received".to_owned(),
1238 ))
1239 } else if status.is_success() {
1240 Ok(())
1241 } else {
1242 Err(NetworkError::Internal(format!(
1243 "HTTP error code {}",
1244 status.code()
1245 )))
1246 }
1247 };
1248 }
1249
1250 fn process_response_chunk(&mut self, _: RequestId, mut chunk: Vec<u8>) {
1251 if self.status.is_ok() {
1252 self.data.append(&mut chunk);
1253 }
1254 }
1255
1256 #[allow(unsafe_code)]
1259 fn process_response_eof(
1260 &mut self,
1261 _: RequestId,
1262 response: Result<ResourceFetchTiming, NetworkError>,
1263 ) {
1264 let global = self.owner.global();
1265
1266 if let Some(window) = global.downcast::<Window>() {
1267 window
1268 .Document()
1269 .finish_load(LoadType::Script(self.url.clone()), CanGc::note());
1270 }
1271
1272 let load = response.and(self.status.clone()).and_then(|_| {
1274 let meta = self.metadata.take().unwrap();
1276
1277 if let Some(content_type) = meta.content_type.map(Serde::into_inner) {
1278 if let Ok(content_type) = Mime::from_str(&content_type.to_string()) {
1279 let essence_mime = content_type.essence_str();
1280
1281 if !SCRIPT_JS_MIMES.contains(&essence_mime) {
1282 return Err(NetworkError::Internal(format!(
1283 "Invalid MIME type: {}",
1284 essence_mime
1285 )));
1286 }
1287 } else {
1288 return Err(NetworkError::Internal(format!(
1289 "Failed to parse MIME type: {}",
1290 content_type
1291 )));
1292 }
1293 } else {
1294 return Err(NetworkError::Internal("No MIME type".into()));
1295 }
1296
1297 let referrer_policy = meta
1300 .headers
1301 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
1302 .into();
1303
1304 if referrer_policy != ReferrerPolicy::EmptyString {
1307 self.options.referrer_policy = referrer_policy;
1308 }
1309
1310 let (source_text, _, _) = UTF_8.decode(&self.data);
1312 Ok(ScriptOrigin::external(
1313 Rc::new(DOMString::from(source_text)),
1314 meta.final_url,
1315 self.options.clone(),
1316 ScriptType::Module,
1317 global.unminified_js_dir(),
1318 ))
1319 });
1320
1321 let module_tree = {
1322 let module_map = global.get_module_map().borrow();
1323 module_map.get(&self.url).unwrap().clone()
1324 };
1325
1326 module_tree.remove_incomplete_fetch_url(&self.url);
1327
1328 match load {
1330 Err(err) => {
1331 error!("Failed to fetch {} with error {:?}", &self.url, err);
1332 module_tree.set_network_error(err);
1333 module_tree.advance_finished_and_link(&global, CanGc::note());
1334 },
1335 Ok(ref resp_mod_script) => {
1336 module_tree.set_text(resp_mod_script.text());
1337
1338 let cx = GlobalScope::get_cx();
1339 rooted!(in(*cx) let mut compiled_module: *mut JSObject = ptr::null_mut());
1340 let compiled_module_result = module_tree.compile_module_script(
1341 &global,
1342 self.owner.clone(),
1343 resp_mod_script.text(),
1344 &self.url,
1345 self.options.clone(),
1346 compiled_module.handle_mut(),
1347 false,
1348 1, self.introduction_type,
1350 CanGc::note(),
1351 );
1352
1353 match compiled_module_result {
1354 Err(exception) => {
1355 module_tree.set_rethrow_error(exception);
1356 module_tree.advance_finished_and_link(&global, CanGc::note());
1357 },
1358 Ok(_) => {
1359 module_tree.set_record(ModuleObject::new(compiled_module.handle()));
1360
1361 module_tree.fetch_module_descendants(
1362 &self.owner,
1363 self.destination,
1364 &self.options,
1365 ModuleIdentity::ModuleUrl(self.url.clone()),
1366 CanGc::note(),
1367 );
1368 },
1369 }
1370 },
1371 }
1372 }
1373
1374 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
1375 &mut self.resource_timing
1376 }
1377
1378 fn resource_timing(&self) -> &ResourceFetchTiming {
1379 &self.resource_timing
1380 }
1381
1382 fn submit_resource_timing(&mut self) {
1383 network_listener::submit_timing(self, CanGc::note())
1384 }
1385
1386 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
1387 let global = &self.resource_timing_global();
1388 global.report_csp_violations(violations, None, None);
1389 }
1390}
1391
1392impl ResourceTimingListener for ModuleContext {
1393 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
1394 let initiator_type = InitiatorType::LocalName("module".to_string());
1395 (initiator_type, self.url.clone())
1396 }
1397
1398 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
1399 self.owner.global()
1400 }
1401}
1402
1403impl PreInvoke for ModuleContext {}
1404
1405#[allow(unsafe_code, non_snake_case)]
1406pub(crate) unsafe fn EnsureModuleHooksInitialized(rt: *mut JSRuntime) {
1409 if GetModuleResolveHook(rt).is_some() {
1410 return;
1411 }
1412
1413 SetModuleResolveHook(rt, Some(HostResolveImportedModule));
1414 SetModuleMetadataHook(rt, Some(HostPopulateImportMeta));
1415 SetScriptPrivateReferenceHooks(
1416 rt,
1417 Some(host_add_ref_top_level_script),
1418 Some(host_release_top_level_script),
1419 );
1420 SetModuleDynamicImportHook(rt, Some(host_import_module_dynamically));
1421}
1422
1423#[allow(unsafe_code)]
1424unsafe extern "C" fn host_add_ref_top_level_script(value: *const Value) {
1425 let val = Rc::from_raw((*value).to_private() as *const ModuleScript);
1426 mem::forget(val.clone());
1427 mem::forget(val);
1428}
1429
1430#[allow(unsafe_code)]
1431unsafe extern "C" fn host_release_top_level_script(value: *const Value) {
1432 let _val = Rc::from_raw((*value).to_private() as *const ModuleScript);
1433}
1434
1435#[allow(unsafe_code)]
1436pub(crate) unsafe extern "C" fn host_import_module_dynamically(
1439 cx: *mut JSContext,
1440 reference_private: RawHandleValue,
1441 specifier: RawHandle<*mut JSObject>,
1442 promise: RawHandle<*mut JSObject>,
1443) -> bool {
1444 let cx = SafeJSContext::from_ptr(cx);
1446 let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
1447 let global_scope = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
1448 let promise = Promise::new_with_js_promise(Handle::from_raw(promise), cx);
1449
1450 if let Err(e) = fetch_an_import_module_script_graph(
1452 &global_scope,
1453 specifier,
1454 reference_private,
1455 promise,
1456 CanGc::note(),
1457 ) {
1458 JS_SetPendingException(*cx, e.handle(), ExceptionStackBehavior::Capture);
1459 return false;
1460 }
1461
1462 true
1463}
1464
1465#[derive(Clone, Debug, JSTraceable, MallocSizeOf)]
1466pub(crate) struct ScriptFetchOptions {
1468 #[no_trace]
1469 pub(crate) referrer: Referrer,
1470 pub(crate) integrity_metadata: String,
1471 #[no_trace]
1472 pub(crate) credentials_mode: CredentialsMode,
1473 pub(crate) cryptographic_nonce: String,
1474 #[no_trace]
1475 pub(crate) parser_metadata: ParserMetadata,
1476 #[no_trace]
1477 pub(crate) referrer_policy: ReferrerPolicy,
1478}
1479
1480impl ScriptFetchOptions {
1481 pub(crate) fn default_classic_script(global: &GlobalScope) -> ScriptFetchOptions {
1483 Self {
1484 cryptographic_nonce: String::new(),
1485 integrity_metadata: String::new(),
1486 referrer: global.get_referrer(),
1487 parser_metadata: ParserMetadata::NotParserInserted,
1488 credentials_mode: CredentialsMode::CredentialsSameOrigin,
1489 referrer_policy: ReferrerPolicy::EmptyString,
1490 }
1491 }
1492
1493 fn descendant_fetch_options(&self) -> ScriptFetchOptions {
1495 Self {
1496 referrer: self.referrer.clone(),
1497 integrity_metadata: String::new(),
1498 cryptographic_nonce: self.cryptographic_nonce.clone(),
1499 credentials_mode: self.credentials_mode,
1500 parser_metadata: self.parser_metadata,
1501 referrer_policy: self.referrer_policy,
1502 }
1503 }
1504}
1505
1506#[allow(unsafe_code)]
1507unsafe fn module_script_from_reference_private(
1508 reference_private: &RawHandle<JSVal>,
1509) -> Option<&ModuleScript> {
1510 if reference_private.get().is_undefined() {
1511 return None;
1512 }
1513 (reference_private.get().to_private() as *const ModuleScript).as_ref()
1514}
1515
1516#[allow(unsafe_code)]
1518fn fetch_an_import_module_script_graph(
1519 global: &GlobalScope,
1520 module_request: RawHandle<*mut JSObject>,
1521 reference_private: RawHandleValue,
1522 promise: Rc<Promise>,
1523 can_gc: CanGc,
1524) -> Result<(), RethrowError> {
1525 let cx = GlobalScope::get_cx();
1527 let specifier = unsafe {
1528 let jsstr = std::ptr::NonNull::new(GetModuleRequestSpecifier(*cx, module_request)).unwrap();
1529 DOMString::from_string(jsstr_to_string(*cx, jsstr))
1530 };
1531 let mut options = ScriptFetchOptions::default_classic_script(global);
1532 let module_data = unsafe { module_script_from_reference_private(&reference_private) };
1533 if let Some(data) = module_data {
1534 options = data.options.descendant_fetch_options();
1535 }
1536 let url = ModuleTree::resolve_module_specifier(global, module_data, specifier, can_gc);
1537
1538 if url.is_err() {
1540 let specifier_error = gen_type_error(global, "Wrong module specifier".to_owned(), can_gc);
1541 return Err(specifier_error);
1542 }
1543
1544 let dynamic_module_id = DynamicModuleId(Uuid::new_v4());
1545
1546 let owner = match unsafe { module_script_from_reference_private(&reference_private) } {
1548 Some(module_data) if module_data.owner.is_some() => module_data.owner.clone().unwrap(),
1549 _ => ModuleOwner::DynamicModule(Trusted::new(&DynamicModuleOwner::new(
1550 global,
1551 promise.clone(),
1552 dynamic_module_id,
1553 can_gc,
1554 ))),
1555 };
1556
1557 let dynamic_module = RootedTraceableBox::new(DynamicModule {
1558 promise,
1559 specifier: Heap::default(),
1560 referencing_private: Heap::default(),
1561 id: dynamic_module_id,
1562 });
1563 dynamic_module.specifier.set(module_request.get());
1564 dynamic_module
1565 .referencing_private
1566 .set(reference_private.get());
1567
1568 let url = url.unwrap();
1569
1570 let mut visited_urls = HashSet::new();
1571 visited_urls.insert(url.clone());
1572
1573 fetch_single_module_script(
1574 owner,
1575 url,
1576 visited_urls,
1577 Destination::Script,
1578 options,
1579 None,
1580 true,
1581 Some(dynamic_module),
1582 Some(IntroductionType::IMPORTED_MODULE),
1583 can_gc,
1584 );
1585 Ok(())
1586}
1587
1588#[allow(unsafe_code, non_snake_case)]
1589unsafe extern "C" fn HostResolveImportedModule(
1592 cx: *mut JSContext,
1593 reference_private: RawHandleValue,
1594 specifier: RawHandle<*mut JSObject>,
1595) -> *mut JSObject {
1596 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1597 let global_scope = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof));
1598
1599 let module_data = module_script_from_reference_private(&reference_private);
1601 let jsstr = std::ptr::NonNull::new(GetModuleRequestSpecifier(cx, specifier)).unwrap();
1602 let specifier = DOMString::from_string(jsstr_to_string(cx, jsstr));
1603 let url =
1604 ModuleTree::resolve_module_specifier(&global_scope, module_data, specifier, CanGc::note());
1605
1606 assert!(url.is_ok());
1608
1609 let parsed_url = url.unwrap();
1610
1611 let module_map = global_scope.get_module_map().borrow();
1613
1614 let module_tree = module_map.get(&parsed_url);
1615
1616 assert!(module_tree.is_some());
1618
1619 let fetched_module_object = module_tree.unwrap().get_record().borrow();
1620
1621 assert!(fetched_module_object.is_some());
1623
1624 if let Some(record) = &*fetched_module_object {
1626 return record.handle().get();
1627 }
1628
1629 unreachable!()
1630}
1631
1632#[allow(unsafe_code, non_snake_case)]
1633unsafe extern "C" fn HostPopulateImportMeta(
1636 cx: *mut JSContext,
1637 reference_private: RawHandleValue,
1638 meta_object: RawHandle<*mut JSObject>,
1639) -> bool {
1640 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1641 let global_scope = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof));
1642
1643 let base_url = match module_script_from_reference_private(&reference_private) {
1645 Some(module_data) => module_data.base_url.clone(),
1646 None => global_scope.api_base_url(),
1647 };
1648
1649 rooted!(in(cx) let url_string = JS_NewStringCopyN(
1650 cx,
1651 base_url.as_str().as_ptr() as *const _,
1652 base_url.as_str().len()
1653 ));
1654
1655 JS_DefineProperty4(
1657 cx,
1658 meta_object,
1659 c"url".as_ptr(),
1660 url_string.handle().into_handle(),
1661 JSPROP_ENUMERATE.into(),
1662 )
1663}
1664
1665pub(crate) fn fetch_external_module_script(
1667 owner: ModuleOwner,
1668 url: ServoUrl,
1669 destination: Destination,
1670 options: ScriptFetchOptions,
1671 can_gc: CanGc,
1672) {
1673 let mut visited_urls = HashSet::new();
1674 visited_urls.insert(url.clone());
1675
1676 fetch_single_module_script(
1678 owner,
1679 url,
1680 visited_urls,
1681 destination,
1682 options,
1683 None,
1684 true,
1685 None,
1686 Some(IntroductionType::SRC_SCRIPT),
1687 can_gc,
1688 )
1689}
1690
1691#[derive(JSTraceable, MallocSizeOf)]
1692#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1693pub(crate) struct DynamicModuleList {
1694 requests: Vec<RootedTraceableBox<DynamicModule>>,
1695
1696 #[ignore_malloc_size_of = "Define in uuid"]
1697 next_id: DynamicModuleId,
1698}
1699
1700impl DynamicModuleList {
1701 pub(crate) fn new() -> Self {
1702 Self {
1703 requests: vec![],
1704 next_id: DynamicModuleId(Uuid::new_v4()),
1705 }
1706 }
1707
1708 fn push(&mut self, mut module: RootedTraceableBox<DynamicModule>) -> DynamicModuleId {
1709 let id = self.next_id;
1710 self.next_id = DynamicModuleId(Uuid::new_v4());
1711 module.id = id;
1712 self.requests.push(module);
1713 id
1714 }
1715
1716 fn remove(&mut self, id: DynamicModuleId) -> RootedTraceableBox<DynamicModule> {
1717 let index = self
1718 .requests
1719 .iter()
1720 .position(|module| module.id == id)
1721 .expect("missing dynamic module");
1722 self.requests.remove(index)
1723 }
1724}
1725
1726#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1727#[derive(JSTraceable, MallocSizeOf)]
1728struct DynamicModule {
1729 #[conditional_malloc_size_of]
1730 promise: Rc<Promise>,
1731 #[ignore_malloc_size_of = "GC types are hard"]
1732 specifier: Heap<*mut JSObject>,
1733 #[ignore_malloc_size_of = "GC types are hard"]
1734 referencing_private: Heap<JSVal>,
1735 #[ignore_malloc_size_of = "Defined in uuid"]
1736 id: DynamicModuleId,
1737}
1738
1739#[allow(clippy::too_many_arguments)]
1741fn fetch_single_module_script(
1742 owner: ModuleOwner,
1743 url: ServoUrl,
1744 visited_urls: HashSet<ServoUrl>,
1745 destination: Destination,
1746 options: ScriptFetchOptions,
1747 parent_identity: Option<ModuleIdentity>,
1748 top_level_module_fetch: bool,
1749 dynamic_module: Option<RootedTraceableBox<DynamicModule>>,
1750 introduction_type: Option<&'static CStr>,
1751 can_gc: CanGc,
1752) {
1753 {
1754 let global = owner.global();
1756 let module_map = global.get_module_map().borrow();
1757
1758 debug!("Start to fetch {}", url);
1759
1760 if let Some(module_tree) = module_map.get(&url.clone()) {
1761 let status = module_tree.get_status();
1762
1763 debug!("Meet a fetched url {} and its status is {:?}", url, status);
1764
1765 match dynamic_module {
1766 Some(module) => module_tree.append_dynamic_module_handler(
1767 owner.clone(),
1768 ModuleIdentity::ModuleUrl(url.clone()),
1769 module,
1770 can_gc,
1771 ),
1772 None if top_level_module_fetch => module_tree.append_handler(
1773 owner.clone(),
1774 ModuleIdentity::ModuleUrl(url.clone()),
1775 options,
1776 can_gc,
1777 ),
1778 None => {},
1780 }
1781
1782 if let Some(parent_identity) = parent_identity {
1783 module_tree.insert_parent_identity(parent_identity);
1784 }
1785
1786 match status {
1787 ModuleStatus::Initial => unreachable!(
1788 "We have the module in module map so its status should not be `initial`"
1789 ),
1790 ModuleStatus::Fetching => {},
1792 ModuleStatus::FetchingDescendants | ModuleStatus::Finished => {
1794 module_tree.advance_finished_and_link(&global, can_gc);
1795 },
1796 }
1797
1798 return;
1799 }
1800 }
1801
1802 let global = owner.global();
1803 let is_external = true;
1804 let module_tree = ModuleTree::new(url.clone(), is_external, visited_urls);
1805 module_tree.set_status(ModuleStatus::Fetching);
1806
1807 match dynamic_module {
1808 Some(module) => module_tree.append_dynamic_module_handler(
1809 owner.clone(),
1810 ModuleIdentity::ModuleUrl(url.clone()),
1811 module,
1812 can_gc,
1813 ),
1814 None if top_level_module_fetch => module_tree.append_handler(
1815 owner.clone(),
1816 ModuleIdentity::ModuleUrl(url.clone()),
1817 options.clone(),
1818 can_gc,
1819 ),
1820 None => {},
1822 }
1823
1824 if let Some(parent_identity) = parent_identity {
1825 module_tree.insert_parent_identity(parent_identity);
1826 }
1827
1828 module_tree.insert_incomplete_fetch_url(&url);
1829
1830 global.set_module_map(url.clone(), module_tree);
1832
1833 let mode = match destination {
1835 Destination::Worker | Destination::SharedWorker if top_level_module_fetch => {
1836 RequestMode::SameOrigin
1837 },
1838 _ => RequestMode::CorsMode,
1839 };
1840
1841 let document: Option<DomRoot<Document>> = match &owner {
1842 ModuleOwner::Worker(_) | ModuleOwner::DynamicModule(_) => None,
1843 ModuleOwner::Window(script) => Some(script.root().owner_document()),
1844 };
1845 let webview_id = document.as_ref().map(|document| document.webview_id());
1846
1847 let request = RequestBuilder::new(webview_id, url.clone(), global.get_referrer())
1849 .destination(destination)
1850 .origin(global.origin().immutable().clone())
1851 .parser_metadata(options.parser_metadata)
1852 .integrity_metadata(options.integrity_metadata.clone())
1853 .credentials_mode(options.credentials_mode)
1854 .referrer_policy(options.referrer_policy)
1855 .mode(mode)
1856 .insecure_requests_policy(global.insecure_requests_policy())
1857 .has_trustworthy_ancestor_origin(global.has_trustworthy_ancestor_origin())
1858 .policy_container(global.policy_container().to_owned())
1859 .cryptographic_nonce_metadata(options.cryptographic_nonce.clone());
1860
1861 let context = Arc::new(Mutex::new(ModuleContext {
1862 owner,
1863 data: vec![],
1864 metadata: None,
1865 url: url.clone(),
1866 destination,
1867 options,
1868 status: Ok(()),
1869 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
1870 introduction_type,
1871 }));
1872
1873 let network_listener = NetworkListener {
1874 context,
1875 task_source: global.task_manager().networking_task_source().to_sendable(),
1876 };
1877 match document {
1878 Some(document) => {
1879 let request = document.prepare_request(request);
1880 document.loader_mut().fetch_async_with_callback(
1881 LoadType::Script(url),
1882 request,
1883 network_listener.into_callback(),
1884 );
1885 },
1886 None => global.fetch_with_network_listener(request, network_listener),
1887 }
1888}
1889
1890#[allow(unsafe_code)]
1891pub(crate) fn fetch_inline_module_script(
1893 owner: ModuleOwner,
1894 module_script_text: Rc<DOMString>,
1895 url: ServoUrl,
1896 script_id: ScriptId,
1897 options: ScriptFetchOptions,
1898 line_number: u64,
1899 can_gc: CanGc,
1900) {
1901 let global = owner.global();
1902 let is_external = false;
1903 let module_tree = ModuleTree::new(url.clone(), is_external, HashSet::new());
1904
1905 let cx = GlobalScope::get_cx();
1906 rooted!(in(*cx) let mut compiled_module: *mut JSObject = ptr::null_mut());
1907 let compiled_module_result = module_tree.compile_module_script(
1908 &global,
1909 owner.clone(),
1910 module_script_text,
1911 &url,
1912 options.clone(),
1913 compiled_module.handle_mut(),
1914 true,
1915 line_number,
1916 Some(IntroductionType::INLINE_SCRIPT),
1917 can_gc,
1918 );
1919
1920 match compiled_module_result {
1921 Ok(_) => {
1922 module_tree.append_handler(
1923 owner.clone(),
1924 ModuleIdentity::ScriptId(script_id),
1925 options.clone(),
1926 can_gc,
1927 );
1928 module_tree.set_record(ModuleObject::new(compiled_module.handle()));
1929
1930 global.set_inline_module_map(script_id, module_tree);
1934
1935 let inline_module_map = global.get_inline_module_map().borrow();
1939 let module_tree = inline_module_map.get(&script_id).unwrap().clone();
1940
1941 module_tree.fetch_module_descendants(
1942 &owner,
1943 Destination::Script,
1944 &options,
1945 ModuleIdentity::ScriptId(script_id),
1946 can_gc,
1947 );
1948 },
1949 Err(exception) => {
1950 module_tree.set_rethrow_error(exception);
1951 module_tree.set_status(ModuleStatus::Finished);
1952 global.set_inline_module_map(script_id, module_tree);
1953 owner.notify_owner_to_finish(ModuleIdentity::ScriptId(script_id), options, can_gc);
1954 },
1955 }
1956}
1957
1958pub(crate) type ModuleSpecifierMap = IndexMap<String, Option<ServoUrl>>;
1959pub(crate) type ModuleIntegrityMap = IndexMap<ServoUrl, String>;
1960
1961#[derive(Default, Eq, Hash, JSTraceable, MallocSizeOf, PartialEq)]
1963pub(crate) struct ResolvedModule {
1964 base_url: String,
1966 specifier: String,
1968 #[no_trace]
1970 specifier_url: Option<ServoUrl>,
1971}
1972
1973impl ResolvedModule {
1974 pub(crate) fn new(
1975 base_url: String,
1976 specifier: String,
1977 specifier_url: Option<ServoUrl>,
1978 ) -> Self {
1979 Self {
1980 base_url,
1981 specifier,
1982 specifier_url,
1983 }
1984 }
1985}
1986
1987#[derive(Default, JSTraceable, MallocSizeOf)]
1989pub(crate) struct ImportMap {
1990 #[no_trace]
1991 imports: ModuleSpecifierMap,
1992 #[no_trace]
1993 scopes: IndexMap<ServoUrl, ModuleSpecifierMap>,
1994 #[no_trace]
1995 integrity: ModuleIntegrityMap,
1996}
1997
1998pub(crate) fn register_import_map(
2000 global: &GlobalScope,
2001 result: Fallible<ImportMap>,
2002 can_gc: CanGc,
2003) {
2004 match result {
2005 Ok(new_import_map) => {
2006 merge_existing_and_new_import_maps(global, new_import_map, can_gc);
2008 },
2009 Err(exception) => {
2010 throw_dom_exception(GlobalScope::get_cx(), global, exception.clone(), can_gc);
2013 },
2014 }
2015}
2016
2017fn merge_existing_and_new_import_maps(
2019 global: &GlobalScope,
2020 new_import_map: ImportMap,
2021 can_gc: CanGc,
2022) {
2023 let new_import_map_scopes = new_import_map.scopes;
2025
2026 let mut old_import_map = global.import_map_mut();
2028
2029 let mut new_import_map_imports = new_import_map.imports;
2031
2032 let resolved_module_set = global.resolved_module_set();
2033 for (scope_prefix, mut scope_imports) in new_import_map_scopes {
2035 for record in resolved_module_set.iter() {
2037 let prefix = scope_prefix.as_str();
2040 if prefix == record.base_url ||
2041 (record.base_url.starts_with(prefix) && prefix.ends_with('\u{002f}'))
2042 {
2043 scope_imports.retain(|key, val| {
2045 if *key == record.specifier ||
2050 (key.ends_with('\u{002f}') &&
2051 record.specifier.starts_with(key) &&
2052 (record.specifier_url.is_none() ||
2053 record
2054 .specifier_url
2055 .as_ref()
2056 .map(|u| u.is_special_scheme())
2057 .unwrap_or_default()))
2058 {
2059 Console::internal_warn(
2062 global,
2063 DOMString::from(format!("Ignored rule: {key} -> {val:?}.")),
2064 );
2065 false
2067 } else {
2068 true
2069 }
2070 })
2071 }
2072 }
2073
2074 if old_import_map.scopes.contains_key(&scope_prefix) {
2076 let merged_module_specifier_map = merge_module_specifier_maps(
2079 global,
2080 scope_imports,
2081 &old_import_map.scopes[&scope_prefix],
2082 can_gc,
2083 );
2084 old_import_map
2085 .scopes
2086 .insert(scope_prefix, merged_module_specifier_map);
2087 } else {
2088 old_import_map.scopes.insert(scope_prefix, scope_imports);
2090 }
2091 }
2092
2093 for (url, integrity) in &new_import_map.integrity {
2095 if old_import_map.integrity.contains_key(url) {
2097 Console::internal_warn(
2100 global,
2101 DOMString::from(format!("Ignored rule: {url} -> {integrity}.")),
2102 );
2103 continue;
2105 }
2106
2107 old_import_map
2109 .integrity
2110 .insert(url.clone(), integrity.clone());
2111 }
2112
2113 for record in resolved_module_set.iter() {
2115 new_import_map_imports.retain(|specifier, val| {
2117 if specifier.starts_with(&record.specifier) {
2119 Console::internal_warn(
2122 global,
2123 DOMString::from(format!("Ignored rule: {specifier} -> {val:?}.")),
2124 );
2125 false
2127 } else {
2128 true
2129 }
2130 });
2131 }
2132
2133 let merged_module_specifier_map = merge_module_specifier_maps(
2136 global,
2137 new_import_map_imports,
2138 &old_import_map.imports,
2139 can_gc,
2140 );
2141 old_import_map.imports = merged_module_specifier_map;
2142}
2143
2144fn merge_module_specifier_maps(
2146 global: &GlobalScope,
2147 new_map: ModuleSpecifierMap,
2148 old_map: &ModuleSpecifierMap,
2149 _can_gc: CanGc,
2150) -> ModuleSpecifierMap {
2151 let mut merged_map = old_map.clone();
2153
2154 for (specifier, url) in new_map {
2156 if old_map.contains_key(&specifier) {
2158 Console::internal_warn(
2161 global,
2162 DOMString::from(format!("Ignored rule: {specifier} -> {url:?}.")),
2163 );
2164
2165 continue;
2167 }
2168
2169 merged_map.insert(specifier, url);
2171 }
2172
2173 merged_map
2174}
2175
2176pub(crate) fn parse_an_import_map_string(
2178 module_owner: ModuleOwner,
2179 input: Rc<DOMString>,
2180 base_url: ServoUrl,
2181 can_gc: CanGc,
2182) -> Fallible<ImportMap> {
2183 let parsed: JsonValue = serde_json::from_str(&input.str())
2185 .map_err(|_| Error::Type("The value needs to be a JSON object.".to_owned()))?;
2186 let JsonValue::Object(mut parsed) = parsed else {
2189 return Err(Error::Type(
2190 "The top-level value needs to be a JSON object.".to_owned(),
2191 ));
2192 };
2193
2194 let mut sorted_and_normalized_imports = ModuleSpecifierMap::new();
2196 if let Some(imports) = parsed.get("imports") {
2198 let JsonValue::Object(imports) = imports else {
2201 return Err(Error::Type(
2202 "The \"imports\" top-level value needs to be a JSON object.".to_owned(),
2203 ));
2204 };
2205 sorted_and_normalized_imports = sort_and_normalize_module_specifier_map(
2208 &module_owner.global(),
2209 imports,
2210 &base_url,
2211 can_gc,
2212 );
2213 }
2214
2215 let mut sorted_and_normalized_scopes: IndexMap<ServoUrl, ModuleSpecifierMap> = IndexMap::new();
2217 if let Some(scopes) = parsed.get("scopes") {
2219 let JsonValue::Object(scopes) = scopes else {
2222 return Err(Error::Type(
2223 "The \"scopes\" top-level value needs to be a JSON object.".to_owned(),
2224 ));
2225 };
2226 sorted_and_normalized_scopes =
2229 sort_and_normalize_scopes(&module_owner.global(), scopes, &base_url, can_gc)?;
2230 }
2231
2232 let mut normalized_integrity = ModuleIntegrityMap::new();
2234 if let Some(integrity) = parsed.get("integrity") {
2236 let JsonValue::Object(integrity) = integrity else {
2239 return Err(Error::Type(
2240 "The \"integrity\" top-level value needs to be a JSON object.".to_owned(),
2241 ));
2242 };
2243 normalized_integrity =
2246 normalize_module_integrity_map(&module_owner.global(), integrity, &base_url, can_gc);
2247 }
2248
2249 parsed.retain(|k, _| !matches!(k.as_str(), "imports" | "scopes" | "integrity"));
2253 if !parsed.is_empty() {
2254 Console::internal_warn(
2255 &module_owner.global(),
2256 DOMString::from(
2257 "Invalid top-level key was present in the import map.
2258 Only \"imports\", \"scopes\", and \"integrity\" are allowed.",
2259 ),
2260 );
2261 }
2262
2263 Ok(ImportMap {
2265 imports: sorted_and_normalized_imports,
2266 scopes: sorted_and_normalized_scopes,
2267 integrity: normalized_integrity,
2268 })
2269}
2270
2271#[allow(unsafe_code)]
2273fn sort_and_normalize_module_specifier_map(
2274 global: &GlobalScope,
2275 original_map: &JsonMap<String, JsonValue>,
2276 base_url: &ServoUrl,
2277 can_gc: CanGc,
2278) -> ModuleSpecifierMap {
2279 let mut normalized = ModuleSpecifierMap::new();
2281
2282 for (specifier_key, value) in original_map {
2284 let Some(normalized_specifier_key) =
2287 normalize_specifier_key(global, specifier_key, base_url, can_gc)
2288 else {
2289 continue;
2291 };
2292
2293 let JsonValue::String(value) = value else {
2295 Console::internal_warn(global, DOMString::from("Addresses need to be strings."));
2298
2299 normalized.insert(normalized_specifier_key, None);
2301 continue;
2303 };
2304
2305 let value = DOMString::from(value.as_str());
2307 let Some(address_url) = ModuleTree::resolve_url_like_module_specifier(&value, base_url)
2308 else {
2309 Console::internal_warn(
2313 global,
2314 DOMString::from(format!(
2315 "Value failed to resolve to module specifier: {value}"
2316 )),
2317 );
2318
2319 normalized.insert(normalized_specifier_key, None);
2321 continue;
2323 };
2324
2325 if specifier_key.ends_with('\u{002f}') && !address_url.as_str().ends_with('\u{002f}') {
2328 Console::internal_warn(
2332 global,
2333 DOMString::from(format!(
2334 "Invalid address for specifier key '{specifier_key}': {address_url}.
2335 Since specifierKey ends with a slash, the address needs to as well."
2336 )),
2337 );
2338
2339 normalized.insert(normalized_specifier_key, None);
2341 continue;
2343 }
2344
2345 normalized.insert(normalized_specifier_key, Some(address_url));
2347 }
2348
2349 normalized.sort_by(|a_key, _, b_key, _| b_key.cmp(a_key));
2352 normalized
2353}
2354
2355fn sort_and_normalize_scopes(
2357 global: &GlobalScope,
2358 original_map: &JsonMap<String, JsonValue>,
2359 base_url: &ServoUrl,
2360 can_gc: CanGc,
2361) -> Fallible<IndexMap<ServoUrl, ModuleSpecifierMap>> {
2362 let mut normalized: IndexMap<ServoUrl, ModuleSpecifierMap> = IndexMap::new();
2364
2365 for (scope_prefix, potential_specifier_map) in original_map {
2367 let JsonValue::Object(potential_specifier_map) = potential_specifier_map else {
2370 return Err(Error::Type(
2371 "The value of the scope with prefix scopePrefix needs to be a JSON object."
2372 .to_owned(),
2373 ));
2374 };
2375
2376 let Ok(scope_prefix_url) = ServoUrl::parse_with_base(Some(base_url), scope_prefix) else {
2378 Console::internal_warn(
2382 global,
2383 DOMString::from(format!(
2384 "Scope prefix URL was not parseable: {scope_prefix}"
2385 )),
2386 );
2387 continue;
2389 };
2390
2391 let normalized_scope_prefix = scope_prefix_url;
2393
2394 let normalized_specifier_map = sort_and_normalize_module_specifier_map(
2397 global,
2398 potential_specifier_map,
2399 base_url,
2400 can_gc,
2401 );
2402 normalized.insert(normalized_scope_prefix, normalized_specifier_map);
2403 }
2404
2405 normalized.sort_by(|a_key, _, b_key, _| b_key.cmp(a_key));
2408 Ok(normalized)
2409}
2410
2411fn normalize_module_integrity_map(
2413 global: &GlobalScope,
2414 original_map: &JsonMap<String, JsonValue>,
2415 base_url: &ServoUrl,
2416 _can_gc: CanGc,
2417) -> ModuleIntegrityMap {
2418 let mut normalized = ModuleIntegrityMap::new();
2420
2421 for (key, value) in original_map {
2423 let Some(resolved_url) =
2426 ModuleTree::resolve_url_like_module_specifier(&DOMString::from(key.as_str()), base_url)
2427 else {
2428 Console::internal_warn(
2432 global,
2433 DOMString::from(format!("Key failed to resolve to module specifier: {key}")),
2434 );
2435 continue;
2437 };
2438
2439 let JsonValue::String(value) = value else {
2441 Console::internal_warn(
2444 global,
2445 DOMString::from("Integrity metadata values need to be strings."),
2446 );
2447 continue;
2449 };
2450
2451 normalized.insert(resolved_url, value.clone());
2453 }
2454
2455 normalized
2457}
2458
2459fn normalize_specifier_key(
2461 global: &GlobalScope,
2462 specifier_key: &str,
2463 base_url: &ServoUrl,
2464 _can_gc: CanGc,
2465) -> Option<String> {
2466 if specifier_key.is_empty() {
2468 Console::internal_warn(
2471 global,
2472 DOMString::from("Specifier keys may not be the empty string."),
2473 );
2474 return None;
2476 }
2477 let url =
2479 ModuleTree::resolve_url_like_module_specifier(&DOMString::from(specifier_key), base_url);
2480
2481 if let Some(url) = url {
2483 return Some(url.into_string());
2484 }
2485
2486 Some(specifier_key.to_string())
2488}
2489
2490pub(crate) fn resolve_imports_match(
2495 normalized_specifier: &str,
2496 as_url: Option<&ServoUrl>,
2497 specifier_map: &ModuleSpecifierMap,
2498 _can_gc: CanGc,
2499) -> Fallible<Option<ServoUrl>> {
2500 for (specifier_key, resolution_result) in specifier_map {
2502 if specifier_key == normalized_specifier {
2504 if let Some(resolution_result) = resolution_result {
2505 return Ok(Some(resolution_result.clone()));
2509 } else {
2510 return Err(Error::Type(
2512 "Resolution of specifierKey was blocked by a null entry.".to_owned(),
2513 ));
2514 }
2515 }
2516
2517 if specifier_key.ends_with('\u{002f}') &&
2522 normalized_specifier.starts_with(specifier_key) &&
2523 (as_url.is_none() || as_url.map(|u| u.is_special_scheme()).unwrap_or_default())
2524 {
2525 let Some(resolution_result) = resolution_result else {
2528 return Err(Error::Type(
2529 "Resolution of specifierKey was blocked by a null entry.".to_owned(),
2530 ));
2531 };
2532
2533 let after_prefix = normalized_specifier
2535 .strip_prefix(specifier_key)
2536 .expect("specifier_key should be the prefix of normalized_specifier");
2537
2538 debug_assert!(resolution_result.as_str().ends_with('\u{002f}'));
2540
2541 let url = ServoUrl::parse_with_base(Some(resolution_result), after_prefix);
2543
2544 let Ok(url) = url else {
2547 return Err(Error::Type(
2548 "Resolution of normalizedSpecifier was blocked since
2549 the afterPrefix portion could not be URL-parsed relative to
2550 the resolutionResult mapped to by the specifierKey prefix."
2551 .to_owned(),
2552 ));
2553 };
2554
2555 if !url.as_str().starts_with(resolution_result.as_str()) {
2558 return Err(Error::Type(
2559 "Resolution of normalizedSpecifier was blocked due to
2560 it backtracking above its prefix specifierKey."
2561 .to_owned(),
2562 ));
2563 }
2564
2565 return Ok(Some(url));
2567 }
2568 }
2569
2570 Ok(None)
2572}