mirror of
https://github.com/servo/servo.git
synced 2025-07-31 11:10:22 +01:00
Auto merge of #20689 - fabricedesre:mutation-take-records, r=emilio
MutationObserver api: Implement takeRecords() and characterData mutations <!-- Please describe your changes on the following line: --> I started by looking at adding support for `takeRecords()` to get fluent.js to work on Servo. To get the wpt tests to pass I ended up implementing character data mutations (most of the plumbing was already there) and this fixed a few more tests. We are still missing support for `disconnect()`, which I keep for a followup. --- <!-- 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 build-geckolib` 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/20689) <!-- Reviewable:end -->
This commit is contained in:
commit
2c9ef9e558
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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue