aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/htmlscriptelement.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/htmlscriptelement.rs')
-rw-r--r--components/script/dom/htmlscriptelement.rs118
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();