diff --git a/components/script/dom/mutationobserver.rs b/components/script/dom/mutationobserver.rs index 48c8fe24b87..84d34af0149 100644 --- a/components/script/dom/mutationobserver.rs +++ b/components/script/dom/mutationobserver.rs @@ -29,9 +29,10 @@ pub struct MutationObserver { record_queue: DOMRefCell>>, } -#[derive(Clone)] -pub enum Mutation { - Attribute { name: LocalName, namespace: Namespace, old_value: DOMString } +pub enum Mutation<'a> { + Attribute { name: LocalName, namespace: Namespace, old_value: DOMString }, + ChildList { added: Option<&'a [&'a Node]>, removed: Option<&'a [&'a Node]>, + prev: Option<&'a Node>, next: Option<&'a Node> }, } #[derive(HeapSizeOf, JSTraceable)] @@ -143,6 +144,12 @@ impl MutationObserver { interestedObservers.push((Root::from_ref(&*registered.observer), paired_string)); } + }, + Mutation::ChildList { .. } => { + if !registered.options.child_list { + continue; + } + interestedObservers.push((Root::from_ref(&*registered.observer), None)); } } } @@ -159,6 +166,9 @@ impl MutationObserver { None }; MutationRecord::attribute_mutated(target, name, namespace, paired_string.clone()) + }, + Mutation::ChildList { ref added, ref removed, ref next, ref prev } => { + MutationRecord::child_list_mutated(target, *added, *removed, *next, *prev) } }; // Step 4.8 diff --git a/components/script/dom/mutationrecord.rs b/components/script/dom/mutationrecord.rs index 439f4ae02b5..00d96e258c4 100644 --- a/components/script/dom/mutationrecord.rs +++ b/components/script/dom/mutationrecord.rs @@ -4,7 +4,7 @@ use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding; use dom::bindings::codegen::Bindings::MutationRecordBinding::MutationRecordBinding::MutationRecordMethods; -use dom::bindings::js::{JS, Root}; +use dom::bindings::js::{JS, MutNullableJS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::node::{Node, window_from_node}; @@ -20,6 +20,10 @@ pub struct MutationRecord { attribute_name: Option, attribute_namespace: Option, old_value: Option, + added_nodes: MutNullableJS, + removed_nodes: MutNullableJS, + next_sibling: Option>, + prev_sibling: Option>, } impl MutationRecord { @@ -32,15 +36,40 @@ impl MutationRecord { target, Some(DOMString::from(&**attribute_name)), attribute_namespace.map(|n| DOMString::from(&**n)), - old_value); + old_value, + None, None, None, None); reflect_dom_object(record, &*window_from_node(target), MutationRecordBinding::Wrap) } + pub fn child_list_mutated(target: &Node, + added_nodes: Option<&[&Node]>, + removed_nodes: Option<&[&Node]>, + next_sibling: Option<&Node>, + prev_sibling: Option<&Node>) -> Root { + let window = window_from_node(target); + let added_nodes = added_nodes.map(|list| NodeList::new_simple_list_slice(&window, list)); + let removed_nodes = removed_nodes.map(|list| NodeList::new_simple_list_slice(&window, list)); + + reflect_dom_object(box MutationRecord::new_inherited("childList", + target, + None, None, None, + added_nodes.as_ref().map(|list| &**list), + removed_nodes.as_ref().map(|list| &**list), + next_sibling, + prev_sibling), + &*window, + MutationRecordBinding::Wrap) + } + fn new_inherited(record_type: &str, target: &Node, attribute_name: Option, attribute_namespace: Option, - old_value: Option) -> MutationRecord { + old_value: Option, + added_nodes: Option<&NodeList>, + removed_nodes: Option<&NodeList>, + next_sibling: Option<&Node>, + prev_sibling: Option<&Node>) -> MutationRecord { MutationRecord { reflector_: Reflector::new(), record_type: DOMString::from(record_type), @@ -48,6 +77,10 @@ impl MutationRecord { attribute_name: attribute_name, attribute_namespace: attribute_namespace, old_value: old_value, + added_nodes: MutNullableJS::new(added_nodes), + removed_nodes: MutNullableJS::new(removed_nodes), + next_sibling: next_sibling.map(JS::from_ref), + prev_sibling: prev_sibling.map(JS::from_ref), } } } @@ -80,24 +113,28 @@ impl MutationRecordMethods for MutationRecord { // https://dom.spec.whatwg.org/#dom-mutationrecord-addednodes fn AddedNodes(&self) -> Root { - let window = window_from_node(&*self.target); - NodeList::empty(&window) + self.added_nodes.or_init(|| { + let window = window_from_node(&*self.target); + NodeList::empty(&window) + }) } // https://dom.spec.whatwg.org/#dom-mutationrecord-removednodes fn RemovedNodes(&self) -> Root { - let window = window_from_node(&*self.target); - NodeList::empty(&window) + self.removed_nodes.or_init(|| { + let window = window_from_node(&*self.target); + NodeList::empty(&window) + }) } // https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling fn GetPreviousSibling(&self) -> Option> { - None + self.prev_sibling.as_ref().map(|node| Root::from_ref(&**node)) } // https://dom.spec.whatwg.org/#dom-mutationrecord-previoussibling fn GetNextSibling(&self) -> Option> { - None + self.next_sibling.as_ref().map(|node| Root::from_ref(&**node)) } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 1ad28974f3d..ff3e5d64787 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -47,7 +47,7 @@ use dom::htmllinkelement::HTMLLinkElement; use dom::htmlmetaelement::HTMLMetaElement; use dom::htmlstyleelement::HTMLStyleElement; use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers}; -use dom::mutationobserver::RegisteredObserver; +use dom::mutationobserver::{Mutation, MutationObserver, RegisteredObserver}; use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::range::WeakRangeVec; @@ -1616,18 +1616,27 @@ impl Node { let new_nodes = if let NodeTypeId::DocumentFragment = node.type_id() { // Step 3. new_nodes.extend(node.children().map(|kid| JS::from_ref(&*kid))); - // Step 4: mutation observers. - // Step 5. + // Step 4. for kid in new_nodes.r() { Node::remove(*kid, node, SuppressObserver::Suppressed); } + // Step 5. vtable_for(&node).children_changed(&ChildrenMutation::replace_all(new_nodes.r(), &[])); + + let mutation = Mutation::ChildList { + added: None, + removed: Some(new_nodes.r()), + prev: None, + next: None, + }; + MutationObserver::queue_a_mutation_record(&node, mutation); + new_nodes.r() } else { // Step 3. ref_slice(&node) }; - // Step 6: mutation observers. + // Step 6. let previous_sibling = match suppress_observers { SuppressObserver::Unsuppressed => { match child { @@ -1646,6 +1655,14 @@ impl Node { if let SuppressObserver::Unsuppressed = suppress_observers { vtable_for(&parent).children_changed( &ChildrenMutation::insert(previous_sibling.r(), new_nodes, child)); + + let mutation = Mutation::ChildList { + added: Some(new_nodes), + removed: None, + prev: previous_sibling.r(), + next: child, + }; + MutationObserver::queue_a_mutation_record(&parent, mutation); } } @@ -1677,9 +1694,19 @@ impl Node { if let Some(node) = node { Node::insert(node, parent, None, SuppressObserver::Suppressed); } - // Step 6: mutation observers. + // Step 6. vtable_for(&parent).children_changed( &ChildrenMutation::replace_all(removed_nodes.r(), added_nodes)); + + if !removed_nodes.is_empty() || !added_nodes.is_empty() { + let mutation = Mutation::ChildList { + added: Some(added_nodes), + removed: Some(removed_nodes.r()), + prev: None, + next: None, + }; + MutationObserver::queue_a_mutation_record(&parent, mutation); + } } // https://dom.spec.whatwg.org/#concept-node-pre-remove @@ -1730,6 +1757,15 @@ impl Node { &ChildrenMutation::replace(old_previous_sibling.r(), &Some(&node), &[], old_next_sibling.r())); + + let removed = [node]; + let mutation = Mutation::ChildList { + added: None, + removed: Some(&removed), + prev: old_previous_sibling.r(), + next: old_next_sibling.r(), + }; + MutationObserver::queue_a_mutation_record(&parent, mutation); } } @@ -2182,6 +2218,14 @@ impl NodeMethods for Node { &ChildrenMutation::replace(previous_sibling.r(), &removed_child, nodes, reference_child)); + let removed = removed_child.map(|r| [r]); + let mutation = Mutation::ChildList { + added: Some(nodes), + removed: removed.as_ref().map(|r| &r[..]), + prev: previous_sibling.r(), + next: reference_child, + }; + MutationObserver::queue_a_mutation_record(&self, mutation); // Step 15. Ok(Root::from_ref(child)) diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index fae181f7d81..39b4cc1c41e 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -47,6 +47,10 @@ impl NodeList { NodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_ref(&*r)).collect())) } + pub fn new_simple_list_slice(window: &Window, slice: &[&Node]) -> Root { + NodeList::new(window, NodeListType::Simple(slice.iter().map(|r| JS::from_ref(*r)).collect())) + } + pub fn new_child_list(window: &Window, node: &Node) -> Root { NodeList::new(window, NodeListType::Children(ChildrenList::new(node))) } diff --git a/tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini b/tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini deleted file mode 100644 index 91a99722ec5..00000000000 --- a/tests/wpt/metadata/dom/nodes/MutationObserver-childList.html.ini +++ /dev/null @@ -1,90 +0,0 @@ -[MutationObserver-childList.html] - type: testharness - expected: TIMEOUT - [childList Node.textContent: replace content mutation] - expected: TIMEOUT - - [childList Node.textContent: no previous content mutation] - expected: TIMEOUT - - [childList Node.textContent: empty string mutation] - expected: TIMEOUT - - [childList Node.normalize mutation] - expected: TIMEOUT - - [childList Node.normalize mutations] - expected: TIMEOUT - - [childList Node.insertBefore: addition mutation] - expected: TIMEOUT - - [childList Node.insertBefore: removal mutation] - expected: TIMEOUT - - [childList Node.insertBefore: removal and addition mutations] - expected: TIMEOUT - - [childList Node.insertBefore: fragment addition mutations] - expected: TIMEOUT - - [childList Node.insertBefore: fragment removal mutations] - expected: TIMEOUT - - [childList Node.insertBefore: last child addition mutation] - expected: TIMEOUT - - [childList Node.appendChild: addition mutation] - expected: TIMEOUT - - [childList Node.appendChild: removal mutation] - expected: TIMEOUT - - [childList Node.appendChild: removal and addition mutations] - expected: TIMEOUT - - [childList Node.appendChild: fragment addition mutations] - expected: TIMEOUT - - [childList Node.appendChild: fragment removal mutations] - expected: TIMEOUT - - [childList Node.appendChild: addition outside document tree mutation] - expected: TIMEOUT - - [childList Node.replaceChild: replacement mutation] - expected: TIMEOUT - - [childList Node.replaceChild: removal mutation] - expected: TIMEOUT - - [childList Node.replaceChild: internal replacement mutation] - expected: TIMEOUT - - [childList Node.removeChild: removal mutation] - expected: TIMEOUT - - [childList Range.deleteContents: child removal mutation] - expected: TIMEOUT - - [childList Range.deleteContents: child and data removal mutation] - expected: TIMEOUT - - [childList Range.extractContents: child removal mutation] - expected: TIMEOUT - - [childList Range.extractContents: child and data removal mutation] - expected: TIMEOUT - - [childList Range.insertNode: child insertion mutation] - expected: TIMEOUT - - [childList Range.insertNode: children insertion mutation] - expected: TIMEOUT - - [childList Range.surroundContents: children removal and addition mutation] - expected: TIMEOUT - - [childList Node.replaceChild: self internal replacement mutation] - expected: TIMEOUT - diff --git a/tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini b/tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini deleted file mode 100644 index 5c86ae99f38..00000000000 --- a/tests/wpt/metadata/dom/nodes/MutationObserver-inner-outer.html.ini +++ /dev/null @@ -1,12 +0,0 @@ -[MutationObserver-inner-outer.html] - type: testharness - expected: TIMEOUT - [innerHTML mutation] - expected: FAIL - - [innerHTML with 2 children mutation] - expected: TIMEOUT - - [outerHTML mutation] - expected: TIMEOUT -