Combine some access to the thread local variable for script thread. (#38752)

This combines some access to the thread local variable for script
thread.

- We introduce a new UserInteractingScriptGuard which on drop handles
  the resetting of was_interacting to the previous value. Sometimes
throughout the code `ScriptThread::is_user_interacting` was reset to the
previous value while sometimes just set to false. This should
remove this footgun.
- This also reduces the amount of thread local access for
MutationObservers and task queue.

Testing: WPT tests should cover this.
Fixes: This addresses part of
https://github.com/servo/servo/issues/37969 but there is probably still
stuff to be done.

---------

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Narfinger 2025-09-11 11:40:32 +02:00 committed by GitHub
parent 5de041e6ef
commit 19f70dccf6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 156 additions and 117 deletions

View file

@ -9,22 +9,18 @@ use dom_struct::dom_struct;
use html5ever::{LocalName, Namespace, ns};
use js::rust::HandleObject;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::MutationObserver_Binding::MutationObserverMethods;
use crate::dom::bindings::codegen::Bindings::MutationObserverBinding::{
MutationCallback, MutationObserverInit,
};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
use crate::dom::mutationrecord::MutationRecord;
use crate::dom::node::{Node, ShadowIncluding};
use crate::dom::window::Window;
use crate::microtask::Microtask;
use crate::script_runtime::CanGc;
use crate::script_thread::ScriptThread;
@ -91,59 +87,12 @@ impl MutationObserver {
}
}
/// <https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask>
pub(crate) fn queue_mutation_observer_microtask() {
// Step 1. If the surrounding agents mutation observer microtask queued is true, then return.
if ScriptThread::is_mutation_observer_microtask_queued() {
return;
}
// Step 2. Set the surrounding agents mutation observer microtask queued to true.
ScriptThread::set_mutation_observer_microtask_queued(true);
// Step 3. Queue a microtask to notify mutation observers.
ScriptThread::enqueue_microtask(Microtask::NotifyMutationObservers);
pub(crate) fn record_queue(&self) -> &DomRefCell<Vec<DomRoot<MutationRecord>>> {
&self.record_queue
}
/// <https://dom.spec.whatwg.org/#notify-mutation-observers>
pub(crate) fn notify_mutation_observers(can_gc: CanGc) {
// Step 1. Set the surrounding agents mutation observer microtask queued to false.
ScriptThread::set_mutation_observer_microtask_queued(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 = ScriptThread::get_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 = ScriptThread::take_signal_slots();
// Step 6. For each mo of notifySet:
for mo in &notify_list {
// Step 6.1 Let records be a clone of mos record queue.
let queue: Vec<DomRoot<MutationRecord>> = mo.record_queue.borrow().clone();
// Step 6.2 Empty mos record queue.
mo.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);
}
pub(crate) fn callback(&self) -> &Rc<MutationCallback> {
&self.callback
}
/// <https://dom.spec.whatwg.org/#queueing-a-mutation-record>
@ -286,7 +235,8 @@ impl MutationObserver {
}
// Step 5
MutationObserver::queue_mutation_observer_microtask();
let mutation_observers = ScriptThread::mutation_observers();
mutation_observers.queue_mutation_observer_microtask(ScriptThread::microtask_queue());
}
}
@ -300,7 +250,7 @@ impl MutationObserverMethods<crate::DomTypeHolder> for MutationObserver {
) -> Fallible<DomRoot<MutationObserver>> {
global.set_exists_mut_observer();
let observer = MutationObserver::new_with_proto(global, proto, callback, can_gc);
ScriptThread::add_mutation_observer(&observer);
ScriptThread::mutation_observers().add_mutation_observer(&observer);
Ok(observer)
}