Files
servo/components/script/script_mutation_observers.rs
WaterWhisperer e4b7962854 script: Fix MutationObserver to use Dom instead of DomRoot (#40608)
converts MutationObserver's record_queue and node_list, as well as
RegisteredObserver's observer field, to use Dom<T> instead.

Testing: `./mach build --use-crown` pass locally
Fixes #40602

Signed-off-by: WaterWhisperer <waterwhisperer24@qq.com>
2025-11-13 19:14:30 +00:00

126 lines
5.0 KiB
Rust
Raw 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.
// Step 3. Empty the surrounding agents pending mutation observers.
let notify_list = self.take_mutation_observers();
// 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()
.iter()
.map(|record| record.as_rooted())
.collect();
// 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()
}
pub(crate) fn take_mutation_observers(&self) -> Vec<DomRoot<MutationObserver>> {
self.mutation_observers
.take()
.iter()
.map(|mo| mo.as_rooted())
.collect()
}
}