mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Bug 1646811 - servo: Better computation of animation keyframes.
This begins to address #26625 by properly applying CSS variables during keyframe computation and no longer using `apply_declarations`. Instead, walk the declarations, combining them into IntermediateComputedKeyframe, maintaining declarations that modify CSS custom properties. Then compute a set of AnimationValues for each keyframe and use those to produce interpolated animation values. Depends on D80233 Differential Revision: https://phabricator.services.mozilla.com/D80234
This commit is contained in:
parent
0afc032d4b
commit
697bb739d1
@ -8,20 +8,21 @@
|
||||
// compile it out so that people remember it exists.
|
||||
|
||||
use crate::bezier::Bezier;
|
||||
use crate::context::SharedStyleContext;
|
||||
use crate::dom::{OpaqueNode, TElement, TNode};
|
||||
use crate::font_metrics::FontMetricsProvider;
|
||||
use crate::context::{CascadeInputs, SharedStyleContext};
|
||||
use crate::dom::{OpaqueNode, TDocument, TElement, TNode};
|
||||
use crate::properties::animated_properties::AnimationValue;
|
||||
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::LonghandIdSet;
|
||||
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
|
||||
use crate::stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
|
||||
use crate::stylesheets::Origin;
|
||||
use crate::properties::{
|
||||
ComputedValues, Importance, LonghandId, LonghandIdSet, PropertyDeclarationBlock,
|
||||
PropertyDeclarationId,
|
||||
};
|
||||
use crate::rule_tree::CascadeLevel;
|
||||
use crate::style_resolver::StyleResolverForElement;
|
||||
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||
use crate::values::animated::{Animate, Procedure};
|
||||
use crate::values::computed::Time;
|
||||
use crate::values::computed::TimingFunction;
|
||||
use crate::values::computed::{Time, TimingFunction};
|
||||
use crate::values::generics::box_::AnimationIterationCount;
|
||||
use crate::values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction};
|
||||
use crate::Atom;
|
||||
@ -172,100 +173,222 @@ pub enum KeyframesIterationState {
|
||||
Finite(f64, f64),
|
||||
}
|
||||
|
||||
/// A single computed keyframe for a CSS Animation.
|
||||
#[derive(Clone, MallocSizeOf)]
|
||||
struct ComputedKeyframeStep {
|
||||
step: KeyframesStep,
|
||||
|
||||
#[ignore_malloc_size_of = "ComputedValues"]
|
||||
style: Arc<ComputedValues>,
|
||||
|
||||
timing_function: TimingFunction,
|
||||
/// A temporary data structure used when calculating ComputedKeyframes for an
|
||||
/// animation. This data structure is used to collapse information for steps
|
||||
/// which may be spread across multiple keyframe declarations into a single
|
||||
/// instance per `start_percentage`.
|
||||
struct IntermediateComputedKeyframe {
|
||||
declarations: PropertyDeclarationBlock,
|
||||
timing_function: Option<TimingFunction>,
|
||||
start_percentage: f32,
|
||||
}
|
||||
|
||||
impl ComputedKeyframeStep {
|
||||
fn generate_for_keyframes<E>(
|
||||
impl IntermediateComputedKeyframe {
|
||||
fn new(start_percentage: f32) -> Self {
|
||||
IntermediateComputedKeyframe {
|
||||
declarations: PropertyDeclarationBlock::new(),
|
||||
timing_function: None,
|
||||
start_percentage,
|
||||
}
|
||||
}
|
||||
|
||||
/// Walk through all keyframe declarations and combine all declarations with the
|
||||
/// same `start_percentage` into individual `IntermediateComputedKeyframe`s.
|
||||
fn generate_for_keyframes(
|
||||
animation: &KeyframesAnimation,
|
||||
context: &SharedStyleContext,
|
||||
base_style: &ComputedValues,
|
||||
) -> Vec<Self> {
|
||||
let mut intermediate_steps: Vec<Self> = Vec::with_capacity(animation.steps.len());
|
||||
let mut current_step = IntermediateComputedKeyframe::new(0.);
|
||||
for step in animation.steps.iter() {
|
||||
let start_percentage = step.start_percentage.0;
|
||||
if start_percentage != current_step.start_percentage {
|
||||
let new_step = IntermediateComputedKeyframe::new(start_percentage);
|
||||
intermediate_steps.push(std::mem::replace(&mut current_step, new_step));
|
||||
}
|
||||
|
||||
current_step.update_from_step(step, context, base_style);
|
||||
}
|
||||
intermediate_steps.push(current_step);
|
||||
|
||||
// We should always have a first and a last step, even if these are just
|
||||
// generated by KeyframesStepValue::ComputedValues.
|
||||
debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.);
|
||||
debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.);
|
||||
|
||||
intermediate_steps
|
||||
}
|
||||
|
||||
fn update_from_step(
|
||||
&mut self,
|
||||
step: &KeyframesStep,
|
||||
context: &SharedStyleContext,
|
||||
base_style: &ComputedValues,
|
||||
) {
|
||||
// Each keyframe declaration may optionally specify a timing function, falling
|
||||
// back to the one defined global for the animation.
|
||||
let guard = &context.guards.author;
|
||||
if let Some(timing_function) = step.get_animation_timing_function(&guard) {
|
||||
self.timing_function = Some(timing_function.to_computed_value_without_context());
|
||||
}
|
||||
|
||||
let block = match step.value {
|
||||
KeyframesStepValue::ComputedValues => return,
|
||||
KeyframesStepValue::Declarations { ref block } => block,
|
||||
};
|
||||
|
||||
// Filter out !important, non-animatable properties, and the
|
||||
// 'display' property (which is only animatable from SMIL).
|
||||
let guard = block.read_with(&guard);
|
||||
for declaration in guard.normal_declaration_iter() {
|
||||
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
|
||||
if id == LonghandId::Display {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !id.is_animatable() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
self.declarations.push(
|
||||
declaration.to_physical(base_style.writing_mode),
|
||||
Importance::Normal,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_style<E>(
|
||||
self,
|
||||
element: E,
|
||||
steps: &[KeyframesStep],
|
||||
context: &SharedStyleContext,
|
||||
base_style: &Arc<ComputedValues>,
|
||||
font_metrics_provider: &dyn FontMetricsProvider,
|
||||
resolver: &mut StyleResolverForElement<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
if !self.declarations.any_normal() {
|
||||
return base_style.clone();
|
||||
}
|
||||
|
||||
let document = element.as_node().owner_doc();
|
||||
let locked_block = Arc::new(document.shared_lock().wrap(self.declarations));
|
||||
let mut important_rules_changed = false;
|
||||
let rule_node = base_style.rules().clone();
|
||||
let new_node = context.stylist.rule_tree().update_rule_at_level(
|
||||
CascadeLevel::Animations,
|
||||
Some(locked_block.borrow_arc()),
|
||||
&rule_node,
|
||||
&context.guards,
|
||||
&mut important_rules_changed,
|
||||
);
|
||||
|
||||
if new_node.is_none() {
|
||||
return base_style.clone();
|
||||
}
|
||||
|
||||
let inputs = CascadeInputs {
|
||||
rules: new_node,
|
||||
visited_rules: base_style.visited_rules().cloned(),
|
||||
};
|
||||
resolver
|
||||
.cascade_style_and_visited_with_default_parents(inputs)
|
||||
.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A single computed keyframe for a CSS Animation.
|
||||
#[derive(Clone, MallocSizeOf)]
|
||||
struct ComputedKeyframe {
|
||||
/// The timing function to use for transitions between this step
|
||||
/// and the next one.
|
||||
timing_function: TimingFunction,
|
||||
|
||||
/// The starting percentage (a number between 0 and 1) which represents
|
||||
/// at what point in an animation iteration this step is.
|
||||
start_percentage: f32,
|
||||
|
||||
/// The animation values to transition to and from when processing this
|
||||
/// keyframe animation step.
|
||||
values: Vec<AnimationValue>,
|
||||
}
|
||||
|
||||
impl ComputedKeyframe {
|
||||
fn generate_for_keyframes<E>(
|
||||
element: E,
|
||||
animation: &KeyframesAnimation,
|
||||
context: &SharedStyleContext,
|
||||
base_style: &Arc<ComputedValues>,
|
||||
default_timing_function: TimingFunction,
|
||||
resolver: &mut StyleResolverForElement<E>,
|
||||
) -> Vec<Self>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
let mut previous_style = base_style.clone();
|
||||
steps
|
||||
let mut animating_properties = LonghandIdSet::new();
|
||||
for property in animation.properties_changed.iter() {
|
||||
debug_assert!(property.is_animatable());
|
||||
animating_properties.insert(property.to_physical(base_style.writing_mode));
|
||||
}
|
||||
|
||||
let animation_values_from_style: Vec<AnimationValue> = animating_properties
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|step| match step.value {
|
||||
KeyframesStepValue::ComputedValues => ComputedKeyframeStep {
|
||||
step,
|
||||
style: base_style.clone(),
|
||||
timing_function: default_timing_function,
|
||||
},
|
||||
KeyframesStepValue::Declarations {
|
||||
block: ref declarations,
|
||||
} => {
|
||||
let guard = declarations.read_with(context.guards.author);
|
||||
|
||||
let iter = || {
|
||||
// It's possible to have !important properties in keyframes
|
||||
// so we have to filter them out.
|
||||
// See the spec issue https://github.com/w3c/csswg-drafts/issues/1824
|
||||
// Also we filter our non-animatable properties.
|
||||
guard
|
||||
.normal_declaration_iter()
|
||||
.filter(|declaration| declaration.is_animatable())
|
||||
.map(|decl| (decl, Origin::Author))
|
||||
};
|
||||
|
||||
// This currently ignores visited styles, which seems acceptable,
|
||||
// as existing browsers don't appear to animate visited styles.
|
||||
//
|
||||
// TODO(mrobinson): We shouldn't be calling `apply_declarations`
|
||||
// here because it doesn't really produce the correct values (for
|
||||
// instance for keyframes that are missing animating properties).
|
||||
// Instead we should do something like what Gecko does in
|
||||
// Servo_StyleSet_GetKeyframesForName.
|
||||
let computed_style = properties::apply_declarations::<E, _, _>(
|
||||
context.stylist.device(),
|
||||
/* pseudo = */ None,
|
||||
previous_style.rules(),
|
||||
&context.guards,
|
||||
iter,
|
||||
Some(&previous_style),
|
||||
Some(&previous_style),
|
||||
Some(&previous_style),
|
||||
font_metrics_provider,
|
||||
CascadeMode::Unvisited {
|
||||
visited_rules: None,
|
||||
},
|
||||
context.quirks_mode(),
|
||||
/* rule_cache = */ None,
|
||||
&mut Default::default(),
|
||||
Some(element),
|
||||
);
|
||||
|
||||
// NB: The spec says that the timing function can be overwritten
|
||||
// from the keyframe style. `animation_timing_function` can never
|
||||
// be empty, always has at least the default value (`ease`).
|
||||
let timing_function = if step.declared_timing_function {
|
||||
computed_style.get_box().animation_timing_function_at(0)
|
||||
} else {
|
||||
default_timing_function
|
||||
};
|
||||
|
||||
previous_style = computed_style.clone();
|
||||
ComputedKeyframeStep {
|
||||
step,
|
||||
style: computed_style,
|
||||
timing_function,
|
||||
}
|
||||
},
|
||||
.map(|property| {
|
||||
AnimationValue::from_computed_values(property, &**base_style)
|
||||
.expect("Unexpected non-animatable property.")
|
||||
})
|
||||
.collect()
|
||||
.collect();
|
||||
|
||||
let intermediate_steps =
|
||||
IntermediateComputedKeyframe::generate_for_keyframes(animation, context, base_style);
|
||||
|
||||
let mut computed_steps: Vec<Self> = Vec::with_capacity(intermediate_steps.len());
|
||||
for (step_index, step) in intermediate_steps.into_iter().enumerate() {
|
||||
let start_percentage = step.start_percentage;
|
||||
let timing_function = step.timing_function.unwrap_or(default_timing_function);
|
||||
let properties_changed_in_step = step.declarations.longhands().clone();
|
||||
let step_style = step.resolve_style(element, context, base_style, resolver);
|
||||
|
||||
let values = {
|
||||
// If a value is not set in a property declaration we use the value from
|
||||
// the style for the first and last keyframe. For intermediate ones, we
|
||||
// use the value from the previous keyframe.
|
||||
//
|
||||
// TODO(mrobinson): According to the spec, we should use an interpolated
|
||||
// value for properties missing from keyframe declarations.
|
||||
let default_values = if start_percentage == 0. || start_percentage == 1.0 {
|
||||
&animation_values_from_style
|
||||
} else {
|
||||
debug_assert!(step_index != 0);
|
||||
&computed_steps[step_index - 1].values
|
||||
};
|
||||
|
||||
// For each property that is animating, pull the value from the resolved
|
||||
// style for this step if it's in one of the declarations. Otherwise, we
|
||||
// use the default value from the set we calculated above.
|
||||
animating_properties
|
||||
.iter()
|
||||
.zip(default_values.iter())
|
||||
.map(|(longhand, default_value)| {
|
||||
if properties_changed_in_step.contains(longhand) {
|
||||
AnimationValue::from_computed_values(longhand, &step_style)
|
||||
.unwrap_or_else(|| default_value.clone())
|
||||
} else {
|
||||
default_value.clone()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
computed_steps.push(ComputedKeyframe {
|
||||
timing_function,
|
||||
start_percentage,
|
||||
values,
|
||||
});
|
||||
}
|
||||
computed_steps
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,7 +405,7 @@ pub struct Animation {
|
||||
properties_changed: LonghandIdSet,
|
||||
|
||||
/// The computed style for each keyframe of this animation.
|
||||
computed_steps: Vec<ComputedKeyframeStep>,
|
||||
computed_steps: Vec<ComputedKeyframe>,
|
||||
|
||||
/// The time this animation started at, which is the current value of the animation
|
||||
/// timeline when this animation was created plus any animation delay.
|
||||
@ -517,7 +640,7 @@ impl Animation {
|
||||
next_keyframe_index = self
|
||||
.computed_steps
|
||||
.iter()
|
||||
.position(|step| total_progress as f32 <= step.step.start_percentage.0);
|
||||
.position(|step| total_progress as f32 <= step.start_percentage);
|
||||
prev_keyframe_index = next_keyframe_index
|
||||
.and_then(|pos| if pos != 0 { Some(pos - 1) } else { None })
|
||||
.unwrap_or(0);
|
||||
@ -527,7 +650,7 @@ impl Animation {
|
||||
.computed_steps
|
||||
.iter()
|
||||
.rev()
|
||||
.position(|step| total_progress as f32 <= 1. - step.step.start_percentage.0)
|
||||
.position(|step| total_progress as f32 <= 1. - step.start_percentage)
|
||||
.map(|pos| num_steps - pos - 1);
|
||||
prev_keyframe_index = next_keyframe_index
|
||||
.and_then(|pos| {
|
||||
@ -553,58 +676,46 @@ impl Animation {
|
||||
None => return,
|
||||
};
|
||||
|
||||
let update_with_single_keyframe_style = |style, computed_style: &Arc<ComputedValues>| {
|
||||
let update_with_single_keyframe_style = |style, keyframe: &ComputedKeyframe| {
|
||||
let mutable_style = Arc::make_mut(style);
|
||||
for property in self.properties_changed.iter().filter_map(|longhand| {
|
||||
AnimationValue::from_computed_values(longhand, &**computed_style)
|
||||
}) {
|
||||
property.set_in_style_for_servo(mutable_style);
|
||||
for value in keyframe.values.iter() {
|
||||
value.set_in_style_for_servo(mutable_style);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: How could we optimise it? Is it such a big deal?
|
||||
let prev_keyframe_style = &prev_keyframe.style;
|
||||
let next_keyframe_style = &next_keyframe.style;
|
||||
if total_progress <= 0.0 {
|
||||
update_with_single_keyframe_style(style, &prev_keyframe_style);
|
||||
update_with_single_keyframe_style(style, &prev_keyframe);
|
||||
return;
|
||||
}
|
||||
|
||||
if total_progress >= 1.0 {
|
||||
update_with_single_keyframe_style(style, &next_keyframe_style);
|
||||
update_with_single_keyframe_style(style, &next_keyframe);
|
||||
return;
|
||||
}
|
||||
|
||||
let relative_timespan =
|
||||
(next_keyframe.step.start_percentage.0 - prev_keyframe.step.start_percentage.0).abs();
|
||||
(next_keyframe.start_percentage - prev_keyframe.start_percentage).abs();
|
||||
let relative_duration = relative_timespan as f64 * duration;
|
||||
let last_keyframe_ended_at = match self.current_direction {
|
||||
AnimationDirection::Normal => {
|
||||
self.started_at + (duration * prev_keyframe.step.start_percentage.0 as f64)
|
||||
self.started_at + (duration * prev_keyframe.start_percentage as f64)
|
||||
},
|
||||
AnimationDirection::Reverse => {
|
||||
self.started_at + (duration * (1. - prev_keyframe.step.start_percentage.0 as f64))
|
||||
self.started_at + (duration * (1. - prev_keyframe.start_percentage as f64))
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let relative_progress = (now - last_keyframe_ended_at) / relative_duration;
|
||||
|
||||
let relative_progress = (now - last_keyframe_ended_at) / relative_duration;
|
||||
let mut new_style = (**style).clone();
|
||||
let mut update_style_for_longhand = |longhand| {
|
||||
let from = AnimationValue::from_computed_values(longhand, &prev_keyframe_style)?;
|
||||
let to = AnimationValue::from_computed_values(longhand, &next_keyframe_style)?;
|
||||
for (from, to) in prev_keyframe.values.iter().zip(next_keyframe.values.iter()) {
|
||||
PropertyAnimation {
|
||||
from,
|
||||
to,
|
||||
from: from.clone(),
|
||||
to: to.clone(),
|
||||
timing_function: prev_keyframe.timing_function,
|
||||
duration: relative_duration as f64,
|
||||
}
|
||||
.update(&mut new_style, relative_progress);
|
||||
None::<()>
|
||||
};
|
||||
|
||||
for property in self.properties_changed.iter() {
|
||||
update_style_for_longhand(property);
|
||||
}
|
||||
|
||||
*Arc::make_mut(style) = new_style;
|
||||
@ -850,7 +961,7 @@ impl ElementAnimationSet {
|
||||
element: E,
|
||||
context: &SharedStyleContext,
|
||||
new_style: &Arc<ComputedValues>,
|
||||
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
|
||||
resolver: &mut StyleResolverForElement<E>,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
@ -860,7 +971,7 @@ impl ElementAnimationSet {
|
||||
}
|
||||
}
|
||||
|
||||
maybe_start_animations(element, &context, &new_style, self, font_metrics);
|
||||
maybe_start_animations(element, &context, &new_style, self, resolver);
|
||||
}
|
||||
|
||||
/// Update our transitions given a new style, canceling or starting new animations
|
||||
@ -1022,7 +1133,7 @@ pub fn maybe_start_animations<E>(
|
||||
context: &SharedStyleContext,
|
||||
new_style: &Arc<ComputedValues>,
|
||||
animation_state: &mut ElementAnimationSet,
|
||||
font_metrics_provider: &dyn FontMetricsProvider,
|
||||
resolver: &mut StyleResolverForElement<E>,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
@ -1077,13 +1188,13 @@ pub fn maybe_start_animations<E>(
|
||||
AnimationPlayState::Running => AnimationState::Pending,
|
||||
};
|
||||
|
||||
let computed_steps = ComputedKeyframeStep::generate_for_keyframes::<E>(
|
||||
let computed_steps = ComputedKeyframe::generate_for_keyframes(
|
||||
element,
|
||||
&keyframe_animation.steps,
|
||||
&keyframe_animation,
|
||||
context,
|
||||
new_style,
|
||||
font_metrics_provider,
|
||||
new_style.get_box().animation_timing_function_mod(i),
|
||||
resolver,
|
||||
);
|
||||
|
||||
let new_animation = Animation {
|
||||
|
@ -17,7 +17,7 @@ use crate::font_metrics::FontMetricsProvider;
|
||||
use crate::media_queries::Device;
|
||||
use crate::properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock};
|
||||
use crate::selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl};
|
||||
use crate::shared_lock::Locked;
|
||||
use crate::shared_lock::{Locked, SharedRwLock};
|
||||
use crate::stylist::CascadeData;
|
||||
use crate::traversal_flags::TraversalFlags;
|
||||
use crate::{Atom, LocalName, Namespace, WeakAtom};
|
||||
@ -128,6 +128,9 @@ pub trait TDocument: Sized + Copy + Clone {
|
||||
{
|
||||
Err(())
|
||||
}
|
||||
|
||||
/// This document's shared lock.
|
||||
fn shared_lock(&self) -> &SharedRwLock;
|
||||
}
|
||||
|
||||
/// The `TNode` trait. This is the main generic trait over which the style
|
||||
|
@ -64,7 +64,7 @@ use crate::properties::{ComputedValues, LonghandId};
|
||||
use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
|
||||
use crate::rule_tree::CascadeLevel as ServoCascadeLevel;
|
||||
use crate::selector_parser::{AttrValue, HorizontalDirection, Lang};
|
||||
use crate::shared_lock::Locked;
|
||||
use crate::shared_lock::{Locked, SharedRwLock};
|
||||
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||
use crate::stylist::CascadeData;
|
||||
use crate::values::computed::font::GenericFontFamily;
|
||||
@ -139,6 +139,10 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
|
||||
bindings::Gecko_Document_GetElementsWithId(self.0, id.as_ptr())
|
||||
}))
|
||||
}
|
||||
|
||||
fn shared_lock(&self) -> &SharedRwLock {
|
||||
&GLOBAL_STYLE_DATA.shared_lock
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple wrapper over `ShadowRoot`.
|
||||
|
@ -20,6 +20,8 @@ use crate::properties::ComputedValues;
|
||||
use crate::rule_tree::{CascadeLevel, StrongRuleNode};
|
||||
use crate::selector_parser::{PseudoElement, RestyleDamage};
|
||||
use crate::style_resolver::ResolvedElementStyles;
|
||||
use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||
use crate::stylist::RuleInclusion;
|
||||
use crate::traversal_flags::TraversalFlags;
|
||||
use selectors::matching::ElementSelectorFlags;
|
||||
use servo_arc::{Arc, ArcBorrow};
|
||||
@ -199,8 +201,6 @@ trait PrivateMatchMethods: TElement {
|
||||
primary_style: &Arc<ComputedValues>,
|
||||
) -> Option<Arc<ComputedValues>> {
|
||||
use crate::context::CascadeInputs;
|
||||
use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||
use crate::stylist::RuleInclusion;
|
||||
|
||||
let rule_node = primary_style.rules();
|
||||
let without_transition_rules = context
|
||||
@ -458,11 +458,18 @@ trait PrivateMatchMethods: TElement {
|
||||
// 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,
|
||||
context,
|
||||
RuleInclusion::All,
|
||||
PseudoElementResolution::IfApplicable,
|
||||
);
|
||||
|
||||
animation_set.update_animations_for_new_style::<Self>(
|
||||
*self,
|
||||
&shared_context,
|
||||
&new_values,
|
||||
&context.thread_local.font_metrics_provider,
|
||||
&mut resolver,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -321,6 +321,13 @@ impl PropertyDeclarationBlock {
|
||||
self.longhands.contains_any_reset()
|
||||
}
|
||||
|
||||
/// Returns a `LonghandIdSet` representing the properties that are changed in
|
||||
/// this block.
|
||||
#[inline]
|
||||
pub fn longhands(&self) -> &LonghandIdSet {
|
||||
&self.longhands
|
||||
}
|
||||
|
||||
/// Get a declaration for a given property.
|
||||
///
|
||||
/// NOTE: This is linear time in the case of custom properties or in the
|
||||
|
Loading…
Reference in New Issue
Block a user