diff options
author | Martin Robinson <mrobinson@igalia.com> | 2020-05-07 18:37:18 +0200 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2020-05-12 10:22:14 +0200 |
commit | 3b0619aedd967238091367f6e03dabf262e116ad (patch) | |
tree | b2427a9f19befc3017860053d3a416fda3d87275 | |
parent | aa9f16ce45f6eb22ffd5bc6a10802ded4cc2319b (diff) | |
download | servo-3b0619aedd967238091367f6e03dabf262e116ad.tar.gz servo-3b0619aedd967238091367f6e03dabf262e116ad.zip |
Move most animation processing to script
This is preparation for sharing this code with layout_2020 and
implementing selective off-the-main-thread animations.
We still look for nodes not in the flow tree in the layout thread.
-rw-r--r-- | Cargo.lock | 3 | ||||
-rw-r--r-- | components/constellation/constellation.rs | 3 | ||||
-rw-r--r-- | components/layout/animation.rs | 218 | ||||
-rw-r--r-- | components/layout/context.rs | 4 | ||||
-rw-r--r-- | components/layout/lib.rs | 1 | ||||
-rw-r--r-- | components/layout_thread/lib.rs | 75 | ||||
-rw-r--r-- | components/layout_thread_2020/lib.rs | 4 | ||||
-rw-r--r-- | components/msg/constellation_msg.rs | 1 | ||||
-rw-r--r-- | components/script/Cargo.toml | 1 | ||||
-rw-r--r-- | components/script/animation_timeline.rs | 2 | ||||
-rw-r--r-- | components/script/animations.rs | 293 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 32 | ||||
-rw-r--r-- | components/script/dom/document.rs | 20 | ||||
-rw-r--r-- | components/script/dom/window.rs | 12 | ||||
-rw-r--r-- | components/script/lib.rs | 1 | ||||
-rw-r--r-- | components/script/script_thread.rs | 56 | ||||
-rw-r--r-- | components/script_layout_interface/Cargo.toml | 2 | ||||
-rw-r--r-- | components/script_layout_interface/message.rs | 16 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 55 | ||||
-rw-r--r-- | components/script_traits/script_msg.rs | 3 | ||||
-rw-r--r-- | components/style/animation.rs | 13 |
21 files changed, 444 insertions, 371 deletions
diff --git a/Cargo.lock b/Cargo.lock index fab3ceb8e28..0f5502f8c31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4562,6 +4562,7 @@ dependencies = [ "enum-iterator", "euclid", "fnv", + "fxhash", "headers", "html5ever", "http", @@ -4641,6 +4642,7 @@ dependencies = [ "canvas_traits", "crossbeam-channel", "euclid", + "fxhash", "gfx_traits", "html5ever", "ipc-channel", @@ -4650,6 +4652,7 @@ dependencies = [ "metrics", "msg", "net_traits", + "parking_lot", "profile_traits", "range", "script_traits", diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 00dcd2a4eb8..423314e6b0a 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -2237,9 +2237,6 @@ where fn handle_request_from_layout(&mut self, message: FromLayoutMsg) { debug!("Constellation got {:?} message", message); match message { - FromLayoutMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => { - self.handle_change_running_animations_state(pipeline_id, animation_state) - }, // Layout sends new sizes for all subframes. This needs to be reflected by all // frame trees in the navigation context containing the subframe. FromLayoutMsg::IFrameSizes(iframe_sizes) => { diff --git a/components/layout/animation.rs b/components/layout/animation.rs deleted file mode 100644 index a331b3b3bbc..00000000000 --- a/components/layout/animation.rs +++ /dev/null @@ -1,218 +0,0 @@ -/* 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/. */ - -//! CSS transitions and animations. - -use crate::display_list::items::OpaqueNode; -use crate::flow::{Flow, GetBaseFlow}; -use crate::opaque_node::OpaqueNodeMethods; -use fxhash::{FxHashMap, FxHashSet}; -use ipc_channel::ipc::IpcSender; -use msg::constellation_msg::PipelineId; -use script_traits::UntrustedNodeAddress; -use script_traits::{ - AnimationState as AnimationsPresentState, ConstellationControlMsg, - LayoutMsg as ConstellationMsg, TransitionOrAnimationEvent, TransitionOrAnimationEventType, -}; -use style::animation::{AnimationState, ElementAnimationSet}; - -/// Processes any new animations that were discovered after style recalculation and -/// remove animations for any disconnected nodes. Send messages that trigger events -/// for any events that changed state. -pub fn do_post_style_animations_update( - constellation_chan: &IpcSender<ConstellationMsg>, - script_chan: &IpcSender<ConstellationControlMsg>, - animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationSet>, - pipeline_id: PipelineId, - now: f64, - out: &mut Vec<UntrustedNodeAddress>, - root_flow: &mut dyn Flow, -) { - let had_running_animations = animation_states - .values() - .any(|state| state.needs_animation_ticks()); - - cancel_animations_for_disconnected_nodes(animation_states, root_flow); - collect_newly_animating_nodes(animation_states, out); - - for (node, animation_state) in animation_states.iter_mut() { - update_animation_state(script_chan, animation_state, pipeline_id, now, *node); - } - - // Remove empty states from our collection of states in order to free - // up space as soon as we are no longer tracking any animations for - // a node. - animation_states.retain(|_, state| !state.is_empty()); - - let have_running_animations = animation_states - .values() - .any(|state| state.needs_animation_ticks()); - let present = match (had_running_animations, have_running_animations) { - (true, false) => AnimationsPresentState::NoAnimationsPresent, - (false, true) => AnimationsPresentState::AnimationsPresent, - _ => return, - }; - constellation_chan - .send(ConstellationMsg::ChangeRunningAnimationsState( - pipeline_id, - present, - )) - .unwrap(); -} - -/// Collect newly animating nodes, which is used by the script process during -/// forced, synchronous reflows to root DOM nodes for the duration of their -/// animations or transitions. -pub fn collect_newly_animating_nodes( - animation_states: &FxHashMap<OpaqueNode, ElementAnimationSet>, - out: &mut Vec<UntrustedNodeAddress>, -) { - // This extends the output vector with an iterator that contains a copy of the node - // address for every new animation. The script thread currently stores a rooted node - // for every property that is transitioning. The current strategy of repeating the - // node address is a holdover from when the code here looked different. - out.extend(animation_states.iter().flat_map(|(node, state)| { - let mut num_new_animations = state - .animations - .iter() - .filter(|animation| animation.is_new) - .count(); - num_new_animations += state - .transitions - .iter() - .filter(|transition| transition.is_new) - .count(); - std::iter::repeat(node.to_untrusted_node_address()).take(num_new_animations) - })); -} - -/// Cancel animations for any nodes which have been removed from the DOM or are display:none. -/// We detect this by looking for nodes that are used in the flow tree. -/// TODO(mrobinson): We should look into a way of doing this during flow tree construction. -/// This also doesn't yet handles nodes that have been reparented. -pub fn cancel_animations_for_disconnected_nodes( - animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationSet>, - root_flow: &mut dyn Flow, -) { - // Assume all nodes have been removed until proven otherwise. - let mut invalid_nodes: FxHashSet<OpaqueNode> = animation_states.keys().cloned().collect(); - fn traverse_flow(flow: &mut dyn Flow, invalid_nodes: &mut FxHashSet<OpaqueNode>) { - flow.mutate_fragments(&mut |fragment| { - invalid_nodes.remove(&fragment.node); - }); - for kid in flow.mut_base().children.iter_mut() { - traverse_flow(kid, invalid_nodes) - } - } - traverse_flow(root_flow, &mut invalid_nodes); - - // Cancel animations for any nodes that are no longer in the flow tree. - for node in &invalid_nodes { - if let Some(state) = animation_states.get_mut(node) { - state.cancel_all_animations(); - } - } -} - -fn update_animation_state( - script_channel: &IpcSender<ConstellationControlMsg>, - animation_state: &mut ElementAnimationSet, - pipeline_id: PipelineId, - now: f64, - node: OpaqueNode, -) { - let send_event = |property_or_animation_name, event_type, elapsed_time| { - script_channel - .send(ConstellationControlMsg::TransitionOrAnimationEvent( - TransitionOrAnimationEvent { - pipeline_id, - event_type, - node: node.to_untrusted_node_address(), - property_or_animation_name, - elapsed_time, - }, - )) - .unwrap() - }; - - handle_canceled_animations(animation_state, now, send_event); - finish_running_animations(animation_state, now, send_event); - handle_new_animations(animation_state, send_event); -} - -/// Walk through the list of running animations and remove all of the ones that -/// have ended. -fn finish_running_animations( - animation_state: &mut ElementAnimationSet, - now: f64, - mut send_event: impl FnMut(String, TransitionOrAnimationEventType, f64), -) { - for animation in animation_state.animations.iter_mut() { - if animation.state == AnimationState::Running && animation.has_ended(now) { - animation.state = AnimationState::Finished; - send_event( - animation.name.to_string(), - TransitionOrAnimationEventType::AnimationEnd, - animation.active_duration(), - ); - } - } - - for transition in animation_state.transitions.iter_mut() { - if transition.state == AnimationState::Running && transition.has_ended(now) { - transition.state = AnimationState::Finished; - send_event( - transition.property_animation.property_name().into(), - TransitionOrAnimationEventType::TransitionEnd, - transition.property_animation.duration, - ); - } - } -} - -/// Send events for canceled animations. Currently this only handles canceled -/// transitions, but eventually this should handle canceled CSS animations as -/// well. -fn handle_canceled_animations( - animation_state: &mut ElementAnimationSet, - now: f64, - mut send_event: impl FnMut(String, TransitionOrAnimationEventType, f64), -) { - for transition in &animation_state.transitions { - if transition.state == AnimationState::Canceled { - // TODO(mrobinson): We need to properly compute the elapsed_time here - // according to https://drafts.csswg.org/css-transitions/#event-transitionevent - send_event( - transition.property_animation.property_name().into(), - TransitionOrAnimationEventType::TransitionCancel, - (now - transition.start_time).max(0.), - ); - } - } - - // TODO(mrobinson): We need to send animationcancel events. - animation_state.clear_canceled_animations(); -} - -fn handle_new_animations( - animation_state: &mut ElementAnimationSet, - mut send_event: impl FnMut(String, TransitionOrAnimationEventType, f64), -) { - for animation in animation_state.animations.iter_mut() { - animation.is_new = false; - } - - for transition in animation_state.transitions.iter_mut() { - if transition.is_new { - // TODO(mrobinson): We need to properly compute the elapsed_time here - // according to https://drafts.csswg.org/css-transitions/#event-transitionevent - send_event( - transition.property_animation.property_name().into(), - TransitionOrAnimationEventType::TransitionRun, - 0., - ); - transition.is_new = false; - } - } -} diff --git a/components/layout/context.rs b/components/layout/context.rs index 00e0c5a0cc1..a17bc1db4ee 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -17,7 +17,6 @@ use net_traits::image_cache::{ use parking_lot::RwLock; use script_layout_interface::{PendingImage, PendingImageState}; use script_traits::Painter; -use script_traits::UntrustedNodeAddress; use servo_atoms::Atom; use servo_url::{ImmutableOrigin, ServoUrl}; use std::cell::{RefCell, RefMut}; @@ -85,9 +84,6 @@ pub struct LayoutContext<'a> { /// A list of in-progress image loads to be shared with the script thread. pub pending_images: Mutex<Vec<PendingImage>>, - - /// A list of nodes that have just initiated a CSS transition or animation. - pub newly_animating_nodes: Mutex<Vec<UntrustedNodeAddress>>, } impl<'a> Drop for LayoutContext<'a> { diff --git a/components/layout/lib.rs b/components/layout/lib.rs index fdf3971fc3f..98e04f7d93e 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -20,7 +20,6 @@ extern crate serde; #[macro_use] pub mod layout_debug; -pub mod animation; mod block; pub mod construct; pub mod context; diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index ff0b11875f7..037fffc34ec 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -29,7 +29,7 @@ use crossbeam_channel::{Receiver, Sender}; use embedder_traits::resources::{self, Resource}; use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D}; use fnv::FnvHashMap; -use fxhash::FxHashMap; +use fxhash::{FxHashMap, FxHashSet}; use gfx::font; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context; @@ -37,7 +37,6 @@ use gfx_traits::{node_id_from_scroll_id, Epoch}; use histogram::Histogram; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; -use layout::animation; use layout::construct::ConstructionResult; use layout::context::malloc_size_of_persistent_local_context; use layout::context::LayoutContext; @@ -191,9 +190,6 @@ pub struct LayoutThread { /// The document-specific shared lock used for author-origin stylesheets document_shared_lock: Option<SharedRwLock>, - /// The animation state for all of our nodes. - animation_states: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>, - /// A counter for epoch messages epoch: Cell<Epoch>, @@ -552,7 +548,6 @@ impl LayoutThread { outstanding_web_fonts: Arc::new(AtomicUsize::new(0)), root_flow: RefCell::new(None), document_shared_lock: None, - animation_states: ServoArc::new(RwLock::new(Default::default())), // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR epoch: Cell::new(Epoch(1)), viewport_size: Size2D::new(Au(0), Au(0)), @@ -613,6 +608,7 @@ impl LayoutThread { snapshot_map: &'a SnapshotMap, origin: ImmutableOrigin, animation_timeline_value: f64, + animation_states: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>, ) -> LayoutContext<'a> { LayoutContext { id: self.id, @@ -622,7 +618,7 @@ impl LayoutThread { options: GLOBAL_STYLE_DATA.options.clone(), guards, visited_styles_enabled: false, - animation_states: self.animation_states.clone(), + animation_states, registered_speculative_painters: &self.registered_painters, current_time_for_animations: animation_timeline_value, traversal_flags: TraversalFlags::empty(), @@ -632,7 +628,6 @@ impl LayoutThread { font_cache_thread: Mutex::new(self.font_cache_thread.clone()), webrender_image_cache: self.webrender_image_cache.clone(), pending_images: Mutex::new(vec![]), - newly_animating_nodes: Mutex::new(vec![]), registered_painters: &self.registered_painters, } } @@ -657,7 +652,6 @@ impl LayoutThread { }, Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint, Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart, - Msg::GetRunningAnimations(..) => LayoutHangAnnotation::GetRunningAnimations, }; self.background_hang_monitor .as_ref() @@ -824,15 +818,6 @@ impl LayoutThread { Msg::SetNavigationStart(time) => { self.paint_time_metrics.set_navigation_start(time); }, - Msg::GetRunningAnimations(sender) => { - let running_animation_count = self - .animation_states - .read() - .values() - .map(|state| state.running_animation_and_transition_count()) - .sum(); - let _ = sender.send(running_animation_count); - }, } true @@ -1413,8 +1398,13 @@ impl LayoutThread { self.stylist.flush(&guards, Some(element), Some(&map)); // Create a layout context for use throughout the following passes. - let mut layout_context = - self.build_layout_context(guards.clone(), &map, origin, data.animation_timeline_value); + let mut layout_context = self.build_layout_context( + guards.clone(), + &map, + origin, + data.animation_timeline_value, + data.animations.clone(), + ); let pool; let (thread_pool, num_threads) = if self.parallel_flag { @@ -1516,8 +1506,6 @@ impl LayoutThread { ) { reflow_result.pending_images = std::mem::replace(&mut *context.pending_images.lock().unwrap(), vec![]); - reflow_result.newly_animating_nodes = - std::mem::replace(&mut *context.newly_animating_nodes.lock().unwrap(), vec![]); let mut root_flow = match self.root_flow.borrow().clone() { Some(root_flow) => root_flow, @@ -1629,6 +1617,33 @@ impl LayoutThread { rw_data.scroll_offsets = layout_scroll_states } + /// Cancel animations for any nodes which have been removed from flow tree. + /// TODO(mrobinson): We should look into a way of doing this during flow tree construction. + /// This also doesn't yet handles nodes that have been reparented. + fn cancel_animations_for_nodes_not_in_flow_tree( + animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationSet>, + root_flow: &mut dyn Flow, + ) { + // Assume all nodes have been removed until proven otherwise. + let mut invalid_nodes: FxHashSet<OpaqueNode> = animation_states.keys().cloned().collect(); + fn traverse_flow(flow: &mut dyn Flow, invalid_nodes: &mut FxHashSet<OpaqueNode>) { + flow.mutate_fragments(&mut |fragment| { + invalid_nodes.remove(&fragment.node); + }); + for kid in flow.mut_base().children.iter_mut() { + traverse_flow(kid, invalid_nodes) + } + } + traverse_flow(root_flow, &mut invalid_nodes); + + // Cancel animations for any nodes that are no longer in the flow tree. + for node in &invalid_nodes { + if let Some(state) = animation_states.get_mut(node) { + state.cancel_all_animations(); + } + } + } + fn perform_post_style_recalc_layout_passes( &self, root_flow: &mut FlowRef, @@ -1638,18 +1653,10 @@ impl LayoutThread { rw_data: &mut LayoutThreadData, context: &mut LayoutContext, ) { - { - let mut newly_animating_nodes = context.newly_animating_nodes.lock().unwrap(); - animation::do_post_style_animations_update( - &self.constellation_chan, - &self.script_chan, - &mut *(self.animation_states.write()), - self.id, - context.style_context.current_time_for_animations, - &mut newly_animating_nodes, - FlowRef::deref_mut(root_flow), - ); - } + Self::cancel_animations_for_nodes_not_in_flow_tree( + &mut *(context.style_context.animation_states.write()), + FlowRef::deref_mut(root_flow), + ); profile( profile_time::ProfilerCategory::LayoutRestyleDamagePropagation, diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 42a5a4d3e00..d97c29459ac 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -615,7 +615,6 @@ impl LayoutThread { }, Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint, Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart, - Msg::GetRunningAnimations(..) => LayoutHangAnnotation::GetRunningAnimations, }; self.background_hang_monitor .as_ref() @@ -766,9 +765,6 @@ impl LayoutThread { Msg::SetNavigationStart(time) => { self.paint_time_metrics.set_navigation_start(time); }, - Msg::GetRunningAnimations(sender) => { - let _ = sender.send(0); - }, } true diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index c70021421d6..8a599405df8 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -541,7 +541,6 @@ pub enum LayoutHangAnnotation { UpdateScrollStateFromScript, RegisterPaint, SetNavigationStart, - GetRunningAnimations, } #[derive(Clone, Copy, Debug, Deserialize, Serialize)] diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index f4d8808c869..9f98ff30909 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -53,6 +53,7 @@ encoding_rs = "0.8" enum-iterator = "0.3" euclid = "0.20" fnv = "1.0" +fxhash = "0.2" headers = "0.2" html5ever = "0.25" http = "0.1" diff --git a/components/script/animation_timeline.rs b/components/script/animation_timeline.rs index e0ad520db61..54127b94e9e 100644 --- a/components/script/animation_timeline.rs +++ b/components/script/animation_timeline.rs @@ -12,7 +12,7 @@ use time; /// A `AnimationTimeline` which is used to synchronize animations during the script /// event loop. #[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)] -pub struct AnimationTimeline { +pub(crate) struct AnimationTimeline { current_value: f64, } diff --git a/components/script/animations.rs b/components/script/animations.rs new file mode 100644 index 00000000000..a666ac3ed84 --- /dev/null +++ b/components/script/animations.rs @@ -0,0 +1,293 @@ +/* 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/. */ + +#![deny(missing_docs)] + +//! The set of animations for a document. + +use crate::dom::window::Window; +use fxhash::FxHashMap; +use libc::c_void; +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use msg::constellation_msg::PipelineId; +use parking_lot::RwLock; +use script_traits::{AnimationState as AnimationsPresentState, ScriptMsg, UntrustedNodeAddress}; +use servo_arc::Arc; +use style::animation::{AnimationState, ElementAnimationSet}; +use style::dom::OpaqueNode; + +/// The set of animations for a document. +/// +/// Make sure to update the MallocSizeOf implementation when changing the +/// contents of this struct. +#[derive(Clone, Debug, Default, JSTraceable)] +pub(crate) struct Animations { + pub sets: Arc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>, + have_running_animations: bool, +} + +impl Animations { + pub(crate) fn new() -> Self { + Animations { + sets: Default::default(), + have_running_animations: false, + } + } + + /// Processes any new animations that were discovered after reflow. Collect messages + /// that trigger events for any animations that changed state. + /// TODO(mrobinson): The specification dictates that this should happen before reflow. + pub(crate) fn do_post_reflow_update(&mut self, window: &Window, now: f64) -> AnimationsUpdate { + let mut update = AnimationsUpdate::new(window.pipeline_id()); + + { + let mut sets = self.sets.write(); + update.collect_newly_animating_nodes(&sets); + + for set in sets.values_mut() { + Self::handle_canceled_animations(set, now, &mut update); + Self::finish_running_animations(set, now, &mut update); + Self::handle_new_animations(set, &mut update); + } + + // Remove empty states from our collection of states in order to free + // up space as soon as we are no longer tracking any animations for + // a node. + sets.retain(|_, state| !state.is_empty()); + } + + self.update_running_animations_presence(window); + + update + } + + pub(crate) fn running_animation_count(&self) -> usize { + self.sets + .read() + .values() + .map(|state| state.running_animation_and_transition_count()) + .sum() + } + + fn update_running_animations_presence(&mut self, window: &Window) { + let have_running_animations = self + .sets + .read() + .values() + .any(|state| state.needs_animation_ticks()); + if have_running_animations == self.have_running_animations { + return; + } + + self.have_running_animations = have_running_animations; + let state = match have_running_animations { + true => AnimationsPresentState::AnimationsPresent, + false => AnimationsPresentState::NoAnimationsPresent, + }; + + window.send_to_constellation(ScriptMsg::ChangeRunningAnimationsState(state)); + } + + /// Walk through the list of running animations and remove all of the ones that + /// have ended. + fn finish_running_animations( + set: &mut ElementAnimationSet, + now: f64, + update: &mut AnimationsUpdate, + ) { + for animation in set.animations.iter_mut() { + if animation.state == AnimationState::Running && animation.has_ended(now) { + animation.state = AnimationState::Finished; + update.add_event( + animation.node, + animation.name.to_string(), + TransitionOrAnimationEventType::AnimationEnd, + animation.active_duration(), + ); + } + } + + for transition in set.transitions.iter_mut() { + if transition.state == AnimationState::Running && transition.has_ended(now) { + transition.state = AnimationState::Finished; + update.add_event( + transition.node, + transition.property_animation.property_name().into(), + TransitionOrAnimationEventType::TransitionEnd, + transition.property_animation.duration, + ); + } + } + } + + /// Send events for canceled animations. Currently this only handles canceled + /// transitions, but eventually this should handle canceled CSS animations as + /// well. + fn handle_canceled_animations( + set: &mut ElementAnimationSet, + now: f64, + update: &mut AnimationsUpdate, + ) { + for transition in &set.transitions { + if transition.state == AnimationState::Canceled { + // TODO(mrobinson): We need to properly compute the elapsed_time here + // according to https://drafts.csswg.org/css-transitions/#event-transitionevent + update.add_event( + transition.node, + transition.property_animation.property_name().into(), + TransitionOrAnimationEventType::TransitionCancel, + (now - transition.start_time).max(0.), + ); + } + } + + // TODO(mrobinson): We need to send animationcancel events. + set.clear_canceled_animations(); + } + + fn handle_new_animations(set: &mut ElementAnimationSet, update: &mut AnimationsUpdate) { + for animation in set.animations.iter_mut() { + animation.is_new = false; + } + + for transition in set.transitions.iter_mut() { + if transition.is_new { + // TODO(mrobinson): We need to properly compute the elapsed_time here + // according to https://drafts.csswg.org/css-transitions/#event-transitionevent + update.add_event( + transition.node, + transition.property_animation.property_name().into(), + TransitionOrAnimationEventType::TransitionRun, + 0., + ); + transition.is_new = false; + } + } + } +} + +impl MallocSizeOf for Animations { + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + self.sets.read().size_of(ops) + self.have_running_animations.size_of(ops) + } +} + +pub(crate) struct AnimationsUpdate { + pub pipeline_id: PipelineId, + pub events: Vec<TransitionOrAnimationEvent>, + pub newly_animating_nodes: Vec<UntrustedNodeAddress>, +} + +impl AnimationsUpdate { + fn new(pipeline_id: PipelineId) -> Self { + AnimationsUpdate { + pipeline_id, + events: Default::default(), + newly_animating_nodes: Default::default(), + } + } + + fn add_event( + &mut self, + node: OpaqueNode, + property_or_animation_name: String, + event_type: TransitionOrAnimationEventType, + elapsed_time: f64, + ) { + let node = UntrustedNodeAddress(node.0 as *const c_void); + self.events.push(TransitionOrAnimationEvent { + pipeline_id: self.pipeline_id, + event_type, + node, + property_or_animation_name, + elapsed_time, + }); + } + + pub(crate) fn is_empty(&self) -> bool { + self.events.is_empty() && self.newly_animating_nodes.is_empty() + } + + /// Collect newly animating nodes, which is used by the script process during + /// forced, synchronous reflows to root DOM nodes for the duration of their + /// animations or transitions. + /// TODO(mrobinson): Look into handling the rooting inside this class. + fn collect_newly_animating_nodes( + &mut self, + animation_states: &FxHashMap<OpaqueNode, ElementAnimationSet>, + ) { + // This extends the output vector with an iterator that contains a copy of the node + // address for every new animation. The script thread currently stores a rooted node + // for every property that is transitioning. The current strategy of repeating the + // node address is a holdover from when the code here looked different. + self.newly_animating_nodes + .extend(animation_states.iter().flat_map(|(node, state)| { + let mut num_new_animations = state + .animations + .iter() + .filter(|animation| animation.is_new) + .count(); + num_new_animations += state + .transitions + .iter() + .filter(|transition| transition.is_new) + .count(); + + let node = UntrustedNodeAddress(node.0 as *const c_void); + std::iter::repeat(node).take(num_new_animations) + })); + } +} + +/// The type of transition event to trigger. These are defined by +/// CSS Transitions § 6.1 and CSS Animations § 4.2 +#[derive(Clone, Debug, Deserialize, JSTraceable, Serialize)] +pub enum TransitionOrAnimationEventType { + /// "The transitionrun event occurs when a transition is created (i.e., when it + /// is added to the set of running transitions)." + TransitionRun, + /// "The transitionend event occurs at the completion of the transition. In the + /// case where a transition is removed before completion, such as if the + /// transition-property is removed, then the event will not fire." + TransitionEnd, + /// "The transitioncancel event occurs when a transition is canceled." + TransitionCancel, + /// "The animationend event occurs when the animation finishes" + AnimationEnd, +} + +impl TransitionOrAnimationEventType { + /// Whether or not this event finalizes the animation or transition. During finalization + /// the DOM object associated with this transition or animation is unrooted. + pub fn finalizes_transition_or_animation(&self) -> bool { + match *self { + Self::TransitionEnd | Self::TransitionCancel | Self::AnimationEnd => true, + Self::TransitionRun => false, + } + } + + /// Whether or not this event is a transition-related event. + pub fn is_transition_event(&self) -> bool { + match *self { + Self::TransitionRun | Self::TransitionEnd | Self::TransitionCancel => true, + Self::AnimationEnd => false, + } + } +} + +#[derive(Deserialize, JSTraceable, Serialize)] +/// A transition or animation event. +pub struct TransitionOrAnimationEvent { + /// The pipeline id of the layout task that sent this message. + pub pipeline_id: PipelineId, + /// The type of transition event this should trigger. + pub event_type: TransitionOrAnimationEventType, + /// The address of the node which owns this transition. + pub node: UntrustedNodeAddress, + /// The name of the property that is transitioning (in the case of a transition) + /// or the name of the animation (in the case of an animation). + pub property_or_animation_name: String, + /// The elapsed time property to send with this transition event. + pub elapsed_time: f64, +} diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index c2543cc984b..26db47233f9 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -93,6 +93,7 @@ use net_traits::response::HttpsState; use net_traits::response::{Response, ResponseBody}; use net_traits::storage_thread::StorageType; use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads}; +use parking_lot::RwLock; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_layout_interface::message::PendingRestyle; @@ -100,9 +101,11 @@ use script_layout_interface::rpc::LayoutRPC; use script_layout_interface::StyleAndOpaqueLayoutData; use script_traits::serializable::BlobImpl; use script_traits::transferable::MessagePortImpl; -use script_traits::{DocumentActivity, DrawAPaintImageResult}; -use script_traits::{MediaSessionActionType, ScriptToConstellationChan, TimerEventId, TimerSource}; -use script_traits::{UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData, WindowSizeType}; +use script_traits::{ + DocumentActivity, DrawAPaintImageResult, MediaSessionActionType, ScriptToConstellationChan, + TimerEventId, TimerSource, UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData, + WindowSizeType, +}; use selectors::matching::ElementSelectorFlags; use serde::{Deserialize, Serialize}; use servo_arc::Arc as ServoArc; @@ -131,6 +134,7 @@ use std::rc::Rc; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime}; +use style::animation::ElementAnimationSet; use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto}; use style::author_styles::AuthorStyles; use style::context::QuirksMode; @@ -172,7 +176,6 @@ unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>); unsafe_no_jsmanaged_fields!(MessagePortImpl); unsafe_no_jsmanaged_fields!(MessagePortId); -unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>); unsafe_no_jsmanaged_fields!(MessagePortRouterId); unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId); @@ -184,8 +187,7 @@ unsafe_no_jsmanaged_fields!(CSSError); unsafe_no_jsmanaged_fields!(&'static Encoding); -unsafe_no_jsmanaged_fields!(RefCell<Decoder>); -unsafe_no_jsmanaged_fields!(RefCell<Vec<u8>>); +unsafe_no_jsmanaged_fields!(Decoder); unsafe_no_jsmanaged_fields!(Reflector); @@ -252,6 +254,12 @@ unsafe impl<T: JSTraceable> JSTraceable for ServoArc<T> { } } +unsafe impl<T: JSTraceable> JSTraceable for RwLock<T> { + unsafe fn trace(&self, trc: *mut JSTracer) { + self.read().trace(trc) + } +} + unsafe impl<T: JSTraceable + ?Sized> JSTraceable for Box<T> { unsafe fn trace(&self, trc: *mut JSTracer) { (**self).trace(trc) @@ -284,6 +292,12 @@ unsafe impl<T: JSTraceable> JSTraceable for DomRefCell<T> { } } +unsafe impl<T: JSTraceable> JSTraceable for RefCell<T> { + unsafe fn trace(&self, trc: *mut JSTracer) { + (*self).borrow().trace(trc) + } +} + unsafe impl JSTraceable for Heap<*mut JSObject> { unsafe fn trace(&self, trc: *mut JSTracer) { if self.get().is_null() { @@ -530,8 +544,7 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId); unsafe_no_jsmanaged_fields!(WebGLVertexArrayId); unsafe_no_jsmanaged_fields!(WebGLVersion); unsafe_no_jsmanaged_fields!(WebGLSLVersion); -unsafe_no_jsmanaged_fields!(RefCell<Option<WebGPU>>); -unsafe_no_jsmanaged_fields!(RefCell<Identities>); +unsafe_no_jsmanaged_fields!(Identities); unsafe_no_jsmanaged_fields!(WebGPU); unsafe_no_jsmanaged_fields!(WebGPUAdapter); unsafe_no_jsmanaged_fields!(WebGPUBuffer); @@ -544,7 +557,7 @@ unsafe_no_jsmanaged_fields!(WebGPUShaderModule); unsafe_no_jsmanaged_fields!(WebGPUCommandBuffer); unsafe_no_jsmanaged_fields!(WebGPUCommandEncoder); unsafe_no_jsmanaged_fields!(WebGPUDevice); -unsafe_no_jsmanaged_fields!(RefCell<Option<RawPass>>); +unsafe_no_jsmanaged_fields!(Option<RawPass>); unsafe_no_jsmanaged_fields!(GPUBufferState); unsafe_no_jsmanaged_fields!(WebXRSwapChainId); unsafe_no_jsmanaged_fields!(MediaList); @@ -586,6 +599,7 @@ unsafe_no_jsmanaged_fields!(MediaSessionActionType); unsafe_no_jsmanaged_fields!(MediaMetadata); unsafe_no_jsmanaged_fields!(WebrenderIpcSender); unsafe_no_jsmanaged_fields!(StreamConsumer); +unsafe_no_jsmanaged_fields!(ElementAnimationSet); unsafe impl<'a> JSTraceable for &'a str { #[inline] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 5e40f433da5..86c5b1caaa1 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::animation_timeline::AnimationTimeline; +use crate::animations::{Animations, AnimationsUpdate}; use crate::document_loader::{DocumentLoader, LoadType}; use crate::dom::attr::Attr; use crate::dom::beforeunloadevent::BeforeUnloadEvent; @@ -384,6 +385,8 @@ pub struct Document { /// A timeline for animations which is used for synchronizing animations. /// https://drafts.csswg.org/web-animations/#timeline animation_timeline: DomRefCell<AnimationTimeline>, + /// Animations for this Document + animations: DomRefCell<Animations>, } #[derive(JSTraceable, MallocSizeOf)] @@ -2913,6 +2916,7 @@ impl Document { } else { DomRefCell::new(AnimationTimeline::new()) }, + animations: DomRefCell::new(Animations::new()), } } @@ -3615,17 +3619,27 @@ impl Document { .collect() } - pub fn advance_animation_timeline_for_testing(&self, delta: f64) { + pub(crate) fn advance_animation_timeline_for_testing(&self, delta: f64) { self.animation_timeline.borrow_mut().advance_specific(delta); } - pub fn update_animation_timeline(&self) { + pub(crate) fn update_animation_timeline(&self) { self.animation_timeline.borrow_mut().update(); } - pub fn current_animation_timeline_value(&self) -> f64 { + pub(crate) fn current_animation_timeline_value(&self) -> f64 { self.animation_timeline.borrow().current_value() } + + pub(crate) fn animations(&self) -> Ref<Animations> { + self.animations.borrow() + } + + pub(crate) fn update_animations(&self) -> AnimationsUpdate { + self.animations + .borrow_mut() + .do_post_reflow_update(&self.window, self.current_animation_timeline_value()) + } } impl Element { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 414ef0f2e45..cbf19039605 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -81,7 +81,7 @@ use dom_struct::dom_struct; use embedder_traits::{EmbedderMsg, EventLoopWaker, PromptDefinition, PromptOrigin, PromptResult}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; -use ipc_channel::ipc::{channel, IpcSender}; +use ipc_channel::ipc::IpcSender; use ipc_channel::router::ROUTER; use js::jsapi::Heap; use js::jsapi::JSAutoRealm; @@ -1305,9 +1305,9 @@ impl WindowMethods for Window { } fn RunningAnimationCount(&self) -> u32 { - let (sender, receiver) = channel().unwrap(); - let _ = self.layout_chan.send(Msg::GetRunningAnimations(sender)); - receiver.recv().unwrap_or(0) as u32 + self.document + .get() + .map_or(0, |d| d.animations().running_animation_count() as u32) } // https://html.spec.whatwg.org/multipage/#dom-name @@ -1643,6 +1643,7 @@ impl Window { dom_count: document.dom_count(), pending_restyles: document.drain_pending_restyles(), animation_timeline_value: document.current_animation_timeline_value(), + animations: document.animations().sets.clone(), }; self.layout_chan @@ -1706,8 +1707,9 @@ impl Window { } } + let update = document.update_animations(); unsafe { - ScriptThread::note_newly_animating_nodes(pipeline_id, complete.newly_animating_nodes); + ScriptThread::process_animations_update(update); } true diff --git a/components/script/lib.rs b/components/script/lib.rs index cd73045456a..886a76e9c95 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -48,6 +48,7 @@ extern crate servo_atoms; extern crate style; mod animation_timeline; +mod animations; #[warn(deprecated)] #[macro_use] mod task; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 008274ded47..9754aa65ede 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -17,6 +17,9 @@ //! a page runs its course and the script thread returns to processing events in the main event //! loop. +use crate::animations::{ + AnimationsUpdate, TransitionOrAnimationEvent, TransitionOrAnimationEventType, +}; use crate::devtools; use crate::document_loader::DocumentLoader; use crate::dom::animationevent::AnimationEvent; @@ -141,8 +144,8 @@ use script_traits::{ LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory, ScriptToConstellationChan, StructuredSerializedData, TimerSchedulerMsg, TouchEventType, - TouchId, TransitionOrAnimationEvent, TransitionOrAnimationEventType, UntrustedNodeAddress, - UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, WindowSizeData, WindowSizeType, + TouchId, UntrustedNodeAddress, UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, + WindowSizeData, WindowSizeType, }; use servo_atoms::Atom; use servo_config::opts; @@ -510,10 +513,8 @@ impl<'a> Iterator for DocumentsIter<'a> { // thread during parsing. For this reason, we don't trace the // incomplete parser contexts during GC. type IncompleteParserContexts = Vec<(PipelineId, ParserContext)>; -unsafe_no_jsmanaged_fields!(RefCell<IncompleteParserContexts>); unsafe_no_jsmanaged_fields!(TaskQueue<MainThreadScriptMsg>); - unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitorRegister); unsafe_no_jsmanaged_fields!(dyn BackgroundHangMonitor); @@ -644,6 +645,9 @@ pub struct ScriptThread { /// of the transition. animating_nodes: DomRefCell<HashMap<PipelineId, Vec<Dom<Node>>>>, + /// Animations events that are pending to be sent. + animation_events: RefCell<Vec<TransitionOrAnimationEvent>>, + /// <https://html.spec.whatwg.org/multipage/#custom-element-reactions-stack> custom_element_reaction_stack: CustomElementReactionStack, @@ -826,20 +830,35 @@ impl ScriptThread { }) } - pub unsafe fn note_newly_animating_nodes( - pipeline_id: PipelineId, - nodes: Vec<UntrustedNodeAddress>, - ) { + /// Consume the list of pointer addresses corresponding to DOM nodes that are animating + /// and root them in a per-pipeline list of nodes. + /// + /// Unsafety: any pointer to invalid memory (ie. a GCed node) will trigger a crash. + /// TODO: ensure caller uses rooted nodes instead of unsafe node addresses. + pub(crate) unsafe fn process_animations_update(mut update: AnimationsUpdate) { + if update.is_empty() { + return; + } + SCRIPT_THREAD_ROOT.with(|root| { let script_thread = &*root.get().unwrap(); + + if !update.events.is_empty() { + script_thread + .animation_events + .borrow_mut() + .append(&mut update.events); + } + let js_runtime = script_thread.js_runtime.rt(); - let new_nodes = nodes + let new_nodes = update + .newly_animating_nodes .into_iter() .map(|n| Dom::from_ref(&*from_untrusted_node_address(js_runtime, n))); script_thread .animating_nodes .borrow_mut() - .entry(pipeline_id) + .entry(update.pipeline_id) .or_insert_with(Vec::new) .extend(new_nodes); }) @@ -1354,6 +1373,7 @@ impl ScriptThread { docs_with_no_blocking_loads: Default::default(), animating_nodes: Default::default(), + animation_events: Default::default(), custom_element_reaction_stack: CustomElementReactionStack::new(), @@ -1620,9 +1640,8 @@ impl ScriptThread { } // Perform step 11.10 from https://html.spec.whatwg.org/multipage/#event-loops. - // TODO(mrobinson): This should also update the current animations and send events - // to conform to the HTML specification. This might mean having events for rooting - // DOM nodes and ultimately all animations living in script. + // TODO(mrobinson): This should also update the current animations to conform to + // the HTML specification. fn update_animations_and_send_events(&self) { for (_, document) in self.documents.borrow().iter() { // Only update the time if it isn't being managed by a test. @@ -1630,6 +1649,13 @@ impl ScriptThread { document.update_animation_timeline(); } } + + // We remove the events because handling these events might trigger + // a reflow which might want to add more events to the queue. + let events = self.animation_events.replace(Vec::new()); + for event in events.into_iter() { + self.handle_transition_or_animation_event(&event); + } } fn categorize_msg(&self, msg: &MixedMessage) -> ScriptThreadEventCategory { @@ -1720,7 +1746,6 @@ impl ScriptThread { FocusIFrame(id, ..) => Some(id), WebDriverScriptCommand(id, ..) => Some(id), TickAllAnimations(id, ..) => Some(id), - TransitionOrAnimationEvent { .. } => None, WebFontLoaded(id) => Some(id), DispatchIFrameLoadEvent { target: _, @@ -1921,9 +1946,6 @@ impl ScriptThread { ConstellationControlMsg::TickAllAnimations(pipeline_id, tick_type) => { self.handle_tick_all_animations(pipeline_id, tick_type) }, - ConstellationControlMsg::TransitionOrAnimationEvent(ref event) => { - self.handle_transition_or_animation_event(event); - }, ConstellationControlMsg::WebFontLoaded(pipeline_id) => { self.handle_web_font_loaded(pipeline_id) }, diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml index 8f759970c62..80724ac1295 100644 --- a/components/script_layout_interface/Cargo.toml +++ b/components/script_layout_interface/Cargo.toml @@ -16,6 +16,7 @@ atomic_refcell = "0.1" canvas_traits = {path = "../canvas_traits"} crossbeam-channel = "0.4" euclid = "0.20" +fxhash = "0.2" gfx_traits = {path = "../gfx_traits"} html5ever = "0.25" ipc-channel = "0.14" @@ -25,6 +26,7 @@ malloc_size_of_derive = "0.1" metrics = {path = "../metrics"} msg = {path = "../msg"} net_traits = {path = "../net_traits"} +parking_lot = "0.9" profile_traits = {path = "../profile_traits"} range = {path = "../range"} script_traits = {path = "../script_traits"} diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index ac5c0c59ba0..1dcbfe7eb33 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -7,20 +7,25 @@ use crate::{PendingImage, TrustedNodeAddress}; use app_units::Au; use crossbeam_channel::{Receiver, Sender}; use euclid::default::{Point2D, Rect}; +use fxhash::FxHashMap; use gfx_traits::Epoch; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use metrics::PaintTimeMetrics; use msg::constellation_msg::{BackgroundHangMonitorRegister, BrowsingContextId, PipelineId}; use net_traits::image_cache::ImageCache; +use parking_lot::RwLock; use profile_traits::mem::ReportsChan; use script_traits::Painter; -use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; -use script_traits::{ScrollState, UntrustedNodeAddress, WindowSizeData}; +use script_traits::{ + ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, ScrollState, + WindowSizeData, +}; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; use servo_url::{ImmutableOrigin, ServoUrl}; use std::sync::atomic::AtomicBool; use std::sync::Arc; +use style::animation::ElementAnimationSet; use style::context::QuirksMode; use style::dom::OpaqueNode; use style::invalidation::element::restyle_hints::RestyleHint; @@ -87,9 +92,6 @@ pub enum Msg { /// Send to layout the precise time when the navigation started. SetNavigationStart(u64), - - /// Request the current number of animations that are running. - GetRunningAnimations(IpcSender<usize>), } #[derive(Debug, PartialEq)] @@ -183,8 +185,6 @@ pub struct Reflow { pub struct ReflowComplete { /// The list of images that were encountered that are in progress. pub pending_images: Vec<PendingImage>, - /// The list of nodes that initiated a CSS transition. - pub newly_animating_nodes: Vec<UntrustedNodeAddress>, } /// Information needed for a script-initiated reflow. @@ -209,6 +209,8 @@ pub struct ScriptReflow { pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>, /// The current animation timeline value. pub animation_timeline_value: f64, + /// The set of animations for this document. + pub animations: ServoArc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>, } pub struct LayoutThreadInit { diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index aeac1ff6087..fc3b19d2b97 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -282,58 +282,6 @@ pub enum UpdatePipelineIdReason { Traversal, } -/// The type of transition event to trigger. These are defined by -/// CSS Transitions § 6.1 and CSS Animations § 4.2 -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum TransitionOrAnimationEventType { - /// "The transitionrun event occurs when a transition is created (i.e., when it - /// is added to the set of running transitions)." - TransitionRun, - /// "The transitionend event occurs at the completion of the transition. In the - /// case where a transition is removed before completion, such as if the - /// transition-property is removed, then the event will not fire." - TransitionEnd, - /// "The transitioncancel event occurs when a transition is canceled." - TransitionCancel, - /// "The animationend event occurs when the animation finishes" - AnimationEnd, -} - -impl TransitionOrAnimationEventType { - /// Whether or not this event finalizes the animation or transition. During finalization - /// the DOM object associated with this transition or animation is unrooted. - pub fn finalizes_transition_or_animation(&self) -> bool { - match *self { - Self::TransitionEnd | Self::TransitionCancel | Self::AnimationEnd => true, - Self::TransitionRun => false, - } - } - - /// Whether or not this event is a transition-related event. - pub fn is_transition_event(&self) -> bool { - match *self { - Self::TransitionRun | Self::TransitionEnd | Self::TransitionCancel => true, - Self::AnimationEnd => false, - } - } -} - -#[derive(Deserialize, Serialize)] -/// A transition or animation event. -pub struct TransitionOrAnimationEvent { - /// The pipeline id of the layout task that sent this message. - pub pipeline_id: PipelineId, - /// The type of transition event this should trigger. - pub event_type: TransitionOrAnimationEventType, - /// The address of the node which owns this transition. - pub node: UntrustedNodeAddress, - /// The name of the property that is transitioning (in the case of a transition) - /// or the name of the animation (in the case of an animation). - pub property_or_animation_name: String, - /// The elapsed time property to send with this transition event. - pub elapsed_time: f64, -} - /// Messages sent from the constellation or layout to the script thread. #[derive(Deserialize, Serialize)] pub enum ConstellationControlMsg { @@ -420,8 +368,6 @@ pub enum ConstellationControlMsg { WebDriverScriptCommand(PipelineId, WebDriverScriptCommand), /// Notifies script thread that all animations are done TickAllAnimations(PipelineId, AnimationTickType), - /// Notifies the script thread that a transition or animation related event should be sent. - TransitionOrAnimationEvent(TransitionOrAnimationEvent), /// Notifies the script thread that a new Web font has been loaded, and thus the page should be /// reflowed. WebFontLoaded(PipelineId), @@ -481,7 +427,6 @@ impl fmt::Debug for ConstellationControlMsg { FocusIFrame(..) => "FocusIFrame", WebDriverScriptCommand(..) => "WebDriverScriptCommand", TickAllAnimations(..) => "TickAllAnimations", - TransitionOrAnimationEvent { .. } => "TransitionOrAnimationEvent", WebFontLoaded(..) => "WebFontLoaded", DispatchIFrameLoadEvent { .. } => "DispatchIFrameLoadEvent", DispatchStorageEvent(..) => "DispatchStorageEvent", diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 245a02ec6d2..b5687d6217e 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -61,8 +61,6 @@ pub struct IFrameSizeMsg { /// Messages from the layout to the constellation. #[derive(Deserialize, Serialize)] pub enum LayoutMsg { - /// Indicates whether this pipeline is currently running animations. - ChangeRunningAnimationsState(PipelineId, AnimationState), /// Inform the constellation of the size of the iframe's viewport. IFrameSizes(Vec<IFrameSizeMsg>), /// Requests that the constellation inform the compositor that it needs to record @@ -76,7 +74,6 @@ impl fmt::Debug for LayoutMsg { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { use self::LayoutMsg::*; let variant = match *self { - ChangeRunningAnimationsState(..) => "ChangeRunningAnimationsState", IFrameSizes(..) => "IFrameSizes", PendingPaintMetric(..) => "PendingPaintMetric", ViewportConstrained(..) => "ViewportConstrained", diff --git a/components/style/animation.rs b/components/style/animation.rs index e928d81de88..f76759abdfb 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -27,7 +27,7 @@ use servo_arc::Arc; use std::fmt; /// Represents an animation for a given property. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, MallocSizeOf)] pub struct PropertyAnimation { /// An `AnimatedProperty` that this `PropertyAnimation` corresponds to. property: AnimatedProperty, @@ -136,7 +136,7 @@ impl PropertyAnimation { } /// This structure represents the state of an animation. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq)] pub enum AnimationState { /// This animation is paused. The inner field is the percentage of progress /// when it was paused, from 0 to 1. @@ -153,7 +153,7 @@ pub enum AnimationState { /// /// If the iteration count is infinite, there's no other state, otherwise we /// have to keep track the current iteration and the max iteration count. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, MallocSizeOf)] pub enum KeyframesIterationState { /// Infinite iterations, so no need to track a state. Infinite, @@ -164,7 +164,7 @@ pub enum KeyframesIterationState { } /// A CSS Animation -#[derive(Clone)] +#[derive(Clone, MallocSizeOf)] pub struct Animation { /// The node associated with this animation. pub node: OpaqueNode, @@ -198,6 +198,7 @@ pub struct Animation { /// The original cascade style, needed to compute the generated keyframes of /// the animation. + #[ignore_malloc_size_of = "ComputedValues"] pub cascade_style: Arc<ComputedValues>, /// Whether or not this animation is new and or has already been tracked @@ -542,7 +543,7 @@ impl fmt::Debug for Animation { } /// A CSS Transition -#[derive(Clone, Debug)] +#[derive(Clone, Debug, MallocSizeOf)] pub struct Transition { /// The node associated with this animation. pub node: OpaqueNode, @@ -597,7 +598,7 @@ impl Transition { } /// Holds the animation state for a particular element. -#[derive(Debug, Default)] +#[derive(Debug, Default, MallocSizeOf)] pub struct ElementAnimationSet { /// The animations for this element. pub animations: Vec<Animation>, |