mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
MutationObserver api: Implement takeRecords() and characterData mutations
This commit is contained in:
parent
c5f7c9ccf3
commit
75434e6fce
9 changed files with 98 additions and 112 deletions
|
@ -185,7 +185,7 @@ impl Attr {
|
|||
let mutation = Mutation::Attribute {
|
||||
name: name.clone(),
|
||||
namespace: namespace.clone(),
|
||||
old_value: old_value.clone(),
|
||||
old_value: Some(old_value.clone()),
|
||||
};
|
||||
|
||||
MutationObserver::queue_a_mutation_record(owner.upcast::<Node>(), mutation);
|
||||
|
|
|
@ -17,6 +17,7 @@ use dom::bindings::str::DOMString;
|
|||
use dom::comment::Comment;
|
||||
use dom::document::Document;
|
||||
use dom::element::Element;
|
||||
use dom::mutationobserver::{Mutation, MutationObserver};
|
||||
use dom::node::{ChildrenMutation, Node, NodeDamage};
|
||||
use dom::processinginstruction::ProcessingInstruction;
|
||||
use dom::text::Text;
|
||||
|
@ -81,6 +82,14 @@ impl CharacterData {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Queue a MutationObserver record before changing the content.
|
||||
fn queue_mutation_record(&self) {
|
||||
let mutation = Mutation::CharacterData {
|
||||
old_value: self.data.borrow().clone(),
|
||||
};
|
||||
MutationObserver::queue_a_mutation_record(self.upcast::<Node>(), mutation);
|
||||
}
|
||||
}
|
||||
|
||||
impl CharacterDataMethods for CharacterData {
|
||||
|
@ -91,6 +100,7 @@ impl CharacterDataMethods for CharacterData {
|
|||
|
||||
// https://dom.spec.whatwg.org/#dom-characterdata-data
|
||||
fn SetData(&self, data: DOMString) {
|
||||
self.queue_mutation_record();
|
||||
let old_length = self.Length();
|
||||
let new_length = data.encode_utf16().count() as u32;
|
||||
*self.data.borrow_mut() = data;
|
||||
|
@ -193,6 +203,8 @@ impl CharacterDataMethods for CharacterData {
|
|||
}
|
||||
};
|
||||
// Step 4: Mutation observers.
|
||||
self.queue_mutation_record();
|
||||
|
||||
// Step 5 to 7.
|
||||
new_data = String::with_capacity(
|
||||
prefix.len() +
|
||||
|
|
|
@ -1163,16 +1163,16 @@ impl Element {
|
|||
pub fn push_attribute(&self, attr: &Attr) {
|
||||
let name = attr.local_name().clone();
|
||||
let namespace = attr.namespace().clone();
|
||||
let value = DOMString::from(&**attr.value());
|
||||
let mutation = Mutation::Attribute {
|
||||
name: name.clone(),
|
||||
namespace: namespace.clone(),
|
||||
old_value: value.clone(),
|
||||
old_value: None,
|
||||
};
|
||||
|
||||
MutationObserver::queue_a_mutation_record(&self.node, mutation);
|
||||
|
||||
if self.get_custom_element_definition().is_some() {
|
||||
let value = DOMString::from(&**attr.value());
|
||||
let reaction = CallbackReaction::AttributeChanged(name, None, Some(value), namespace);
|
||||
ScriptThread::enqueue_callback_reaction(self, reaction, None);
|
||||
}
|
||||
|
@ -1311,7 +1311,7 @@ impl Element {
|
|||
let mutation = Mutation::Attribute {
|
||||
name: name.clone(),
|
||||
namespace: namespace.clone(),
|
||||
old_value: old_value.clone(),
|
||||
old_value: Some(old_value.clone()),
|
||||
};
|
||||
|
||||
MutationObserver::queue_a_mutation_record(&self.node, mutation);
|
||||
|
|
|
@ -30,7 +30,8 @@ pub struct MutationObserver {
|
|||
}
|
||||
|
||||
pub enum Mutation<'a> {
|
||||
Attribute { name: LocalName, namespace: Namespace, old_value: DOMString },
|
||||
Attribute { name: LocalName, namespace: Namespace, old_value: Option<DOMString> },
|
||||
CharacterData { old_value: DOMString },
|
||||
ChildList { added: Option<&'a [&'a Node]>, removed: Option<&'a [&'a Node]>,
|
||||
prev: Option<&'a Node>, next: Option<&'a Node> },
|
||||
}
|
||||
|
@ -110,7 +111,8 @@ impl MutationObserver {
|
|||
return;
|
||||
}
|
||||
// Step 1
|
||||
let mut interestedObservers: Vec<(DomRoot<MutationObserver>, Option<DOMString>)> = vec![];
|
||||
let mut interested_observers: Vec<(DomRoot<MutationObserver>, Option<DOMString>)> = vec![];
|
||||
|
||||
// Step 2 & 3
|
||||
for node in target.inclusive_ancestors() {
|
||||
for registered in &*node.registered_mutation_observers() {
|
||||
|
@ -135,32 +137,52 @@ impl MutationObserver {
|
|||
}
|
||||
// Step 3.1.2
|
||||
let paired_string = if registered.options.attribute_old_value {
|
||||
old_value.clone()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Step 3.1.1
|
||||
let idx = interested_observers.iter().position(|&(ref o, _)|
|
||||
&**o as *const _ == &*registered.observer as *const _);
|
||||
if let Some(idx) = idx {
|
||||
interested_observers[idx].1 = paired_string;
|
||||
} else {
|
||||
interested_observers.push((DomRoot::from_ref(&*registered.observer),
|
||||
paired_string));
|
||||
}
|
||||
},
|
||||
Mutation::CharacterData { ref old_value } => {
|
||||
if !registered.options.character_data {
|
||||
continue;
|
||||
}
|
||||
// Step 3.1.2
|
||||
let paired_string = if registered.options.character_data_old_value {
|
||||
Some(old_value.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Step 3.1.1
|
||||
let idx = interestedObservers.iter().position(|&(ref o, _)|
|
||||
let idx = interested_observers.iter().position(|&(ref o, _)|
|
||||
&**o as *const _ == &*registered.observer as *const _);
|
||||
if let Some(idx) = idx {
|
||||
interestedObservers[idx].1 = paired_string;
|
||||
interested_observers[idx].1 = paired_string;
|
||||
} else {
|
||||
interestedObservers.push((DomRoot::from_ref(&*registered.observer),
|
||||
paired_string));
|
||||
interested_observers.push((DomRoot::from_ref(&*registered.observer),
|
||||
paired_string));
|
||||
}
|
||||
},
|
||||
Mutation::ChildList { .. } => {
|
||||
if !registered.options.child_list {
|
||||
continue;
|
||||
}
|
||||
interestedObservers.push((DomRoot::from_ref(&*registered.observer), None));
|
||||
interested_observers.push((DomRoot::from_ref(&*registered.observer), None));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4
|
||||
for &(ref observer, ref paired_string) in &interestedObservers {
|
||||
for &(ref observer, ref paired_string) in &interested_observers {
|
||||
// Steps 4.1-4.7
|
||||
let record = match attr_type {
|
||||
Mutation::Attribute { ref name, ref namespace, .. } => {
|
||||
|
@ -171,6 +193,9 @@ impl MutationObserver {
|
|||
};
|
||||
MutationRecord::attribute_mutated(target, name, namespace, paired_string.clone())
|
||||
},
|
||||
Mutation::CharacterData { .. } => {
|
||||
MutationRecord::character_data_mutated(target, paired_string.clone())
|
||||
}
|
||||
Mutation::ChildList { ref added, ref removed, ref next, ref prev } => {
|
||||
MutationRecord::child_list_mutated(target, *added, *removed, *next, *prev)
|
||||
}
|
||||
|
@ -265,4 +290,11 @@ impl MutationObserverMethods for MutationObserver {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// https://dom.spec.whatwg.org/#dom-mutationobserver-takerecords
|
||||
fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
|
||||
let records: Vec<DomRoot<MutationRecord>> = self.record_queue.borrow().clone();
|
||||
self.record_queue.borrow_mut().clear();
|
||||
records
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,12 @@ pub struct MutationRecord {
|
|||
|
||||
impl MutationRecord {
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn attribute_mutated(target: &Node,
|
||||
attribute_name: &LocalName,
|
||||
attribute_namespace: Option<&Namespace>,
|
||||
old_value: Option<DOMString>) -> DomRoot<MutationRecord> {
|
||||
pub fn attribute_mutated(
|
||||
target: &Node,
|
||||
attribute_name: &LocalName,
|
||||
attribute_namespace: Option<&Namespace>,
|
||||
old_value: Option<DOMString>,
|
||||
) -> DomRoot<MutationRecord> {
|
||||
let record = Box::new(MutationRecord::new_inherited(
|
||||
"attributes",
|
||||
target,
|
||||
|
@ -43,11 +45,30 @@ impl MutationRecord {
|
|||
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>) -> DomRoot<MutationRecord> {
|
||||
pub fn character_data_mutated(
|
||||
target: &Node,
|
||||
old_value: Option<DOMString>,
|
||||
) -> DomRoot<MutationRecord> {
|
||||
reflect_dom_object(
|
||||
Box::new(MutationRecord::new_inherited(
|
||||
"characterData",
|
||||
target,
|
||||
None, None,
|
||||
old_value,
|
||||
None, None, None, None
|
||||
)),
|
||||
&*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>,
|
||||
) -> DomRoot<MutationRecord> {
|
||||
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));
|
||||
|
@ -67,15 +88,17 @@ impl MutationRecord {
|
|||
)
|
||||
}
|
||||
|
||||
fn new_inherited(record_type: &str,
|
||||
target: &Node,
|
||||
attribute_name: Option<DOMString>,
|
||||
attribute_namespace: Option<DOMString>,
|
||||
old_value: Option<DOMString>,
|
||||
added_nodes: Option<&NodeList>,
|
||||
removed_nodes: Option<&NodeList>,
|
||||
next_sibling: Option<&Node>,
|
||||
prev_sibling: Option<&Node>) -> MutationRecord {
|
||||
fn new_inherited(
|
||||
record_type: &str,
|
||||
target: &Node,
|
||||
attribute_name: Option<DOMString>,
|
||||
attribute_namespace: Option<DOMString>,
|
||||
old_value: Option<DOMString>,
|
||||
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),
|
||||
|
|
|
@ -12,7 +12,7 @@ interface MutationObserver {
|
|||
[Throws]
|
||||
void observe(Node target, optional MutationObserverInit options);
|
||||
//void disconnect();
|
||||
//sequence<MutationRecord> takeRecords();
|
||||
sequence<MutationRecord> takeRecords();
|
||||
};
|
||||
|
||||
callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer);
|
||||
|
|
|
@ -1,28 +1,5 @@
|
|||
[MutationObserver-attributes.html]
|
||||
type: testharness
|
||||
[attributes Element.id: same value mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes Element.className: new value mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes Element.classList.add: single token addition mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes Element.classList.add: multiple tokens addition mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes Element.setAttribute: classname mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes Element.setAttributeNS: creation mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes Element.setAttributeNS: prefixed attribute creation mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes Element.className: empty string update mutation]
|
||||
expected: FAIL
|
||||
|
||||
[attributes/attributeFilter Element.id/Element.className: update mutation]
|
||||
expected: FAIL
|
||||
|
@ -32,4 +9,3 @@
|
|||
|
||||
[attributeFilter alone Element.id/Element.className: multiple filter update mutation]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
[MutationObserver-characterData.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[characterData Text.data: simple mutation without oldValue]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.data: simple mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.appendData: simple mutation]
|
||||
expected: TIMEOUT
|
||||
|
@ -15,46 +10,3 @@
|
|||
|
||||
[characterData Text.appendData: null string mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.insertData: simple mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.insertData: empty string mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.insertData: null string mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.deleteData: simple mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.deleteData: empty mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.replaceData: simple mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Text.replaceData: empty mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData ProcessingInstruction: data mutations]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Comment: data mutations]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Range.deleteContents: child and data removal mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Range.deleteContents: child and data removal mutation (2)]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Range.extractContents: child and data removal mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData Range.extractContents: child and data removal mutation (2)]
|
||||
expected: TIMEOUT
|
||||
|
||||
[characterData/characterDataOldValue alone Text.data: simple mutation]
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,11 +1,2 @@
|
|||
[MutationObserver-takeRecords.html]
|
||||
type: testharness
|
||||
[unreachabled test]
|
||||
expected: FAIL
|
||||
|
||||
[All records present]
|
||||
expected: FAIL
|
||||
|
||||
[No more records present]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue