script/
script_mutation_observers.rs1use std::cell::Cell;
6use std::rc::Rc;
7
8use script_bindings::callback::ExceptionHandling;
9use script_bindings::inheritance::Castable;
10use script_bindings::root::{Dom, DomRoot};
11use script_bindings::script_runtime::CanGc;
12
13use crate::dom::bindings::cell::DomRefCell;
14use crate::dom::types::{EventTarget, HTMLSlotElement, MutationObserver, MutationRecord};
15use crate::microtask::{Microtask, MicrotaskQueue};
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 js::context::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.callback().Call_(
74 &**mo,
75 queue,
76 mo,
77 ExceptionHandling::Report,
78 CanGc::from_cx(cx),
79 );
80 }
81 }
82
83 for slot in signal_set {
86 slot.upcast::<EventTarget>()
87 .fire_event(cx, atom!("slotchange"));
88 }
89 }
90
91 pub(crate) fn queue_mutation_observer_microtask(&self, microtask_queue: Rc<MicrotaskQueue>) {
93 if self.mutation_observer_microtask_queued.get() {
95 return;
96 }
97
98 self.mutation_observer_microtask_queued.set(true);
100
101 crate::script_thread::with_script_thread(|script_thread| {
103 microtask_queue.enqueue(Microtask::NotifyMutationObservers, script_thread.get_cx());
104 });
105 }
106
107 pub(crate) fn add_signal_slot(&self, observer: &HTMLSlotElement) {
108 self.signal_slots.borrow_mut().push(Dom::from_ref(observer));
109 }
110
111 pub(crate) fn take_signal_slots(&self) -> Vec<DomRoot<HTMLSlotElement>> {
112 self.signal_slots
113 .take()
114 .into_iter()
115 .inspect(|slot| {
116 slot.remove_from_signal_slots();
117 })
118 .map(|slot| slot.as_rooted())
119 .collect()
120 }
121
122 pub(crate) fn take_mutation_observers(&self) -> Vec<DomRoot<MutationObserver>> {
123 self.mutation_observers
124 .take()
125 .iter()
126 .map(|mo| mo.as_rooted())
127 .collect()
128 }
129}