Skip to content

Commit 323760f

Browse files
author
bors-servo
authored
Auto merge of servo#16933 - MortimerGoro:mutation_children, r=jdm
Implement MutationObserver childList mutations. <!-- Please describe your changes on the following line: --> Implement MutationObserver childList mutations --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16933) <!-- Reviewable:end -->
2 parents 05a26a2 + 0290e40 commit 323760f

File tree

6 files changed

+112
-119
lines changed

6 files changed

+112
-119
lines changed

components/script/dom/mutationobserver.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ pub struct MutationObserver {
2929
record_queue: DOMRefCell<Vec<Root<MutationRecord>>>,
3030
}
3131

32-
#[derive(Clone)]
33-
pub enum Mutation {
34-
Attribute { name: LocalName, namespace: Namespace, old_value: DOMString }
32+
pub enum Mutation<'a> {
33+
Attribute { name: LocalName, namespace: Namespace, old_value: DOMString },
34+
ChildList { added: Option<&'a [&'a Node]>, removed: Option<&'a [&'a Node]>,
35+
prev: Option<&'a Node>, next: Option<&'a Node> },
3536
}
3637

3738
#[derive(HeapSizeOf, JSTraceable)]
@@ -143,6 +144,12 @@ impl MutationObserver {
143144
interestedObservers.push((Root::from_ref(&*registered.observer),
144145
paired_string));
145146
}
147+
},
148+
Mutation::ChildList { .. } => {
149+
if !registered.options.child_list {
150+
continue;
151+
}
152+
interestedObservers.push((Root::from_ref(&*registered.observer), None));
146153
}
147154
}
148155
}
@@ -159,6 +166,9 @@ impl MutationObserver {
159166
None
160167
};
161168
MutationRecord::attribute_mutated(target, name, namespace, paired_string.clone())
169+
},
170+
Mutation::ChildList { ref added, ref removed, ref next, ref prev } => {
171+
MutationRecord::child_list_mutated(target, *added, *removed, *next, *prev)
162172
}
163173
};
164174
// Step 4.8

components/script/dom/mutationrecord.rs

+46-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding;
66
use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding::MutationRecordMethods;
7-
use dom::bindings::js::{JS, Root};
7+
use dom::bindings::js::{JS, MutNullableJS, Root};
88
use dom::bindings::reflector::{Reflector, reflect_dom_object};
99
use dom::bindings::str::DOMString;
1010
use dom::node::{Node, window_from_node};
@@ -20,6 +20,10 @@ pub struct MutationRecord {
2020
attribute_name: Option<DOMString>,
2121
attribute_namespace: Option<DOMString>,
2222
old_value: Option<DOMString>,
23+
added_nodes: MutNullableJS<NodeList>,
24+
removed_nodes: MutNullableJS<NodeList>,
25+
next_sibling: Option<JS<Node>>,
26+
prev_sibling: Option<JS<Node>>,
2327
}
2428

2529
impl MutationRecord {
@@ -32,22 +36,51 @@ impl MutationRecord {
3236
target,
3337
Some(DOMString::from(&**attribute_name)),
3438
attribute_namespace.map(|n| DOMString::from(&**n)),
35-
old_value);
39+
old_value,
40+
None, None, None, None);
3641
reflect_dom_object(record, &*window_from_node(target), MutationRecordBinding::Wrap)
3742
}
3843

