diff options
Diffstat (limited to 'components/script/dom/htmlscriptelement.rs')
-rw-r--r-- | components/script/dom/htmlscriptelement.rs | 118 |
1 files changed, 82 insertions, 36 deletions
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index a32b481d252..3b0f17a881f 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -59,13 +59,14 @@ pub struct HTMLScriptElement { non_blocking: Cell<bool>, /// https://html.spec.whatwg.org/multipage/#ready-to-be-parser-executed - /// - /// (currently unused) ready_to_be_parser_executed: Cell<bool>, /// Document of the parser that created this element parser_document: JS<Document>, + /// The source this script was loaded from + load: DOMRefCell<Option<ScriptOrigin>>, + #[ignore_heap_size_of = "Defined in rust-encoding"] /// https://html.spec.whatwg.org/multipage/#concept-script-encoding block_character_encoding: DOMRefCell<EncodingRef>, @@ -82,6 +83,7 @@ impl HTMLScriptElement { non_blocking: Cell::new(creator != ElementCreator::ParserCreated), ready_to_be_parser_executed: Cell::new(false), parser_document: JS::from_ref(document), + load: DOMRefCell::new(None), block_character_encoding: DOMRefCell::new(UTF_8 as EncodingRef), } } @@ -116,7 +118,7 @@ static SCRIPT_JS_MIMES: StaticStringVec = &[ "text/x-javascript", ]; -#[derive(HeapSizeOf)] +#[derive(HeapSizeOf, JSTraceable)] pub enum ScriptOrigin { Internal(String, Url), External(Result<(Metadata, Vec<u8>), String>), @@ -130,8 +132,6 @@ struct ScriptContext { data: Vec<u8>, /// The response metadata received to date. metadata: Option<Metadata>, - /// Whether the owning document's parser should resume once the response completes. - resume_on_completion: bool, /// The initial URL requested. url: Url, } @@ -153,23 +153,20 @@ impl AsyncResponseListener for ScriptContext { (metadata, data) }); let elem = self.elem.root(); - - elem.r().execute(ScriptOrigin::External(load)); + // TODO: maybe set this to None again after script execution to save memory. + *elem.r().load.borrow_mut() = Some(ScriptOrigin::External(load)); + elem.ready_to_be_parser_executed.set(true); let document = document_from_node(elem.r()); document.r().finish_load(LoadType::Script(self.url.clone())); - - if self.resume_on_completion { - document.r().get_current_parser().unwrap().r().resume(); - } } } impl PreInvoke for ScriptContext {} impl HTMLScriptElement { + /// https://html.spec.whatwg.org/multipage/#prepare-a-script pub fn prepare(&self) -> NextParserState { - // https://html.spec.whatwg.org/multipage/#prepare-a-script // Step 1. if self.already_started.get() { return NextParserState::Continue; @@ -180,7 +177,9 @@ impl HTMLScriptElement { // Step 3. let element = self.upcast::<Element>(); - if was_parser_inserted && element.has_attribute(&atom!("async")) { + let async = element.has_attribute(&atom!("async")); + // Note: confusingly, this is done if the element does *not* have an "async" attribute. + if was_parser_inserted && !async { self.non_blocking.set(true); } // Step 4. @@ -205,8 +204,8 @@ impl HTMLScriptElement { self.already_started.set(true); // Step 10. - let document_from_node_ref = document_from_node(self); - let document_from_node_ref = document_from_node_ref.r(); + let doc = document_from_node(self); + let document_from_node_ref = doc.r(); if self.parser_inserted.get() && &*self.parser_document != document_from_node_ref { return NextParserState::Continue; } @@ -247,8 +246,9 @@ impl HTMLScriptElement { let window = window_from_node(self); let window = window.r(); let base_url = window.get_url(); + let deferred = element.has_attribute(&atom!("defer")); - let load = match element.get_attribute(&ns!(""), &atom!("src")) { + let is_external = match element.get_attribute(&ns!(""), &atom!("src")) { // Step 14. Some(ref src) => { // Step 14.1 @@ -274,8 +274,6 @@ impl HTMLScriptElement { // state of the element's `crossorigin` content attribute, the origin being // the origin of the script element's node document, and the default origin // behaviour set to taint. - let doc = document_from_node(self); - let script_chan = window.script_chan(); let elem = Trusted::new(window.get_cx(), self, script_chan.clone()); @@ -283,7 +281,6 @@ impl HTMLScriptElement { elem: elem, data: vec!(), metadata: None, - resume_on_completion: self.parser_inserted.get(), url: url.clone(), })); @@ -300,36 +297,85 @@ impl HTMLScriptElement { }); doc.r().load_async(LoadType::Script(url), response_target); - - if self.parser_inserted.get() { - doc.r().get_current_parser().unwrap().r().suspend(); - } - return NextParserState::Suspend; } } + true }, - None => ScriptOrigin::Internal(text, base_url), + None => false, }; // Step 15. - // TODO: Add support for the `defer` and `async` attributes. (For now, we fetch all - // scripts synchronously and execute them immediately.) - self.execute(load); - NextParserState::Continue + // Step 15.a, has src, has defer, was parser-inserted, is not async. + if is_external && + deferred && + was_parser_inserted && + !async { + doc.r().add_deferred_script(self); + // Second part implemented in Document::process_deferred_scripts. + return NextParserState::Continue; + // Step 15.b, has src, was parser-inserted, is not async. + } else if is_external && + was_parser_inserted && + !async { + doc.r().set_pending_parsing_blocking_script(Some(self)); + // Second part implemented in the load result handler. + // Step 15.c, doesn't have src, was parser-inserted, is blocked on stylesheet. + } else if !is_external && + was_parser_inserted && + // TODO: check for script nesting levels. + doc.r().get_script_blocking_stylesheets_count() > 0 { + doc.r().set_pending_parsing_blocking_script(Some(self)); + *self.load.borrow_mut() = Some(ScriptOrigin::Internal(text, base_url)); + self.ready_to_be_parser_executed.set(true); + // Step 15.d, has src, isn't async, isn't non-blocking. + } else if is_external && + !async && + !self.non_blocking.get() { + doc.r().push_asap_in_order_script(self); + // Second part implemented in Document::process_asap_scripts. + // Step 15.e, has src. + } else if is_external { + doc.r().add_asap_script(self); + // Second part implemented in Document::process_asap_scripts. + // Step 15.f, otherwise. + } else { + assert!(!text.is_empty()); + self.ready_to_be_parser_executed.set(true); + *self.load.borrow_mut() = Some(ScriptOrigin::Internal(text, base_url)); + self.execute(); + return NextParserState::Continue; + } + // TODO: make this suspension happen automatically. + if was_parser_inserted { + if let Some(parser) = doc.r().get_current_parser() { + parser.r().suspend(); + } + } + return NextParserState::Suspend; } - pub fn execute(&self, load: ScriptOrigin) { + pub fn is_ready_to_be_executed(&self) -> bool { + self.ready_to_be_parser_executed.get() + } + + /// https://html.spec.whatwg.org/multipage/#execute-the-script-block + pub fn execute(&self) { + assert!(self.ready_to_be_parser_executed.get()); + // Step 1. - // TODO: If the element is flagged as "parser-inserted", but the - // element's node document is not the Document of the parser that - // created the element, then abort these steps. + let doc = document_from_node(self); + if self.parser_inserted.get() && &*doc != &*self.parser_document { + return; + } + + let load = self.load.borrow_mut().take().unwrap(); // Step 2. let (source, external, url) = match load { // Step 2.a. ScriptOrigin::External(Err(e)) => { error!("error loading script {}", e); - self.queue_error_event(); + self.dispatch_error_event(); return; } @@ -516,8 +562,8 @@ impl VirtualMethods for HTMLScriptElement { fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) { self.super_type().unwrap().attribute_mutated(attr, mutation); - match attr.local_name() { - &atom!("src") => { + match *attr.local_name() { + atom!("src") => { if let AttributeMutation::Set(_) = mutation { if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() { self.prepare(); |