script/dom/mutationobserver/
mutationobserver.rs1use std::cell::LazyCell;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use html5ever::{LocalName, Namespace, ns};
11use js::rust::HandleObject;
12use script_bindings::cell::DomRefCell;
13use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
14
15use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserver_Binding::MutationObserverMethods;
16use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::{
17 MutationCallback, MutationObserverInit,
18};
19use crate::dom::bindings::error::{Error, Fallible};
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::root::{Dom, DomRoot};
22use crate::dom::bindings::str::DOMString;
23use crate::dom::iterators::ShadowIncluding;
24use crate::dom::mutationrecord::MutationRecord;
25use crate::dom::node::Node;
26use crate::dom::window::Window;
27use crate::script_runtime::CanGc;
28use crate::script_thread::ScriptThread;
29
30#[dom_struct]
31pub(crate) struct MutationObserver {
32 reflector_: Reflector,
33 #[conditional_malloc_size_of]
34 callback: Rc<MutationCallback>,
35 record_queue: DomRefCell<Vec<Dom<MutationRecord>>>,
36 node_list: DomRefCell<Vec<Dom<Node>>>,
37}
38
39pub(crate) enum Mutation<'a> {
40 Attribute {
41 name: LocalName,
42 namespace: Namespace,
43 old_value: Option<DOMString>,
44 },
45 CharacterData {
46 old_value: String,
47 },
48 ChildList {
49 added: Option<&'a [&'a Node]>,
50 removed: Option<&'a [&'a Node]>,
51 prev: Option<&'a Node>,
52 next: Option<&'a Node>,
53 },
54}
55
56#[derive(JSTraceable, MallocSizeOf)]
57#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
58pub(crate) struct RegisteredObserver {
59 pub(crate) observer: Dom<MutationObserver>,
60 options: ObserverOptions,
61}
62
63#[derive(JSTraceable, MallocSizeOf)]
64pub(crate) struct ObserverOptions {
65 attribute_old_value: bool,
66 attributes: bool,
67 character_data: bool,
68 character_data_old_value: bool,
69 child_list: bool,
70 subtree: bool,
71 attribute_filter: Vec<DOMString>,
72}
73
74impl MutationObserver {
75 fn new_with_proto(
76 global: &Window,
77 proto: Option<HandleObject>,
78 callback: Rc<MutationCallback>,
79 can_gc: CanGc,
80 ) -> DomRoot<MutationObserver> {
81 let boxed_observer = Box::new(MutationObserver::new_inherited(callback));
82 reflect_dom_object_with_proto(boxed_observer, global, proto, can_gc)
83 }
84
85 fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
86 MutationObserver {
87 reflector_: Reflector::new(),
88 callback,
89 record_queue: DomRefCell::new(vec![]),
90 node_list: DomRefCell::new(vec![]),
91 }
92 }
93
94 pub(crate) fn record_queue(&self) -> &DomRefCell<Vec<Dom<MutationRecord>>> {
95 &self.record_queue
96 }
97
98 pub(crate) fn callback(&self) -> &Rc<MutationCallback> {
99 &self.callback
100 }
101
102 pub(crate) fn queue_a_mutation_record<'a, F>(
104 target: &Node,
105 attr_type: LazyCell<Mutation<'a>, F>,
106 ) where
107 F: FnOnce() -> Mutation<'a>,
108 {
109 if !target.global().as_window().get_exists_mut_observer() {
110 return;
111 }
112 let mut interested_observers: HashMap<DomRoot<MutationObserver>, Option<DOMString>> =
114 HashMap::new();
115
116 for node in target.inclusive_ancestors(ShadowIncluding::No) {
119 let registered = node.registered_mutation_observers();
120 if registered.is_none() {
121 continue;
122 }
123
124 for registered in &*registered.unwrap() {
126 if &*node != target && !registered.options.subtree {
128 continue;
129 }
130
131 match *attr_type {
132 Mutation::Attribute {
134 ref name,
135 ref namespace,
136 ref old_value,
137 } => {
138 if !registered.options.attributes {
140 continue;
141 }
142 if !registered.options.attribute_filter.is_empty() {
145 if *namespace != ns!() {
146 continue;
147 }
148 if !registered
149 .options
150 .attribute_filter
151 .iter()
152 .any(|s| *s == **name)
153 {
154 continue;
155 }
156 }
157 let mo = registered.observer.as_rooted();
159 if registered.options.attribute_old_value {
161 interested_observers.insert(mo, old_value.clone());
163 } else {
164 interested_observers.entry(mo).or_insert(None);
166 }
167 },
168 Mutation::CharacterData { ref old_value } => {
170 if !registered.options.character_data {
172 continue;
173 }
174 let mo = registered.observer.as_rooted();
176 if registered.options.character_data_old_value {
177 interested_observers
179 .insert(mo, Some(DOMString::from(old_value.clone())));
180 } else {
181 interested_observers.entry(mo).or_insert(None);
183 }
184 },
185 Mutation::ChildList { .. } => {
187 if !registered.options.child_list {
189 continue;
190 }
191 let mo = registered.observer.as_rooted();
193 interested_observers.entry(mo).or_insert(None);
195 },
196 }
197 }
198 }
199
200 for (observer, mapped_old_value) in interested_observers {
202 let record = match *attr_type {
204 Mutation::Attribute {
205 ref name,
206 ref namespace,
207 ..
208 } => {
209 let namespace = if *namespace != ns!() {
210 Some(namespace)
211 } else {
212 None
213 };
214 MutationRecord::attribute_mutated(
215 target,
216 name,
217 namespace,
218 mapped_old_value,
219 CanGc::deprecated_note(),
220 )
221 },
222 Mutation::CharacterData { .. } => MutationRecord::character_data_mutated(
223 target,
224 mapped_old_value,
225 CanGc::deprecated_note(),
226 ),
227 Mutation::ChildList {
228 ref added,
229 ref removed,
230 ref next,
231 ref prev,
232 } => MutationRecord::child_list_mutated(
233 target,
234 *added,
235 *removed,
236 *next,
237 *prev,
238 CanGc::deprecated_note(),
239 ),
240 };
241 observer
243 .record_queue
244 .borrow_mut()
245 .push(Dom::from_ref(&*record));
246 ScriptThread::mutation_observers().add_mutation_observer(&observer);
248 }
249
250 let mutation_observers = ScriptThread::mutation_observers();
252 mutation_observers.queue_mutation_observer_microtask(ScriptThread::microtask_queue());
253 }
254}
255
256impl MutationObserverMethods<crate::DomTypeHolder> for MutationObserver {
257 fn Constructor(
259 global: &Window,
260 proto: Option<HandleObject>,
261 can_gc: CanGc,
262 callback: Rc<MutationCallback>,
263 ) -> Fallible<DomRoot<MutationObserver>> {
264 global.set_exists_mut_observer();
265 let observer = MutationObserver::new_with_proto(global, proto, callback, can_gc);
266 ScriptThread::mutation_observers().add_mutation_observer(&observer);
267 Ok(observer)
268 }
269
270 fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> {
272 let attribute_filter = options.attributeFilter.clone().unwrap_or_default();
273 let attribute_old_value = options.attributeOldValue.unwrap_or(false);
274 let mut attributes = options.attributes.unwrap_or(false);
275 let mut character_data = options.characterData.unwrap_or(false);
276 let character_data_old_value = options.characterDataOldValue.unwrap_or(false);
277 let child_list = options.childList;
278 let subtree = options.subtree;
279
280 if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) &&
282 options.attributes.is_none()
283 {
284 attributes = true;
285 }
286
287 if options.characterDataOldValue.is_some() && options.characterData.is_none() {
289 character_data = true;
290 }
291
292 if !child_list && !attributes && !character_data {
294 return Err(Error::Type(
295 c"One of childList, attributes, or characterData must be true".into(),
296 ));
297 }
298
299 if attribute_old_value && !attributes {
301 return Err(Error::Type(
302 c"attributeOldValue is true but attributes is false".into(),
303 ));
304 }
305
306 if options.attributeFilter.is_some() && !attributes {
308 return Err(Error::Type(
309 c"attributeFilter is present but attributes is false".into(),
310 ));
311 }
312
313 if character_data_old_value && !character_data {
315 return Err(Error::Type(
316 c"characterDataOldValue is true but characterData is false".into(),
317 ));
318 }
319
320 let add_new_observer = {
322 let mut replaced = false;
323 for registered in &mut *target.registered_mutation_observers_mut() {
324 if !std::ptr::eq(&*registered.observer, self) {
325 continue;
326 }
327 registered.options.attribute_old_value = attribute_old_value;
329 registered.options.attributes = attributes;
330 registered.options.character_data = character_data;
331 registered.options.character_data_old_value = character_data_old_value;
332 registered.options.child_list = child_list;
333 registered.options.subtree = subtree;
334 registered
335 .options
336 .attribute_filter
337 .clone_from(&attribute_filter);
338 replaced = true;
339 }
340 !replaced
341 };
342
343 if add_new_observer {
345 target.add_mutation_observer(RegisteredObserver {
346 observer: Dom::from_ref(self),
347 options: ObserverOptions {
348 attributes,
349 attribute_old_value,
350 character_data,
351 character_data_old_value,
352 subtree,
353 attribute_filter,
354 child_list,
355 },
356 });
357
358 self.node_list.borrow_mut().push(Dom::from_ref(target));
359 }
360
361 Ok(())
362 }
363
364 fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
366 let records: Vec<DomRoot<MutationRecord>> = self
367 .record_queue
368 .borrow()
369 .iter()
370 .map(|record| record.as_rooted())
371 .collect();
372 self.record_queue.borrow_mut().clear();
373 records
374 }
375
376 fn Disconnect(&self) {
378 let nodes = self
380 .node_list
381 .borrow()
382 .iter()
383 .map(|node| node.as_rooted())
384 .collect::<Vec<_>>();
385 self.node_list.borrow_mut().clear();
386
387 for node in nodes {
388 node.remove_mutation_observer(self);
389 }
390
391 self.record_queue.borrow_mut().clear();
393 }
394}