script/event_loop/
script_mutation_observers.rs1use std::cell::Cell;
6
7use js::context::JSContext;
8use script_bindings::callback::ExceptionHandling;
9use script_bindings::cell::DomRefCell;
10use script_bindings::inheritance::Castable;
11use script_bindings::root::{Dom, DomRoot};
12
13use crate::dom::types::{EventTarget, HTMLSlotElement, MutationObserver, MutationRecord};
14use crate::runtime::job_queue::NotifyMutationObserversMicrotask;
15
16#[derive(JSTraceable, Default)]
19#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
20#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
21pub(crate) struct ScriptMutationObservers {
22 mutation_observer_microtask_queued: Cell<bool>,
24
25 mutation_observers: DomRefCell<Vec<Dom<MutationObserver>>>,
27
28 signal_slots: DomRefCell<Vec<Dom<HTMLSlotElement>>>,
30}
31
32impl ScriptMutationObservers {
33 pub(crate) fn add_mutation_observer(&self, observer: &MutationObserver) {
34 self.mutation_observers
35 .borrow_mut()
36 .push(Dom::from_ref(observer));
37 }
38
39 pub(crate) fn notify_mutation_observers(&self, cx: &mut JSContext) {
41 self.mutation_observer_microtask_queued.set(false);
43
44 let notify_list = self.take_mutation_observers();
47
48 let signal_set: Vec<DomRoot<HTMLSlotElement>> = self.take_signal_slots();
51
52 for mo in notify_list.iter() {
54 let record_queue = mo.record_queue();
55
56 let queue: Vec<DomRoot<MutationRecord>> = record_queue
58 .borrow()
59 .iter()
60 .map(|record| record.as_rooted())
61 .collect();
62
63 record_queue.borrow_mut().clear();
65
66 if !queue.is_empty() {
72 let _ = mo
73 .callback()
74 .Call_(cx, &**mo, queue, mo, ExceptionHandling::Report);
75 }
76 }
77
78 for slot in signal_set {
81 slot.upcast::<EventTarget>()
82 .fire_bubbling_event(cx, atom!("slotchange"));
83 }
84 }
85
86 pub(crate) fn queue_mutation_observer_microtask(&self, cx: &JSContext) {
88 if self.mutation_observer_microtask_queued.get() {
90 return;
91 }
92
93 self.mutation_observer_microtask_queued.set(true);
95
96 crate::runtime::job_queue::enqueue(cx, Box::new(NotifyMutationObserversMicrotask::new()));
98 }
99
100 pub(crate) fn add_signal_slot(&self, observer: &HTMLSlotElement) {
101 self.signal_slots.borrow_mut().push(Dom::from_ref(observer));
102 }
103
104 pub(crate) fn take_signal_slots(&self) -> Vec<DomRoot<HTMLSlotElement>> {
105 self.signal_slots
106 .take()
107 .into_iter()
108 .inspect(|slot| {
109 slot.remove_from_signal_slots();
110 })
111 .map(|slot| slot.as_rooted())
112 .collect()
113 }
114
115 pub(crate) fn take_mutation_observers(&self) -> Vec<DomRoot<MutationObserver>> {
116 self.mutation_observers
117 .take()
118 .iter()
119 .map(|mo| mo.as_rooted())
120 .collect()
121 }
122}