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.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_(&**mo, queue, mo, ExceptionHandling::Report, can_gc);
76 }
77 }
78
79 for slot in signal_set {
82 slot.upcast::<EventTarget>()
83 .fire_event(atom!("slotchange"), can_gc);
84 }
85 }
86
87 pub(crate) fn queue_mutation_observer_microtask(&self, microtask_queue: Rc<MicrotaskQueue>) {
89 if self.mutation_observer_microtask_queued.get() {
91 return;
92 }
93
94 self.mutation_observer_microtask_queued.set(true);
96
97 crate::script_thread::with_script_thread(|script_thread| {
99 microtask_queue.enqueue(Microtask::NotifyMutationObservers, script_thread.get_cx());
100 });
101 }
102
103 pub(crate) fn add_signal_slot(&self, observer: &HTMLSlotElement) {
104 self.signal_slots.borrow_mut().push(Dom::from_ref(observer));
105 }
106
107 pub(crate) fn take_signal_slots(&self) -> Vec<DomRoot<HTMLSlotElement>> {
108 self.signal_slots
109 .take()
110 .into_iter()
111 .inspect(|slot| {
112 slot.remove_from_signal_slots();
113 })
114 .map(|slot| slot.as_rooted())
115 .collect()
116 }
117
118 pub(crate) fn take_mutation_observers(&self) -> Vec<DomRoot<MutationObserver>> {
119 self.mutation_observers
120 .take()
121 .iter()
122 .map(|mo| mo.as_rooted())
123 .collect()
124 }
125}