mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Implement ChildNode::before & ChildNode::after
Continued from #6536 The current implementations of `ChildNode::before` and `ChildNode::after` do not match the WHATWG spec. This commit updates the implementations to match the spec. Our current implementation of `ChildNode::after` passes all the WPT tests. So I made sure to add a regression test that failed with the current implementation. There are a few other unit tests I added to exhaust other corner cases I encountered.
This commit is contained in:
parent
e74a13c01d
commit
8cfccda542
4 changed files with 139 additions and 41 deletions
|
@ -801,40 +801,54 @@ impl<'a> NodeHelpers for &'a Node {
|
|||
|
||||
// https://dom.spec.whatwg.org/#dom-childnode-before
|
||||
fn before(self, nodes: Vec<NodeOrString>) -> ErrorResult {
|
||||
match self.parent_node.get() {
|
||||
None => {
|
||||
// Step 1.
|
||||
Ok(())
|
||||
},
|
||||
Some(ref parent_node) => {
|
||||
let parent = &self.parent_node;
|
||||
|
||||
// Step 2.
|
||||
let doc = self.owner_doc();
|
||||
let node = try!(doc.r().node_from_nodes_and_strings(nodes));
|
||||
let parent = match parent.get() {
|
||||
None => return Ok(()),
|
||||
Some(ref parent) => parent.root(),
|
||||
};
|
||||
|
||||
// Step 3.
|
||||
Node::pre_insert(node.r(), parent_node.root().r(),
|
||||
Some(self)).map(|_| ())
|
||||
},
|
||||
}
|
||||
let viable_previous_sibling = first_node_not_in(self.preceding_siblings(), &nodes);
|
||||
|
||||
// Step 4.
|
||||
let node = try!(self.owner_doc().node_from_nodes_and_strings(nodes));
|
||||
|
||||
// Step 5.
|
||||
let viable_previous_sibling = match viable_previous_sibling {
|
||||
Some(ref viable_previous_sibling) => viable_previous_sibling.next_sibling.get(),
|
||||
None => parent.first_child.get(),
|
||||
}.map(|s| s.root());
|
||||
|
||||
// Step 6.
|
||||
try!(Node::pre_insert(&node, &parent, viable_previous_sibling.r()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-childnode-after
|
||||
fn after(self, nodes: Vec<NodeOrString>) -> ErrorResult {
|
||||
match self.parent_node.get() {
|
||||
None => {
|
||||
// Step 1.
|
||||
Ok(())
|
||||
},
|
||||
Some(ref parent_node) => {
|
||||
let parent = &self.parent_node;
|
||||
|
||||
// Step 2.
|
||||
let doc = self.owner_doc();
|
||||
let node = try!(doc.r().node_from_nodes_and_strings(nodes));
|
||||
let parent = match parent.get() {
|
||||
None => return Ok(()),
|
||||
Some(ref parent) => parent.root(),
|
||||
};
|
||||
|
||||
// Step 3.
|
||||
// FIXME(https://github.com/servo/servo/issues/5720)
|
||||
let next_sibling = self.next_sibling.get().map(Root::from_rooted);
|
||||
Node::pre_insert(node.r(), parent_node.root().r(),
|
||||
next_sibling.r()).map(|_| ())
|
||||
},
|
||||
}
|
||||
let viable_next_sibling = first_node_not_in(self.following_siblings(), &nodes);
|
||||
|
||||
// Step 4.
|
||||
let node = try!(self.owner_doc().node_from_nodes_and_strings(nodes));
|
||||
|
||||
// Step 5.
|
||||
try!(Node::pre_insert(&node, &parent, viable_next_sibling.r()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-childnode-replacewith
|
||||
|
@ -1027,6 +1041,21 @@ impl<'a> NodeHelpers for &'a Node {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Iterate through `nodes` until we find a `Node` that is not in `not_in`
|
||||
fn first_node_not_in<I>(mut nodes: I, not_in: &[NodeOrString]) -> Option<Root<Node>>
|
||||
where I: Iterator<Item=Root<Node>>
|
||||
{
|
||||
nodes.find(|node| {
|
||||
not_in.iter().all(|n| {
|
||||
match n {
|
||||
&NodeOrString::eNode(ref n) => n != node,
|
||||
_ => true,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// If the given untrusted node address represents a valid DOM node in the given runtime,
|
||||
/// returns it.
|
||||
#[allow(unsafe_code)]
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
[ChildNode-before.html]
|
||||
type: testharness
|
||||
[Comment.before() with context object itself as the argument.]
|
||||
expected: FAIL
|
||||
|
||||
[Element.before() with context object itself as the argument.]
|
||||
expected: FAIL
|
||||
|
||||
[Text.before() with context object itself as the argument.]
|
||||
expected: FAIL
|
||||
|
|
@ -72,6 +72,16 @@ function test_after(child, nodeName, innerHTML) {
|
|||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.after() with context object itself as the argument.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div')
|
||||
var x = document.createElement('x');
|
||||
parent.appendChild(x);
|
||||
parent.appendChild(child);
|
||||
child.after(child, x);
|
||||
var expected = innerHTML + '<x></x>';
|
||||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.after() with context object itself and node as the arguments, switching positions.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div');
|
||||
var x = document.createElement('x');
|
||||
|
@ -85,6 +95,36 @@ function test_after(child, nodeName, innerHTML) {
|
|||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.after() with all siblings of child as arguments.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div')
|
||||
var x = document.createElement('x');
|
||||
var y = document.createElement('y');
|
||||
var z = document.createElement('z');
|
||||
parent.appendChild(child);
|
||||
parent.appendChild(x);
|
||||
parent.appendChild(y);
|
||||
parent.appendChild(z);
|
||||
child.after(x, y);
|
||||
var expected = innerHTML + '<x></x><y></y><z></z>';
|
||||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.before() with some siblings of child as arguments; no changes in tree; viable sibling is first child.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div')
|
||||
var v = document.createElement('v');
|
||||
var x = document.createElement('x');
|
||||
var y = document.createElement('y');
|
||||
var z = document.createElement('z');
|
||||
parent.appendChild(child);
|
||||
parent.appendChild(v);
|
||||
parent.appendChild(x);
|
||||
parent.appendChild(y);
|
||||
parent.appendChild(z);
|
||||
child.after(v, x);
|
||||
var expected = innerHTML + '<v></v><x></x><y></y><z></z>';
|
||||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.after() with some siblings of child as arguments; no changes in tree.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div');
|
||||
var x = document.createElement('x');
|
||||
|
|
|
@ -72,6 +72,16 @@ function test_before(child, nodeName, innerHTML) {
|
|||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.before() with context object itself as the argument.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div')
|
||||
var x = document.createElement('x');
|
||||
parent.appendChild(child);
|
||||
parent.appendChild(x);
|
||||
child.before(x, child);
|
||||
var expected = '<x></x>' + innerHTML;
|
||||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.before() with context object itself and node as the arguments, switching positions.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div');
|
||||
var x = document.createElement('x');
|
||||
|
@ -85,6 +95,36 @@ function test_before(child, nodeName, innerHTML) {
|
|||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.before() with all siblings of child as arguments.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div')
|
||||
var x = document.createElement('x');
|
||||
var y = document.createElement('y');
|
||||
var z = document.createElement('z');
|
||||
parent.appendChild(x);
|
||||
parent.appendChild(y);
|
||||
parent.appendChild(z);
|
||||
parent.appendChild(child);
|
||||
child.before(y, z);
|
||||
var expected = '<x></x><y></y><z></z>' + innerHTML;
|
||||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.before() with some siblings of child as arguments; no changes in tree; viable sibling is first child.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div')
|
||||
var v = document.createElement('v');
|
||||
var x = document.createElement('x');
|
||||
var y = document.createElement('y');
|
||||
var z = document.createElement('z');
|
||||
parent.appendChild(v);
|
||||
parent.appendChild(x);
|
||||
parent.appendChild(y);
|
||||
parent.appendChild(z);
|
||||
parent.appendChild(child);
|
||||
child.before(y, z);
|
||||
var expected = '<v></v><x></x><y></y><z></z>' + innerHTML;
|
||||
assert_equals(parent.innerHTML, expected);
|
||||
}, nodeName + '.before() with some siblings of child as arguments; no changes in tree.');
|
||||
|
||||
test(function() {
|
||||
var parent = document.createElement('div');
|
||||
var x = document.createElement('x');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue