Add ElementAnimationState and PossibleElementAnimationState

This refactor is preparation for implementing a specification
compliant transitions and animations processing model.

These data structures hold all the animation information about a single
node. Since adding, updating, and modifying animations for a single node
are all interdependent, it makes sense to start encapsulating animation
data and functionality into a single data structure. This also opens up
the possibility for easier concurrency in the future by more easily
allowing per-node mutexes.
This commit is contained in:
Martin Robinson 2020-04-22 16:55:24 +02:00
parent 5504d9259d
commit 8f988be18a
9 changed files with 332 additions and 454 deletions

View file

@ -7,13 +7,15 @@
#![allow(unsafe_code)]
#![deny(missing_docs)]
#[cfg(feature = "servo")]
use crate::animation;
use crate::computed_value_flags::ComputedValueFlags;
use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
use crate::context::{SharedStyleContext, StyleContext};
use crate::data::ElementData;
use crate::dom::TElement;
#[cfg(feature = "servo")]
use crate::dom::{OpaqueNode, TNode};
use crate::dom::TNode;
use crate::invalidation::element::restyle_hints::RestyleHint;
use crate::properties::longhands::display::computed_value::T as Display;
use crate::properties::ComputedValues;
@ -437,55 +439,48 @@ trait PrivateMatchMethods: TElement {
_restyle_hint: RestyleHint,
_important_rules_changed: bool,
) {
use crate::animation;
let this_opaque = self.as_node().opaque();
let mut expired_transitions = vec![];
let shared_context = context.shared;
if let Some(ref mut old_values) = *old_values {
// We apply the expired transitions and animations to the old style
// here, because we later compare the old style to the new style in
// `start_transitions_if_applicable`. If the styles differ then it will
// cause the expired transition to restart.
//
// TODO(mrobinson): We should really be following spec behavior and calculate
// after-change-style and before-change-style here.
Self::collect_and_update_style_for_expired_transitions(
shared_context,
this_opaque,
old_values,
&mut expired_transitions,
);
let mut animation_states = shared_context.animation_states.write();
let mut animation_state = animation_states.remove(&this_opaque).unwrap_or_default();
Self::update_style_for_animations(
if let Some(ref mut old_values) = *old_values {
animation_state.compute_before_change_style::<Self>(
shared_context,
this_opaque,
old_values,
&context.thread_local.font_metrics_provider,
);
}
let new_animations_sender = &context.thread_local.new_animations_sender;
// Trigger any present animations if necessary.
animation::maybe_start_animations(
*self,
&shared_context,
new_animations_sender,
this_opaque,
&new_values,
&mut animation_state,
);
// Trigger transitions if necessary. This will set `new_values` to
// the starting value of the transition if it did trigger a transition.
if let Some(ref values) = old_values {
animation::update_transitions(
&shared_context,
new_animations_sender,
if let Some(ref old_values) = old_values {
let transitioning_properties = animation::start_transitions_if_applicable(
shared_context,
this_opaque,
&values,
old_values,
new_values,
&expired_transitions,
&mut animation_state,
);
animation_state
.cancel_transitions_with_nontransitioning_properties(&transitioning_properties);
}
animation_state.finished_animations.clear();
// If the ElementAnimationState is empty, don't push it to save
// memory and to avoid extra processing later.
if !animation_state.is_empty() {
animation_states.insert(this_opaque, animation_state);
}
}
@ -601,67 +596,6 @@ trait PrivateMatchMethods: TElement {
// properties, we can stop the cascade.
ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle
}
#[cfg(feature = "servo")]
fn collect_and_update_style_for_expired_transitions(
context: &SharedStyleContext,
node: OpaqueNode,
style: &mut Arc<ComputedValues>,
expired_transitions: &mut Vec<crate::animation::PropertyAnimation>,
) {
use crate::animation::Animation;
let mut all_expired_animations = context.expired_animations.write();
if let Some(animations) = all_expired_animations.remove(&node) {
debug!("removing expired animations for {:?}", node);
for animation in animations {
debug!("Updating expired animation {:?}", animation);
// TODO: support animation-fill-mode
if let Animation::Transition(_, _, property_animation) = animation {
property_animation.update(Arc::make_mut(style), 1.0);
expired_transitions.push(property_animation);
}
}
}
}
#[cfg(feature = "servo")]
fn update_style_for_animations(
context: &SharedStyleContext,
node: OpaqueNode,
style: &mut Arc<ComputedValues>,
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
) {
use crate::animation::{self, Animation, AnimationUpdate};
let mut all_running_animations = context.running_animations.write();
let running_animations = match all_running_animations.get_mut(&node) {
Some(running_animations) => running_animations,
None => return,
};
for running_animation in running_animations.iter_mut() {
let update = match *running_animation {
Animation::Transition(..) => continue,
Animation::Keyframes(..) => animation::update_style_for_animation::<Self>(
context,
running_animation,
style,
font_metrics,
),
};
match *running_animation {
Animation::Transition(..) => unreachable!(),
Animation::Keyframes(_, _, _, ref mut state) => match update {
AnimationUpdate::Regular => {},
AnimationUpdate::AnimationCanceled => {
state.expired = true;
},
},
}
}
}
}
impl<E: TElement> PrivateMatchMethods for E {}