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