use std::cell::Cell;
use std::rc::Rc;
use js::context::JSContext;
use script_bindings::callback::ExceptionHandling;
use script_bindings::cell::DomRefCell;
use script_bindings::inheritance::Castable;
use script_bindings::root::{Dom, DomRoot};
use crate::dom::types::{EventTarget, HTMLSlotElement, MutationObserver, MutationRecord};
use crate::microtask::{MicrotaskQueue, NotifyMutationObserversMicrotask};
#[derive(JSTraceable, Default)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct ScriptMutationObservers {
mutation_observer_microtask_queued: Cell<bool>,
mutation_observers: DomRefCell<Vec<Dom<MutationObserver>>>,
signal_slots: DomRefCell<Vec<Dom<HTMLSlotElement>>>,
}
impl ScriptMutationObservers {
pub(crate) fn add_mutation_observer(&self, observer: &MutationObserver) {
self.mutation_observers
.borrow_mut()
.push(Dom::from_ref(observer));
}
pub(crate) fn notify_mutation_observers(&self, cx: &mut JSContext) {
self.mutation_observer_microtask_queued.set(false);
let notify_list = self.take_mutation_observers();
let signal_set: Vec<DomRoot<HTMLSlotElement>> = self.take_signal_slots();
for mo in notify_list.iter() {
let record_queue = mo.record_queue();
let queue: Vec<DomRoot<MutationRecord>> = record_queue
.borrow()
.iter()
.map(|record| record.as_rooted())
.collect();
record_queue.borrow_mut().clear();
if !queue.is_empty() {
let _ = mo
.callback()
.Call_(cx, &**mo, queue, mo, ExceptionHandling::Report);
}
}
for slot in signal_set {
slot.upcast::<EventTarget>()
.fire_bubbling_event(cx, atom!("slotchange"));
}
}
pub(crate) fn queue_mutation_observer_microtask(
&self,
cx: &JSContext,
microtask_queue: Rc<MicrotaskQueue>,
) {
if self.mutation_observer_microtask_queued.get() {
return;
}
self.mutation_observer_microtask_queued.set(true);
microtask_queue.enqueue(cx, Box::new(NotifyMutationObserversMicrotask::new()));
}
pub(crate) fn add_signal_slot(&self, observer: &HTMLSlotElement) {
self.signal_slots.borrow_mut().push(Dom::from_ref(observer));
}
pub(crate) fn take_signal_slots(&self) -> Vec<DomRoot<HTMLSlotElement>> {
self.signal_slots
.take()
.into_iter()
.inspect(|slot| {
slot.remove_from_signal_slots();
})
.map(|slot| slot.as_rooted())
.collect()
}
pub(crate) fn take_mutation_observers(&self) -> Vec<DomRoot<MutationObserver>> {
self.mutation_observers
.take()
.iter()
.map(|mo| mo.as_rooted())
.collect()
}
}