44+
pub fn child_list_mutated(target: &Node,
45+
added_nodes: Option<&[&Node]>,
46+
removed_nodes: Option<&[&Node]>,
47+
next_sibling: Option<&Node>,
48+
prev_sibling: Option<&Node>) -> Root<MutationRecord> {
49+
let window = window_from_node(target);
50+
let added_nodes = added_nodes.map(|list| NodeList::new_simple_list_slice(&window, list));
51+
let removed_nodes = removed_nodes.map(|list| NodeList::new_simple_list_slice(&window, list));
52+
53+
reflect_dom_object(box MutationRecord::new_inherited("childList",
54+
target,
55+
None, None, None,
56+
added_nodes.as_ref().map(|list| &**list),
57+
removed_nodes.as_ref().map(|list| &**list),
58+
next_sibling,
59+
prev_sibling),
60+
&*window,
61+
MutationRecordBinding::Wrap)
62+
}
63+
3964
fn new_inherited(record_type: &str,
4065
target: &Node,
4166
attribute_name: Option<DOMString>,
4267
attribute_namespace: Option<DOMString>,
43-
old_value: Option<DOMString>) -> MutationRecord {
68+
old_value: Option<DOMString>,
69+
added_nodes: Option<&NodeList>,
70+
removed_nodes: Option<&NodeList>,
71+
next_sibling: Option<&Node>,
72+
prev_sibling: Option<&Node>) -> MutationRecord {
4473
MutationRecord {
4574
reflector_: Reflector::new(),
4675
record_type: DOMString::from(record_type),
4776
target: JS::from_ref(target),
4877
attribute_name: attribute_name,
4978
attribute_namespace: attribute_namespace,
5079
old_value: old_value,
80+
added_nodes: MutNullableJS::new(added_nodes),
81+
removed_nodes: MutNullableJS::new(removed_nodes),
82+
next_sibling: next_sibling.map(JS::from_ref),
83+
prev_sibling: prev_sibling.map(JS::from_ref),
5184
}
5285
}
5386
}
@@ -80,24 +113,28 @@ impl MutationRecordMethods for MutationRecord {
80113

81114
// https://dom.spec.whatwg.org/#dom-mutationrecord-addednodes
82115
fn AddedNodes(&self) -> Root<NodeList> {
83-
let window = window_from_node(&*self.target);
84-
NodeList::empty(&window)
116+
self.added_nodes.or_init(|| {
117+
let window = window_from_node(&*self.target);
118+
NodeList::empty(&window)
119+
})
85120
}
86121

87122
// https://dom.spec.whatwg.org/#dom-mutationrecord-removednodes
88123
fn RemovedNodes(&self) -> Root<NodeList> {
89-
let window = window_from_node(&*self.target);
90-
NodeList::empty(&window)
124+
self.removed_nodes.or_init(|| {
125+
let window = window_from_node(&*self.target);
126+
NodeList::empty(&window)
127+
})
91128
}
92129

93130
// https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling
94131
fn GetPreviousSibling(&self) -> Option<Root<Node>> {
95-
None
132+
self.prev_sibling.as_ref().map(|node| Root::from_ref(&**node))
96133
}
97134

98135
// https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling
99136
fn GetNextSibling(&self) -> Option<Root<Node>> {
100-
None
137+
self.next_sibling.as_ref().map(|node| Root::from_ref(&**node))
101138
}
102139

103140
}

components/script/dom/node.rs

