mirror of
https://github.com/servo/servo.git
synced 2025-06-21 15:49:04 +01:00
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:
parent
9b7425000b
commit
3b82b11054
4 changed files with 224 additions and 57 deletions
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue