diff --git a/Cargo.lock b/Cargo.lock index 190172ab40b..ba027fe771f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2849,6 +2849,7 @@ dependencies = [ "embedder_traits", "euclid", "fnv", + "fxhash", "gfx", "gfx_traits", "html5ever", diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index 3cfc43f3a7a..e36acb4bffe 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -20,6 +20,7 @@ cssparser = "0.27" embedder_traits = { path = "../embedder_traits" } euclid = "0.20" fnv = "1.0" +fxhash = "0.2" gfx = { path = "../gfx" } gfx_traits = { path = "../gfx_traits" } html5ever = "0.25" diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 68bce6ca9e4..94abc8dfc65 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -26,6 +26,7 @@ use crate::style_ext::{Display, DisplayGeneratingBox}; use crate::DefiniteContainingBlock; use app_units::Au; use euclid::default::{Point2D, Rect, Size2D}; +use fxhash::FxHashSet; use gfx_traits::print_tree::PrintTree; use script_layout_interface::wrapper_traits::LayoutNode; use script_layout_interface::{LayoutElementType, LayoutNodeType}; @@ -303,6 +304,15 @@ impl FragmentTree { }) } + pub fn remove_nodes_in_fragment_tree_from_set(&self, set: &mut FxHashSet) { + self.find(|fragment, _| { + if let Some(tag) = fragment.tag().as_ref() { + set.remove(tag); + } + None::<()> + }); + } + pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect { let mut bounding_box = PhysicalRect::zero(); self.find(|fragment, containing_block| { diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index a62a172d19d..13c6697d21b 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -27,7 +27,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_cache_thread::FontCacheThread; use gfx::font_context; use gfx_traits::{node_id_from_scroll_id, Epoch}; @@ -80,9 +80,11 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; use std::thread; use std::time::Duration; +use style::animation::ElementAnimationSet; use style::context::{ QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext, }; +use style::dom::OpaqueNode; use style::dom::{TDocument, TElement, TNode}; use style::driver; use style::error_reporting::RustLogReporter; @@ -572,6 +574,7 @@ impl LayoutThread { snapshot_map: &'a SnapshotMap, origin: ImmutableOrigin, animation_timeline_value: f64, + animation_states: ServoArc>>, ) -> LayoutContext<'a> { LayoutContext { id: self.id, @@ -581,7 +584,7 @@ impl LayoutThread { options: GLOBAL_STYLE_DATA.options.clone(), guards, visited_styles_enabled: false, - animation_states: Default::default(), + animation_states, registered_speculative_painters: &self.registered_painters, current_time_for_animations: animation_timeline_value, traversal_flags: TraversalFlags::empty(), @@ -1062,8 +1065,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 traversal = RecalcStyle::new(layout_context); let token = { @@ -1273,6 +1281,11 @@ impl LayoutThread { document: Option<&ServoLayoutDocument>, context: &mut LayoutContext, ) { + Self::cancel_animations_for_nodes_not_in_fragment_tree( + &mut *(context.style_context.animation_states.write()), + &fragment_tree, + ); + if self.trace_layout { if let Some(box_tree) = &*self.box_tree.borrow() { layout_debug::begin_trace(box_tree.clone(), fragment_tree.clone()); @@ -1356,6 +1369,25 @@ impl LayoutThread { }, }) } + + /// Cancel animations for any nodes which have been removed from fragment 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_fragment_tree( + animation_states: &mut FxHashMap, + root: &FragmentTree, + ) { + // Assume all nodes have been removed until proven otherwise. + let mut invalid_nodes: FxHashSet = animation_states.keys().cloned().collect(); + root.remove_nodes_in_fragment_tree_from_set(&mut invalid_nodes); + + // Cancel animations for any nodes that are no longer in the fragment tree. + for node in &invalid_nodes { + if let Some(state) = animation_states.get_mut(node) { + state.cancel_all_animations(); + } + } + } } impl ProfilerMetadataFactory for LayoutThread { diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 243acb76ea3..4a9b85a0883 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -161,7 +161,6 @@ ${helpers.predefined_type( "Time", "computed::Time::zero()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::Time::zero()", parse_method="parse_non_negative", vector=True, @@ -176,7 +175,6 @@ ${helpers.predefined_type( "TimingFunction", "computed::TimingFunction::ease()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::TimingFunction::ease()", vector=True, need_index=True, @@ -190,7 +188,6 @@ ${helpers.predefined_type( "TransitionProperty", "computed::TransitionProperty::all()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::TransitionProperty::all()", vector=True, allow_empty="NotInitial", @@ -205,7 +202,6 @@ ${helpers.predefined_type( "Time", "computed::Time::zero()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::Time::zero()", vector=True, need_index=True, @@ -221,7 +217,6 @@ ${helpers.predefined_type( "AnimationName", "computed::AnimationName::none()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::AnimationName::none()", vector=True, need_index=True, @@ -236,7 +231,6 @@ ${helpers.predefined_type( "Time", "computed::Time::zero()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::Time::zero()", parse_method="parse_non_negative", vector=True, @@ -253,7 +247,6 @@ ${helpers.predefined_type( "TimingFunction", "computed::TimingFunction::ease()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::TimingFunction::ease()", vector=True, need_index=True, @@ -268,7 +261,6 @@ ${helpers.predefined_type( "AnimationIterationCount", "computed::AnimationIterationCount::one()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::AnimationIterationCount::one()", vector=True, need_index=True, @@ -283,7 +275,6 @@ ${helpers.single_keyword( "animation-direction", "normal reverse alternate alternate-reverse", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", need_index=True, animation_value_type="none", vector=True, @@ -299,7 +290,6 @@ ${helpers.single_keyword( "animation-play-state", "running paused", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", need_index=True, animation_value_type="none", vector=True, @@ -328,7 +318,6 @@ ${helpers.predefined_type( "Time", "computed::Time::zero()", engines="gecko servo-2013 servo-2020", - servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::Time::zero()", vector=True, need_index=True, diff --git a/components/style/properties/shorthands/box.mako.rs b/components/style/properties/shorthands/box.mako.rs index c5fe60834ed..c5cc80829c7 100644 --- a/components/style/properties/shorthands/box.mako.rs +++ b/components/style/properties/shorthands/box.mako.rs @@ -28,7 +28,6 @@ ${helpers.two_properties_shorthand( "(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)", )} -#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] macro_rules! try_parse_one { ($context: expr, $input: expr, $var: ident, $prop_module: ident) => { if $var.is_none() { @@ -43,7 +42,7 @@ macro_rules! try_parse_one { } <%helpers:shorthand name="transition" - engines="gecko servo-2013" + engines="gecko servo-2013 servo-2020" extra_prefixes="moz:layout.css.prefixes.transitions webkit" sub_properties="transition-property transition-duration transition-timing-function @@ -189,7 +188,7 @@ macro_rules! try_parse_one { <%helpers:shorthand name="animation" - engines="gecko servo-2013" + engines="gecko servo-2013 servo-2020" extra_prefixes="moz:layout.css.prefixes.animations webkit" sub_properties="animation-name animation-duration animation-timing-function animation-delay diff --git a/tests/wpt/include-layout-2020.ini b/tests/wpt/include-layout-2020.ini index b5409f289a3..b7ed804bba8 100644 --- a/tests/wpt/include-layout-2020.ini +++ b/tests/wpt/include-layout-2020.ini @@ -9,6 +9,8 @@ skip: true skip: true [CSS2] skip: false + [css-animations] + skip: false [css-backgrounds] skip: false [css-content] @@ -23,6 +25,8 @@ skip: true skip: false [css-transforms] skip: false + [css-transitions] + skip: false [css-ui] skip: false [filter-effects]