servo: Merge #19397 - Implement the create an element for token algorithm (from cbrewster:create_element_for_token); r=jdm,nox
authorJosh Matthews <josh@joshmatthews.net>
Wed, 17 Jan 2018 10:57:06 -0600
changeset 454015 5cf0e7a54c3f402c1bbc7c847e57c569638a4c41
parent 454014 030453672f86258c0973943180c03b3c0a60db13
child 454016 34c24c0414fada237b9f174054d3c0e3b6a5bd58
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm, nox
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #19397 - Implement the create an element for token algorithm (from cbrewster:create_element_for_token); r=jdm,nox <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #19392 and fix #19393 (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: fa82a6bbcef23c1ff4dc9d34f5f4ad75b622b3be
servo/components/script/dom/bindings/settings_stack.rs
servo/components/script/dom/document.rs
servo/components/script/dom/servoparser/async_html.rs
servo/components/script/dom/servoparser/html.rs
servo/components/script/dom/servoparser/mod.rs
servo/components/script/dom/servoparser/xml.rs
--- a/servo/components/script/dom/bindings/settings_stack.rs
+++ b/servo/components/script/dom/bindings/settings_stack.rs
@@ -30,16 +30,22 @@ struct StackEntry {
 
 /// Traces the script settings stack.
 pub unsafe fn trace(tracer: *mut JSTracer) {
     STACK.with(|stack| {
         stack.borrow().trace(tracer);
     })
 }
 
+pub fn is_execution_stack_empty() -> bool {
+    STACK.with(|stack| {
+        stack.borrow().is_empty()
+    })
+}
+
 /// RAII struct that pushes and pops entries from the script settings stack.
 pub struct AutoEntryScript {
     global: DomRoot<GlobalScope>,
 }
 
 impl AutoEntryScript {
     /// <https://html.spec.whatwg.org/multipage/#prepare-to-run-script>
     pub fn new(global: &GlobalScope) -> Self {
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -359,16 +359,18 @@ pub struct Document {
     /// whenever any element with the same ID as the form attribute
     /// is inserted or removed from the document.
     /// See https://html.spec.whatwg.org/multipage/#form-owner
     form_id_listener_map: DomRefCell<HashMap<Atom, HashSet<Dom<Element>>>>,
     interactive_time: DomRefCell<InteractiveMetrics>,
     tti_window: DomRefCell<InteractiveWindow>,
     /// RAII canceller for Fetch
     canceller: FetchCanceller,
+    /// https://html.spec.whatwg.org/multipage/#throw-on-dynamic-markup-insertion-counter
+    throw_on_dynamic_markup_insertion_counter: Cell<u64>,
 }
 
 #[derive(JSTraceable, MallocSizeOf)]
 struct ImagesFilter;
 impl CollectionFilter for ImagesFilter {
     fn filter(&self, elem: &Element, _root: &Node) -> bool {
         elem.is::<HTMLImageElement>()
     }
@@ -1889,17 +1891,22 @@ impl Document {
     }
 
     pub fn get_current_parser(&self) -> Option<DomRoot<ServoParser>> {
         self.current_parser.get()
     }
 
     pub fn can_invoke_script(&self) -> bool {
         match self.get_current_parser() {
-            Some(parser) => parser.parser_is_not_active(),
+            Some(parser) => {
+                // It is safe to run script if the parser is not actively parsing,
+                // or if it is impossible to interact with the token stream.
+                parser.parser_is_not_active() ||
+                self.throw_on_dynamic_markup_insertion_counter.get() > 0
+            }
             None => true,
         }
     }
 
     /// Iterate over all iframes in the document.
     pub fn iter_iframes(&self) -> impl Iterator<Item=DomRoot<HTMLIFrameElement>> {
         self.upcast::<Node>()
             .traverse_preorder()
@@ -2048,16 +2055,26 @@ impl Document {
 
         registry.lookup_definition(local_name, is)
     }
 
     fn send_to_constellation(&self, msg: ScriptMsg) {
         let global_scope = self.window.upcast::<GlobalScope>();
         global_scope.script_to_constellation_chan().send(msg).unwrap();
     }
+
+    pub fn increment_throw_on_dynamic_markup_insertion_counter(&self) {
+        let counter = self.throw_on_dynamic_markup_insertion_counter.get();
+        self.throw_on_dynamic_markup_insertion_counter.set(counter + 1);
+    }
+
+    pub fn decrement_throw_on_dynamic_markup_insertion_counter(&self) {
+        let counter = self.throw_on_dynamic_markup_insertion_counter.get();
+        self.throw_on_dynamic_markup_insertion_counter.set(counter - 1);
+    }
 }
 
 #[derive(MallocSizeOf, PartialEq)]
 pub enum DocumentSource {
     FromParser,
     NotFromParser,
 }
 
@@ -2289,16 +2306,17 @@ impl Document {
             ignore_destructive_writes_counter: Default::default(),
             spurious_animation_frames: Cell::new(0),
             dom_count: Cell::new(1),
             fullscreen_element: MutNullableDom::new(None),
             form_id_listener_map: Default::default(),
             interactive_time: DomRefCell::new(interactive_time),
             tti_window: DomRefCell::new(InteractiveWindow::new()),
             canceller: canceller,
+            throw_on_dynamic_markup_insertion_counter: Cell::new(0),
         }
     }
 
     // https://dom.spec.whatwg.org/#dom-document-document
     pub fn Constructor(window: &Window) -> Fallible<DomRoot<Document>> {
         let doc = window.Document();
         let docloader = DocumentLoader::new(&*doc.loader());
         Ok(Document::new(window,
@@ -3712,17 +3730,19 @@ impl DocumentMethods for Document {
     // https://html.spec.whatwg.org/multipage/#dom-document-open
     fn Open(&self, type_: DOMString, replace: DOMString) -> Fallible<DomRoot<Document>> {
         if !self.is_html_document() {
             // Step 1.
             return Err(Error::InvalidState);
         }
 
         // Step 2.
-        // TODO: handle throw-on-dynamic-markup-insertion counter.
+        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
+            return Err(Error::InvalidState);
+        }
 
         if !self.is_active() {
             // Step 3.
             return Ok(DomRoot::from_ref(self));
         }
 
         let entry_responsible_document = GlobalScope::entry().as_window().Document();
 
@@ -3858,17 +3878,20 @@ impl DocumentMethods for Document {
     // https://html.spec.whatwg.org/multipage/#dom-document-write
     fn Write(&self, text: Vec<DOMString>) -> ErrorResult {
         if !self.is_html_document() {
             // Step 1.
             return Err(Error::InvalidState);
         }
 
         // Step 2.
-        // TODO: handle throw-on-dynamic-markup-insertion counter.
+        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
+            return Err(Error::InvalidState);
+        }
+
         if !self.is_active() {
             // Step 3.
             return Ok(());
         }
 
         let parser = match self.get_current_parser() {
             Some(ref parser) if parser.can_write() => DomRoot::from_ref(&**parser),
             _ => {
@@ -3905,17 +3928,19 @@ impl DocumentMethods for Document {
     // https://html.spec.whatwg.org/multipage/#dom-document-close
     fn Close(&self) -> ErrorResult {
         if !self.is_html_document() {
             // Step 1.
             return Err(Error::InvalidState);
         }
 
         // Step 2.
-        // TODO: handle throw-on-dynamic-markup-insertion counter.
+        if self.throw_on_dynamic_markup_insertion_counter.get() > 0 {
+            return Err(Error::InvalidState);
+        }
 
         let parser = match self.get_current_parser() {
             Some(ref parser) if parser.is_script_created() => DomRoot::from_ref(&**parser),
             _ => {
                 // Step 3.
                 return Ok(());
             }
         };
--- a/servo/components/script/dom/servoparser/async_html.rs
+++ b/servo/components/script/dom/servoparser/async_html.rs
@@ -7,24 +7,25 @@
 use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
 use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::root::{Dom, DomRoot};
 use dom::bindings::str::DOMString;
 use dom::comment::Comment;
 use dom::document::Document;
 use dom::documenttype::DocumentType;
-use dom::element::{CustomElementCreationMode, Element, ElementCreator};
+use dom::element::{Element, ElementCreator};
 use dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
 use dom::htmlscriptelement::HTMLScriptElement;
 use dom::htmltemplateelement::HTMLTemplateElement;
 use dom::node::Node;
 use dom::processinginstruction::ProcessingInstruction;
+use dom::servoparser::{ElementAttribute, create_element_for_token, ParsingAlgorithm};
 use dom::virtualmethods::vtable_for;
-use html5ever::{Attribute as HtmlAttribute, ExpandedName, LocalName, QualName};
+use html5ever::{Attribute as HtmlAttribute, ExpandedName, QualName};
 use html5ever::buffer_queue::BufferQueue;
 use html5ever::tendril::{SendTendril, StrTendril, Tendril};
 use html5ever::tendril::fmt::UTF8;
 use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts, TokenizerResult};
 use html5ever::tree_builder::{ElementFlags, NodeOrText as HtmlNodeOrText, NextParserState, QuirksMode, TreeSink};
 use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts};
 use servo_url::ServoUrl;
 use std::borrow::Cow;
@@ -330,30 +331,28 @@ impl Tokenizer {
         match op {
             ParseOperation::GetTemplateContents { target, contents } => {
                 let target = DomRoot::from_ref(&**self.get_node(&target));
                 let template = target.downcast::<HTMLTemplateElement>().expect(
                     "Tried to extract contents from non-template element while parsing");
                 self.insert_node(contents, Dom::from_ref(template.Content().upcast()));
             }
             ParseOperation::CreateElement { node, name, attrs, current_line } => {
-                let is = attrs.iter()
-                              .find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
-                              .map(|attr| LocalName::from(&*attr.value));
-
-                let elem = Element::create(name,
-                                           is,
-                                           &*self.document,
-                                           ElementCreator::ParserCreated(current_line),
-                                           CustomElementCreationMode::Synchronous);
-                for attr in attrs {
-                    elem.set_attribute_from_parser(attr.name, DOMString::from(attr.value), None);
-                }
-
-                self.insert_node(node, Dom::from_ref(elem.upcast()));
+                let attrs = attrs
+                    .into_iter()
+                    .map(|attr| ElementAttribute::new(attr.name, DOMString::from(attr.value)))
+                    .collect();
+                let element = create_element_for_token(
+                    name,
+                    attrs,
+                    &*self.document,
+                    ElementCreator::ParserCreated(current_line),
+                    ParsingAlgorithm::Normal
+                );
+                self.insert_node(node, Dom::from_ref(element.upcast()));
             }
             ParseOperation::CreateComment { text, node } => {
                 let comment = Comment::new(DOMString::from(text), document);
                 self.insert_node(node, Dom::from_ref(&comment.upcast()));
             }
             ParseOperation::AppendBeforeSibling { sibling, node } => {
                 self.append_before_sibling(sibling, node);
             }
--- a/servo/components/script/dom/servoparser/html.rs
+++ b/servo/components/script/dom/servoparser/html.rs
@@ -11,17 +11,17 @@ use dom::bindings::trace::JSTraceable;
 use dom::characterdata::CharacterData;
 use dom::document::Document;
 use dom::documenttype::DocumentType;
 use dom::element::Element;
 use dom::htmlscriptelement::HTMLScriptElement;
 use dom::htmltemplateelement::HTMLTemplateElement;
 use dom::node::Node;
 use dom::processinginstruction::ProcessingInstruction;
-use dom::servoparser::Sink;
+use dom::servoparser::{ParsingAlgorithm, Sink};
 use html5ever::QualName;
 use html5ever::buffer_queue::BufferQueue;
 use html5ever::serialize::{AttrRef, Serialize, Serializer};
 use html5ever::serialize::TraversalScope;
 use html5ever::serialize::TraversalScope::IncludeNode;
 use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts, TokenizerResult};
 use html5ever::tree_builder::{Tracer as HtmlTracer, TreeBuilder, TreeBuilderOpts};
 use js::jsapi::JSTracer;
@@ -34,23 +34,25 @@ pub struct Tokenizer {
     #[ignore_malloc_size_of = "Defined in html5ever"]
     inner: HtmlTokenizer<TreeBuilder<Dom<Node>, Sink>>,
 }
 
 impl Tokenizer {
     pub fn new(
             document: &Document,
             url: ServoUrl,
-            fragment_context: Option<super::FragmentContext>)
+            fragment_context: Option<super::FragmentContext>,
+            parsing_algorithm: ParsingAlgorithm)
             -> Self {
         let sink = Sink {
             base_url: url,
             document: Dom::from_ref(document),
             current_line: 1,
             script: Default::default(),
+            parsing_algorithm: parsing_algorithm,
         };
 
         let options = TreeBuilderOpts {
             ignore_missing_rules: true,
             .. Default::default()
         };
 
         let inner = if let Some(fc) = fragment_context {
--- a/servo/components/script/dom/servoparser/mod.rs
+++ b/servo/components/script/dom/servoparser/mod.rs
@@ -8,16 +8,17 @@ use dom::bindings::codegen::Bindings::Do
 use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
 use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
 use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
 use dom::bindings::codegen::Bindings::ServoParserBinding;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::{Reflector, reflect_dom_object};
 use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference};
+use dom::bindings::settings_stack::is_execution_stack_empty;
 use dom::bindings::str::DOMString;
 use dom::characterdata::CharacterData;
 use dom::comment::Comment;
 use dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
 use dom::documenttype::DocumentType;
 use dom::element::{Element, ElementCreator, CustomElementCreationMode};
 use dom::globalscope::GlobalScope;
 use dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
@@ -96,30 +97,50 @@ pub struct ServoParser {
 }
 
 #[derive(PartialEq)]
 enum LastChunkState {
     Received,
     NotReceived,
 }
 
+pub struct ElementAttribute {
+    name: QualName,
+    value: DOMString
+}
+
+#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
+pub enum ParsingAlgorithm {
+    Normal,
+    Fragment,
+}
+
+impl ElementAttribute {
+    pub fn new(name: QualName, value: DOMString) -> ElementAttribute {
+        ElementAttribute {
+            name: name,
+            value: value
+        }
+    }
+}
+
 impl ServoParser {
     pub fn parser_is_not_active(&self) -> bool {
         self.can_write() || self.tokenizer.try_borrow_mut().is_ok()
     }
 
     pub fn parse_html_document(document: &Document, input: DOMString, url: ServoUrl) {
         let parser = if PREFS.get("dom.servoparser.async_html_tokenizer.enabled").as_boolean().unwrap() {
             ServoParser::new(document,
                              Tokenizer::AsyncHtml(self::async_html::Tokenizer::new(document, url, None)),
                              LastChunkState::NotReceived,
                              ParserKind::Normal)
         } else {
             ServoParser::new(document,
-                             Tokenizer::Html(self::html::Tokenizer::new(document, url, None)),
+                             Tokenizer::Html(self::html::Tokenizer::new(document, url, None, ParsingAlgorithm::Normal)),
                              LastChunkState::NotReceived,
                              ParserKind::Normal)
         };
         parser.parse_string_chunk(String::from(input));
     }
 
     // https://html.spec.whatwg.org/multipage/#parsing-html-fragments
     pub fn parse_html_fragment(context: &Element, input: DOMString) -> impl Iterator<Item=DomRoot<Node>> {
@@ -155,33 +176,41 @@ impl ServoParser {
         let fragment_context = FragmentContext {
             context_elem: context_node,
             form_elem: form.r(),
         };
 
         let parser = ServoParser::new(&document,
                                       Tokenizer::Html(self::html::Tokenizer::new(&document,
                                                                                  url,
-                                                                                 Some(fragment_context))),
+                                                                                 Some(fragment_context),
+                                                                                 ParsingAlgorithm::Fragment)),
                                       LastChunkState::Received,
                                       ParserKind::Normal);
         parser.parse_string_chunk(String::from(input));
 
         // Step 14.
         let root_element = document.GetDocumentElement().expect("no document element");
         FragmentParsingResult {
             inner: root_element.upcast::<Node>().children(),
         }
     }
 
     pub fn parse_html_script_input(document: &Document, url: ServoUrl, type_: &str) {
-        let parser = ServoParser::new(document,
-                                      Tokenizer::Html(self::html::Tokenizer::new(document, url, None)),
-                                      LastChunkState::NotReceived,
-                                      ParserKind::ScriptCreated);
+        let parser = ServoParser::new(
+            document,
+            Tokenizer::Html(self::html::Tokenizer::new(
+                document,
+                url,
+                None,
+                ParsingAlgorithm::Normal,
+            )),
+            LastChunkState::NotReceived,
+            ParserKind::ScriptCreated,
+        );
         document.set_current_parser(Some(&parser));
         if !type_.eq_ignore_ascii_case("text/html") {
             parser.parse_string_chunk("<pre>\n".to_owned());
             parser.tokenizer.borrow_mut().set_plaintext_state();
         }
     }
 
     pub fn parse_xml_document(document: &Document, input: DOMString, url: ServoUrl) {
@@ -743,16 +772,17 @@ fn insert(parent: &Node, reference_child
 
 #[derive(JSTraceable, MallocSizeOf)]
 #[must_root]
 pub struct Sink {
     base_url: ServoUrl,
     document: Dom<Document>,
     current_line: u64,
     script: MutNullableDom<HTMLScriptElement>,
+    parsing_algorithm: ParsingAlgorithm,
 }
 
 impl Sink {
     fn same_tree(&self, x: &Dom<Node>, y: &Dom<Node>) -> bool {
         let x = x.downcast::<Element>().expect("Element node expected");
         let y = y.downcast::<Element>().expect("Element node expected");
 
         x.is_in_same_home_subtree(y)
@@ -790,31 +820,28 @@ impl TreeSink for Sink {
         ExpandedName {
             ns: elem.namespace(),
             local: elem.local_name(),
         }
     }
 
     fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>, _flags: ElementFlags)
             -> Dom<Node> {
-        let is = attrs.iter()
-                      .find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
-                      .map(|attr| LocalName::from(&*attr.value));
-
-        let elem = Element::create(name,
-                                   is,
-                                   &*self.document,
-                                   ElementCreator::ParserCreated(self.current_line),
-                                   CustomElementCreationMode::Synchronous);
-
-        for attr in attrs {
-            elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None);
-        }
-
-        Dom::from_ref(elem.upcast())
+        let attrs = attrs
+            .into_iter()
+            .map(|attr| ElementAttribute::new(attr.name, DOMString::from(String::from(attr.value))))
+            .collect();
+        let element = create_element_for_token(
+            name,
+            attrs,
+            &*self.document,
+            ElementCreator::ParserCreated(self.current_line),
+            self.parsing_algorithm,
+        );
+        Dom::from_ref(element.upcast())
     }
 
     fn create_comment(&mut self, text: StrTendril) -> Dom<Node> {
         let comment = Comment::new(DOMString::from(String::from(text)), &*self.document);
         Dom::from_ref(comment.upcast())
     }
 
     fn create_pi(&mut self, target: StrTendril, data: StrTendril) -> Dom<Node> {
@@ -945,8 +972,69 @@ impl TreeSink for Sink {
         self.current_line = line_number;
     }
 
     fn pop(&mut self, node: &Dom<Node>) {
         let node = DomRoot::from_ref(&**node);
         vtable_for(&node).pop();
     }
 }
+
+/// https://html.spec.whatwg.org/multipage/#create-an-element-for-the-token
+fn create_element_for_token(
+    name: QualName,
+    attrs: Vec<ElementAttribute>,
+    document: &Document,
+    creator: ElementCreator,
+    parsing_algorithm: ParsingAlgorithm,
+) -> DomRoot<Element> {
+    // Step 3.
+    let is = attrs.iter()
+        .find(|attr| attr.name.local.eq_str_ignore_ascii_case("is"))
+        .map(|attr| LocalName::from(&*attr.value));
+
+    // Step 4.
+    let definition = document.lookup_custom_element_definition(&name.ns, &name.local, is.as_ref());
+
+    // Step 5.
+    let will_execute_script = definition.is_some() && parsing_algorithm != ParsingAlgorithm::Fragment;
+
+    // Step 6.
+    if will_execute_script {
+        // Step 6.1.
+        document.increment_throw_on_dynamic_markup_insertion_counter();
+        // Step 6.2
+        if is_execution_stack_empty() {
+            document.window().upcast::<GlobalScope>().perform_a_microtask_checkpoint();
+        }
+        // Step 6.3
+        ScriptThread::push_new_element_queue()
+    }
+
+    // Step 7.
+    let creation_mode = if will_execute_script {
+        CustomElementCreationMode::Synchronous
+    } else {
+        CustomElementCreationMode::Asynchronous
+    };
+    let element = Element::create(name, is, document, creator, creation_mode);
+
+    // Step 8.
+    for attr in attrs {
+        element.set_attribute_from_parser(attr.name, attr.value, None);
+    }
+
+    // Step 9.
+    if will_execute_script {
+        // Steps 9.1 - 9.2.
+        ScriptThread::pop_current_element_queue();
+        // Step 9.3.
+        document.decrement_throw_on_dynamic_markup_insertion_counter();
+    }
+
+    // TODO: Step 10.
+    // TODO: Step 11.
+
+    // Step 12 is handled in `associate_with_form`.
+
+    // Step 13.
+    element
+}
--- a/servo/components/script/dom/servoparser/xml.rs
+++ b/servo/components/script/dom/servoparser/xml.rs
@@ -4,17 +4,17 @@
 
 #![allow(unrooted_must_root)]
 
 use dom::bindings::root::{Dom, DomRoot};
 use dom::bindings::trace::JSTraceable;
 use dom::document::Document;
 use dom::htmlscriptelement::HTMLScriptElement;
 use dom::node::Node;
-use dom::servoparser::Sink;
+use dom::servoparser::{ParsingAlgorithm, Sink};
 use js::jsapi::JSTracer;
 use servo_url::ServoUrl;
 use xml5ever::buffer_queue::BufferQueue;
 use xml5ever::tokenizer::XmlTokenizer;
 use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder};
 
 #[derive(JSTraceable, MallocSizeOf)]
 #[must_root]
@@ -25,16 +25,17 @@ pub struct Tokenizer {
 
 impl Tokenizer {
     pub fn new(document: &Document, url: ServoUrl) -> Self {
         let sink = Sink {
             base_url: url,
             document: Dom::from_ref(document),
             current_line: 1,
             script: Default::default(),
+            parsing_algorithm: ParsingAlgorithm::Normal,
         };
 
         let tb = XmlTreeBuilder::new(sink, Default::default());
         let tok = XmlTokenizer::new(tb, Default::default());
 
         Tokenizer {
             inner: tok,
         }