1use std::cell::LazyCell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use html5ever::{LocalName, Namespace, ns};
10use js::rust::HandleObject;
11
12use crate::dom::bindings::cell::DomRefCell;
13use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserver_Binding::MutationObserverMethods;
14use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::{
15 MutationCallback, MutationObserverInit,
16};
17use crate::dom::bindings::error::{Error, Fallible};
18use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::bindings::str::DOMString;
21use crate::dom::mutationrecord::MutationRecord;
22use crate::dom::node::{Node, ShadowIncluding};
23use crate::dom::window::Window;
24use crate::script_runtime::CanGc;
25use crate::script_thread::ScriptThread;
26
27#[dom_struct]
28pub(crate) struct MutationObserver {
29 reflector_: Reflector,
30 #[ignore_malloc_size_of = "can't measure Rc values"]
31 callback: Rc<MutationCallback>,
32 record_queue: DomRefCell<Vec<DomRoot<MutationRecord>>>,
33 node_list: DomRefCell<Vec<DomRoot<Node>>>,
34}
35
36pub(crate) enum Mutation<'a> {
37 Attribute {
38 name: LocalName,
39 namespace: Namespace,
40 old_value: Option<DOMString>,
41 },
42 CharacterData {
43 old_value: DOMString,
44 },
45 ChildList {
46 added: Option<&'a [&'a Node]>,
47 removed: Option<&'a [&'a Node]>,
48 prev: Option<&'a Node>,
49 next: Option<&'a Node>,
50 },
51}
52
53#[derive(JSTraceable, MallocSizeOf)]
54pub(crate) struct RegisteredObserver {
55 pub(crate) observer: DomRoot<MutationObserver>,
56 options: ObserverOptions,
57}
58
59#[derive(JSTraceable, MallocSizeOf)]
60pub(crate) struct ObserverOptions {
61 attribute_old_value: bool,
62 attributes: bool,
63 character_data: bool,
64 character_data_old_value: bool,
65 child_list: bool,
66 subtree: bool,
67 attribute_filter: Vec<DOMString>,
68}
69
70impl MutationObserver {
71 fn new_with_proto(
72 global: &Window,
73 proto: Option<HandleObject>,
74 callback: Rc<MutationCallback>,
75 can_gc: CanGc,
76 ) -> DomRoot<MutationObserver> {
77 let boxed_observer = Box::new(MutationObserver::new_inherited(callback));
78 reflect_dom_object_with_proto(boxed_observer, global, proto, can_gc)
79 }
80
81 fn new_inherited(callback: Rc<MutationCallback>) -> MutationObserver {
82 MutationObserver {
83 reflector_: Reflector::new(),
84 callback,
85 record_queue: DomRefCell::new(vec![]),
86 node_list: DomRefCell::new(vec![]),
87 }
88 }
89
90 pub(crate) fn record_queue(&self) -> &DomRefCell<Vec<DomRoot<MutationRecord>>> {
91 &self.record_queue
92 }
93
94 pub(crate) fn callback(&self) -> &Rc<MutationCallback> {
95 &self.callback
96 }
97
98 pub(crate) fn queue_a_mutation_record<'a, F>(
100 target: &Node,
101 attr_type: LazyCell<Mutation<'a>, F>,
102 ) where
103 F: FnOnce() -> Mutation<'a>,
104 {
105 if !target.global().as_window().get_exists_mut_observer() {
106 return;
107 }
108 let mut interested_observers: Vec<(DomRoot<MutationObserver>, Option<DOMString>)> = vec![];
110
111 for node in target.inclusive_ancestors(ShadowIncluding::No) {
113 let registered = node.registered_mutation_observers();
114 if registered.is_none() {
115 continue;
116 }
117
118 for registered in &*registered.unwrap() {
119 if &*node != target && !registered.options.subtree {
120 continue;
121 }
122
123 match *attr_type {
124 Mutation::Attribute {
125 ref name,
126 ref namespace,
127 ref old_value,
128 } => {
129 if !registered.options.attributes {
131 continue;
132 }
133 if !registered.options.attribute_filter.is_empty() {
134 if *namespace != ns!() {
135 continue;
136 }
137 if !registered
138 .options
139 .attribute_filter
140 .iter()
141 .any(|s| **s == **name)
142 {
143 continue;
144 }
145 }
146 let paired_string = if registered.options.attribute_old_value {
148 old_value.clone()
149 } else {
150 None
151 };
152 let idx = interested_observers
154 .iter()
155 .position(|(o, _)| std::ptr::eq(&**o, &*registered.observer));
156 if let Some(idx) = idx {
157 interested_observers[idx].1 = paired_string;
158 } else {
159 interested_observers
160 .push((DomRoot::from_ref(&*registered.observer), paired_string));
161 }
162 },
163 Mutation::CharacterData { ref old_value } => {
164 if !registered.options.character_data {
165 continue;
166 }
167 let paired_string = if registered.options.character_data_old_value {
169 Some(old_value.clone())
170 } else {
171 None
172 };
173 let idx = interested_observers
175 .iter()
176 .position(|(o, _)| std::ptr::eq(&**o, &*registered.observer));
177 if let Some(idx) = idx {
178 interested_observers[idx].1 = paired_string;
179 } else {
180 interested_observers
181 .push((DomRoot::from_ref(&*registered.observer), paired_string));
182 }
183 },
184 Mutation::ChildList { .. } => {
185 if !registered.options.child_list {
186 continue;
187 }
188 interested_observers.push((DomRoot::from_ref(&*registered.observer), None));
189 },
190 }
191 }
192 }
193
194 for (observer, paired_string) in interested_observers {
196 let record = match *attr_type {
198 Mutation::Attribute {
199 ref name,
200 ref namespace,
201 ..
202 } => {
203 let namespace = if *namespace != ns!() {
204 Some(namespace)
205 } else {
206 None
207 };
208 MutationRecord::attribute_mutated(
209 target,
210 name,
211 namespace,
212 paired_string,
213 CanGc::note(),
214 )
215 },
216 Mutation::CharacterData { .. } => {
217 MutationRecord::character_data_mutated(target, paired_string, CanGc::note())
218 },
219 Mutation::ChildList {
220 ref added,
221 ref removed,
222 ref next,
223 ref prev,
224 } => MutationRecord::child_list_mutated(
225 target,
226 *added,
227 *removed,
228 *next,
229 *prev,
230 CanGc::note(),
231 ),
232 };
233 observer.record_queue.borrow_mut().push(record);
235 }
236
237 let mutation_observers = ScriptThread::mutation_observers();
239 mutation_observers.queue_mutation_observer_microtask(ScriptThread::microtask_queue());
240 }
241}
242
243impl MutationObserverMethods<crate::DomTypeHolder> for MutationObserver {
244 fn Constructor(
246 global: &Window,
247 proto: Option<HandleObject>,
248 can_gc: CanGc,
249 callback: Rc<MutationCallback>,
250 ) -> Fallible<DomRoot<MutationObserver>> {
251 global.set_exists_mut_observer();
252 let observer = MutationObserver::new_with_proto(global, proto, callback, can_gc);
253 ScriptThread::mutation_observers().add_mutation_observer(&observer);
254 Ok(observer)
255 }
256
257 fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> {
259 let attribute_filter = options.attributeFilter.clone().unwrap_or_default();
260 let attribute_old_value = options.attributeOldValue.unwrap_or(false);
261 let mut attributes = options.attributes.unwrap_or(false);
262 let mut character_data = options.characterData.unwrap_or(false);
263 let character_data_old_value = options.characterDataOldValue.unwrap_or(false);
264 let child_list = options.childList;
265 let subtree = options.subtree;
266
267 if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) &&
269 options.attributes.is_none()
270 {
271 attributes = true;
272 }
273
274 if options.characterDataOldValue.is_some() && options.characterData.is_none() {
276 character_data = true;
277 }
278
279 if !child_list && !attributes && !character_data {
281 return Err(Error::Type(
282 "One of childList, attributes, or characterData must be true".into(),
283 ));
284 }
285
286 if attribute_old_value && !attributes {
288 return Err(Error::Type(
289 "attributeOldValue is true but attributes is false".into(),
290 ));
291 }
292
293 if options.attributeFilter.is_some() && !attributes {
295 return Err(Error::Type(
296 "attributeFilter is present but attributes is false".into(),
297 ));
298 }
299
300 if character_data_old_value && !character_data {
302 return Err(Error::Type(
303 "characterDataOldValue is true but characterData is false".into(),
304 ));
305 }
306
307 let add_new_observer = {
309 let mut replaced = false;
310 for registered in &mut *target.registered_mutation_observers_mut() {
311 if !std::ptr::eq(&*registered.observer, self) {
312 continue;
313 }
314 registered.options.attribute_old_value = attribute_old_value;
316 registered.options.attributes = attributes;
317 registered.options.character_data = character_data;
318 registered.options.character_data_old_value = character_data_old_value;
319 registered.options.child_list = child_list;
320 registered.options.subtree = subtree;
321 registered
322 .options
323 .attribute_filter
324 .clone_from(&attribute_filter);
325 replaced = true;
326 }
327 !replaced
328 };
329
330 if add_new_observer {
332 target.add_mutation_observer(RegisteredObserver {
333 observer: DomRoot::from_ref(self),
334 options: ObserverOptions {
335 attributes,
336 attribute_old_value,
337 character_data,
338 character_data_old_value,
339 subtree,
340 attribute_filter,
341 child_list,
342 },
343 });
344
345 self.node_list.borrow_mut().push(DomRoot::from_ref(target));
346 }
347
348 Ok(())
349 }
350
351 fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
353 let records: Vec<DomRoot<MutationRecord>> = self.record_queue.borrow().clone();
354 self.record_queue.borrow_mut().clear();
355 records
356 }
357
358 fn Disconnect(&self) {
360 let mut nodes = self.node_list.borrow_mut();
362 for node in nodes.drain(..) {
363 node.remove_mutation_observer(self);
364 }
365
366 self.record_queue.borrow_mut().clear();
368 }
369}