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::mutationrecord::MutationRecord;
24use crate::dom::node::{Node, ShadowIncluding};
25use crate::dom::window::Window;
26use crate::script_runtime::CanGc;
27use crate::script_thread::ScriptThread;
28
29#[dom_struct]
30pub(crate) struct MutationObserver {
31 reflector_: Reflector,
32 #[conditional_malloc_size_of]
33 callback: Rc<MutationCallback>,
34 record_queue: DomRefCell<Vec<Dom<MutationRecord>>>,
35 node_list: DomRefCell<Vec<Dom<Node>>>,
36}
37
38pub(crate) enum Mutation<'a> {
39 Attribute {
40 name: LocalName,
41 namespace: Namespace,
42 old_value: Option<DOMString>,
43 },
44 CharacterData {
45 old_value: String,
46 },
47 ChildList {
48 added: Option<&'a [&'a Node]>,
49 removed: Option<&'a [&'a Node]>,
50 prev: Option<&'a Node>,
51 next: Option<&'a Node>,
52 },
53}
54
55#[derive(JSTraceable, MallocSizeOf)]
56#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
57pub(crate) struct RegisteredObserver {
58 pub(crate) observer: Dom<MutationObserver>,
59 options: ObserverOptions,
60}
61
62#[derive(JSTraceable, MallocSizeOf)]
63pub(crate) struct ObserverOptions {
64 attribute_old_value: bool,
65 attributes: bool,
66 character_data: bool,
67 character_data_old_value: bool,
68 child_list: bool,
69 subtree: bool,
70 attribute_filter: Vec<DOMString>,
71}
72
73impl MutationObserver {
74 fn new_with_proto(
75 global: &Window,
76 proto: Option<HandleObject>,
77 callback: Rc<MutationCallback>,
78 can_gc: CanGc,
79 ) -> DomRoot<MutationObserver> {
80 let boxed_observer = Box::new(MutationObserver::new_inherited(callback));
81 reflect_dom_object_with_proto(boxed_observer, global, proto, can_gc)
82 }
83
84 fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
85 MutationObserver {
86 reflector_: Reflector::new(),
87 callback,
88 record_queue: DomRefCell::new(vec![]),
89 node_list: DomRefCell::new(vec![]),
90 }
91 }
92
93 pub(crate) fn record_queue(&self) -> &DomRefCell<Vec<Dom<MutationRecord>>> {
94 &self.record_queue
95 }
96
97 pub(crate) fn callback(&self) -> &Rc<MutationCallback> {
98 &self.callback
99 }
100
101 pub(crate) fn queue_a_mutation_record<'a, F>(
103 target: &Node,
104 attr_type: LazyCell<Mutation<'a>, F>,
105 ) where
106 F: FnOnce() -> Mutation<'a>,
107 {
108 if !target.global().as_window().get_exists_mut_observer() {
109 return;
110 }
111 let mut interested_observers: HashMap<DomRoot<MutationObserver>, Option<DOMString>> =
113 HashMap::new();
114
115 for node in target.inclusive_ancestors(ShadowIncluding::No) {
118 let registered = node.registered_mutation_observers();
119 if registered.is_none() {
120 continue;
121 }
122
123 for registered in &*registered.unwrap() {
125 if &*node != target && !registered.options.subtree {
127 continue;
128 }
129
130 match *attr_type {
131 Mutation::Attribute {
133 ref name,
134 ref namespace,
135 ref old_value,
136 } => {
137 if !registered.options.attributes {
139 continue;
140 }
141 if !registered.options.attribute_filter.is_empty() {
144 if *namespace != ns!() {
145 continue;
146 }
147 if !registered
148 .options
149 .attribute_filter
150 .iter()
151 .any(|s| *s == **name)
152 {
153 continue;
154 }
155 }
156 let mo = registered.observer.as_rooted();
158 if registered.options.attribute_old_value {
160 interested_observers.insert(mo, old_value.clone());
162 } else {
163 interested_observers.entry(mo).or_insert(None);
165 }
166 },
167 Mutation::CharacterData { ref old_value } => {
169 if !registered.options.character_data {
171 continue;
172 }
173 let mo = registered.observer.as_rooted();
175 if registered.options.character_data_old_value {
176 interested_observers
178 .insert(mo, Some(DOMString::from(old_value.clone())));
179 } else {
180 interested_observers.entry(mo).or_insert(None);
182 }
183 },
184 Mutation::ChildList { .. } => {
186 if !registered.options.child_list {
188 continue;
189 }
190 let mo = registered.observer.as_rooted();
192 interested_observers.entry(mo).or_insert(None);
194 },
195 }
196 }
197 }
198
199 for (observer, mapped_old_value) in interested_observers {
201 let record = match *attr_type {
203 Mutation::Attribute {
204 ref name,
205 ref namespace,
206 ..
207 } => {
208 let namespace = if *namespace != ns!() {
209 Some(namespace)
210 } else {
211 None
212 };
213 MutationRecord::attribute_mutated(
214 target,
215 name,
216 namespace,
217 mapped_old_value,
218 CanGc::deprecated_note(),
219 )
220 },
221 Mutation::CharacterData { .. } => MutationRecord::character_data_mutated(
222 target,
223 mapped_old_value,
224 CanGc::deprecated_note(),
225 ),
226 Mutation::ChildList {
227 ref added,
228 ref removed,
229 ref next,
230 ref prev,
231 } => MutationRecord::child_list_mutated(
232 target,
233 *added,
234 *removed,
235 *next,
236 *prev,
237 CanGc::deprecated_note(),
238 ),
239 };
240 observer
242 .record_queue
243 .borrow_mut()
244 .push(Dom::from_ref(&*record));
245 ScriptThread::mutation_observers().add_mutation_observer(&observer);
247 }
248
249 let mutation_observers = ScriptThread::mutation_observers();
251 mutation_observers.queue_mutation_observer_microtask(ScriptThread::microtask_queue());
252 }
253}
254
255impl MutationObserverMethods<crate::DomTypeHolder> for MutationObserver {
256 fn Constructor(
258 global: &Window,
259 proto: Option<HandleObject>,
260 can_gc: CanGc,
261 callback: Rc<MutationCallback>,
262 ) -> Fallible<DomRoot<MutationObserver>> {
263 global.set_exists_mut_observer();
264 let observer = MutationObserver::new_with_proto(global, proto, callback, can_gc);
265 ScriptThread::mutation_observers().add_mutation_observer(&observer);
266 Ok(observer)
267 }
268
269 fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> {
271 let attribute_filter = options.attributeFilter.clone().unwrap_or_default();
272 let attribute_old_value = options.attributeOldValue.unwrap_or(false);
273 let mut attributes = options.attributes.unwrap_or(false);
274 let mut character_data = options.characterData.unwrap_or(false);
275 let character_data_old_value = options.characterDataOldValue.unwrap_or(false);
276 let child_list = options.childList;
277 let subtree = options.subtree;
278
279 if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) &&
281 options.attributes.is_none()
282 {
283 attributes = true;
284 }
285
286 if options.characterDataOldValue.is_some() && options.characterData.is_none() {
288 character_data = true;
289 }
290
291 if !child_list && !attributes && !character_data {
293 return Err(Error::Type(
294 c"One of childList, attributes, or characterData must be true".into(),
295 ));
296 }
297
298 if attribute_old_value && !attributes {
300 return Err(Error::Type(
301 c"attributeOldValue is true but attributes is false".into(),
302 ));
303 }
304
305 if options.attributeFilter.is_some() && !attributes {
307 return Err(Error::Type(
308 c"attributeFilter is present but attributes is false".into(),
309 ));
310 }
311
312 if character_data_old_value && !character_data {
314 return Err(Error::Type(
315 c"characterDataOldValue is true but characterData is false".into(),
316 ));
317 }
318
319 let add_new_observer = {
321 let mut replaced = false;
322 for registered in &mut *target.registered_mutation_observers_mut() {
323 if !std::ptr::eq(&*registered.observer, self) {
324 continue;
325 }
326 registered.options.attribute_old_value = attribute_old_value;
328 registered.options.attributes = attributes;
329 registered.options.character_data = character_data;
330 registered.options.character_data_old_value = character_data_old_value;
331 registered.options.child_list = child_list;
332 registered.options.subtree = subtree;
333 registered
334 .options
335 .attribute_filter
336 .clone_from(&attribute_filter);
337 replaced = true;
338 }
339 !replaced
340 };
341
342 if add_new_observer {
344 target.add_mutation_observer(RegisteredObserver {
345 observer: Dom::from_ref(self),
346 options: ObserverOptions {
347 attributes,
348 attribute_old_value,
349 character_data,
350 character_data_old_value,
351 subtree,
352 attribute_filter,
353 child_list,
354 },
355 });
356
357 self.node_list.borrow_mut().push(Dom::from_ref(target));
358 }
359
360 Ok(())
361 }
362
363 fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
365 let records: Vec<DomRoot<MutationRecord>> = self
366 .record_queue
367 .borrow()
368 .iter()
369 .map(|record| record.as_rooted())
370 .collect();
371 self.record_queue.borrow_mut().clear();
372 records
373 }
374
375 fn Disconnect(&self) {
377 let nodes = self
379 .node_list
380 .borrow()
381 .iter()
382 .map(|node| node.as_rooted())
383 .collect::<Vec<_>>();
384 self.node_list.borrow_mut().clear();
385
386 for node in nodes {
387 node.remove_mutation_observer(self);
388 }
389
390 self.record_queue.borrow_mut().clear();
392 }
393}