script/dom/mutationobserver/
mutationobserver.rs1use std::cell::LazyCell;
6
7use dom_struct::dom_struct;
8use html5ever::{LocalName, Namespace, ns};
9use js::context::JSContext;
10use js::rust::HandleObject;
11use rustc_hash::FxHashMap;
12use script_bindings::callback::{OwnerWindow, RootedCallback, TracedCallback};
13use script_bindings::cell::DomRefCell;
14use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
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::event_loop::script_thread::ScriptThread;
29
30#[dom_struct]
31pub(crate) struct MutationObserver {
32 reflector_: Reflector,
33 callback: TracedCallback<MutationCallback>,
34 record_queue: DomRefCell<Vec<Dom<MutationRecord>>>,
35 node_list: DomRefCell<Vec<Dom<Node>>>,
36}
37
38pub(crate) enum Mutation<'a> {
39 Attribute {
40 name: LocalName,
41 namespace: Namespace,
42 old_value: Option<DOMString>,
43 },
44 CharacterData {
45 old_value: String,
46 },
47 ChildList {
48 added: Option<&'a [&'a Node]>,
49 removed: Option<&'a [&'a Node]>,
50 prev: Option<&'a Node>,
51 next: Option<&'a Node>,
52 },
53}
54
55#[derive(JSTraceable, MallocSizeOf)]
56#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
57pub(crate) struct RegisteredObserver {
58 pub(crate) observer: Dom<MutationObserver>,
59 options: ObserverOptions,
60}
61
62#[derive(JSTraceable, MallocSizeOf)]
63pub(crate) struct ObserverOptions {
64 attribute_old_value: bool,
65 attributes: bool,
66 character_data: bool,
67 character_data_old_value: bool,
68 child_list: bool,
69 subtree: bool,
70 attribute_filter: Vec<DOMString>,
71}
72
73impl MutationObserver {
74 fn new_with_proto(
75 cx: &mut JSContext,
76 global: &Window,
77 proto: Option<HandleObject>,
78 callback: RootedCallback<MutationCallback>,
79 ) -> DomRoot<MutationObserver> {
80 let boxed_observer = Box::new(MutationObserver::new_inherited(callback));
81 reflect_dom_object_with_proto(cx, boxed_observer, global, proto)
82 }
83
84 fn new_inherited(callback: RootedCallback<MutationCallback>) -> MutationObserver {
85 MutationObserver {
86 reflector_: Reflector::new(),
87 callback: callback.to_traced(),
88 record_queue: DomRefCell::new(vec![]),
89 node_list: DomRefCell::new(vec![]),
90 }
91 }
92
93 pub(crate) fn record_queue(&self) -> &DomRefCell<Vec<Dom<MutationRecord>>> {
94 &self.record_queue
95 }
96
97 pub(crate) fn callback(&self) -> &TracedCallback<MutationCallback> {
98 &self.callback
99 }
100
101 pub(crate) fn queue_a_mutation_record<'a, F>(
103 cx: &mut JSContext,
104 target: &Node,
105 attr_type: LazyCell<Mutation<'a>, F>,
106 ) where
107 F: FnOnce() -> Mutation<'a>,
108 {
109 if !target.global().as_window().get_exists_mut_observer() {
110 return;
111 }
112 let mut interested_observers: FxHashMap<DomRoot<MutationObserver>, Option<DOMString>> =
114 FxHashMap::default();
115
116 for node in target.inclusive_ancestors_unrooted(cx.no_gc(), ShadowIncluding::No) {
119 let registered = node.registered_mutation_observers();
120 if registered.is_none() {
121 continue;
122 }
123
124 for registered in &*registered.unwrap() {
126 if *node != target && !registered.options.subtree {
128 continue;
129 }
130
131 match *attr_type {
132 Mutation::Attribute {
134 ref name,
135 ref namespace,
136 ref old_value,
137 } => {
138 if !registered.options.attributes {
140 continue;
141 }
142 if !registered.options.attribute_filter.is_empty() {
145 if *namespace != ns!() {
146 continue;
147 }
148 if !registered
149 .options
150 .attribute_filter
151 .iter()
152 .any(|s| *s == **name)
153 {
154 continue;
155 }
156 }
157 let mo = registered.observer.as_rooted();
159 if registered.options.attribute_old_value {
161 interested_observers.insert(mo, old_value.clone());
163 } else {
164 interested_observers.entry(mo).or_insert(None);
166 }
167 },
168 Mutation::CharacterData { ref old_value } => {
170 if !registered.options.character_data {
172 continue;
173 }
174 let mo = registered.observer.as_rooted();
176 if registered.options.character_data_old_value {
177 interested_observers
179 .insert(mo, Some(DOMString::from(old_value.clone())));
180 } else {
181 interested_observers.entry(mo).or_insert(None);
183 }
184 },
185 Mutation::ChildList { .. } => {
187 if !registered.options.child_list {
189 continue;
190 }
191 let mo = registered.observer.as_rooted();
193 interested_observers.entry(mo).or_insert(None);
195 },
196 }
197 }
198 }
199
200 for (observer, mapped_old_value) in interested_observers {
202 let record = match *attr_type {
204 Mutation::Attribute {
205 ref name,
206 ref namespace,
207 ..
208 } => {
209 let namespace = if *namespace != ns!() {
210 Some(namespace)
211 } else {
212 None
213 };
214 MutationRecord::attribute_mutated(cx, target, name, namespace, mapped_old_value)
215 },
216 Mutation::CharacterData { .. } => {
217 MutationRecord::character_data_mutated(cx, target, mapped_old_value)
218 },
219 Mutation::ChildList {
220 ref added,
221 ref removed,
222 ref next,
223 ref prev,
224 } => MutationRecord::child_list_mutated(cx, target, *added, *removed, *next, *prev),
225 };
226 observer
228 .record_queue
229 .borrow_mut()
230 .push(Dom::from_ref(&*record));
231 ScriptThread::mutation_observers().add_mutation_observer(&observer);
233 }
234
235 let mutation_observers = ScriptThread::mutation_observers();
237 mutation_observers.queue_mutation_observer_microtask(cx);
238 }
239}
240
241impl MutationObserverMethods<crate::DomTypeHolder> for MutationObserver {
242 fn Constructor(
244 cx: &mut JSContext,
245 global: &Window,
246 proto: Option<HandleObject>,
247 callback: RootedCallback<MutationCallback>,
248 ) -> Fallible<DomRoot<MutationObserver>> {
249 global.set_exists_mut_observer();
250 let observer = MutationObserver::new_with_proto(cx, global, proto, callback);
251 ScriptThread::mutation_observers().add_mutation_observer(&observer);
252 Ok(observer)
253 }
254
255 fn Observe(&self, target: &Node, options: &MutationObserverInit) -> Fallible<()> {
257 let attribute_filter = options.attributeFilter.clone().unwrap_or_default();
258 let attribute_old_value = options.attributeOldValue.unwrap_or(false);
259 let mut attributes = options.attributes.unwrap_or(false);
260 let mut character_data = options.characterData.unwrap_or(false);
261 let character_data_old_value = options.characterDataOldValue.unwrap_or(false);
262 let child_list = options.childList;
263 let subtree = options.subtree;
264
265 if (options.attributeOldValue.is_some() || options.attributeFilter.is_some()) &&
267 options.attributes.is_none()
268 {
269 attributes = true;
270 }
271
272 if options.characterDataOldValue.is_some() && options.characterData.is_none() {
274 character_data = true;
275 }
276
277 if !child_list && !attributes && !character_data {
279 return Err(Error::Type(
280 c"One of childList, attributes, or characterData must be true".into(),
281 ));
282 }
283
284 if attribute_old_value && !attributes {
286 return Err(Error::Type(
287 c"attributeOldValue is true but attributes is false".into(),
288 ));
289 }
290
291 if options.attributeFilter.is_some() && !attributes {
293 return Err(Error::Type(
294 c"attributeFilter is present but attributes is false".into(),
295 ));
296 }
297
298 if character_data_old_value && !character_data {
300 return Err(Error::Type(
301 c"characterDataOldValue is true but characterData is false".into(),
302 ));
303 }
304
305 let add_new_observer = {
307 let mut replaced = false;
308 for registered in &mut *target.registered_mutation_observers_mut() {
309 if registered.observer != self {
310 continue;
311 }
312 registered.options.attribute_old_value = attribute_old_value;
314 registered.options.attributes = attributes;
315 registered.options.character_data = character_data;
316 registered.options.character_data_old_value = character_data_old_value;
317 registered.options.child_list = child_list;
318 registered.options.subtree = subtree;
319 registered
320 .options
321 .attribute_filter
322 .clone_from(&attribute_filter);
323 replaced = true;
324 }
325 !replaced
326 };
327
328 if add_new_observer {
330 target.add_mutation_observer(RegisteredObserver {
331 observer: Dom::from_ref(self),
332 options: ObserverOptions {
333 attributes,
334 attribute_old_value,
335 character_data,
336 character_data_old_value,
337 subtree,
338 attribute_filter,
339 child_list,
340 },
341 });
342
343 self.node_list.borrow_mut().push(Dom::from_ref(target));
344 }
345
346 Ok(())
347 }
348
349 fn TakeRecords(&self) -> Vec<DomRoot<MutationRecord>> {
351 let records: Vec<DomRoot<MutationRecord>> = self
352 .record_queue
353 .borrow()
354 .iter()
355 .map(|record| record.as_rooted())
356 .collect();
357 self.record_queue.borrow_mut().clear();
358 records
359 }
360
361 fn Disconnect(&self) {
363 let nodes = self
365 .node_list
366 .borrow()
367 .iter()
368 .map(|node| node.as_rooted())
369 .collect::<Vec<_>>();
370 self.node_list.borrow_mut().clear();
371
372 for node in nodes {
373 node.remove_mutation_observer(self);
374 }
375
376 self.record_queue.borrow_mut().clear();
378 }
379}
380
381impl OwnerWindow<crate::DomTypeHolder> for MutationObserver {}