diff options
author | Austin Hicks <camlorn@camlorn.net> | 2017-06-25 15:28:46 -0400 |
---|---|---|
committer | Austin Hicks <camlorn@camlorn.net> | 2017-07-17 12:24:30 -0400 |
commit | 033b31979bb52460fd4845c02cc60294942c1b5b (patch) | |
tree | 2f29b49d6b20035679475b8dfa35820b834dd4cd /components/script/dom/servoparser/html.rs | |
parent | 2bb4f65100ca07e9281c8ad04cd498ff1346387f (diff) | |
download | servo-033b31979bb52460fd4845c02cc60294942c1b5b.tar.gz servo-033b31979bb52460fd4845c02cc60294942c1b5b.zip |
Convert node serialization to a purely iterative algorithm.
We maintain a stack of open element nodes and non-node elements and use it to determine when to close them.
Diffstat (limited to 'components/script/dom/servoparser/html.rs')
-rw-r--r-- | components/script/dom/servoparser/html.rs | 184 |
1 files changed, 121 insertions, 63 deletions
diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs index 6d8f894a074..749174d1417 100644 --- a/components/script/dom/servoparser/html.rs +++ b/components/script/dom/servoparser/html.rs @@ -14,7 +14,7 @@ use dom::documenttype::DocumentType; use dom::element::Element; use dom::htmlscriptelement::HTMLScriptElement; use dom::htmltemplateelement::HTMLTemplateElement; -use dom::node::Node; +use dom::node::{Node, TreeIterator}; use dom::processinginstruction::ProcessingInstruction; use dom::servoparser::Sink; use html5ever::QualName; @@ -115,79 +115,137 @@ unsafe impl JSTraceable for HtmlTokenizer<TreeBuilder<JS<Node>, Sink>> { } } -impl<'a> Serialize for &'a Node { - fn serialize<S: Serializer>(&self, serializer: &mut S, - traversal_scope: TraversalScope) -> io::Result<()> { - let node = *self; - match (traversal_scope, node.type_id()) { - (_, NodeTypeId::Element(..)) => { - let elem = node.downcast::<Element>().unwrap(); - let name = QualName::new(None, elem.namespace().clone(), - elem.local_name().clone()); - if traversal_scope == IncludeNode { - let attrs = elem.attrs().iter().map(|attr| { - let qname = QualName::new(None, attr.namespace().clone(), - attr.local_name().clone()); - let value = attr.value().clone(); - (qname, value) - }).collect::<Vec<_>>(); - let attr_refs = attrs.iter().map(|&(ref qname, ref value)| { - let ar: AttrRef = (&qname, &**value); - ar - }); - serializer.start_elem(name.clone(), attr_refs)?; - } +fn start_element<S: Serializer>(node: &Element, serializer: &mut S) -> io::Result<()> { + let name = QualName::new(None, node.namespace().clone(), + node.local_name().clone()); + let attrs = node.attrs().iter().map(|attr| { + let qname = QualName::new(None, attr.namespace().clone(), + attr.local_name().clone()); + let value = attr.value().clone(); + (qname, value) + }).collect::<Vec<_>>(); + let attr_refs = attrs.iter().map(|&(ref qname, ref value)| { + let ar: AttrRef = (&qname, &**value); + ar + }); + serializer.start_elem(name, attr_refs)?; + Ok(()) +} - let children = if let Some(tpl) = node.downcast::<HTMLTemplateElement>() { - // https://github.com/w3c/DOM-Parsing/issues/1 - tpl.Content().upcast::<Node>().children() - } else { - node.children() - }; +fn end_element<S: Serializer>(node: &Element, serializer: &mut S) -> io::Result<()> { + let name = QualName::new(None, node.namespace().clone(), + node.local_name().clone()); + serializer.end_elem(name) +} - for handle in children { - (&*handle).serialize(serializer, IncludeNode)?; - } - if traversal_scope == IncludeNode { - serializer.end_elem(name.clone())?; - } - Ok(()) - }, +enum SerializationCommand { + OpenElement(Root<Element>), + CloseElement(Root<Element>), + SerializeNonelement(Root<Node>), +} - (ChildrenOnly, NodeTypeId::Document(_)) => { - for handle in node.children() { - (&*handle).serialize(serializer, IncludeNode)?; - } - Ok(()) - }, +struct SerializationIterator { + stack: Vec<SerializationCommand>, +} - (ChildrenOnly, _) => Ok(()), +fn rev_children_iter(n: &Node) -> impl Iterator<Item=Root<Node>>{ + match n.downcast::<HTMLTemplateElement>() { + Some(t) => t.Content().upcast::<Node>().rev_children(), + None => n.rev_children(), + } +} - (IncludeNode, NodeTypeId::DocumentType) => { - let doctype = node.downcast::<DocumentType>().unwrap(); - serializer.write_doctype(&doctype.name()) - }, +impl SerializationIterator { + fn new(node: &Node, skip_first: bool) -> SerializationIterator { + let mut ret = SerializationIterator { + stack: vec![], + }; + if skip_first { + for c in rev_children_iter(node) { + ret.push_node(&*c); + } + } else { + ret.push_node(node); + } + ret + } + + fn push_node(&mut self, n: &Node) { + match n.downcast::<Element>() { + Some(e) => self.stack.push(SerializationCommand::OpenElement(Root::from_ref(e))), + None => self.stack.push(SerializationCommand::SerializeNonelement(Root::from_ref(n))), + } + } +} + +impl Iterator for SerializationIterator { + type Item = SerializationCommand; + + fn next(&mut self) -> Option<SerializationCommand> { + let res = self.stack.pop(); + + if let Some(SerializationCommand::OpenElement(ref e)) = res { + self.stack.push(SerializationCommand::CloseElement(e.clone())); + for c in rev_children_iter(&*e.upcast::<Node>()) { + self.push_node(&c); + } + } + + res + } +} + +impl<'a> Serialize for &'a Node { + fn serialize<S: Serializer>(&self, serializer: &mut S, + traversal_scope: TraversalScope) -> io::Result<()> { + let node = *self; - (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::Text)) => { - let cdata = node.downcast::<CharacterData>().unwrap(); - serializer.write_text(&cdata.data()) - }, - (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::Comment)) => { - let cdata = node.downcast::<CharacterData>().unwrap(); - serializer.write_comment(&cdata.data()) - }, + let iter = SerializationIterator::new(node, traversal_scope == ChildrenOnly); - (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction)) => { - let pi = node.downcast::<ProcessingInstruction>().unwrap(); - let data = pi.upcast::<CharacterData>().data(); - serializer.write_processing_instruction(&pi.target(), &data) - }, + for cmd in iter { + match cmd { + SerializationCommand::OpenElement(n) => { + start_element(&n, serializer)?; + } - (IncludeNode, NodeTypeId::DocumentFragment) => Ok(()), + SerializationCommand::CloseElement(n) => { + end_element(&&n, serializer)?; + } - (IncludeNode, NodeTypeId::Document(_)) => panic!("Can't serialize Document node itself"), + SerializationCommand::SerializeNonelement(n) => { + match n.type_id() { + NodeTypeId::DocumentType => { + let doctype = n.downcast::<DocumentType>().unwrap(); + serializer.write_doctype(&doctype.name())?; + }, + + NodeTypeId::CharacterData(CharacterDataTypeId::Text) => { + let cdata = n.downcast::<CharacterData>().unwrap(); + serializer.write_text(&cdata.data())?; + }, + + NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => { + let cdata = n.downcast::<CharacterData>().unwrap(); + serializer.write_comment(&cdata.data())?; + }, + + NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => { + let pi = n.downcast::<ProcessingInstruction>().unwrap(); + let data = pi.upcast::<CharacterData>().data(); + serializer.write_processing_instruction(&pi.target(), &data)?; + }, + + NodeTypeId::DocumentFragment => {} + + NodeTypeId::Document(_) => panic!("Can't serialize Document node itself"), + NodeTypeId::Element(_) => panic!("Element shouldn't appear here"), + } + } + } } + + Ok(()) } } |