+49-5
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use dom::htmllinkelement::HTMLLinkElement;
4747
use dom::htmlmetaelement::HTMLMetaElement;
4848
use dom::htmlstyleelement::HTMLStyleElement;
4949
use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
50-
use dom::mutationobserver::RegisteredObserver;
50+
use dom::mutationobserver::{Mutation, MutationObserver, RegisteredObserver};
5151
use dom::nodelist::NodeList;
5252
use dom::processinginstruction::ProcessingInstruction;
5353
use dom::range::WeakRangeVec;
@@ -1616,18 +1616,27 @@ impl Node {
16161616
let new_nodes = if let NodeTypeId::DocumentFragment = node.type_id() {
16171617
// Step 3.
16181618
new_nodes.extend(node.children().map(|kid| JS::from_ref(&*kid)));
1619-
// Step 4: mutation observers.
1620-
// Step 5.
1619+
// Step 4.
16211620
for kid in new_nodes.r() {
16221621
Node::remove(*kid, node, SuppressObserver::Suppressed);
16231622
}
1623+
// Step 5.
16241624
vtable_for(&node).children_changed(&ChildrenMutation::replace_all(new_nodes.r(), &[]));
1625+
1626+
let mutation = Mutation::ChildList {
1627+
added: None,
1628+
removed: Some(new_nodes.r()),
1629+
prev: None,
1630+
next: None,
1631+
};
1632+
MutationObserver::queue_a_mutation_record(&node, mutation);
1633+
16251634
new_nodes.r()
16261635
} else {
16271636
// Step 3.
16281637
ref_slice(&node)
16291638
};
1630-
// Step 6: mutation observers.
1639+
// Step 6.
16311640
let previous_sibling = match suppress_observers {
16321641
SuppressObserver::Unsuppressed => {
16331642
match child {
@@ -1646,6 +1655,14 @@ impl Node {
16461655
if let SuppressObserver::Unsuppressed = suppress_observers {
16471656
vtable_for(&parent).children_changed(
16481657
&ChildrenMutation::insert(previous_sibling.r(), new_nodes, child));
1658+
1659+
let mutation = Mutation::ChildList {
1660+
added: Some(new_nodes),
1661+
removed: None,
1662+
prev: previous_sibling.r(),
1663+
next: child,
1664+
};
1665+
MutationObserver::queue_a_mutation_record(&parent, mutation);
16491666
}
16501667
}
16511668

@@ -1677,9 +1694,19 @@ impl Node {
16771694
if let Some(node) = node {
16781695
Node::insert(node, parent, None, SuppressObserver::Suppressed);
16791696
}
1680-
// Step 6: mutation observers.
1697+
// Step 6.
16811698
vtable_for(&parent).children_changed(
16821699
&ChildrenMutation::replace_all(removed_nodes.r(), added_nodes));
1700+
1701+
if !removed_nodes.is_empty() || !added_nodes.is_empty() {
1702+
let mutation = Mutation::ChildList {
1703+
added: Some(added_nodes),
1704+
removed: Some(removed_nodes.r()),
1705+
prev: None,
1706+
next: None,
1707+
};
1708+
MutationObserver::queue_a_mutation_record(&parent, mutation);
1709+
}
16831710
}
16841711

16851712
// https://dom.spec.whatwg.org/#concept-node-pre-remove
@@ -1730,6 +1757,15 @@ impl Node {
17301757
&ChildrenMutation::replace(old_previous_sibling.r(),
17311758
&Some(&node), &[],
17321759
old_next_sibling.r()));
1760+
1761+
let removed = [node];
1762+
let mutation = Mutation::ChildList {
1763+
added: None,
1764+
removed: Some(&removed),
1765+
prev: old_previous_sibling.r(),
1766+
next: old_next_sibling.r(),
1767+
};
1768+
MutationObserver::queue_a_mutation_record(&parent, mutation);
17331769
}
17341770
}
17351771

@@ -2182,6 +2218,14 @@ impl NodeMethods for Node {
21822218
&ChildrenMutation::replace(previous_sibling.r(),
21832219
&removed_child, nodes,
21842220
reference_child));
2221+
let removed = removed_child.map(|r| [r]);
2222+
let mutation = Mutation::ChildList {
2223+
added: Some(nodes),
2224+
removed: removed.as_ref().map(|r| &r[..]),
2225+
prev: previous_sibling.r(),
2226+
next: reference_child,
2227+
};
2228+
MutationObserver::queue_a_mutation_record(&self, mutation);
21852229

21862230
// Step 15.
21872231
Ok(Root::from_ref(child))

components/script/dom/nodelist.rs

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ impl NodeList {
4747
NodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_ref(&*r)).collect()))
4848
}
4949

50+
pub fn new_simple_list_slice(window: &Window, slice: &[&Node]) -> Root<NodeList> {
51+
NodeList::new(window, NodeListType::Simple(slice.iter().map(|r| JS::from_ref(*r)).collect()))
52+
}
53+
5054
pub fn new_child_list(window: &Window, node: &Node) -> Root<NodeList> {
5155
NodeList::new(window, NodeListType::Children(ChildrenList::new(node)))
5256
}

tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini

-90
This file was deleted.

tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini

-12
This file was deleted.

0 commit comments

Comments
 (0)