diff --git a/components/script/animations.rs b/components/script/animations.rs index aaef7dcdc3f..0c9366dbcf3 100644 --- a/components/script/animations.rs +++ b/components/script/animations.rs @@ -457,7 +457,12 @@ impl Animations { }); } + /// An implementation of the final steps of + /// . pub(crate) fn send_pending_events(&self, window: &Window, can_gc: CanGc) { + // > 4. Let events to dispatch be a copy of doc’s pending animation event queue. + // > 5. Clear doc’s pending animation event queue. + // // Take all of the events here, in case sending one of these events // triggers adding new events by forcing a layout. let events = std::mem::take(&mut *self.pending_events.borrow_mut()); @@ -465,6 +470,18 @@ impl Animations { return; } + // > 6. Perform a stable sort of the animation events in events to dispatch as follows: + // > 1. Sort the events by their scheduled event time such that events that were + // > scheduled to occur earlier sort before events scheduled to occur later, and + // > events whose scheduled event time is unresolved sort before events with a + // > resolved scheduled event time. + // > 2. Within events with equal scheduled event times, sort by their composite + // > order. + // + // TODO: Sorting of animation events isn't done yet. + + // 7. Dispatch each of the events in events to dispatch at their corresponding + // target using the order established in the previous step. for event in events.into_iter() { // We root the node here to ensure that sending this event doesn't // unroot it as a side-effect. diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 436b7c0d4d9..7021cbe62d5 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -182,7 +182,7 @@ use crate::dom::window::{ReflowReason, Window}; use crate::dom::windowproxy::WindowProxy; use crate::fetch::FetchCanceller; use crate::network_listener::{NetworkListener, PreInvoke}; -use crate::realms::{AlreadyInRealm, InRealm}; +use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; use crate::script_runtime::{CanGc, CommonScriptMsg, ScriptThreadEventCategory}; use crate::script_thread::{MainThreadScriptMsg, ScriptThread}; use crate::stylesheet_set::StylesheetSetRef; @@ -2054,7 +2054,7 @@ impl Document { } /// - pub fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 { + pub(crate) fn request_animation_frame(&self, callback: AnimationFrameCallback) -> u32 { let ident = self.animation_frame_ident.get() + 1; self.animation_frame_ident.set(ident); @@ -2089,7 +2089,7 @@ impl Document { } /// - pub fn cancel_animation_frame(&self, ident: u32) { + pub(crate) fn cancel_animation_frame(&self, ident: u32) { let mut list = self.animation_frame_list.borrow_mut(); if let Some(pair) = list.iter_mut().find(|pair| pair.0 == ident) { pair.1 = None; @@ -2097,7 +2097,7 @@ impl Document { } /// - pub fn run_the_animation_frame_callbacks(&self, can_gc: CanGc) { + pub(crate) fn run_the_animation_frame_callbacks(&self, can_gc: CanGc) { rooted_vec!(let mut animation_frame_list); mem::swap( &mut *animation_frame_list, @@ -3450,29 +3450,6 @@ impl Document { } } - /// Note a pending animation tick, to be processed at the next `update_the_rendering` task. - pub fn note_pending_animation_tick(&self, tick_type: AnimationTickType) { - self.pending_animation_ticks.borrow_mut().extend(tick_type); - } - - /// Whether this document has received an animation tick for rafs. - pub fn has_received_raf_tick(&self) -> bool { - self.pending_animation_ticks - .borrow() - .contains(AnimationTickType::REQUEST_ANIMATION_FRAME) - } - - /// As part of a `update_the_rendering` task, tick all pending animations. - pub fn tick_all_animations(&self, should_run_rafs: bool, can_gc: CanGc) { - let tick_type = mem::take(&mut *self.pending_animation_ticks.borrow_mut()); - if should_run_rafs { - self.run_the_animation_frame_callbacks(can_gc); - } - if tick_type.contains(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS) { - self.maybe_mark_animating_nodes_as_dirty(); - } - } - /// Note a pending compositor event, to be processed at the next `update_the_rendering` task. pub fn note_pending_compositor_event(&self, event: CompositorEvent) { let mut pending_compositor_events = self.pending_compositor_events.borrow_mut(); @@ -4162,22 +4139,20 @@ impl Document { .collect() } - pub(crate) fn advance_animation_timeline_for_testing(&self, delta: f64) { - self.animation_timeline.borrow_mut().advance_specific(delta); - let current_timeline_value = self.current_animation_timeline_value(); - self.animations - .borrow() - .update_for_new_timeline_value(&self.window, current_timeline_value); + /// Note a pending animation tick, to be processed at the next `update_the_rendering` task. + pub(crate) fn note_pending_animation_tick(&self, tick_type: AnimationTickType) { + self.pending_animation_ticks.borrow_mut().extend(tick_type); } - pub(crate) fn update_animation_timeline(&self) { - // Only update the time if it isn't being managed by a test. - if !pref!(layout.animations.test.enabled) { - self.animation_timeline.borrow_mut().update(); - } + /// Whether this document has received an animation tick for rafs. + pub(crate) fn has_received_raf_tick(&self) -> bool { + self.pending_animation_ticks + .borrow() + .contains(AnimationTickType::REQUEST_ANIMATION_FRAME) + } - // We still want to update the animations, because our timeline - // value might have been advanced previously via the TestBinding. + pub(crate) fn advance_animation_timeline_for_testing(&self, delta: f64) { + self.animation_timeline.borrow_mut().advance_specific(delta); let current_timeline_value = self.current_animation_timeline_value(); self.animations .borrow() @@ -4214,6 +4189,35 @@ impl Document { self.animations.borrow().cancel_animations_for_node(node); } + /// An implementation of . + pub(crate) fn update_animations_and_send_events(&self, can_gc: CanGc) { + // Only update the time if it isn't being managed by a test. + if !pref!(layout.animations.test.enabled) { + self.animation_timeline.borrow_mut().update(); + } + + // > 1. Update the current time of all timelines associated with doc passing now + // > as the timestamp. + // > 2. Remove replaced animations for doc. + // + // We still want to update the animations, because our timeline + // value might have been advanced previously via the TestBinding. + let current_timeline_value = self.current_animation_timeline_value(); + self.animations + .borrow() + .update_for_new_timeline_value(&self.window, current_timeline_value); + self.maybe_mark_animating_nodes_as_dirty(); + + // > 3. Perform a microtask checkpoint. + self.window() + .upcast::() + .perform_a_microtask_checkpoint(can_gc); + + // Steps 4 through 7 occur inside `send_pending_events().` + let _realm = enter_realm(self); + self.animations().send_pending_events(self.window(), can_gc); + } + pub(crate) fn will_declaratively_refresh(&self) -> bool { self.declarative_refresh.borrow().is_some() } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index d26de14250c..9f080566a8a 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1665,8 +1665,10 @@ impl ScriptThread { document.react_to_environment_changes() } - // Update animations and send events. - self.update_animations_and_send_events(can_gc); + // > 11. For each doc of docs, update animations and send events for doc, passing + // > in relative high resolution time given frameTimestamp and doc's relevant + // > global object as the timestamp [WEBANIMATIONS] + document.update_animations_and_send_events(can_gc); // TODO(#31866): Implement "run the fullscreen steps" from // https://fullscreen.spec.whatwg.org/multipage/#run-the-fullscreen-steps. @@ -1674,8 +1676,12 @@ impl ScriptThread { // TODO(#31868): Implement the "context lost steps" from // https://html.spec.whatwg.org/multipage/#context-lost-steps. - // Run the animation frame callbacks. - document.tick_all_animations(should_run_rafs, can_gc); + // > 14. For each doc of docs, run the animation frame callbacks for doc, passing + // > in the relative high resolution time given frameTimestamp and doc's + // > relevant global object as the timestamp. + if should_run_rafs { + document.run_the_animation_frame_callbacks(can_gc); + } // Run the resize observer steps. let _realm = enter_realm(&*document); @@ -2050,22 +2056,6 @@ impl ScriptThread { true } - // Perform step 7.10 from https://html.spec.whatwg.org/multipage/#event-loop-processing-model. - // Described at: https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events - fn update_animations_and_send_events(&self, can_gc: CanGc) { - for (_, document) in self.documents.borrow().iter() { - document.update_animation_timeline(); - document.maybe_mark_animating_nodes_as_dirty(); - } - - for (_, document) in self.documents.borrow().iter() { - let _realm = enter_realm(&*document); - document - .animations() - .send_pending_events(document.window(), can_gc); - } - } - fn categorize_msg(&self, msg: &MixedMessage) -> ScriptThreadEventCategory { match *msg { MixedMessage::FromConstellation(ref inner_msg) => match *inner_msg {