servo/components/script/script_mutation_observers.rs
Narfinger 033da09800
Move signals_slots to ScriptMutationObservers (#39275)
Both places where we access signals_slots already have a reference to
ScriptMutationObserver. This saves us another access to
with_script_thread.


Testing: This does not change functionality.
Fixes: Part of addressing: https://github.com/servo/servo/issues/37969

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2025-09-12 18:43:59 +00:00

113 lines
4.7 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use std::rc::Rc;
use script_bindings::callback::ExceptionHandling;
use script_bindings::inheritance::Castable;
use script_bindings::root::{Dom, DomRoot};
use script_bindings::script_runtime::CanGc;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::types::{EventTarget, HTMLSlotElement, MutationObserver, MutationRecord};
use crate::microtask::{Microtask, MicrotaskQueue};
/// A helper struct for mutation observers used in `ScriptThread`
/// Since the Rc is always stored in ScriptThread, it's always reachable by the GC.
#[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 {
/// Microtask Queue for adding support for mutation observer microtasks
mutation_observer_microtask_queued: Cell<bool>,
/// The unit of related similar-origin browsing contexts' list of MutationObserver objects
mutation_observers: DomRefCell<Vec<Dom<MutationObserver>>>,
/// <https://dom.spec.whatwg.org/#signal-slot-list>
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));
}
/// <https://dom.spec.whatwg.org/#notify-mutation-observers>
pub(crate) fn notify_mutation_observers(&self, can_gc: CanGc) {
// Step 1. Set the surrounding agents mutation observer microtask queued to false.
self.mutation_observer_microtask_queued.set(false);
// Step 2. Let notifySet be a clone of the surrounding agents pending mutation observers.
// TODO Step 3. Empty the surrounding agents pending mutation observers.
let notify_list = self.mutation_observers.borrow();
// Step 4. Let signalSet be a clone of the surrounding agents signal slots.
// Step 5. Empty the surrounding agents signal slots.
let signal_set: Vec<DomRoot<HTMLSlotElement>> = self.take_signal_slots();
// Step 6. For each mo of notifySet:
for mo in notify_list.iter() {
let record_queue = mo.record_queue();
// Step 6.1 Let records be a clone of mos record queue.
let queue: Vec<DomRoot<MutationRecord>> = record_queue.borrow().clone();
// Step 6.2 Empty mos record queue.
record_queue.borrow_mut().clear();
// TODO Step 6.3 For each node of mos node list, remove all transient registered observers
// whose observer is mo from nodes registered observer list.
// Step 6.4 If records is not empty, then invoke mos callback with « records,
// mo » and "report", and with callback this value mo.
if !queue.is_empty() {
let _ = mo
.callback()
.Call_(&**mo, queue, mo, ExceptionHandling::Report, can_gc);
}
}
// Step 6. For each slot of signalSet, fire an event named slotchange,
// with its bubbles attribute set to true, at slot.
for slot in signal_set {
slot.upcast::<EventTarget>()
.fire_event(atom!("slotchange"), can_gc);
}
}
/// <https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask>
pub(crate) fn queue_mutation_observer_microtask(&self, microtask_queue: Rc<MicrotaskQueue>) {
// Step 1. If the surrounding agents mutation observer microtask queued is true, then return.
if self.mutation_observer_microtask_queued.get() {
return;
}
// Step 2. Set the surrounding agents mutation observer microtask queued to true.
self.mutation_observer_microtask_queued.set(true);
// Step 3. Queue a microtask to notify mutation observers.
crate::script_thread::with_script_thread(|script_thread| {
microtask_queue.enqueue(Microtask::NotifyMutationObservers, script_thread.get_cx());
});
}
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()
}
}