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