Implement Node::replaceChild()

Implements Node:replaceChild() according to spec below:
http://dom.spec.whatwg.org/#concept-node-replace

Closes #1430.
This commit is contained in:
Bruno de Oliveira Abinader 2014-01-06 18:32:55 -04:00
parent 9b7425000b
commit 3b82b11054
4 changed files with 224 additions and 57 deletions

View file

@ -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)