Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert node serialization to a purely iterative algorithm. #17520

Merged
merged 1 commit into from Jul 17, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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(())
}
}
@@ -13737,6 +13737,12 @@
{}
]
],
"mozilla/deep_serialization_succeeds.html": [
[
"/_mozilla/mozilla/deep_serialization_succeeds.html",
{}
]
],
"mozilla/deterministic-raf.html": [
[
"/_mozilla/mozilla/deterministic-raf.html",
"67d8cdd3e3a1656ba315fcbf6dae7236680bac16",
"reftest"
],
"mozilla/deep_serialization_succeeds.html": [
"cb3d201d577c17d19babf1f6c04ba882fa42048e",
"testharness"
],
"mozilla/details_ui_closed.html": [
"2acbe3afbec267bad4dd986803e636740a707507",
"reftest"
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>Deep Serialization Test</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>

// The test here is that Servo doesn't crash.

test(function() {
var first = document.createElement('div');
var last = first;
for (var i = 0; i < 3000; i++) {
var e = document.createElement('div');
last.appendChild(e);
last = e;
}
last.textContent = "abcdef";
var elem = first;
assert_regexp_match(elem.outerHTML, /abcdef/);
}, "Test that deep trees can serialize without crashing.");
</script>
</body>
</html>
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.