diff options
author | Matt Brubeck <mbrubeck@limpet.net> | 2014-10-16 18:05:29 -0700 |
---|---|---|
committer | Matt Brubeck <mbrubeck@limpet.net> | 2014-10-29 14:30:42 -0700 |
commit | 65a0d1fe9a14e09b675cc24cb59c9aebf1615020 (patch) | |
tree | 0ca47c82edcc96f91761f1777b94a79a50dc6745 /components/script/dom/htmlscriptelement.rs | |
parent | 5858fccf873ce30896def4f58aa8c67d1ddd09f1 (diff) | |
download | servo-65a0d1fe9a14e09b675cc24cb59c9aebf1615020.tar.gz servo-65a0d1fe9a14e09b675cc24cb59c9aebf1615020.zip |
Infrastructure for synchronous script loading
This implements the parts of the "prepare a script element" algorithm that are
required for synchronous scripts. It also adds some infrastructure for future
support of the `async` and `defer` attributes.
Diffstat (limited to 'components/script/dom/htmlscriptelement.rs')
-rw-r--r-- | components/script/dom/htmlscriptelement.rs | 142 |
1 files changed, 135 insertions, 7 deletions
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index b970596fbdd..9c20435f67a 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -14,13 +14,35 @@ use dom::document::Document; use dom::element::{HTMLScriptElementTypeId, Element, AttributeHandlers}; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlelement::HTMLElement; -use dom::node::{Node, NodeHelpers, ElementNodeTypeId}; +use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node}; +use dom::window::WindowHelpers; +use encoding::all::UTF_8; +use encoding::types::{Encoding, DecodeReplace}; +use servo_net::resource_task::load_whole_resource; use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS, StaticStringVec}; +use std::cell::Cell; +use url::UrlParser; #[dom_struct] pub struct HTMLScriptElement { htmlelement: HTMLElement, + + /// https://html.spec.whatwg.org/multipage/scripting.html#already-started + already_started: Cell<bool>, + + /// https://html.spec.whatwg.org/multipage/scripting.html#parser-inserted + parser_inserted: Cell<bool>, + + /// https://html.spec.whatwg.org/multipage/scripting.html#non-blocking + /// + /// (currently unused) + non_blocking: Cell<bool>, + + /// https://html.spec.whatwg.org/multipage/scripting.html#ready-to-be-parser-executed + /// + /// (currently unused) + ready_to_be_parser_executed: Cell<bool>, } impl HTMLScriptElementDerived for EventTarget { @@ -30,22 +52,30 @@ impl HTMLScriptElementDerived for EventTarget { } impl HTMLScriptElement { - fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLScriptElement { + fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>, + parser_inserted: bool) -> HTMLScriptElement { HTMLScriptElement { - htmlelement: HTMLElement::new_inherited(HTMLScriptElementTypeId, localName, prefix, document) + htmlelement: HTMLElement::new_inherited(HTMLScriptElementTypeId, localName, prefix, document), + already_started: Cell::new(false), + parser_inserted: Cell::new(parser_inserted), + non_blocking: Cell::new(!parser_inserted), + ready_to_be_parser_executed: Cell::new(false), } } #[allow(unrooted_must_root)] - pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLScriptElement> { - let element = HTMLScriptElement::new_inherited(localName, prefix, document); + pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>, + parser_inserted: bool) -> Temporary<HTMLScriptElement> { + let element = HTMLScriptElement::new_inherited(localName, prefix, document, parser_inserted); Node::reflect_node(box element, document, HTMLScriptElementBinding::Wrap) } } pub trait HTMLScriptElementHelpers { - /// Prepare a script (<http://www.whatwg.org/html/#prepare-a-script>), - /// steps 6 and 7. + /// Prepare a script (<http://www.whatwg.org/html/#prepare-a-script>) + fn prepare(self); + + /// Prepare a script, steps 6 and 7. fn is_javascript(self) -> bool; } @@ -71,6 +101,104 @@ static SCRIPT_JS_MIMES: StaticStringVec = &[ ]; impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { + fn prepare(self) { + // https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script + // Step 1. + if self.already_started.get() { + return; + } + // Step 2. + let was_parser_inserted = self.parser_inserted.get(); + self.parser_inserted.set(false); + + // Step 3. + let element: JSRef<Element> = ElementCast::from_ref(self); + if was_parser_inserted && element.has_attribute(&atom!("async")) { + self.non_blocking.set(true); + } + // Step 4. + let text = self.Text(); + if text.len() == 0 && !element.has_attribute(&atom!("src")) { + return; + } + // Step 5. + let node: JSRef<Node> = NodeCast::from_ref(self); + if !node.is_in_doc() { + return; + } + // Step 6, 7. + if !self.is_javascript() { + return; + } + // Step 8. + if was_parser_inserted { + self.parser_inserted.set(true); + self.non_blocking.set(false); + } + // Step 9. + self.already_started.set(true); + + // Step 10. + // 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. + + // Step 11. + // TODO: If scripting is disabled for the script element, then the user agent must abort + // these steps at this point. The script is not executed. + + // Step 12. + // TODO: If the script element has an `event` attribute and a `for` attribute, then run + // these substeps... + + // Step 13. + // TODO: If the script element has a `charset` attribute, then let the script block's + // character encoding for this script element be the result of getting an encoding from the + // value of the `charset` attribute. + + // Step 14 and 15. + // TODO: Add support for the `defer` and `async` attributes. (For now, we fetch all + // scripts synchronously and execute them immediately.) + let window = window_from_node(self).root(); + let page = window.page(); + let base_url = page.get_url(); + + let (source, url) = match element.get_attribute(ns!(""), &atom!("src")).root() { + Some(src) => { + if src.deref().Value().is_empty() { + // TODO: queue a task to fire a simple event named `error` at the element + return; + } + match UrlParser::new().base_url(&base_url).parse(src.deref().Value().as_slice()) { + Ok(url) => { + // TODO: Do a potentially CORS-enabled fetch with the mode being the current + // 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. + match load_whole_resource(&page.resource_task, url) { + Ok((metadata, bytes)) => { + // TODO: use the charset from step 13. + let source = UTF_8.decode(bytes.as_slice(), DecodeReplace).unwrap(); + (source, metadata.final_url) + } + Err(_) => { + error!("error loading script {}", src.deref().Value()); + return; + } + } + } + Err(_) => { + // TODO: queue a task to fire a simple event named `error` at the element + error!("error parsing URL for script {}", src.deref().Value()); + return; + } + } + } + None => (text, base_url) + }; + + window.evaluate_script_with_result(source.as_slice(), url.serialize().as_slice()); + } + fn is_javascript(self) -> bool { let element: JSRef<Element> = ElementCast::from_ref(self); match element.get_attribute(ns!(""), &atom!("type")).root().map(|s| s.Value()) { |