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, can_gc: CanGc) {
42 self.mutation_observer_microtask_queued.set(false);
44
45 let notify_list = self.mutation_observers.borrow();
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.borrow().clone();
59
60 record_queue.borrow_mut().clear();
62
63 if !queue.is_empty() {
69 let _ = mo
70 .callback()
71 .Call_(&**mo, queue, mo, ExceptionHandling::Report, can_gc);
72 }
73 }
74
75 for slot in signal_set {
78 slot.upcast::<EventTarget>()
79 .fire_event(atom!("slotchange"), can_gc);
80 }
81 }
82
83 pub(crate) fn queue_mutation_observer_microtask(&self, microtask_queue: Rc<MicrotaskQueue>) {
85 if self.mutation_observer_microtask_queued.get() {
87 return;
88 }
89
90 self.mutation_observer_microtask_queued.set(true);
92
93 crate::script_thread::with_script_thread(|script_thread| {
95 microtask_queue.enqueue(Microtask::NotifyMutationObservers, script_thread.get_cx());
96 });
97 }
98
99 pub(crate) fn add_signal_slot(&self, observer: &HTMLSlotElement) {
100 self.signal_slots.borrow_mut().push(Dom::from_ref(observer));
101 }
102
103 pub(crate) fn take_signal_slots(&self) -> Vec<DomRoot<HTMLSlotElement>> {
104 self.signal_slots
105 .take()
106 .into_iter()
107 .inspect(|slot| {
108 slot.remove_from_signal_slots();
109 })
110 .map(|slot| slot.as_rooted())
111 .collect()
112 }
113}