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::callback::OwnerWindow;
14use script_bindings::cell::DomRefCell;
15use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_cx};
16
17use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserver_Binding::MutationObserverMethods;
18use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::{
19 MutationCallback, MutationObserverInit,
20};
21use crate::dom::bindings::error::{Error, Fallible};
22use crate::dom::bindings::reflector::DomGlobal;
23use crate::dom::bindings::root::{Dom, DomRoot};
24use crate::dom::bindings::str::DOMString;
25use crate::dom::iterators::ShadowIncluding;
26use crate::dom::mutationrecord::MutationRecord;
27use crate::dom::node::Node;
28use crate::dom::window::Window;
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 cx: &mut JSContext,
106 target: &Node,
107 attr_type: LazyCell<Mutation<'a>, F>,
108 ) where
109 F: FnOnce() -> Mutation<'a>,
110 {
111 if !target.global().as_window().get_exists_mut_observer() {
112 return;
113 }
114 let mut interested_observers: HashMap<DomRoot<MutationObserver>, Option<DOMString>> =
116 HashMap::new();
117
118 for node in target.inclusive_ancestors(ShadowIncluding::No) {
121 let registered = node.registered_mutation_observers();
122 if registered.is_none() {
123 continue;
124 }
125
126 for registered in &*registered.unwrap() {
128 if &*node != target && !registered.options.subtree {
130 continue;
131 }
132
133 match *attr_type {
134 Mutation::Attribute {
136 ref name,
137 ref namespace,
138 ref old_value,
139 } => {
140 if !registered.options.attributes {
142 continue;
143 }
144 if !registered.options.attribute_filter.is_empty() {
147 if *namespace != ns!() {
148 continue;
149 }
150 if !registered
151 .options
152 .attribute_filter
153 .iter()
154 .any(|s| *s == **name)
155 {
156 continue;
157 }
158 }
159 let mo = registered.observer.as_rooted();
161 if registered.options.attribute_old_value {
163 interested_observers.insert(mo, old_value.clone());
165 } else {
166 interested_observers.entry(mo).or_insert(None);
168 }
169 },
170 Mutation::CharacterData { ref old_value } => {
172 if !registered.options.character_data {
174 continue;
175 }
176 let mo = registered.observer.as_rooted();
178 if registered.options.character_data_old_value {
179 interested_observers
181 .insert(mo, Some(DOMString::from(old_value.clone())));
182 } else {
183 interested_observers.entry(mo).or_insert(None);
185 }
186 },
187 Mutation::ChildList { .. } => {
189 if !registered.options.child_list {
191 continue;
192 }
193 let mo = registered.observer.as_rooted();
195 interested_observers.entry(mo).or_insert(None);
197 },
198 }
199 }
200 }
201
202 for (observer, mapped_old_value) in interested_observers {
204 let record = match *attr_type {
206 Mutation::Attribute {
207 ref name,
208 ref namespace,
209 ..
210 } => {
211 let namespace = if *namespace != ns!() {
212 Some(namespace)
213 } else {
214 None
215 };
216 MutationRecord::attribute_mutated(cx, target, name, namespace, mapped_old_value)
217 },
218 Mutation::CharacterData { .. } => {
219 MutationRecord::character_data_mutated(cx, target, mapped_old_value)
220 },
221 Mutation::ChildList {
222 ref added,
223 ref removed,
224 ref next,
225 ref prev,
226 } => MutationRecord::child_list_mutated(cx, target, *added, *removed, *next, *prev),
227 };
228 observer
230 .record_queue
231 .borrow_mut()
232 .push(Dom::from_ref(&*record));
233 ScriptThread::mutation_observers().add_mutation_observer(&observer);
235 }
236
237 let mutation_observers = ScriptThread::mutation_observers();
239 mutation_observers.queue_mutation_observer_microtask(cx, ScriptThread::microtask_queue());
240 }
241}
242
243impl MutationObserverMethods<crate::DomTypeHolder> for MutationObserver {
244 fn Constructor(
246 cx: &mut JSContext,
247 global: &Window,
248 proto: Option<HandleObject>,
249 callback: Rc<MutationCallback>,
250 ) -> Fallible<DomRoot<MutationObserver>> {
251 global.set_exists_mut_observer();
252 let observer = MutationObserver::new_with_proto(cx, global, proto, callback);
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 c"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 c"attributeOldValue is true but attributes is false".into(),
290 ));
291 }
292
293 if options.attributeFilter.is_some() && !attributes {
295 return Err(Error::Type(
296 c"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 c"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: Dom::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(Dom::from_ref(target));
346 }
347
348 Ok(())
349 }
350
351 fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
353 let records: Vec<DomRoot<MutationRecord>> = self
354 .record_queue
355 .borrow()
356 .iter()
357 .map(|record| record.as_rooted())
358 .collect();
359 self.record_queue.borrow_mut().clear();
360 records
361 }
362
363 fn Disconnect(&self) {
365 let nodes = self
367 .node_list
368 .borrow()
369 .iter()
370 .map(|node| node.as_rooted())
371 .collect::<Vec<_>>();
372 self.node_list.borrow_mut().clear();
373
374 for node in nodes {
375 node.remove_mutation_observer(self);
376 }
377
378 self.record_queue.borrow_mut().clear();
380 }
381}
382
383impl OwnerWindow<crate::DomTypeHolder> for MutationObserver {}