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