diff options
author | Bruno de Oliveira Abinader <bruno.d@partner.samsung.com> | 2014-01-06 18:32:55 -0400 |
---|---|---|
committer | Bruno de Oliveira Abinader <bruno.d@partner.samsung.com> | 2014-01-16 10:25:17 -0400 |
commit | 3b82b11054645908c42ccd95f826322a2dc8a329 (patch) | |
tree | d279adfde8c70ea88cac30823f48230bac8b94ac /src/components/script/dom/node.rs | |
parent | 9b7425000b6b5df1e409699f8faead47eb9e41a8 (diff) | |
download | servo-3b82b11054645908c42ccd95f826322a2dc8a329.tar.gz servo-3b82b11054645908c42ccd95f826322a2dc8a329.zip |
Implement Node::replaceChild()
Implements Node:replaceChild() according to spec below:
http://dom.spec.whatwg.org/#concept-node-replace
Closes #1430.
Diffstat (limited to 'src/components/script/dom/node.rs')
-rw-r--r-- | src/components/script/dom/node.rs | 226 |
1 files changed, 171 insertions, 55 deletions
diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index c9f8f06f7b7..1d7acb41906 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -258,6 +258,37 @@ impl AbstractNode { AbstractNode::from_box(boxed_node) } } + + pub fn is_inclusive_ancestor_of(&self, parent: AbstractNode) -> bool { + *self == parent || parent.ancestors().any(|ancestor| ancestor == *self) + } + + pub fn is_parent_of(&self, child: AbstractNode) -> bool { + child.parent_node() == Some(*self) + } + + fn followed_by_doctype(child: AbstractNode) -> bool { + let mut iter = child; + loop { + match iter.next_sibling() { + Some(sibling) => { + if sibling.is_doctype() { + return true; + } + iter = sibling; + }, + None => return false + } + } + } + + fn inclusively_followed_by_doctype(child: Option<AbstractNode>) -> bool { + match child { + Some(child) if child.is_doctype() => true, + Some(child) => AbstractNode::followed_by_doctype(child), + None => false + } + } } impl<'a> AbstractNode { @@ -528,7 +559,7 @@ impl AbstractNode { } pub fn ReplaceChild(self, node: AbstractNode, child: AbstractNode) -> Fallible<AbstractNode> { - self.mut_node().ReplaceChild(node, child) + self.node().ReplaceChild(self, node, child) } pub fn RemoveChild(self, node: AbstractNode) -> Fallible<AbstractNode> { @@ -1041,87 +1072,49 @@ impl Node { // http://dom.spec.whatwg.org/#concept-node-pre-insert fn pre_insert(node: AbstractNode, parent: AbstractNode, child: Option<AbstractNode>) -> Fallible<AbstractNode> { - fn is_inclusive_ancestor_of(node: AbstractNode, parent: AbstractNode) -> bool { - node == parent || parent.ancestors().any(|ancestor| ancestor == node) - } - // Step 1. match parent.type_id() { DocumentNodeTypeId(..) | DocumentFragmentNodeTypeId | ElementNodeTypeId(..) => (), - _ => { - return Err(HierarchyRequest); - }, + _ => return Err(HierarchyRequest) } // Step 2. - if is_inclusive_ancestor_of(node, parent) { + if node.is_inclusive_ancestor_of(parent) { return Err(HierarchyRequest); } // Step 3. match child { - Some(child) => { - if child.parent_node() != Some(parent) { - return Err(NotFound); - } - }, - None => (), + Some(child) if !parent.is_parent_of(child) => return Err(NotFound), + _ => () } - // Step 4. - match node.type_id() { - DocumentFragmentNodeTypeId | - DoctypeNodeTypeId | - ElementNodeTypeId(_) | - TextNodeTypeId | - // ProcessingInstructionNodeTypeId | - CommentNodeTypeId => (), - DocumentNodeTypeId(..) => return Err(HierarchyRequest), - } - - // Step 5. + // Step 4-5. match node.type_id() { TextNodeTypeId => { match node.parent_node() { Some(parent) if parent.is_document() => return Err(HierarchyRequest), _ => () } - }, + } DoctypeNodeTypeId => { match node.parent_node() { Some(parent) if !parent.is_document() => return Err(HierarchyRequest), _ => () } - }, - _ => (), + } + DocumentFragmentNodeTypeId | + ElementNodeTypeId(_) | + // ProcessingInstructionNodeTypeId | + CommentNodeTypeId => (), + DocumentNodeTypeId(..) => return Err(HierarchyRequest) } // Step 6. match parent.type_id() { DocumentNodeTypeId(_) => { - fn inclusively_followed_by_doctype(child: Option<AbstractNode>) -> bool{ - match child { - Some(child) if child.is_doctype() => true, - Some(child) => { - let mut iter = child; - loop { - match iter.next_sibling() { - Some(sibling) => { - if sibling.is_doctype() { - return true; - } - iter = sibling; - }, - None => return false, - } - } - }, - None => false, - } - } - match node.type_id() { // Step 6.1 DocumentFragmentNodeTypeId => { @@ -1138,7 +1131,7 @@ impl Node { if parent.child_elements().len() > 0 { return Err(HierarchyRequest); } - if inclusively_followed_by_doctype(child) { + if AbstractNode::inclusively_followed_by_doctype(child) { return Err(HierarchyRequest); } }, @@ -1153,7 +1146,7 @@ impl Node { if parent.child_elements().len() > 0 { return Err(HierarchyRequest); } - if inclusively_followed_by_doctype(child) { + if AbstractNode::inclusively_followed_by_doctype(child) { return Err(HierarchyRequest); } }, @@ -1362,9 +1355,132 @@ impl Node { Node::pre_insert(node, abstract_self, None) } - pub fn ReplaceChild(&mut self, _node: AbstractNode, _child: AbstractNode) + // http://dom.spec.whatwg.org/#concept-node-replace + pub fn ReplaceChild(&self, parent: AbstractNode, node: AbstractNode, child: AbstractNode) -> Fallible<AbstractNode> { - fail!("stub") + // Step 1. + match parent.type_id() { + DocumentNodeTypeId(..) | + DocumentFragmentNodeTypeId | + ElementNodeTypeId(..) => (), + _ => return Err(HierarchyRequest) + } + + // Step 2. + if node.is_inclusive_ancestor_of(parent) { + return Err(HierarchyRequest); + } + + // Step 3. + if !parent.is_parent_of(child) { + return Err(NotFound); + } + + // Step 4-5. + match node.type_id() { + TextNodeTypeId if parent.is_document() => return Err(HierarchyRequest), + DoctypeNodeTypeId if !parent.is_document() => return Err(HierarchyRequest), + DocumentFragmentNodeTypeId | + DoctypeNodeTypeId | + ElementNodeTypeId(..) | + TextNodeTypeId | + // ProcessingInstructionNodeTypeId | + CommentNodeTypeId => (), + DocumentNodeTypeId(..) => return Err(HierarchyRequest) + } + + // Step 6. + match parent.type_id() { + DocumentNodeTypeId(..) => { + match node.type_id() { + // Step 6.1 + DocumentFragmentNodeTypeId => { + // Step 6.1.1(b) + if node.children().any(|c| c.is_text()) { + return Err(HierarchyRequest); + } + match node.child_elements().len() { + 0 => (), + // Step 6.1.2 + 1 => { + if parent.child_elements().any(|c| c != child) { + return Err(HierarchyRequest); + } + if AbstractNode::followed_by_doctype(child) { + return Err(HierarchyRequest); + } + }, + // Step 6.1.1(a) + _ => return Err(HierarchyRequest) + } + }, + // Step 6.2 + ElementNodeTypeId(..) => { + if parent.child_elements().any(|c| c != child) { + return Err(HierarchyRequest); + } + if AbstractNode::followed_by_doctype(child) { + return Err(HierarchyRequest); + } + }, + // Step 6.3 + DoctypeNodeTypeId => { + if parent.children().any(|c| c.is_doctype() && c != child) { + return Err(HierarchyRequest); + } + if parent.children() + .take_while(|&c| c != child) + .any(|c| c.is_element()) { + return Err(HierarchyRequest); + } + }, + TextNodeTypeId | + // ProcessingInstructionNodeTypeId | + CommentNodeTypeId => (), + DocumentNodeTypeId(..) => unreachable!() + } + }, + _ => () + } + + // Ok if not caught by previous error checks. + if node == child { + return Ok(child); + } + + // Step 7-8. + let reference_child = if child.next_sibling() != Some(node) { + child.next_sibling() + } else { + node.next_sibling() + }; + + // Step 9. + Node::adopt(node, parent.node().owner_doc()); + + { + let suppress_observers = true; + + // Step 10. + Node::remove(child, parent, suppress_observers); + + // Step 11. + Node::insert(node, parent, reference_child, suppress_observers); + } + + // Step 12-14. + // Step 13: mutation records. + child.node_removed(); + if node.type_id() == DocumentFragmentNodeTypeId { + for child_node in node.children() { + child_node.node_inserted(); + } + } else { + node.node_inserted(); + } + + // Step 15. + Ok(child) } pub fn RemoveChild(&self, abstract_self: AbstractNode, node: AbstractNode) |