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