Don't fire slotchange events when there's already a pending event for the same slot (#35222)

* Don't fire slotchange events if there is already a pending event for the same slot

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Update WPT expectations

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-01-30 14:57:04 +01:00 committed by GitHub
parent 5e9de2cb61
commit 64b40ea700
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 33 additions and 49 deletions

View file

@ -2,7 +2,7 @@
* 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::RefCell;
use std::cell::{Cell, RefCell};
use dom_struct::dom_struct;
use html5ever::{local_name, namespace_url, ns, LocalName, Prefix};
@ -26,6 +26,7 @@ use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::mutationobserver::MutationObserver;
use crate::dom::node::{Node, ShadowIncluding};
use crate::dom::text::Text;
use crate::dom::virtualmethods::VirtualMethods;
@ -42,6 +43,11 @@ pub struct HTMLSlotElement {
/// <https://html.spec.whatwg.org/multipage/#manually-assigned-nodes>
manually_assigned_nodes: RefCell<Vec<Slottable>>,
/// Whether there is a queued signal change for this element
///
/// Necessary to avoid triggering too many slotchange events
is_in_agents_signal_slots: Cell<bool>,
}
impl HTMLSlotElementMethods<crate::DomTypeHolder> for HTMLSlotElement {
@ -165,6 +171,7 @@ impl HTMLSlotElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
assigned_nodes: Default::default(),
manually_assigned_nodes: Default::default(),
is_in_agents_signal_slots: Default::default(),
}
}
@ -319,7 +326,7 @@ impl HTMLSlotElement {
// then run signal a slot change for slot.
let slots_are_identical = self.assigned_nodes.borrow().iter().eq(slottables.iter());
if !slots_are_identical {
ScriptThread::signal_a_slot_change(self);
self.signal_a_slot_change();
}
// Step 3. Set slots assigned nodes to slottables.
@ -330,6 +337,25 @@ impl HTMLSlotElement {
slottable.set_assigned_slot(DomRoot::from_ref(self));
}
}
/// <https://dom.spec.whatwg.org/#signal-a-slot-change>
pub(crate) fn signal_a_slot_change(&self) {
if self.is_in_agents_signal_slots.get() {
return;
}
self.is_in_agents_signal_slots.set(true);
// Step 1. Append slot to slots relevant agents signal slots.
ScriptThread::add_signal_slot(self);
// Step 2. Queue a mutation observer microtask.
MutationObserver::queue_mutation_observer_microtask();
}
pub(crate) fn remove_from_signal_slots(&self) {
debug_assert!(self.is_in_agents_signal_slots.get());
self.is_in_agents_signal_slots.set(false);
}
}
impl Slottable {

View file

@ -2171,7 +2171,7 @@ impl Node {
if parent.is_in_a_shadow_tree() {
if let Some(slot_element) = parent.downcast::<HTMLSlotElement>() {
if !slot_element.has_assigned_nodes() {
ScriptThread::signal_a_slot_change(slot_element);
slot_element.signal_a_slot_change();
}
}
}
@ -2373,7 +2373,7 @@ impl Node {
if parent.is_in_a_shadow_tree() {
if let Some(slot_element) = parent.downcast::<HTMLSlotElement>() {
if !slot_element.has_assigned_nodes() {
ScriptThread::signal_a_slot_change(slot_element);
slot_element.signal_a_slot_change();
}
}
}