1use 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::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 #[ignore_malloc_size_of = "can't measure Rc values"]
32 callback: Rc<MutationCallback>,
33 record_queue: DomRefCell<Vec<DomRoot<MutationRecord>>>,
34 node_list: DomRefCell<Vec<DomRoot<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)]
55pub(crate) struct RegisteredObserver {
56 pub(crate) observer: DomRoot<MutationObserver>,
57 options: ObserverOptions,
58}
59
60#[derive(JSTraceable, MallocSizeOf)]
61pub(crate) struct ObserverOptions {
62 attribute_old_value: bool,
63 attributes: bool,
64 character_data: bool,
65 character_data_old_value: bool,
66 child_list: bool,
67 subtree: bool,
68 attribute_filter: Vec<DOMString>,
69}
70
71impl MutationObserver {
72 fn new_with_proto(
73 global: &Window,
74 proto: Option<HandleObject>,
75 callback: Rc<MutationCallback>,
76 can_gc: CanGc,
77 ) -> DomRoot<MutationObserver> {
78 let boxed_observer = Box::new(MutationObserver::new_inherited(callback));
79 reflect_dom_object_with_proto(boxed_observer, global, proto, can_gc)
80 }
81
82 fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
83 MutationObserver {
84 reflector_: Reflector::new(),
85 callback,
86 record_queue: DomRefCell::new(vec![]),
87 node_list: DomRefCell::new(vec![]),
88 }
89 }
90
91 pub(crate) fn record_queue(&self) -> &DomRefCell<Vec<DomRoot<MutationRecord>>> {
92 &self.record_queue
93 }
94
95 pub(crate) fn callback(&self) -> &Rc<MutationCallback> {
96 &self.callback
97 }
98
99 pub(crate) fn queue_a_mutation_record<'a, F>(
101 target: &Node,
102 attr_type: LazyCell<Mutation<'a>, F>,
103 ) where
104 F: FnOnce() -> Mutation<'a>,
105 {
106 if !target.global().as_window().get_exists_mut_observer() {
107 return;
108 }
109 let mut interested_observers: HashMap<DomRoot<MutationObserver>, Option<DOMString>> =
111 HashMap::new();
112
113 for node in target.inclusive_ancestors(ShadowIncluding::No) {
116 let registered = node.registered_mutation_observers();
117 if registered.is_none() {
118 continue;
119 }
120
121 for registered in &*registered.unwrap() {
123 if &*node != target && !registered.options.subtree {
125 continue;
126 }
127
128 match *attr_type {
129 Mutation::Attribute {
131 ref name,
132 ref namespace,
133 ref old_value,
134 } => {
135 if !registered.options.attributes {
137 continue;
138 }
139 if !registered.options.attribute_filter.is_empty() {
142 if *namespace != ns!() {
143 continue;
144 }
145 if !registered
146 .options
147 .attribute_filter
148 .iter()
149 .any(|s| *s == **name)
150 {
151 continue;
152 }
153 }
154 let mo = registered.observer.clone();
156 if registered.options.attribute_old_value {
158 interested_observers.insert(mo, old_value.clone());
160 } else {
161 interested_observers.entry(mo).or_insert(None);
163 }
164 },
165 Mutation::CharacterData { ref old_value } => {
167 if !registered.options.character_data {
169 continue;
170 }
171 let mo = registered.observer.clone();
173 if registered.options.character_data_old_value {
174 interested_observers
176 .insert(mo, Some(DOMString::from(old_value.clone())));
177 } else {
178 interested_observers.entry(mo).or_insert(None);
180 }
181 },
182 Mutation::ChildList { .. } => {
184 if !registered.options.child_list {
186 continue;
187 }
188 let mo = registered.observer.clone();
190 interested_observers.entry(mo).or_insert(None);
192 },
193 }
194 }
195 }
196
197 for (observer, mapped_old_value) in interested_observers {
199 let record = match *attr_type {
201 Mutation::Attribute {
202 ref name,
203 ref namespace,
204 ..
205 } => {
206 let namespace = if *namespace != ns!() {
207 Some(namespace)
208 } else {
209 None
210 };
211 MutationRecord::attribute_mutated(
212 target,
213 name,
214 namespace,
215 mapped_old_value,
216 CanGc::note(),
217 )
218 },
219 Mutation::CharacterData { .. } => {
220 MutationRecord::character_data_mutated(target, mapped_old_value, CanGc::note())
221 },
222 Mutation::ChildList {
223 ref added,
224 ref removed,
225 ref next,
226 ref prev,
227 } => MutationRecord::child_list_mutated(
228 target,
229 *added,
230 *removed,
231 *next,
232 *prev,
233 CanGc::note(),
234 ),
235 };
236 observer.record_queue.borrow_mut().push(record);
238 ScriptThread::mutation_observers().add_mutation_observer(&observer);
240 }
241
242 let mutation_observers = ScriptThread::mutation_observers();
244 mutation_observers.queue_mutation_observer_microtask(ScriptThread::microtask_queue());
245 }
246}
247
248impl MutationObserverMethods<crate::DomTypeHolder> for MutationObserver {
249 fn Constructor(
251 global: &Window,
252 proto: Option<HandleObject>,
253 can_gc: CanGc,
254 callback: Rc<MutationCallback>,
255 ) -> Fallible<DomRoot<MutationObserver>> {
256 global.set_exists_mut_observer();
257 let observer = MutationObserver::new_with_proto(global, proto, callback, can_gc);
258 ScriptThread::mutation_observers().add_mutation_observer(&observer);
259 Ok(observer)
260 }
261
262 fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> {
264 let attribute_filter = options.attributeFilter.clone().unwrap_or_default();
265 let attribute_old_value = options.attributeOldValue.unwrap_or(false);
266 let mut attributes = options.attributes.unwrap_or(false);
267 let mut character_data = options.characterData.unwrap_or(false);
268 let character_data_old_value = options.characterDataOldValue.unwrap_or(false);
269 let child_list = options.childList;
270 let subtree = options.subtree;
271
272 if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) &&
274 options.attributes.is_none()
275 {
276 attributes = true;
277 }
278
279 if options.characterDataOldValue.is_some() && options.characterData.is_none() {
281 character_data = true;
282 }
283
284 if !child_list && !attributes && !character_data {
286 return Err(Error::Type(
287 "One of childList, attributes, or characterData must be true".into(),
288 ));
289 }
290
291 if attribute_old_value && !attributes {
293 return Err(Error::Type(
294 "attributeOldValue is true but attributes is false".into(),
295 ));
296 }
297
298 if options.attributeFilter.is_some() && !attributes {
300 return Err(Error::Type(
301 "attributeFilter is present but attributes is false".into(),
302 ));
303 }
304
305 if character_data_old_value && !character_data {
307 return Err(Error::Type(
308 "characterDataOldValue is true but characterData is false".into(),
309 ));
310 }
311
312 let add_new_observer = {
314 let mut replaced = false;
315 for registered in &mut *target.registered_mutation_observers_mut() {
316 if !std::ptr::eq(&*registered.observer, self) {
317 continue;
318 }
319 registered.options.attribute_old_value = attribute_old_value;
321 registered.options.attributes = attributes;
322 registered.options.character_data = character_data;
323 registered.options.character_data_old_value = character_data_old_value;
324 registered.options.child_list = child_list;
325 registered.options.subtree = subtree;
326 registered
327 .options
328 .attribute_filter
329 .clone_from(&attribute_filter);
330 replaced = true;
331 }
332 !replaced
333 };
334
335 if add_new_observer {
337 target.add_mutation_observer(RegisteredObserver {
338 observer: DomRoot::from_ref(self),
339 options: ObserverOptions {
340 attributes,
341 attribute_old_value,
342 character_data,
343 character_data_old_value,
344 subtree,
345 attribute_filter,
346 child_list,
347 },
348 });
349
350 self.node_list.borrow_mut().push(DomRoot::from_ref(target));
351 }
352
353 Ok(())
354 }
355
356 fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
358 let records: Vec<DomRoot<MutationRecord>> = self.record_queue.borrow().clone();
359 self.record_queue.borrow_mut().clear();
360 records
361 }
362
363 fn Disconnect(&self) {
365 let mut nodes = self.node_list.borrow_mut();
367 for node in nodes.drain(..) {
368 node.remove_mutation_observer(self);
369 }
370
371 self.record_queue.borrow_mut().clear();
373 }
374}