mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Fire slot change events when the slot content changes (#35137)
* Add the onslotchange attribute to ShadowRoot Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Add spec comments to MutationObserver::queue_mutation_observer_microtask Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Add DomRefCell::take Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Add spec comments to notify_mutation_observers Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Fire slotchange events when a slot changes Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * ./mach fmt Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Fix check for when to dispatch slot events Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Potentially fire slot change events in Node::remove Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update WPT expectations Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * ./mach fmt Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Bump stylo Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Move "signal a slot change" into ScriptThread impl 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:
parent
cd93841ba1
commit
859cc6ab9b
17 changed files with 152 additions and 119 deletions
|
@ -151,3 +151,14 @@ impl<T> DomRefCell<T> {
|
|||
self.value.try_borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> DomRefCell<T> {
|
||||
/// Takes the wrapped value, leaving `Default::default()` in its place.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value is currently borrowed.
|
||||
pub(crate) fn take(&self) -> T {
|
||||
self.value.take()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ use crate::dom::node::{Node, ShadowIncluding};
|
|||
use crate::dom::text::Text;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::script_runtime::CanGc;
|
||||
use crate::ScriptThread;
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#the-slot-element>
|
||||
#[dom_struct]
|
||||
|
@ -185,6 +186,10 @@ impl HTMLSlotElement {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn has_assigned_nodes(&self) -> bool {
|
||||
!self.assigned_nodes.borrow().is_empty()
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#find-flattened-slotables>
|
||||
fn find_flattened_slottables(&self, result: &mut RootedVec<Slottable>) {
|
||||
// Step 1. Let result be an empty list.
|
||||
|
@ -312,8 +317,12 @@ impl HTMLSlotElement {
|
|||
rooted_vec!(let mut slottables);
|
||||
self.find_slottables(&mut slottables);
|
||||
|
||||
// Step 2. TODO If slottables and slot’s assigned nodes are not identical,
|
||||
// Step 2. If slottables and slot’s assigned nodes are not identical,
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Step 3. Set slot’s assigned nodes to slottables.
|
||||
*self.assigned_nodes.borrow_mut() = slottables.iter().cloned().collect();
|
||||
|
|
|
@ -15,9 +15,11 @@ 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::{reflect_dom_object_with_proto, DomObject, Reflector};
|
||||
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;
|
||||
|
@ -90,35 +92,57 @@ impl MutationObserver {
|
|||
|
||||
/// <https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask>
|
||||
pub(crate) fn queue_mutation_observer_microtask() {
|
||||
// Step 1
|
||||
// Step 1. If the surrounding agent’s mutation observer microtask queued is true, then return.
|
||||
if ScriptThread::is_mutation_observer_microtask_queued() {
|
||||
return;
|
||||
}
|
||||
// Step 2
|
||||
|
||||
// Step 2. Set the surrounding agent’s mutation observer microtask queued to true.
|
||||
ScriptThread::set_mutation_observer_microtask_queued(true);
|
||||
// Step 3
|
||||
|
||||
// Step 3. Queue a microtask to notify mutation observers.
|
||||
ScriptThread::enqueue_microtask(Microtask::NotifyMutationObservers);
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#notify-mutation-observers>
|
||||
pub(crate) fn notify_mutation_observers() {
|
||||
// Step 1
|
||||
pub(crate) fn notify_mutation_observers(can_gc: CanGc) {
|
||||
// Step 1. Set the surrounding agent’s mutation observer microtask queued to false.
|
||||
ScriptThread::set_mutation_observer_microtask_queued(false);
|
||||
// Step 2
|
||||
|
||||
// Step 2. Let notifySet be a clone of the surrounding agent’s pending mutation observers.
|
||||
// TODO Step 3. Empty the surrounding agent’s pending mutation observers.
|
||||
let notify_list = ScriptThread::get_mutation_observers();
|
||||
// TODO: steps 3-4 (slots)
|
||||
// Step 5
|
||||
|
||||
// Step 4. Let signalSet be a clone of the surrounding agent’s signal slots.
|
||||
// Step 5. Empty the surrounding agent’s signal slots.
|
||||
let signal_set = ScriptThread::take_signal_slots();
|
||||
|
||||
// Step 6. For each mo of notifySet:
|
||||
for mo in ¬ify_list {
|
||||
// Step 6.1 Let records be a clone of mo’s record queue.
|
||||
let queue: Vec<DomRoot<MutationRecord>> = mo.record_queue.borrow().clone();
|
||||
|
||||
// Step 6.2 Empty mo’s record queue.
|
||||
mo.record_queue.borrow_mut().clear();
|
||||
// TODO: Step 5.3 Remove all transient registered observers whose observer is mo.
|
||||
|
||||
// TODO Step 6.3 For each node of mo’s node list, remove all transient registered observers
|
||||
// whose observer is mo from node’s registered observer list.
|
||||
|
||||
// Step 6.4 If records is not empty, then invoke mo’s 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);
|
||||
}
|
||||
}
|
||||
// TODO: Step 6 (slot signals)
|
||||
|
||||
// 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/#queueing-a-mutation-record>
|
||||
|
|
|
@ -2323,6 +2323,48 @@ impl Node {
|
|||
let old_next_sibling = node.GetNextSibling();
|
||||
// Steps 9-10 are handled in unbind_from_tree.
|
||||
parent.remove_child(node, cached_index);
|
||||
|
||||
// Step 12. If node is assigned, then run assign slottables for node’s assigned slot.
|
||||
let assigned_slot = node
|
||||
.downcast::<Element>()
|
||||
.and_then(|e| e.assigned_slot())
|
||||
.or_else(|| {
|
||||
node.downcast::<Text>().and_then(|text| {
|
||||
text.slottable_data()
|
||||
.borrow()
|
||||
.assigned_slot
|
||||
.as_ref()
|
||||
.map(Dom::as_rooted)
|
||||
})
|
||||
});
|
||||
if let Some(slot) = assigned_slot {
|
||||
slot.assign_slottables();
|
||||
}
|
||||
|
||||
// Step 13. If parent’s root is a shadow root, and parent is a slot whose assigned nodes is the empty list,
|
||||
// then run signal a slot change for parent.
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 14. If node has an inclusive descendant that is a slot:
|
||||
let has_slot_descendant = node
|
||||
.traverse_preorder(ShadowIncluding::No)
|
||||
.any(|elem| elem.is::<HTMLSlotElement>());
|
||||
if has_slot_descendant {
|
||||
// Step 14.1 Run assign slottables for a tree with parent’s root.
|
||||
parent
|
||||
.GetRootNode(&GetRootNodeOptions::empty())
|
||||
.assign_slottables_for_a_tree();
|
||||
|
||||
// Step 14.2 Run assign slottables for a tree with node.
|
||||
node.assign_slottables_for_a_tree();
|
||||
}
|
||||
|
||||
// Step 11. transient registered observers
|
||||
// Step 12.
|
||||
if let SuppressObserver::Unsuppressed = suppress_observers {
|
||||
|
|
|
@ -325,6 +325,9 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
|
|||
fn SlotAssignment(&self) -> SlotAssignmentMode {
|
||||
self.slot_assignment_mode
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange
|
||||
event_handler!(onslotchange, GetOnslotchange, SetOnslotchange);
|
||||
}
|
||||
|
||||
impl VirtualMethods for ShadowRoot {
|
||||
|
|
|
@ -140,7 +140,7 @@ impl MicrotaskQueue {
|
|||
ScriptThread::invoke_backup_element_queue(can_gc);
|
||||
},
|
||||
Microtask::NotifyMutationObservers => {
|
||||
MutationObserver::notify_mutation_observers();
|
||||
MutationObserver::notify_mutation_observers(can_gc);
|
||||
},
|
||||
Microtask::ReadableStreamTeeReadRequest(ref task) => {
|
||||
task.microtask_chunk_steps(can_gc)
|
||||
|
|
|
@ -128,6 +128,7 @@ use crate::dom::element::Element;
|
|||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::htmlanchorelement::HTMLAnchorElement;
|
||||
use crate::dom::htmliframeelement::HTMLIFrameElement;
|
||||
use crate::dom::htmlslotelement::HTMLSlotElement;
|
||||
use crate::dom::mutationobserver::MutationObserver;
|
||||
use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
|
||||
use crate::dom::performanceentry::PerformanceEntry;
|
||||
|
@ -259,6 +260,9 @@ pub struct ScriptThread {
|
|||
/// 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>>>,
|
||||
|
||||
/// A handle to the WebGL thread
|
||||
#[no_trace]
|
||||
webgl_chan: Option<WebGLPipeline>,
|
||||
|
@ -508,6 +512,26 @@ impl ScriptThread {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn add_signal_slot(observer: &HTMLSlotElement) {
|
||||
with_script_thread(|script_thread| {
|
||||
script_thread
|
||||
.signal_slots
|
||||
.borrow_mut()
|
||||
.push(Dom::from_ref(observer));
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn take_signal_slots() -> Vec<DomRoot<HTMLSlotElement>> {
|
||||
with_script_thread(|script_thread| {
|
||||
script_thread
|
||||
.signal_slots
|
||||
.take()
|
||||
.into_iter()
|
||||
.map(|slot| slot.as_rooted())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn mark_document_with_no_blocked_loads(doc: &Document) {
|
||||
with_script_thread(|script_thread| {
|
||||
script_thread
|
||||
|
@ -914,6 +938,7 @@ impl ScriptThread {
|
|||
closed_pipelines: DomRefCell::new(HashSet::new()),
|
||||
mutation_observer_microtask_queued: Default::default(),
|
||||
mutation_observers: Default::default(),
|
||||
signal_slots: Default::default(),
|
||||
system_font_service,
|
||||
webgl_chan: state.webgl_chan,
|
||||
#[cfg(feature = "webxr")]
|
||||
|
@ -3718,6 +3743,15 @@ impl ScriptThread {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#signal-a-slot-change>
|
||||
pub(crate) fn signal_a_slot_change(slot: &HTMLSlotElement) {
|
||||
// Step 1. Append slot to slot’s relevant agent’s signal slots.
|
||||
ScriptThread::add_signal_slot(slot);
|
||||
|
||||
// Step 2. Queue a mutation observer microtask.
|
||||
MutationObserver::queue_mutation_observer_microtask();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ScriptThread {
|
||||
|
|
|
@ -14,7 +14,7 @@ interface ShadowRoot : DocumentFragment {
|
|||
readonly attribute boolean clonable;
|
||||
// readonly attribute boolean serializable;
|
||||
readonly attribute Element host;
|
||||
// attribute EventHandler onslotchange;
|
||||
attribute EventHandler onslotchange;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue