Bug 1646811 - servo: Add DocumentAnimationSet and AnimationSetKey.
authorMartin Robinson <mrobinson@igalia.com>
Thu, 18 Jun 2020 18:14:02 +0000
changeset 536354 04f8394b23bb2fcc2f19d6baf1a31a66be6bc297
parent 536353 cb3a0bb7571a752bb87866cdc721e75f9314416a
child 536355 0da5430c2b2eb7ad0592dc831be1945750813132
push id37520
push userdluca@mozilla.com
push dateFri, 19 Jun 2020 04:04:08 +0000
treeherdermozilla-central@d1a4f9157858 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1646811
milestone79.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1646811 - servo: Add DocumentAnimationSet and AnimationSetKey. This will be used in order to hold animations for pseudo elements in the DocumentAnimationSet. Also no longer store the OpaqueNode in the animation and transition data structures. This is already part of the DocumentAnimationSet key. Depends on D80241 Differential Revision: https://phabricator.services.mozilla.com/D80242
servo/components/style/animation.rs
servo/components/style/context.rs
servo/components/style/matching.rs
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -14,23 +14,26 @@ use crate::properties::animated_properti
 use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
 use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode;
 use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
 use crate::properties::{
     ComputedValues, Importance, LonghandId, LonghandIdSet, PropertyDeclarationBlock,
     PropertyDeclarationId,
 };
 use crate::rule_tree::CascadeLevel;
+use crate::shared_lock::{Locked, SharedRwLock};
 use crate::style_resolver::StyleResolverForElement;
 use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
 use crate::values::animated::{Animate, Procedure};
 use crate::values::computed::{Time, TimingFunction};
 use crate::values::generics::box_::AnimationIterationCount;
 use crate::values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction};
 use crate::Atom;
