aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/script_module.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/script_module.rs')
-rw-r--r--components/script/script_module.rs1118
1 files changed, 531 insertions, 587 deletions
diff --git a/components/script/script_module.rs b/components/script/script_module.rs
index ae8d57cad98..0ffe66d540e 100644
--- a/components/script/script_module.rs
+++ b/components/script/script_module.rs
@@ -12,7 +12,7 @@ use crate::dom::bindings::conversions::jsstring_to_str;
use crate::dom::bindings::error::report_pending_exception;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::inheritance::Castable;
-use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
+use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::settings_stack::AutoIncumbentScript;
@@ -37,19 +37,19 @@ use crate::task::TaskBox;
use crate::task_source::TaskSourceName;
use encoding_rs::UTF_8;
use hyper_serde::Serde;
+use indexmap::{IndexMap, IndexSet};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsapi::Handle as RawHandle;
use js::jsapi::HandleObject;
use js::jsapi::HandleValue as RawHandleValue;
-use js::jsapi::{
- CompileModule, ExceptionStackBehavior, GetModuleResolveHook, JSRuntime, SetModuleResolveHook,
-};
+use js::jsapi::{CompileModule, ExceptionStackBehavior};
+use js::jsapi::{GetModuleResolveHook, JSRuntime, SetModuleResolveHook};
use js::jsapi::{GetRequestedModules, SetModuleMetadataHook};
-use js::jsapi::{GetWaitForAllPromise, ModuleEvaluate, ModuleInstantiate};
use js::jsapi::{Heap, JSContext, JS_ClearPendingException, SetModulePrivate};
use js::jsapi::{JSAutoRealm, JSObject, JSString};
use js::jsapi::{JS_DefineProperty4, JS_NewStringCopyN, JSPROP_ENUMERATE};
+use js::jsapi::{ModuleEvaluate, ModuleInstantiate};
use js::jsapi::{SetModuleDynamicImportHook, SetScriptPrivateReferenceHooks};
use js::jsval::{JSVal, PrivateValue, UndefinedValue};
use js::rust::jsapi_wrapped::{GetRequestedModuleSpecifier, JS_GetPendingException};
@@ -57,9 +57,7 @@ use js::rust::jsapi_wrapped::{JS_GetArrayLength, JS_GetElement};
use js::rust::transform_u16_to_source_text;
use js::rust::wrappers::JS_SetPendingException;
use js::rust::CompileOptionsWrapper;
-use js::rust::IntoHandle;
-use js::rust::RootedObjectVectorWrapper;
-use js::rust::{Handle, HandleValue};
+use js::rust::{Handle, HandleValue, IntoHandle};
use mime::Mime;
use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
use net_traits::request::{Referrer, RequestBuilder, RequestMode};
@@ -67,22 +65,19 @@ use net_traits::{FetchMetadata, Metadata};
use net_traits::{FetchResponseListener, NetworkError};
use net_traits::{ResourceFetchTiming, ResourceTimingType};
use servo_url::ServoUrl;
-use std::cmp::Ordering;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashSet;
use std::ffi;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use url::ParseError as UrlParseError;
-use indexmap::IndexSet;
-
#[allow(unsafe_code)]
-unsafe fn gen_type_error(global: &GlobalScope, string: String) -> ModuleError {
+unsafe fn gen_type_error(global: &GlobalScope, string: String) -> RethrowError {
rooted!(in(*global.get_cx()) let mut thrown = UndefinedValue());
Error::Type(string).to_jsval(*global.get_cx(), &global, thrown.handle_mut());
- return ModuleError::RawException(RootedTraceableBox::from_box(Heap::boxed(thrown.get())));
+ return RethrowError(RootedTraceableBox::from_box(Heap::boxed(thrown.get())));
}
#[derive(JSTraceable)]
@@ -96,64 +91,55 @@ impl ModuleObject {
}
#[derive(JSTraceable)]
-pub enum ModuleError {
- Network(NetworkError),
- RawException(RootedTraceableBox<Heap<JSVal>>),
-}
-
-impl Eq for ModuleError {}
+pub struct RethrowError(RootedTraceableBox<Heap<JSVal>>);
-impl PartialEq for ModuleError {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (Self::Network(_), Self::RawException(_)) |
- (Self::RawException(_), Self::Network(_)) => false,
- _ => true,
- }
+impl RethrowError {
+ fn handle(&self) -> Handle<JSVal> {
+ self.0.handle()
}
}
-impl Ord for ModuleError {
- fn cmp(&self, other: &Self) -> Ordering {
- match (self, other) {
- (Self::Network(_), Self::RawException(_)) => Ordering::Greater,
- (Self::RawException(_), Self::Network(_)) => Ordering::Less,
- _ => Ordering::Equal,
- }
+impl Clone for RethrowError {
+ fn clone(&self) -> Self {
+ Self(RootedTraceableBox::from_box(Heap::boxed(
+ self.0.get().clone(),
+ )))
}
}
-impl PartialOrd for ModuleError {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
+struct ModuleScript {
+ base_url: ServoUrl,
}
-impl ModuleError {
- #[allow(unsafe_code)]
- pub fn handle(&self) -> Handle<JSVal> {
- match self {
- Self::Network(_) => unreachable!(),
- Self::RawException(exception) => exception.handle(),
- }
- }
+/// Identity for a module which will be
+/// used to retrieve the module when we'd
+/// like to get it from module map.
+///
+/// For example, we will save module parents with
+/// module identity so that we can get module tree
+/// from a descendant no matter the parent is an
+/// inline script or a external script
+#[derive(Clone, Eq, Hash, JSTraceable, PartialEq)]
+pub enum ModuleIdentity {
+ ScriptId(ScriptId),
+ ModuleUrl(ServoUrl),
}
-impl Clone for ModuleError {
- fn clone(&self) -> Self {
+impl ModuleIdentity {
+ pub fn get_module_tree(&self, global: &GlobalScope) -> Rc<ModuleTree> {
match self {
- Self::Network(network_error) => Self::Network(network_error.clone()),
- Self::RawException(exception) => Self::RawException(RootedTraceableBox::from_box(
- Heap::boxed(exception.get().clone()),
- )),
+ ModuleIdentity::ModuleUrl(url) => {
+ let module_map = global.get_module_map().borrow();
+ module_map.get(&url.clone()).unwrap().clone()
+ },
+ ModuleIdentity::ScriptId(script_id) => {
+ let inline_module_map = global.get_inline_module_map().borrow();
+ inline_module_map.get(&script_id).unwrap().clone()
+ },
}
}
}
-struct ModuleScript {
- base_url: ServoUrl,
-}
-
#[derive(JSTraceable)]
pub struct ModuleTree {
url: ServoUrl,
@@ -168,36 +154,37 @@ pub struct ModuleTree {
// By default all maps in web specs are ordered maps
// (https://infra.spec.whatwg.org/#ordered-map), however we can usually get away with using
// stdlib maps and sets because we rarely iterate over them.
- parent_urls: DomRefCell<IndexSet<ServoUrl>>,
+ parent_identities: DomRefCell<IndexSet<ModuleIdentity>>,
descendant_urls: DomRefCell<IndexSet<ServoUrl>>,
+ // A set to memoize which descendants are under fetching
+ incomplete_fetch_urls: DomRefCell<IndexSet<ServoUrl>>,
visited_urls: DomRefCell<HashSet<ServoUrl>>,
- error: DomRefCell<Option<ModuleError>>,
+ rethrow_error: DomRefCell<Option<RethrowError>>,
+ network_error: DomRefCell<Option<NetworkError>>,
+ // A promise for owners to execute when the module tree
+ // is finished
promise: DomRefCell<Option<Rc<Promise>>>,
+ external: bool,
}
impl ModuleTree {
- pub fn new(url: ServoUrl) -> Self {
+ pub fn new(url: ServoUrl, external: bool, visited_urls: HashSet<ServoUrl>) -> Self {
ModuleTree {
url,
text: DomRefCell::new(DOMString::new()),
record: DomRefCell::new(None),
status: DomRefCell::new(ModuleStatus::Initial),
- parent_urls: DomRefCell::new(IndexSet::new()),
+ parent_identities: DomRefCell::new(IndexSet::new()),
descendant_urls: DomRefCell::new(IndexSet::new()),
- visited_urls: DomRefCell::new(HashSet::new()),
- error: DomRefCell::new(None),
+ incomplete_fetch_urls: DomRefCell::new(IndexSet::new()),
+ visited_urls: DomRefCell::new(visited_urls),
+ rethrow_error: DomRefCell::new(None),
+ network_error: DomRefCell::new(None),
promise: DomRefCell::new(None),
+ external,
}
}
- pub fn get_promise(&self) -> &DomRefCell<Option<Rc<Promise>>> {
- &self.promise
- }
-
- pub fn set_promise(&self, promise: Rc<Promise>) {
- *self.promise.borrow_mut() = Some(promise);
- }
-
pub fn get_status(&self) -> ModuleStatus {
self.status.borrow().clone()
}
@@ -214,12 +201,20 @@ impl ModuleTree {
*self.record.borrow_mut() = Some(record);
}
- pub fn get_error(&self) -> &DomRefCell<Option<ModuleError>> {
- &self.error
+ pub fn get_rethrow_error(&self) -> &DomRefCell<Option<RethrowError>> {
+ &self.rethrow_error
+ }
+
+ pub fn set_rethrow_error(&self, rethrow_error: RethrowError) {
+ *self.rethrow_error.borrow_mut() = Some(rethrow_error);
}
- pub fn set_error(&self, error: Option<ModuleError>) {
- *self.error.borrow_mut() = error;
+ pub fn get_network_error(&self) -> &DomRefCell<Option<NetworkError>> {
+ &self.network_error
+ }
+
+ pub fn set_network_error(&self, network_error: NetworkError) {
+ *self.network_error.borrow_mut() = Some(network_error);
}
pub fn get_text(&self) -> &DomRefCell<DOMString> {
@@ -230,104 +225,127 @@ impl ModuleTree {
*self.text.borrow_mut() = module_text;
}
- pub fn get_parent_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
- &self.parent_urls
+ pub fn get_incomplete_fetch_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
+ &self.incomplete_fetch_urls
}
- pub fn insert_parent_url(&self, parent_url: ServoUrl) {
- self.parent_urls.borrow_mut().insert(parent_url);
+ pub fn get_descendant_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
+ &self.descendant_urls
}
- pub fn append_parent_urls(&self, parent_urls: IndexSet<ServoUrl>) {
- self.parent_urls.borrow_mut().extend(parent_urls);
+ pub fn get_parent_urls(&self) -> IndexSet<ServoUrl> {
+ let parent_identities = self.parent_identities.borrow();
+
+ parent_identities
+ .iter()
+ .filter_map(|parent_identity| match parent_identity {
+ ModuleIdentity::ScriptId(_) => None,
+ ModuleIdentity::ModuleUrl(url) => Some(url.clone()),
+ })
+ .collect()
}
- pub fn get_descendant_urls(&self) -> &DomRefCell<IndexSet<ServoUrl>> {
- &self.descendant_urls
+ pub fn insert_parent_identity(&self, parent_identity: ModuleIdentity) {
+ self.parent_identities.borrow_mut().insert(parent_identity);
}
- pub fn append_descendant_urls(&self, descendant_urls: IndexSet<ServoUrl>) {
- self.descendant_urls.borrow_mut().extend(descendant_urls);
+ pub fn insert_incomplete_fetch_url(&self, dependency: ServoUrl) {
+ self.incomplete_fetch_urls.borrow_mut().insert(dependency);
}
- /// recursively checks if all of the transitive descendants are
- /// in the FetchingDescendants or later status
- fn recursive_check_descendants(
- module_tree: &ModuleTree,
- module_map: &HashMap<ServoUrl, Rc<ModuleTree>>,
- discovered_urls: &mut HashSet<ServoUrl>,
- ) -> bool {
- discovered_urls.insert(module_tree.url.clone());
+ pub fn remove_incomplete_fetch_url(&self, dependency: ServoUrl) {
+ self.incomplete_fetch_urls.borrow_mut().remove(&dependency);
+ }
- let descendant_urls = module_tree.descendant_urls.borrow();
+ /// Find circular dependencies in non-recursive way
+ ///
+ /// This function is basically referred to
+ /// [this blog post](https://breakingcode.wordpress.com/2013/03/11/an-example-dependency-resolution-algorithm-in-python/).
+ ///
+ /// The only difference is, in that blog post, its algorithm will throw errors while finding circular
+ /// dependencies; however, in our use case, we'd like to find circular dependencies so we will just
+ /// return it.
+ pub fn find_circular_dependencies(&self, global: &GlobalScope) -> IndexSet<ServoUrl> {
+ let module_map = global.get_module_map().borrow();
- for descendant_module in descendant_urls
+ // A map for checking dependencies and using the module url as key
+ let mut module_deps: IndexMap<ServoUrl, IndexSet<ServoUrl>> = module_map
.iter()
- .filter_map(|url| module_map.get(&url.clone()))
- {
- if discovered_urls.contains(&descendant_module.url) {
- continue;
- }
+ .map(|(module_url, module)| {
+ (module_url.clone(), module.descendant_urls.borrow().clone())
+ })
+ .collect();
- let descendant_status = descendant_module.get_status();
- if descendant_status < ModuleStatus::FetchingDescendants {
- return false;
+ while module_deps.len() != 0 {
+ // Get all dependencies with no dependencies
+ let ready: IndexSet<ServoUrl> = module_deps
+ .iter()
+ .filter_map(|(module_url, descendant_urls)| {
+ if descendant_urls.len() == 0 {
+ Some(module_url.clone())
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ // If there's no ready module but we're still in the loop,
+ // it means we find circular modules, then we can return them.
+ if ready.len() == 0 {
+ return module_deps
+ .iter()
+ .map(|(url, _)| url.clone())
+ .collect::<IndexSet<ServoUrl>>();
}
- let all_ready_descendants = ModuleTree::recursive_check_descendants(
- &descendant_module,
- module_map,
- discovered_urls,
- );
+ // Remove ready modules from the dependency map
+ for module_url in ready.iter() {
+ module_deps.remove(&module_url.clone());
+ }
- if !all_ready_descendants {
- return false;
+ // Also make sure to remove the ready modules from the
+ // remaining module dependencies as well
+ for (_, deps) in module_deps.iter_mut() {
+ *deps = deps
+ .difference(&ready)
+ .into_iter()
+ .cloned()
+ .collect::<IndexSet<ServoUrl>>();
}
}
- return true;
- }
-
- fn has_all_ready_descendants(&self, module_map: &HashMap<ServoUrl, Rc<ModuleTree>>) -> bool {
- let mut discovered_urls = HashSet::new();
-
- return ModuleTree::recursive_check_descendants(&self, module_map, &mut discovered_urls);
+ IndexSet::new()
}
- pub fn get_visited_urls(&self) -> &DomRefCell<HashSet<ServoUrl>> {
- &self.visited_urls
- }
-
- pub fn append_handler(&self, owner: ModuleOwner, module_url: ServoUrl, is_top_level: bool) {
- let promise = self.promise.borrow();
-
- let resolve_this = owner.clone();
- let reject_this = owner.clone();
-
- let resolved_url = module_url.clone();
- let rejected_url = module_url.clone();
+ // We just leverage the power of Promise to run the task for `finish` the owner.
+ // Thus, we will always `resolve` it and no need to register a callback for `reject`
+ pub fn append_handler(&self, owner: ModuleOwner, module_identity: ModuleIdentity) {
+ let this = owner.clone();
+ let identity = module_identity.clone();
let handler = PromiseNativeHandler::new(
&owner.global(),
Some(ModuleHandler::new(Box::new(
task!(fetched_resolve: move || {
- resolve_this.finish_module_load(Some(resolved_url), is_top_level);
- }),
- ))),
- Some(ModuleHandler::new(Box::new(
- task!(failure_reject: move || {
- reject_this.finish_module_load(Some(rejected_url), is_top_level);
+ this.notify_owner_to_finish(identity);
}),
))),
+ None,
);
- let global = owner.global();
- let realm = enter_realm(&*global);
+ let realm = enter_realm(&*owner.global());
let comp = InRealm::Entered(&realm);
-
- let promise = promise.as_ref().unwrap();
-
- promise.append_native_handler(&handler, comp);
+ let _ais = AutoIncumbentScript::new(&*owner.global());
+
+ let mut promise = self.promise.borrow_mut();
+ match promise.as_ref() {
+ Some(promise) => promise.append_native_handler(&handler, comp),
+ None => {
+ let new_promise = Promise::new_in_current_realm(&owner.global(), comp);
+ new_promise.append_native_handler(&handler, comp);
+ *promise = Some(new_promise);
+ },
+ }
}
}
@@ -336,8 +354,6 @@ pub enum ModuleStatus {
Initial,
Fetching,
FetchingDescendants,
- FetchFailed,
- Ready,
Finished,
}
@@ -350,7 +366,7 @@ impl ModuleTree {
global: &GlobalScope,
module_script_text: DOMString,
url: ServoUrl,
- ) -> Result<ModuleObject, ModuleError> {
+ ) -> Result<ModuleObject, RethrowError> {
let module: Vec<u16> = module_script_text.encode_utf16().collect();
let url_cstr = ffi::CString::new(url.as_str().as_bytes()).unwrap();
@@ -377,9 +393,9 @@ impl ModuleTree {
));
JS_ClearPendingException(*global.get_cx());
- return Err(ModuleError::RawException(RootedTraceableBox::from_box(
- Heap::boxed(exception.get()),
- )));
+ return Err(RethrowError(RootedTraceableBox::from_box(Heap::boxed(
+ exception.get(),
+ ))));
}
let module_script_data = Box::new(ModuleScript {
@@ -403,11 +419,13 @@ impl ModuleTree {
}
#[allow(unsafe_code)]
+ /// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-and-link-a-module-script
+ /// Step 5-2.
pub fn instantiate_module_tree(
&self,
global: &GlobalScope,
module_record: HandleObject,
- ) -> Result<(), ModuleError> {
+ ) -> Result<(), RethrowError> {
let _ac = JSAutoRealm::new(*global.get_cx(), *global.reflector().get_jsobject());
unsafe {
@@ -421,9 +439,9 @@ impl ModuleTree {
));
JS_ClearPendingException(*global.get_cx());
- Err(ModuleError::RawException(RootedTraceableBox::from_box(
- Heap::boxed(exception.get()),
- )))
+ Err(RethrowError(RootedTraceableBox::from_box(Heap::boxed(
+ exception.get(),
+ ))))
} else {
debug!("module instantiated successfully");
@@ -437,7 +455,7 @@ impl ModuleTree {
&self,
global: &GlobalScope,
module_record: HandleObject,
- ) -> Result<(), ModuleError> {
+ ) -> Result<(), RethrowError> {
let _ac = JSAutoRealm::new(*global.get_cx(), *global.reflector().get_jsobject());
unsafe {
@@ -451,9 +469,9 @@ impl ModuleTree {
));
JS_ClearPendingException(*global.get_cx());
- Err(ModuleError::RawException(RootedTraceableBox::from_box(
- Heap::boxed(exception.get()),
- )))
+ Err(RethrowError(RootedTraceableBox::from_box(Heap::boxed(
+ exception.get(),
+ ))))
} else {
debug!("module evaluated successfully");
Ok(())
@@ -463,7 +481,7 @@ impl ModuleTree {
#[allow(unsafe_code)]
pub fn report_error(&self, global: &GlobalScope) {
- let module_error = self.error.borrow();
+ let module_error = self.rethrow_error.borrow();
if let Some(exception) = &*module_error {
unsafe {
@@ -478,54 +496,13 @@ impl ModuleTree {
}
}
- /// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-a-module-script
- /// Step 5.
- pub fn resolve_requested_modules(
- &self,
- global: &GlobalScope,
- ) -> Result<IndexSet<ServoUrl>, ModuleError> {
- let status = self.get_status();
-
- assert_ne!(status, ModuleStatus::Initial);
- assert_ne!(status, ModuleStatus::Fetching);
-
- let record = self.record.borrow();
-
- if let Some(raw_record) = &*record {
- let valid_specifier_urls = self.resolve_requested_module_specifiers(
- &global,
- raw_record.handle(),
- self.url.clone(),
- );
-
- return valid_specifier_urls.map(|parsed_urls| {
- parsed_urls
- .iter()
- .filter_map(|parsed_url| {
- let mut visited = self.visited_urls.borrow_mut();
-
- if !visited.contains(&parsed_url) {
- visited.insert(parsed_url.clone());
-
- Some(parsed_url.clone())
- } else {
- None
- }
- })
- .collect::<IndexSet<ServoUrl>>()
- });
- }
-
- unreachable!("Didn't have record while resolving its requested module")
- }
-
#[allow(unsafe_code)]
fn resolve_requested_module_specifiers(
&self,
global: &GlobalScope,
module_object: HandleObject,
base_url: ServoUrl,
- ) -> Result<IndexSet<ServoUrl>, ModuleError> {
+ ) -> Result<IndexSet<ServoUrl>, RethrowError> {
let _ac = JSAutoRealm::new(*global.get_cx(), *global.reflector().get_jsobject());
let mut specifier_urls = IndexSet::new();
@@ -615,26 +592,27 @@ impl ModuleTree {
/// https://html.spec.whatwg.org/multipage/#finding-the-first-parse-error
fn find_first_parse_error(
+ &self,
global: &GlobalScope,
- module_tree: &ModuleTree,
discovered_urls: &mut HashSet<ServoUrl>,
- ) -> Option<ModuleError> {
+ ) -> (Option<NetworkError>, Option<RethrowError>) {
// 3.
- discovered_urls.insert(module_tree.url.clone());
+ discovered_urls.insert(self.url.clone());
// 4.
- let module_map = global.get_module_map().borrow();
- let record = module_tree.get_record().borrow();
+ let record = self.get_record().borrow();
if record.is_none() {
- let module_error = module_tree.get_error().borrow();
-
- return module_error.clone();
+ return (
+ self.network_error.borrow().clone(),
+ self.rethrow_error.borrow().clone(),
+ );
}
- // 5-6.
- let mut errors: Vec<ModuleError> = Vec::new();
- let descendant_urls = module_tree.get_descendant_urls().borrow();
+ let module_map = global.get_module_map().borrow();
+ let mut parse_error: Option<RethrowError> = None;
+ // 5-6.
+ let descendant_urls = self.descendant_urls.borrow();
for descendant_module in descendant_urls
.iter()
// 7.
@@ -646,20 +624,243 @@ impl ModuleTree {
}
// 8-3.
- let child_parse_error =
- ModuleTree::find_first_parse_error(&global, &descendant_module, discovered_urls);
+ let (child_network_error, child_parse_error) =
+ descendant_module.find_first_parse_error(&global, discovered_urls);
+
+ // Due to network error's priority higher than parse error,
+ // we will return directly when we meet a network error.
+ if child_network_error.is_some() {
+ return (child_network_error, None);
+ }
// 8-4.
- if let Some(child_error) = child_parse_error {
- errors.push(child_error);
+ //
+ // In case of having any network error in other descendants,
+ // we will store the "first" parse error and keep running this
+ // loop to ensure we don't have any network error.
+ if child_parse_error.is_some() && parse_error.is_none() {
+ parse_error = child_parse_error;
}
}
// Step 9.
- return errors.into_iter().max();
+ return (None, parse_error);
+ }
+
+ #[allow(unsafe_code)]
+ /// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-a-module-script
+ fn fetch_module_descendants(
+ &self,
+ owner: &ModuleOwner,
+ destination: Destination,
+ credentials_mode: CredentialsMode,
+ parent_identity: ModuleIdentity,
+ ) {
+ debug!("Start to load dependencies of {}", self.url.clone());
+
+ let global = owner.global();
+
+ self.set_status(ModuleStatus::FetchingDescendants);
+
+ let specifier_urls = {
+ let raw_record = self.record.borrow();
+ match raw_record.as_ref() {
+ // Step 1.
+ None => {
+ self.set_status(ModuleStatus::Finished);
+ debug!(
+ "Module {} doesn't have module record but tried to load descendants.",
+ self.url.clone()
+ );
+ return;
+ },
+ // Step 5.
+ Some(raw_record) => self.resolve_requested_module_specifiers(
+ &global,
+ raw_record.handle(),
+ self.url.clone(),
+ ),
+ }
+ };
+
+ match specifier_urls {
+ // Step 3.
+ Ok(valid_specifier_urls) if valid_specifier_urls.len() == 0 => {
+ debug!("Module {} doesn't have any dependencies.", self.url.clone());
+ self.advance_finished_and_link(&global);
+ },
+ Ok(valid_specifier_urls) => {
+ self.descendant_urls
+ .borrow_mut()
+ .extend(valid_specifier_urls.clone());
+
+ let mut urls = IndexSet::new();
+ let mut visited_urls = self.visited_urls.borrow_mut();
+
+ for parsed_url in valid_specifier_urls {
+ // Step 5-3.
+ if !visited_urls.contains(&parsed_url) {
+ // Step 5-3-1.
+ urls.insert(parsed_url.clone());
+ // Step 5-3-2.
+ visited_urls.insert(parsed_url.clone());
+
+ self.insert_incomplete_fetch_url(parsed_url.clone());
+ }
+ }
+
+ // Step 3.
+ if urls.len() == 0 {
+ debug!(
+ "After checking with visited urls, module {} doesn't have dependencies to load.",
+ self.url.clone()
+ );
+ self.advance_finished_and_link(&global);
+ return;
+ }
+
+ // Step 8.
+ for url in urls {
+ // https://html.spec.whatwg.org/multipage/#internal-module-script-graph-fetching-procedure
+ // Step 1.
+ assert!(visited_urls.get(&url).is_some());
+
+ // Step 2.
+ fetch_single_module_script(
+ owner.clone(),
+ url.clone(),
+ visited_urls.clone(),
+ destination.clone(),
+ Referrer::Client,
+ ParserMetadata::NotParserInserted,
+ "".to_owned(), // integrity
+ credentials_mode.clone(),
+ Some(parent_identity.clone()),
+ false,
+ );
+ }
+ },
+ Err(error) => {
+ self.set_rethrow_error(error);
+ self.advance_finished_and_link(&global);
+ },
+ }
+ }
+
+ /// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-and-link-a-module-script
+ /// step 4-7.
+ fn advance_finished_and_link(&self, global: &GlobalScope) {
+ {
+ let descendant_urls = self.descendant_urls.borrow();
+
+ // Check if there's any dependencies under fetching.
+ //
+ // We can't only check `incomplete fetches` here because...
+ //
+ // For example, module `A` has descendants `B`, `C`
+ // while `A` has added them to incomplete fetches, it's possible
+ // `B` has finished but `C` is not yet fired its fetch; in this case,
+ // `incomplete fetches` will be `zero` but the module is actually not ready
+ // to finish. Thus, we need to check dependencies directly instead of
+ // incomplete fetches here.
+ if !is_all_dependencies_ready(&descendant_urls, &global) {
+ // When we found the `incomplete fetches` is bigger than zero,
+ // we will need to check if there's any circular dependency.
+ //
+ // If there's no circular dependencies but there are incomplete fetches,
+ // it means it needs to wait for finish.
+ //
+ // Or, if there are circular dependencies, then we need to confirm
+ // no circular dependencies are fetching.
+ //
+ // if there's any circular dependencies and they all proceeds to status
+ // higher than `FetchingDescendants`, then it means we can proceed to finish.
+ let circular_deps = self.find_circular_dependencies(&global);
+
+ if circular_deps.len() == 0 || !is_all_dependencies_ready(&circular_deps, &global) {
+ return;
+ }
+ }
+ }
+
+ self.set_status(ModuleStatus::Finished);
+
+ debug!("Going to advance and finish for: {}", self.url.clone());
+
+ {
+ // Notify parents of this module to finish
+ //
+ // Before notifying, if the parent module has already had zero incomplete
+ // fetches, then it means we don't need to notify it.
+ let parent_identities = self.parent_identities.borrow();
+ for parent_identity in parent_identities.iter() {
+ let parent_tree = parent_identity.get_module_tree(&global);
+
+ let incomplete_count_before_remove = {
+ let incomplete_urls = parent_tree.get_incomplete_fetch_urls().borrow();
+ incomplete_urls.len()
+ };
+
+ if incomplete_count_before_remove > 0 {
+ parent_tree.remove_incomplete_fetch_url(self.url.clone());
+ parent_tree.advance_finished_and_link(&global);
+ }
+ }
+ }
+
+ let mut discovered_urls: HashSet<ServoUrl> = HashSet::new();
+ let (network_error, rethrow_error) =
+ self.find_first_parse_error(&global, &mut discovered_urls);
+
+ match (network_error, rethrow_error) {
+ (Some(network_error), _) => {
+ self.set_network_error(network_error);
+ },
+ (None, None) => {
+ let module_record = self.get_record().borrow();
+ if let Some(record) = &*module_record {
+ let instantiated = self.instantiate_module_tree(&global, record.handle());
+
+ if let Err(exception) = instantiated {
+ self.set_rethrow_error(exception);
+ }
+ }
+ },
+ (None, Some(error)) => {
+ self.set_rethrow_error(error);
+ },
+ }
+
+ let promise = self.promise.borrow();
+ if let Some(promise) = promise.as_ref() {
+ promise.resolve_native(&());
+ }
}
}
+// Iterate the given dependency urls to see if it and its descendants are fetching or not.
+// When a module status is `FetchingDescendants`, it's possible that the module is a circular
+// module so we will also check its descendants.
+fn is_all_dependencies_ready(dependencies: &IndexSet<ServoUrl>, global: &GlobalScope) -> bool {
+ dependencies.iter().all(|dep| {
+ let module_map = global.get_module_map().borrow();
+ match module_map.get(&dep) {
+ Some(module) => {
+ let module_descendants = module.get_descendant_urls().borrow();
+
+ module.get_status() >= ModuleStatus::FetchingDescendants &&
+ module_descendants.iter().all(|descendant_url| {
+ match module_map.get(&descendant_url) {
+ Some(m) => m.get_status() >= ModuleStatus::FetchingDescendants,
+ None => false,
+ }
+ })
+ },
+ None => false,
+ }
+ })
+}
+
#[derive(JSTraceable, MallocSizeOf)]
struct ModuleHandler {
#[ignore_malloc_size_of = "Measuring trait objects is hard"]
@@ -698,45 +899,7 @@ impl ModuleOwner {
}
}
- fn gen_promise_with_final_handler(
- &self,
- module_url: Option<ServoUrl>,
- is_top_level: bool,
- ) -> Rc<Promise> {
- let resolve_this = self.clone();
- let reject_this = self.clone();
-
- let resolved_url = module_url.clone();
- let rejected_url = module_url.clone();
-
- let handler = PromiseNativeHandler::new(
- &self.global(),
- Some(ModuleHandler::new(Box::new(
- task!(fetched_resolve: move || {
- resolve_this.finish_module_load(resolved_url, is_top_level);
- }),
- ))),
- Some(ModuleHandler::new(Box::new(
- task!(failure_reject: move || {
- reject_this.finish_module_load(rejected_url, is_top_level);
- }),
- ))),
- );
-
- let global = self.global();
- let realm = enter_realm(&*global);
- let comp = InRealm::Entered(&realm);
-
- let promise = Promise::new_in_current_realm(&self.global(), comp);
-
- promise.append_native_handler(&handler, comp);
-
- promise
- }
-
- /// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-and-link-a-module-script
- /// step 4-7.
- pub fn finish_module_load(&self, module_url: Option<ServoUrl>, is_top_level: bool) {
+ pub fn notify_owner_to_finish(&self, module_identity: ModuleIdentity) {
match &self {
ModuleOwner::Worker(_) => unimplemented!(),
ModuleOwner::Window(script) => {
@@ -744,112 +907,39 @@ impl ModuleOwner {
let document = document_from_node(&*script.root());
- let module_map = global.get_module_map().borrow();
-
- let (module_tree, mut load) = if let Some(script_src) = module_url.clone() {
- let module_tree = module_map.get(&script_src.clone()).unwrap().clone();
-
- let load = Ok(ScriptOrigin::external(
- module_tree.get_text().borrow().clone(),
- script_src.clone(),
- ScriptType::Module,
- ));
-
- debug!(
- "Going to finish external script from {}",
- script_src.clone()
- );
-
- (module_tree, load)
- } else {
- let module_tree = {
- let inline_module_map = global.get_inline_module_map().borrow();
- inline_module_map
- .get(&script.root().get_script_id())
- .unwrap()
- .clone()
- };
-
- let base_url = document.base_url();
-
- let load = Ok(ScriptOrigin::internal(
- module_tree.get_text().borrow().clone(),
- base_url.clone(),
- ScriptType::Module,
- ));
-
- debug!("Going to finish internal script from {}", base_url.clone());
-
- (module_tree, load)
+ let load = {
+ let module_tree = module_identity.get_module_tree(&global);
+
+ let network_error = module_tree.get_network_error().borrow();
+ match network_error.as_ref() {
+ Some(network_error) => Err(network_error.clone()),
+ None => match module_identity {
+ ModuleIdentity::ModuleUrl(script_src) => Ok(ScriptOrigin::external(
+ module_tree.get_text().borrow().clone(),
+ script_src.clone(),
+ ScriptType::Module,
+ )),
+ ModuleIdentity::ScriptId(_) => Ok(ScriptOrigin::internal(
+ module_tree.get_text().borrow().clone(),
+ document.base_url().clone(),
+ ScriptType::Module,
+ )),
+ },
+ }
};
- module_tree.set_status(ModuleStatus::Finished);
-
- if !module_tree.has_all_ready_descendants(&module_map) {
- return;
- }
-
- let parent_urls = module_tree.get_parent_urls().borrow();
- let parent_all_ready = parent_urls
- .iter()
- .filter_map(|parent_url| module_map.get(&parent_url.clone()))
- .all(|parent_tree| parent_tree.has_all_ready_descendants(&module_map));
-
- if !parent_all_ready {
- return;
- }
-
- parent_urls
- .iter()
- .filter_map(|parent_url| module_map.get(&parent_url.clone()))
- .for_each(|parent_tree| {
- let parent_promise = parent_tree.get_promise().borrow();
- if let Some(promise) = parent_promise.as_ref() {
- promise.resolve_native(&());
- }
- });
-
- let mut discovered_urls: HashSet<ServoUrl> = HashSet::new();
- let module_error =
- ModuleTree::find_first_parse_error(&global, &module_tree, &mut discovered_urls);
-
- match module_error {
- None => {
- let module_record = module_tree.get_record().borrow();
- if let Some(record) = &*module_record {
- let instantiated =
- module_tree.instantiate_module_tree(&global, record.handle());
-
- if let Err(exception) = instantiated {
- module_tree.set_error(Some(exception.clone()));
- }
- }
- },
- Some(ModuleError::RawException(exception)) => {
- module_tree.set_error(Some(ModuleError::RawException(exception)));
- },
- Some(ModuleError::Network(network_error)) => {
- module_tree.set_error(Some(ModuleError::Network(network_error.clone())));
+ let r#async = script
+ .root()
+ .upcast::<Element>()
+ .has_attribute(&local_name!("async"));
- // Change the `result` load of the script into `network` error
- load = Err(network_error);
- },
+ if !r#async && (&*script.root()).get_parser_inserted() {
+ document.deferred_script_loaded(&*script.root(), load);
+ } else if !r#async && !(&*script.root()).get_non_blocking() {
+ document.asap_in_order_script_loaded(&*script.root(), load);
+ } else {
+ document.asap_script_loaded(&*script.root(), load);
};
-
- if is_top_level {
- let r#async = script
- .root()
- .upcast::<Element>()
- .has_attribute(&local_name!("async"));
-
- if !r#async && (&*script.root()).get_parser_inserted() {
- document.deferred_script_loaded(&*script.root(), load);
- } else if !r#async && !(&*script.root()).get_non_blocking() {
- document.asap_in_order_script_loaded(&*script.root(), load);
- } else {
- document.asap_script_loaded(&*script.root(), load);
- };
- }
},
}
}
@@ -959,73 +1049,46 @@ impl FetchResponseListener for ModuleContext {
))
});
- if let Err(err) = load {
- // Step 9.
- error!("Failed to fetch {} with error {:?}", self.url.clone(), err);
- let module_tree = {
- let module_map = global.get_module_map().borrow();
- module_map.get(&self.url.clone()).unwrap().clone()
- };
-
- module_tree.set_status(ModuleStatus::FetchFailed);
-
- module_tree.set_error(Some(ModuleError::Network(err)));
-
- let promise = module_tree.get_promise().borrow();
- promise.as_ref().unwrap().resolve_native(&());
+ let module_tree = {
+ let module_map = global.get_module_map().borrow();
+ module_map.get(&self.url.clone()).unwrap().clone()
+ };
- return;
- }
+ module_tree.remove_incomplete_fetch_url(self.url.clone());
// Step 12.
- if let Ok(ref resp_mod_script) = load {
- let module_tree = {
- let module_map = global.get_module_map().borrow();
- module_map.get(&self.url.clone()).unwrap().clone()
- };
-
- module_tree.set_text(resp_mod_script.text());
-
- let compiled_module = module_tree.compile_module_script(
- &global,
- resp_mod_script.text(),
- self.url.clone(),
- );
-
- match compiled_module {
- Err(exception) => {
- module_tree.set_error(Some(exception));
-
- let promise = module_tree.get_promise().borrow();
- promise.as_ref().unwrap().resolve_native(&());
-
- return;
- },
- Ok(record) => {
- module_tree.set_record(record);
-
- {
- let mut visited = module_tree.get_visited_urls().borrow_mut();
- visited.insert(self.url.clone());
- }
-
- let descendant_results = fetch_module_descendants_and_link(
- &self.owner,
- &module_tree,
- self.destination.clone(),
- self.credentials_mode.clone(),
- );
+ match load {
+ Err(err) => {
+ error!("Failed to fetch {} with error {:?}", self.url.clone(), err);
+ module_tree.set_network_error(err);
+ module_tree.advance_finished_and_link(&global);
+ },
+ Ok(ref resp_mod_script) => {
+ module_tree.set_text(resp_mod_script.text());
- // Resolve the request of this module tree promise directly
- // when there's no descendant
- if descendant_results.is_none() {
- module_tree.set_status(ModuleStatus::Ready);
+ let compiled_module = module_tree.compile_module_script(
+ &global,
+ resp_mod_script.text(),
+ self.url.clone(),
+ );
- let promise = module_tree.get_promise().borrow();
- promise.as_ref().unwrap().resolve_native(&());
- }
- },
- }
+ match compiled_module {
+ Err(exception) => {
+ module_tree.set_rethrow_error(exception);
+ module_tree.advance_finished_and_link(&global);
+ },
+ Ok(record) => {
+ module_tree.set_record(record);
+
+ module_tree.fetch_module_descendants(
+ &self.owner,
+ self.destination.clone(),
+ self.credentials_mode.clone(),
+ ModuleIdentity::ModuleUrl(self.url.clone()),
+ );
+ },
+ }
+ },
}
}
@@ -1159,11 +1222,15 @@ pub fn fetch_external_module_script(
destination: Destination,
integrity_metadata: String,
credentials_mode: CredentialsMode,
-) -> Rc<Promise> {
+) {
+ let mut visited_urls = HashSet::new();
+ visited_urls.insert(url.clone());
+
// Step 1.
fetch_single_module_script(
owner,
url,
+ visited_urls,
destination,
Referrer::Client,
ParserMetadata::NotParserInserted,
@@ -1171,21 +1238,22 @@ pub fn fetch_external_module_script(
credentials_mode,
None,
true,
- )
+ );
}
/// https://html.spec.whatwg.org/multipage/#fetch-a-single-module-script
pub fn fetch_single_module_script(
owner: ModuleOwner,
url: ServoUrl,
+ visited_urls: HashSet<ServoUrl>,
destination: Destination,
referrer: Referrer,
parser_metadata: ParserMetadata,
integrity_metadata: String,
credentials_mode: CredentialsMode,
- parent_url: Option<ServoUrl>,
+ parent_identity: Option<ModuleIdentity>,
top_level_module_fetch: bool,
-) -> Rc<Promise> {
+) {
{
// Step 1.
let global = owner.global();
@@ -1196,49 +1264,47 @@ pub fn fetch_single_module_script(
if let Some(module_tree) = module_map.get(&url.clone()) {
let status = module_tree.get_status();
- let promise = module_tree.get_promise().borrow();
-
debug!("Meet a fetched url {} and its status is {:?}", url, status);
- assert!(promise.is_some());
-
- module_tree.append_handler(owner.clone(), url.clone(), top_level_module_fetch);
+ if top_level_module_fetch {
+ module_tree.append_handler(owner.clone(), ModuleIdentity::ModuleUrl(url.clone()));
+ }
- let promise = promise.as_ref().unwrap();
+ if let Some(parent_identity) = parent_identity {
+ module_tree.insert_parent_identity(parent_identity);
+ }
match status {
ModuleStatus::Initial => unreachable!(
"We have the module in module map so its status should not be `initial`"
),
// Step 2.
- ModuleStatus::Fetching => return promise.clone(),
- ModuleStatus::FetchingDescendants => {
- if module_tree.has_all_ready_descendants(&module_map) {
- promise.resolve_native(&());
- }
- },
+ ModuleStatus::Fetching => {},
// Step 3.
- ModuleStatus::FetchFailed | ModuleStatus::Ready | ModuleStatus::Finished => {
- promise.resolve_native(&());
+ ModuleStatus::FetchingDescendants | ModuleStatus::Finished => {
+ module_tree.advance_finished_and_link(&global);
},
}
- return promise.clone();
+ return;
}
}
let global = owner.global();
-
- let module_tree = ModuleTree::new(url.clone());
+ let is_external = true;
+ let module_tree = ModuleTree::new(url.clone(), is_external, visited_urls);
module_tree.set_status(ModuleStatus::Fetching);
- let promise = owner.gen_promise_with_final_handler(Some(url.clone()), top_level_module_fetch);
+ if top_level_module_fetch {
+ module_tree.append_handler(owner.clone(), ModuleIdentity::ModuleUrl(url.clone()));
+ }
- module_tree.set_promise(promise.clone());
- if let Some(parent_url) = parent_url {
- module_tree.insert_parent_url(parent_url);
+ if let Some(parent_identity) = parent_identity {
+ module_tree.insert_parent_identity(parent_identity);
}
+ module_tree.insert_incomplete_fetch_url(url.clone());
+
// Step 4.
global.set_module_map(url.clone(), module_tree);
@@ -1277,11 +1343,13 @@ pub fn fetch_single_module_script(
}));
let (action_sender, action_receiver) = ipc::channel().unwrap();
+ let task_source = global.networking_task_source();
+ let canceller = global.task_canceller(TaskSourceName::Networking);
let listener = NetworkListener {
context,
- task_source: global.networking_task_source(),
- canceller: Some(global.task_canceller(TaskSourceName::Networking)),
+ task_source,
+ canceller: Some(canceller),
};
ROUTER.add_route(
@@ -1294,8 +1362,6 @@ pub fn fetch_single_module_script(
if let Some(doc) = document {
doc.fetch_async(LoadType::Script(url), request, action_sender);
}
-
- promise
}
#[allow(unsafe_code)]
@@ -1308,162 +1374,40 @@ pub fn fetch_inline_module_script(
credentials_mode: CredentialsMode,
) {
let global = owner.global();
-
- let module_tree = ModuleTree::new(url.clone());
-
- let promise = owner.gen_promise_with_final_handler(None, true);
-
- module_tree.set_promise(promise.clone());
+ let is_external = false;
+ let module_tree = ModuleTree::new(url.clone(), is_external, HashSet::new());
let compiled_module =
module_tree.compile_module_script(&global, module_script_text, url.clone());
match compiled_module {
Ok(record) => {
+ module_tree.append_handler(owner.clone(), ModuleIdentity::ScriptId(script_id.clone()));
module_tree.set_record(record);
- let descendant_results = fetch_module_descendants_and_link(
+ // We need to set `module_tree` into inline module map in case
+ // of that the module descendants finished right after the
+ // fetch module descendants step.
+ global.set_inline_module_map(script_id, module_tree);
+
+ // Due to needed to set `module_tree` to inline module_map first,
+ // we will need to retrieve it again so that we can do the fetch
+ // module descendants step.
+ let inline_module_map = global.get_inline_module_map().borrow();
+ let module_tree = inline_module_map.get(&script_id).unwrap().clone();
+
+ module_tree.fetch_module_descendants(
&owner,
- &module_tree,
Destination::Script,
credentials_mode,
+ ModuleIdentity::ScriptId(script_id),
);
-
- global.set_inline_module_map(script_id, module_tree);
-
- if descendant_results.is_none() {
- promise.resolve_native(&());
- }
},
Err(exception) => {
- module_tree.set_status(ModuleStatus::Ready);
- module_tree.set_error(Some(exception));
- global.set_inline_module_map(script_id, module_tree);
- promise.resolve_native(&());
- },
- }
-}
-
-/// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-and-link-a-module-script
-/// Step 1-3.
-#[allow(unsafe_code)]
-fn fetch_module_descendants_and_link(
- owner: &ModuleOwner,
- module_tree: &ModuleTree,
- destination: Destination,
- credentials_mode: CredentialsMode,
-) -> Option<Rc<Promise>> {
- let descendant_results =
- fetch_module_descendants(owner, module_tree, destination, credentials_mode);
-
- match descendant_results {
- Ok(descendants) => {
- if descendants.len() > 0 {
- unsafe {
- let global = owner.global();
-
- let realm = enter_realm(&*global);
- let _ais = AutoIncumbentScript::new(&*global);
-
- let abv = RootedObjectVectorWrapper::new(*global.get_cx());
-
- for descendant in descendants {
- assert!(abv.append(descendant.promise_obj().get()));
- }
-
- rooted!(
- in(*global.get_cx())
- let raw_promise_all = GetWaitForAllPromise(*global.get_cx(), abv.handle())
- );
-
- let promise_all =
- Promise::new_with_js_promise(raw_promise_all.handle(), global.get_cx());
-
- let promise = module_tree.get_promise().borrow();
- let promise = promise.as_ref().unwrap().clone();
-
- let resolve_promise = TrustedPromise::new(promise.clone());
- let reject_promise = TrustedPromise::new(promise.clone());
-
- let handler = PromiseNativeHandler::new(
- &global,
- Some(ModuleHandler::new(Box::new(
- task!(all_fetched_resolve: move || {
- let promise = resolve_promise.root();
- promise.resolve_native(&());
- }),
- ))),
- Some(ModuleHandler::new(Box::new(
- task!(all_failure_reject: move || {
- let promise = reject_promise.root();
- promise.reject_native(&());
- }),
- ))),
- );
-
- let comp = InRealm::Entered(&realm);
- promise_all.append_native_handler(&handler, comp);
-
- return Some(promise_all);
- }
- }
- },
- Err(err) => {
- module_tree.set_error(Some(err));
+ module_tree.set_rethrow_error(exception);
+ module_tree.set_status(ModuleStatus::Finished);
+ global.set_inline_module_map(script_id.clone(), module_tree);
+ owner.notify_owner_to_finish(ModuleIdentity::ScriptId(script_id));
},
}
-
- None
-}
-
-#[allow(unsafe_code)]
-/// https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-a-module-script
-fn fetch_module_descendants(
- owner: &ModuleOwner,
- module_tree: &ModuleTree,
- destination: Destination,
- credentials_mode: CredentialsMode,
-) -> Result<Vec<Rc<Promise>>, ModuleError> {
- debug!("Start to load dependencies of {}", module_tree.url.clone());
-
- let global = owner.global();
-
- module_tree.set_status(ModuleStatus::FetchingDescendants);
-
- module_tree
- .resolve_requested_modules(&global)
- .map(|requested_urls| {
- module_tree.append_descendant_urls(requested_urls.clone());
-
- let parent_urls = module_tree.get_parent_urls().borrow();
-
- if parent_urls.intersection(&requested_urls).count() > 0 {
- return Vec::new();
- }
-
- requested_urls
- .iter()
- .map(|requested_url| {
- // https://html.spec.whatwg.org/multipage/#internal-module-script-graph-fetching-procedure
- // Step 1.
- {
- let visited = module_tree.get_visited_urls().borrow();
- assert!(visited.get(&requested_url).is_some());
- }
-
- // Step 2.
- fetch_single_module_script(
- owner.clone(),
- requested_url.clone(),
- destination.clone(),
- Referrer::Client,
- ParserMetadata::NotParserInserted,
- "".to_owned(),
- credentials_mode.clone(),
- Some(module_tree.url.clone()),
- false,
- )
- })
- .collect()
- })
}