+use fxhash::FxHashMap;
+use parking_lot::RwLock;
 use servo_arc::Arc;
 use std::fmt;
 
 /// Represents an animation for a given property.
 #[derive(Clone, Debug, MallocSizeOf)]
 pub struct PropertyAnimation {
     /// The value we are animating from.
     from: AnimationValue,
@@ -388,19 +391,16 @@ impl ComputedKeyframe {
         }
         computed_steps
     }
 }
 
 /// A CSS Animation
 #[derive(Clone, MallocSizeOf)]
 pub struct Animation {
-    /// The node associated with this animation.
-    pub node: OpaqueNode,
-
     /// The name of this animation as defined by the style.
     pub name: Atom,
 
     /// The properties that change in this animation.
     properties_changed: LonghandIdSet,
 
     /// The computed style for each keyframe of this animation.
     computed_steps: Vec<ComputedKeyframe>,
@@ -731,19 +731,16 @@ impl fmt::Debug for Animation {
             .field("cascade_style", &())
             .finish()
     }
 }
 
 /// A CSS Transition
 #[derive(Clone, Debug, MallocSizeOf)]
 pub struct Transition {
-    /// The node associated with this animation.
-    pub node: OpaqueNode,
-
     /// The start time of this transition, which is the current value of the animation
     /// timeline when this transition was created plus any animation delay.
     pub start_time: f64,
 
     /// The delay used for this transition.
     pub delay: f64,
 
     /// The internal style `PropertyAnimation` for this transition.
@@ -886,17 +883,17 @@ impl ElementAnimationSet {
             if transition.state != AnimationState::Finished {
                 self.dirty = true;
                 transition.state = AnimationState::Canceled;
             }
         }
     }
 
     pub(crate) fn apply_active_animations(
-        &mut self,
+        &self,
         context: &SharedStyleContext,
         style: &mut Arc<ComputedValues>,
     ) {
         let now = context.current_time_for_animations;
         let mutable_style = Arc::make_mut(style);
         if let Some(map) = self.get_value_map_for_active_animations(now) {
             for value in map.values() {
                 value.set_in_style_for_servo(mutable_style);
@@ -982,17 +979,16 @@ impl ElementAnimationSet {
     }
 
     /// Update our transitions given a new style, canceling or starting new animations
     /// when appropriate.
     pub fn update_transitions_for_new_style(
         &mut self,
         might_need_transitions_update: bool,
         context: &SharedStyleContext,
-        opaque_node: OpaqueNode,
         old_style: Option<&Arc<ComputedValues>>,
         after_change_style: &Arc<ComputedValues>,
     ) {
         // If this is the first style, we don't trigger any transitions and we assume
         // there were no previously triggered transitions.
         let mut before_change_style = match old_style {
             Some(old_style) => Arc::clone(old_style),
             None => return,
@@ -1010,17 +1006,16 @@ impl ElementAnimationSet {
 
         // We convert old values into `before-change-style` here.
         if self.has_active_transition() || self.has_active_animation() {
             self.apply_active_animations(context, &mut before_change_style);
         }
 
         let transitioning_properties = start_transitions_if_applicable(
             context,
-            opaque_node,
             &before_change_style,
             after_change_style,
             self,
         );
 
         // Cancel any non-finished transitions that have properties which no longer transition.
         for transition in self.transitions.iter_mut() {
             if transition.state == AnimationState::Finished {
@@ -1032,17 +1027,16 @@ impl ElementAnimationSet {
             transition.state = AnimationState::Canceled;
             self.dirty = true;
         }
     }
 
     fn start_transition_if_applicable(
         &mut self,
         context: &SharedStyleContext,
-        opaque_node: OpaqueNode,
         longhand_id: LonghandId,
         index: usize,
         old_style: &ComputedValues,
         new_style: &Arc<ComputedValues>,
     ) {
         let box_style = new_style.get_box();
         let timing_function = box_style.transition_timing_function_mod(index);
         let duration = box_style.transition_duration_mod(index);
@@ -1074,17 +1068,16 @@ impl ElementAnimationSet {
         {
             return;
         }
 
         // We are going to start a new transition, but we might have to update
         // it if we are replacing a reversed transition.
         let reversing_adjusted_start_value = property_animation.from.clone();
         let mut new_transition = Transition {
-            node: opaque_node,
             start_time: now + delay,
             delay,
             property_animation,
             state: AnimationState::Pending,
             is_new: true,
             reversing_adjusted_start_value,
             reversing_shortening_factor: 1.0,
         };
@@ -1138,36 +1131,102 @@ impl ElementAnimationSet {
         for animation in &self.animations {
             animation.get_property_declaration_at_time(now, &mut map);
         }
 
         Some(map)
     }
 }
 
+#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
+/// A key that is used to identify nodes in the `DocumentAnimationSet`.
+pub struct AnimationSetKey(pub OpaqueNode);
+
+#[derive(Clone, Debug, Default, MallocSizeOf)]
+/// A set of animations for a document.
+pub struct DocumentAnimationSet {
+    /// The `ElementAnimationSet`s that this set contains.
+    #[ignore_malloc_size_of = "Arc is hard"]
+    pub sets: Arc<RwLock<FxHashMap<AnimationSetKey, ElementAnimationSet>>>,
+}
+
+impl DocumentAnimationSet {
+    /// Return whether or not the provided node has active CSS animations.
+    pub fn has_active_animations(&self, key: &AnimationSetKey) -> bool {
+        self.sets
+            .read()
+            .get(key)
+            .map(|set| set.has_active_animation())
+            .unwrap_or(false)
+    }
+
+    /// Return whether or not the provided node has active CSS transitions.
+    pub fn has_active_transitions(&self, key: &AnimationSetKey) -> bool {
+        self.sets
+            .read()
+            .get(key)
+            .map(|set| set.has_active_transition())
+            .unwrap_or(false)
+    }
+
+    /// Return a locked PropertyDeclarationBlock with animation values for the given
+    /// key and time.
+    pub fn get_animation_declarations(
+        &self,
+        key: &AnimationSetKey,
+        time: f64,
+        shared_lock: &SharedRwLock,
+    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
+        self.sets
+            .read()
+            .get(key)
+            .and_then(|set| set.get_value_map_for_active_animations(time))
+            .map(|map| {
+                let block = PropertyDeclarationBlock::from_animation_value_map(&map);
+                Arc::new(shared_lock.wrap(block))
+            })
+    }
+
+    /// Return a locked PropertyDeclarationBlock with transition values for the given
+    /// key and time.
+    pub fn get_transition_declarations(
+        &self,
+        key: &AnimationSetKey,
+        time: f64,
+        shared_lock: &SharedRwLock,
+    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
+        self.sets
+            .read()
+            .get(key)
+            .and_then(|set| set.get_value_map_for_active_transitions(time))
+            .map(|map| {
+                let block = PropertyDeclarationBlock::from_animation_value_map(&map);
+                Arc::new(shared_lock.wrap(block))
+            })
+    }
+}
+
 /// Kick off any new transitions for this node and return all of the properties that are
 /// transitioning. This is at the end of calculating style for a single node.
 pub fn start_transitions_if_applicable(
     context: &SharedStyleContext,
-    opaque_node: OpaqueNode,
     old_style: &ComputedValues,
     new_style: &Arc<ComputedValues>,
     animation_state: &mut ElementAnimationSet,
 ) -> LonghandIdSet {
     let mut properties_that_transition = LonghandIdSet::new();
     for transition in new_style.transition_properties() {
         let physical_property = transition.longhand_id.to_physical(new_style.writing_mode);
         if properties_that_transition.contains(physical_property) {
             continue;
         }
 
         properties_that_transition.insert(physical_property);
         animation_state.start_transition_if_applicable(
             context,
-            opaque_node,
             physical_property,
             transition.index,
             old_style,
             new_style,
         );
     }
 
     properties_that_transition
@@ -1240,17 +1299,16 @@ pub fn maybe_start_animations<E>(
             &keyframe_animation,
             context,
             new_style,
             new_style.get_box().animation_timing_function_mod(i),
             resolver,
         );
 
         let new_animation = Animation {
-            node: element.as_node().opaque(),
             name: name.clone(),
             properties_changed: keyframe_animation.properties_changed,
             computed_steps,
             started_at: animation_start,
             duration: duration as f64,
             fill_mode: box_style.animation_fill_mode_mod(i),
             delay: delay as f64,
             iteration_state,
--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -1,20 +1,18 @@
 /* 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/. */
 
 //! The context within which style is calculated.
 
 #[cfg(feature = "servo")]
-use crate::animation::ElementAnimationSet;
+use crate::animation::DocumentAnimationSet;
 use crate::bloom::StyleBloom;
 use crate::data::{EagerPseudoStyles, ElementData};
-#[cfg(feature = "servo")]
-use crate::dom::OpaqueNode;
 use crate::dom::{SendElement, TElement};
 use crate::font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")]
 use crate::gecko_bindings::structs;
 use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
 use crate::properties::ComputedValues;
 #[cfg(feature = "servo")]
 use crate::properties::PropertyId;
@@ -26,20 +24,19 @@ use crate::sharing::StyleSharingCache;
 use crate::stylist::Stylist;
 use crate::thread_state::{self, ThreadState};
 use crate::traversal::DomTraversal;
 use crate::traversal_flags::TraversalFlags;
 use app_units::Au;
 use euclid::default::Size2D;
 use euclid::Scale;
 use fxhash::FxHashMap;
-#[cfg(feature = "servo")]
-use parking_lot::RwLock;
 use selectors::matching::ElementSelectorFlags;
 use selectors::NthIndexCache;
+#[cfg(feature = "gecko")]
 use servo_arc::Arc;
 #[cfg(feature = "servo")]
 use servo_atoms::Atom;
 use std::fmt;
 use std::ops;
 use style_traits::CSSPixel;
 use style_traits::DevicePixel;
 #[cfg(feature = "servo")]
@@ -162,17 +159,17 @@ pub struct SharedStyleContext<'a> {
     /// Flags controlling how we traverse the tree.
     pub traversal_flags: TraversalFlags,
 
     /// A map with our snapshots in order to handle restyle hints.
     pub snapshot_map: &'a SnapshotMap,
 
     /// The state of all animations for our styled elements.
     #[cfg(feature = "servo")]
-    pub animation_states: Arc<RwLock<FxHashMap<OpaqueNode, ElementAnimationSet>>>,
+    pub animations: DocumentAnimationSet,
 
     /// Paint worklets
     #[cfg(feature = "servo")]
     pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
 }
 
 impl<'a> SharedStyleContext<'a> {
     /// Return a suitable viewport size in order to be used for viewport units.
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -522,39 +522,40 @@ trait PrivateMatchMethods: TElement {
 
     #[cfg(feature = "servo")]
     fn process_animations_for_style(
         &self,
         context: &mut StyleContext<Self>,
         old_values: &mut Option<Arc<ComputedValues>>,
         new_values: &mut Arc<ComputedValues>,
     ) -> bool {
-        use crate::animation::AnimationState;
+        use crate::animation::{AnimationSetKey, AnimationState};
 
         // We need to call this before accessing the `ElementAnimationSet` from the
         // map because this call will do a RwLock::read().
         let needs_animations_update =
             self.needs_animations_update(context, old_values.as_ref().map(|s| &**s), new_values);
         let might_need_transitions_update = self.might_need_transitions_update(
             context,
             old_values.as_ref().map(|s| &**s),
             new_values,
         );
 
         let mut after_change_style = None;
         if might_need_transitions_update {
             after_change_style = self.after_change_style(context, new_values);
         }
 
-        let this_opaque = self.as_node().opaque();
+        let key = AnimationSetKey(self.as_node().opaque());
         let shared_context = context.shared;
         let mut animation_set = shared_context
-            .animation_states
+            .animations
+            .sets
             .write()
-            .remove(&this_opaque)
+            .remove(&key)
             .unwrap_or_default();
 
         // Starting animations is expensive, because we have to recalculate the style
         // for all the keyframes. We only want to do this if we think that there's a
         // chance that the animations really changed.
         if needs_animations_update {
             let mut resolver = StyleResolverForElement::new(
                 *self,
@@ -569,17 +570,16 @@ trait PrivateMatchMethods: TElement {
                 &new_values,
                 &mut resolver,
             );
         }
 
         animation_set.update_transitions_for_new_style(
             might_need_transitions_update,
             &shared_context,
-            this_opaque,
             old_values.as_ref(),
             after_change_style.as_ref().unwrap_or(new_values),
         );
 
         // We clear away any finished transitions, but retain animations, because they
         // might still be used for proper calculation of `animation-fill-mode`. This
         // should change the computed values in the style, so we don't need to mark
         // this set as dirty.
@@ -588,19 +588,20 @@ trait PrivateMatchMethods: TElement {
             .retain(|transition| transition.state != AnimationState::Finished);
 
         // If the ElementAnimationSet is empty, and don't store it in order to
         // save memory and to avoid extra processing later.
         let changed_animations = animation_set.dirty;
         if !animation_set.is_empty() {
             animation_set.dirty = false;
             shared_context
-                .animation_states
+                .animations
+                .sets
                 .write()
-                .insert(this_opaque, animation_set);
+                .insert(key, animation_set);
         }
 
         changed_animations
     }
 
     /// Computes and applies non-redundant damage.
     fn accumulate_damage_for(
         &self,