mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Include animations and transitions in the cascade
Instead of applying animations and transitions to styled elements, include them in the cascade. This allows them to interact properly with things like font-size and !important rules.
This commit is contained in:
parent
8b56b7a3c2
commit
364235ac0c
17 changed files with 314 additions and 216 deletions
|
@ -464,6 +464,54 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn may_have_animations(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn animation_rule(
|
||||||
|
&self,
|
||||||
|
context: &SharedStyleContext,
|
||||||
|
) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
|
||||||
|
let node = self.as_node();
|
||||||
|
let document = node.owner_doc();
|
||||||
|
context
|
||||||
|
.animation_states
|
||||||
|
.read()
|
||||||
|
.get(&node.opaque())
|
||||||
|
.and_then(|set| {
|
||||||
|
set.get_value_map_for_active_animations(context.current_time_for_animations)
|
||||||
|
})
|
||||||
|
.map(|map| {
|
||||||
|
Arc::new(
|
||||||
|
document
|
||||||
|
.style_shared_lock()
|
||||||
|
.wrap(PropertyDeclarationBlock::from_animation_value_map(&map)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transition_rule(
|
||||||
|
&self,
|
||||||
|
context: &SharedStyleContext,
|
||||||
|
) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
|
||||||
|
let node = self.as_node();
|
||||||
|
let document = node.owner_doc();
|
||||||
|
context
|
||||||
|
.animation_states
|
||||||
|
.read()
|
||||||
|
.get(&node.opaque())
|
||||||
|
.and_then(|set| {
|
||||||
|
set.get_value_map_for_active_transitions(context.current_time_for_animations)
|
||||||
|
})
|
||||||
|
.map(|map| {
|
||||||
|
Arc::new(
|
||||||
|
document
|
||||||
|
.style_shared_lock()
|
||||||
|
.wrap(PropertyDeclarationBlock::from_animation_value_map(&map)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn state(&self) -> ElementState {
|
fn state(&self) -> ElementState {
|
||||||
self.element.get_state_for_layout()
|
self.element.get_state_for_layout()
|
||||||
}
|
}
|
||||||
|
@ -581,11 +629,8 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
self.element.has_selector_flags(flags)
|
self.element.has_selector_flags(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_animations(&self) -> bool {
|
fn has_animations(&self, context: &SharedStyleContext) -> bool {
|
||||||
// We use this function not only for Gecko but also for Servo to know if this element has
|
return self.has_css_animations(context) || self.has_css_transitions(context);
|
||||||
// animations, so we maybe try to get the important rules of this element. This is used for
|
|
||||||
// off-main thread animations, but we don't support it on Servo, so return false directly.
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_animations(&self, context: &SharedStyleContext) -> bool {
|
fn has_css_animations(&self, context: &SharedStyleContext) -> bool {
|
||||||
|
@ -597,8 +642,13 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_transitions(&self) -> bool {
|
fn has_css_transitions(&self, context: &SharedStyleContext) -> bool {
|
||||||
unreachable!("this should be only called on gecko");
|
context
|
||||||
|
.animation_states
|
||||||
|
.read()
|
||||||
|
.get(&self.as_node().opaque())
|
||||||
|
.map(|set| set.has_active_transition())
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -472,6 +472,54 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn may_have_animations(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn animation_rule(
|
||||||
|
&self,
|
||||||
|
context: &SharedStyleContext,
|
||||||
|
) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
|
||||||
|
let node = self.as_node();
|
||||||
|
let document = node.owner_doc();
|
||||||
|
context
|
||||||
|
.animation_states
|
||||||
|
.read()
|
||||||
|
.get(&node.opaque())
|
||||||
|
.and_then(|set| {
|
||||||
|
set.get_value_map_for_active_animations(context.current_time_for_animations)
|
||||||
|
})
|
||||||
|
.map(|map| {
|
||||||
|
Arc::new(
|
||||||
|
document
|
||||||
|
.style_shared_lock()
|
||||||
|
.wrap(PropertyDeclarationBlock::from_animation_value_map(&map)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transition_rule(
|
||||||
|
&self,
|
||||||
|
context: &SharedStyleContext,
|
||||||
|
) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
|
||||||
|
let node = self.as_node();
|
||||||
|
let document = node.owner_doc();
|
||||||
|
context
|
||||||
|
.animation_states
|
||||||
|
.read()
|
||||||
|
.get(&node.opaque())
|
||||||
|
.and_then(|set| {
|
||||||
|
set.get_value_map_for_active_transitions(context.current_time_for_animations)
|
||||||
|
})
|
||||||
|
.map(|map| {
|
||||||
|
Arc::new(
|
||||||
|
document
|
||||||
|
.style_shared_lock()
|
||||||
|
.wrap(PropertyDeclarationBlock::from_animation_value_map(&map)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn state(&self) -> ElementState {
|
fn state(&self) -> ElementState {
|
||||||
self.element.get_state_for_layout()
|
self.element.get_state_for_layout()
|
||||||
}
|
}
|
||||||
|
@ -589,11 +637,8 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
self.element.has_selector_flags(flags)
|
self.element.has_selector_flags(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_animations(&self) -> bool {
|
fn has_animations(&self, context: &SharedStyleContext) -> bool {
|
||||||
// We use this function not only for Gecko but also for Servo to know if this element has
|
return self.has_css_animations(context) || self.has_css_transitions(context);
|
||||||
// animations, so we maybe try to get the important rules of this element. This is used for
|
|
||||||
// off-main thread animations, but we don't support it on Servo, so return false directly.
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_animations(&self, context: &SharedStyleContext) -> bool {
|
fn has_css_animations(&self, context: &SharedStyleContext) -> bool {
|
||||||
|
@ -605,8 +650,13 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_transitions(&self) -> bool {
|
fn has_css_transitions(&self, context: &SharedStyleContext) -> bool {
|
||||||
unreachable!("this should be only called on gecko");
|
context
|
||||||
|
.animation_states
|
||||||
|
.read()
|
||||||
|
.get(&self.as_node().opaque())
|
||||||
|
.map(|set| set.has_active_transition())
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
use crate::bezier::Bezier;
|
use crate::bezier::Bezier;
|
||||||
use crate::context::{CascadeInputs, SharedStyleContext};
|
use crate::context::{CascadeInputs, SharedStyleContext};
|
||||||
use crate::dom::{OpaqueNode, TDocument, TElement, TNode};
|
use crate::dom::{OpaqueNode, TDocument, TElement, TNode};
|
||||||
use crate::properties::animated_properties::AnimationValue;
|
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
|
||||||
use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
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_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::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
||||||
|
@ -127,13 +127,11 @@ impl PropertyAnimation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the given animation at a given point of progress.
|
/// Update the given animation at a given point of progress.
|
||||||
fn update(&self, style: &mut ComputedValues, progress: f64) {
|
fn calculate_value(&self, progress: f64) -> Result<AnimationValue, ()> {
|
||||||
let procedure = Procedure::Interpolate {
|
let procedure = Procedure::Interpolate {
|
||||||
progress: self.timing_function_output(progress),
|
progress: self.timing_function_output(progress),
|
||||||
};
|
};
|
||||||
if let Ok(new_value) = self.from.animate(&self.to, procedure) {
|
self.from.animate(&self.to, procedure)
|
||||||
new_value.set_in_style_for_servo(style);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,16 +596,14 @@ impl Animation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the given style to reflect the values specified by this `Animation`
|
/// Fill in an `AnimationValueMap` with values calculated from this animation at
|
||||||
/// at the time provided by the given `SharedStyleContext`.
|
/// the given time value.
|
||||||
fn update_style(&self, context: &SharedStyleContext, style: &mut Arc<ComputedValues>) {
|
fn get_property_declaration_at_time(&self, now: f64, map: &mut AnimationValueMap) {
|
||||||
let duration = self.duration;
|
let duration = self.duration;
|
||||||
let started_at = self.started_at;
|
let started_at = self.started_at;
|
||||||
|
|
||||||
let now = match self.state {
|
let now = match self.state {
|
||||||
AnimationState::Running | AnimationState::Pending | AnimationState::Finished => {
|
AnimationState::Running | AnimationState::Pending | AnimationState::Finished => now,
|
||||||
context.current_time_for_animations
|
|
||||||
},
|
|
||||||
AnimationState::Paused(progress) => started_at + duration * progress,
|
AnimationState::Paused(progress) => started_at + duration * progress,
|
||||||
AnimationState::Canceled => return,
|
AnimationState::Canceled => return,
|
||||||
};
|
};
|
||||||
|
@ -666,7 +662,7 @@ impl Animation {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Animation::update_style: keyframe from {:?} to {:?}",
|
"Animation::get_property_declaration_at_time: keyframe from {:?} to {:?}",
|
||||||
prev_keyframe_index, next_keyframe_index
|
prev_keyframe_index, next_keyframe_index
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -676,20 +672,19 @@ impl Animation {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let update_with_single_keyframe_style = |style, keyframe: &ComputedKeyframe| {
|
let mut add_declarations_to_map = |keyframe: &ComputedKeyframe| {
|
||||||
let mutable_style = Arc::make_mut(style);
|
|
||||||
for value in keyframe.values.iter() {
|
for value in keyframe.values.iter() {
|
||||||
value.set_in_style_for_servo(mutable_style);
|
map.insert(value.id(), value.clone());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if total_progress <= 0.0 {
|
if total_progress <= 0.0 {
|
||||||
update_with_single_keyframe_style(style, &prev_keyframe);
|
add_declarations_to_map(&prev_keyframe);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if total_progress >= 1.0 {
|
if total_progress >= 1.0 {
|
||||||
update_with_single_keyframe_style(style, &next_keyframe);
|
add_declarations_to_map(&next_keyframe);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,18 +702,18 @@ impl Animation {
|
||||||
};
|
};
|
||||||
|
|
||||||
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();
|
|
||||||
for (from, to) in prev_keyframe.values.iter().zip(next_keyframe.values.iter()) {
|
for (from, to) in prev_keyframe.values.iter().zip(next_keyframe.values.iter()) {
|
||||||
PropertyAnimation {
|
let animation = PropertyAnimation {
|
||||||
from: from.clone(),
|
from: from.clone(),
|
||||||
to: to.clone(),
|
to: to.clone(),
|
||||||
timing_function: prev_keyframe.timing_function,
|
timing_function: prev_keyframe.timing_function,
|
||||||
duration: relative_duration as f64,
|
duration: relative_duration as f64,
|
||||||
}
|
};
|
||||||
.update(&mut new_style, relative_progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
*Arc::make_mut(style) = new_style;
|
if let Ok(value) = animation.calculate_value(relative_progress) {
|
||||||
|
map.insert(value.id(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -799,7 +794,10 @@ impl Transition {
|
||||||
// time of the style change event, times the reversing shortening
|
// time of the style change event, times the reversing shortening
|
||||||
// factor of the old transition
|
// factor of the old transition
|
||||||
// 2. 1 minus the reversing shortening factor of the old transition."
|
// 2. 1 minus the reversing shortening factor of the old transition."
|
||||||
let transition_progress = replaced_transition.progress(now);
|
let transition_progress = ((now - replaced_transition.start_time) /
|
||||||
|
(replaced_transition.property_animation.duration))
|
||||||
|
.min(1.0)
|
||||||
|
.max(0.0);
|
||||||
let timing_function_output = replaced_animation.timing_function_output(transition_progress);
|
let timing_function_output = replaced_animation.timing_function_output(transition_progress);
|
||||||
let old_reversing_shortening_factor = replaced_transition.reversing_shortening_factor;
|
let old_reversing_shortening_factor = replaced_transition.reversing_shortening_factor;
|
||||||
self.reversing_shortening_factor = ((timing_function_output *
|
self.reversing_shortening_factor = ((timing_function_output *
|
||||||
|
@ -845,25 +843,16 @@ impl Transition {
|
||||||
time >= self.start_time + (self.property_animation.duration)
|
time >= self.start_time + (self.property_animation.duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this animation has the same end value as another one.
|
/// Update the given animation at a given point of progress.
|
||||||
#[inline]
|
pub fn calculate_value(&self, time: f64) -> Option<AnimationValue> {
|
||||||
fn progress(&self, now: f64) -> f64 {
|
let progress = (time - self.start_time) / (self.property_animation.duration);
|
||||||
let progress = (now - self.start_time) / (self.property_animation.duration);
|
if progress < 0.0 {
|
||||||
progress.min(1.0)
|
return None;
|
||||||
}
|
|
||||||
|
|
||||||
/// Update a style to the value specified by this `Transition` given a `SharedStyleContext`.
|
|
||||||
fn update_style(&self, context: &SharedStyleContext, style: &mut Arc<ComputedValues>) {
|
|
||||||
// Never apply canceled transitions to a style.
|
|
||||||
if self.state == AnimationState::Canceled {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = self.progress(context.current_time_for_animations);
|
self.property_animation
|
||||||
if progress >= 0.0 {
|
.calculate_value(progress.min(1.0))
|
||||||
self.property_animation
|
.ok()
|
||||||
.update(Arc::make_mut(style), progress);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,17 +864,30 @@ pub struct ElementAnimationSet {
|
||||||
|
|
||||||
/// The transitions for this element.
|
/// The transitions for this element.
|
||||||
pub transitions: Vec<Transition>,
|
pub transitions: Vec<Transition>,
|
||||||
|
|
||||||
|
/// Whether or not this ElementAnimationSet has had animations or transitions
|
||||||
|
/// which have been added, removed, or had their state changed.
|
||||||
|
pub dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementAnimationSet {
|
impl ElementAnimationSet {
|
||||||
/// Cancel all animations in this `ElementAnimationSet`. This is typically called
|
/// Cancel all animations in this `ElementAnimationSet`. This is typically called
|
||||||
/// when the element has been removed from the DOM.
|
/// when the element has been removed from the DOM.
|
||||||
pub fn cancel_all_animations(&mut self) {
|
pub fn cancel_all_animations(&mut self) {
|
||||||
|
self.dirty = !self.animations.is_empty();
|
||||||
for animation in self.animations.iter_mut() {
|
for animation in self.animations.iter_mut() {
|
||||||
animation.state = AnimationState::Canceled;
|
animation.state = AnimationState::Canceled;
|
||||||
}
|
}
|
||||||
|
self.cancel_active_transitions();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel_active_transitions(&mut self) {
|
||||||
|
self.dirty = !self.transitions.is_empty();
|
||||||
|
|
||||||
for transition in self.transitions.iter_mut() {
|
for transition in self.transitions.iter_mut() {
|
||||||
transition.state = AnimationState::Canceled;
|
if transition.state != AnimationState::Finished {
|
||||||
|
transition.state = AnimationState::Canceled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -894,12 +896,18 @@ impl ElementAnimationSet {
|
||||||
context: &SharedStyleContext,
|
context: &SharedStyleContext,
|
||||||
style: &mut Arc<ComputedValues>,
|
style: &mut Arc<ComputedValues>,
|
||||||
) {
|
) {
|
||||||
for animation in &self.animations {
|
let now = context.current_time_for_animations;
|
||||||
animation.update_style(context, style);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for transition in &self.transitions {
|
if let Some(map) = self.get_value_map_for_active_transitions(now) {
|
||||||
transition.update_style(context, style);
|
for value in map.values() {
|
||||||
|
value.set_in_style_for_servo(mutable_style);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -978,6 +986,7 @@ impl ElementAnimationSet {
|
||||||
/// when appropriate.
|
/// when appropriate.
|
||||||
pub fn update_transitions_for_new_style(
|
pub fn update_transitions_for_new_style(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
might_need_transitions_update: bool,
|
||||||
context: &SharedStyleContext,
|
context: &SharedStyleContext,
|
||||||
opaque_node: OpaqueNode,
|
opaque_node: OpaqueNode,
|
||||||
old_style: Option<&Arc<ComputedValues>>,
|
old_style: Option<&Arc<ComputedValues>>,
|
||||||
|
@ -990,12 +999,18 @@ impl ElementAnimationSet {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If the style of this element is display:none, then cancel all active transitions.
|
||||||
|
if after_change_style.get_box().clone_display().is_none() {
|
||||||
|
self.cancel_active_transitions();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !might_need_transitions_update {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We convert old values into `before-change-style` here.
|
// We convert old values into `before-change-style` here.
|
||||||
// See https://drafts.csswg.org/css-transitions/#starting. We need to clone the
|
|
||||||
// style because this might still be a reference to the original `old_style` and
|
|
||||||
// we want to preserve that so that we can later properly calculate restyle damage.
|
|
||||||
if self.has_active_transition() || self.has_active_animation() {
|
if self.has_active_transition() || self.has_active_animation() {
|
||||||
before_change_style = before_change_style.clone();
|
|
||||||
self.apply_active_animations(context, &mut before_change_style);
|
self.apply_active_animations(context, &mut before_change_style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,6 +1031,7 @@ impl ElementAnimationSet {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
transition.state = AnimationState::Canceled;
|
transition.state = AnimationState::Canceled;
|
||||||
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1086,6 +1102,45 @@ impl ElementAnimationSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.transitions.push(new_transition);
|
self.transitions.push(new_transition);
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s
|
||||||
|
/// active transitions at the given time value.
|
||||||
|
pub fn get_value_map_for_active_transitions(&self, now: f64) -> Option<AnimationValueMap> {
|
||||||
|
if !self.has_active_transition() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut map =
|
||||||
|
AnimationValueMap::with_capacity_and_hasher(self.transitions.len(), Default::default());
|
||||||
|
for transition in &self.transitions {
|
||||||
|
if transition.state == AnimationState::Canceled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let value = match transition.calculate_value(now) {
|
||||||
|
Some(value) => value,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
map.insert(value.id(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s
|
||||||
|
/// active animations at the given time value.
|
||||||
|
pub fn get_value_map_for_active_animations(&self, now: f64) -> Option<AnimationValueMap> {
|
||||||
|
if !self.has_active_animation() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut map = Default::default();
|
||||||
|
for animation in &self.animations {
|
||||||
|
animation.get_property_declaration_at_time(now, &mut map);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,13 +1153,6 @@ pub fn start_transitions_if_applicable(
|
||||||
new_style: &Arc<ComputedValues>,
|
new_style: &Arc<ComputedValues>,
|
||||||
animation_state: &mut ElementAnimationSet,
|
animation_state: &mut ElementAnimationSet,
|
||||||
) -> LonghandIdSet {
|
) -> LonghandIdSet {
|
||||||
// If the style of this element is display:none, then we don't start any transitions
|
|
||||||
// and we cancel any currently running transitions by returning an empty LonghandIdSet.
|
|
||||||
let box_style = new_style.get_box();
|
|
||||||
if box_style.clone_display().is_none() {
|
|
||||||
return LonghandIdSet::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut properties_that_transition = LonghandIdSet::new();
|
let mut properties_that_transition = LonghandIdSet::new();
|
||||||
for transition in new_style.transition_properties() {
|
for transition in new_style.transition_properties() {
|
||||||
let physical_property = transition.longhand_id.to_physical(new_style.writing_mode);
|
let physical_property = transition.longhand_id.to_physical(new_style.writing_mode);
|
||||||
|
@ -1214,6 +1262,8 @@ pub fn maybe_start_animations<E>(
|
||||||
is_new: true,
|
is_new: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
animation_state.dirty = true;
|
||||||
|
|
||||||
// If the animation was already present in the list for the node, just update its state.
|
// If the animation was already present in the list for the node, just update its state.
|
||||||
for existing_animation in animation_state.animations.iter_mut() {
|
for existing_animation in animation_state.animations.iter_mut() {
|
||||||
if existing_animation.state == AnimationState::Canceled {
|
if existing_animation.state == AnimationState::Canceled {
|
||||||
|
|
|
@ -479,23 +479,25 @@ pub trait TElement:
|
||||||
/// Get the combined animation and transition rules.
|
/// Get the combined animation and transition rules.
|
||||||
///
|
///
|
||||||
/// FIXME(emilio): Is this really useful?
|
/// FIXME(emilio): Is this really useful?
|
||||||
fn animation_rules(&self) -> AnimationRules {
|
fn animation_rules(&self, context: &SharedStyleContext) -> AnimationRules {
|
||||||
if !self.may_have_animations() {
|
if !self.may_have_animations() {
|
||||||
return AnimationRules(None, None);
|
return AnimationRules(None, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationRules(self.animation_rule(), self.transition_rule())
|
AnimationRules(self.animation_rule(context), self.transition_rule(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this element's animation rule.
|
/// Get this element's animation rule.
|
||||||
fn animation_rule(&self) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
fn animation_rule(
|
||||||
None
|
&self,
|
||||||
}
|
_: &SharedStyleContext,
|
||||||
|
) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;
|
||||||
|
|
||||||
/// Get this element's transition rule.
|
/// Get this element's transition rule.
|
||||||
fn transition_rule(&self) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
|
fn transition_rule(
|
||||||
None
|
&self,
|
||||||
}
|
context: &SharedStyleContext,
|
||||||
|
) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;
|
||||||
|
|
||||||
/// Get this element's state, for non-tree-structural pseudos.
|
/// Get this element's state, for non-tree-structural pseudos.
|
||||||
fn state(&self) -> ElementState;
|
fn state(&self) -> ElementState;
|
||||||
|
@ -729,9 +731,7 @@ pub trait TElement:
|
||||||
/// In Gecko, element has a flag that represents the element may have
|
/// In Gecko, element has a flag that represents the element may have
|
||||||
/// any type of animations or not to bail out animation stuff early.
|
/// any type of animations or not to bail out animation stuff early.
|
||||||
/// Whereas Servo doesn't have such flag.
|
/// Whereas Servo doesn't have such flag.
|
||||||
fn may_have_animations(&self) -> bool {
|
fn may_have_animations(&self) -> bool;
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a task to update various animation state on a given (pseudo-)element.
|
/// Creates a task to update various animation state on a given (pseudo-)element.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -748,14 +748,14 @@ pub trait TElement:
|
||||||
/// Returns true if the element has relevant animations. Relevant
|
/// Returns true if the element has relevant animations. Relevant
|
||||||
/// animations are those animations that are affecting the element's style
|
/// animations are those animations that are affecting the element's style
|
||||||
/// or are scheduled to do so in the future.
|
/// or are scheduled to do so in the future.
|
||||||
fn has_animations(&self) -> bool;
|
fn has_animations(&self, context: &SharedStyleContext) -> bool;
|
||||||
|
|
||||||
/// Returns true if the element has a CSS animation.
|
/// Returns true if the element has a CSS animation.
|
||||||
fn has_css_animations(&self, context: &SharedStyleContext) -> bool;
|
fn has_css_animations(&self, context: &SharedStyleContext) -> bool;
|
||||||
|
|
||||||
/// Returns true if the element has a CSS transition (including running transitions and
|
/// Returns true if the element has a CSS transition (including running transitions and
|
||||||
/// completed transitions).
|
/// completed transitions).
|
||||||
fn has_css_transitions(&self) -> bool;
|
fn has_css_transitions(&self, context: &SharedStyleContext) -> bool;
|
||||||
|
|
||||||
/// Returns true if the element has animation restyle hints.
|
/// Returns true if the element has animation restyle hints.
|
||||||
fn has_animation_restyle_hints(&self) -> bool {
|
fn has_animation_restyle_hints(&self) -> bool {
|
||||||
|
|
|
@ -1524,32 +1524,10 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }
|
self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_css_transitions(&self) -> bool {
|
fn has_css_transitions(&self, _: &SharedStyleContext) -> bool {
|
||||||
self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
|
self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn might_need_transitions_update(
|
|
||||||
&self,
|
|
||||||
old_style: Option<&ComputedValues>,
|
|
||||||
new_style: &ComputedValues,
|
|
||||||
) -> bool {
|
|
||||||
let old_style = match old_style {
|
|
||||||
Some(v) => v,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_box_style = new_style.get_box();
|
|
||||||
if !self.has_css_transitions() && !new_box_style.specifies_transitions() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_box_style.clone_display().is_none() || old_style.clone_display().is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect if there are any changes that require us to update transitions.
|
// Detect if there are any changes that require us to update transitions.
|
||||||
// This is used as a more thoroughgoing check than the, cheaper
|
// This is used as a more thoroughgoing check than the, cheaper
|
||||||
// might_need_transitions_update check.
|
// might_need_transitions_update check.
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use crate::computed_value_flags::ComputedValueFlags;
|
use crate::computed_value_flags::ComputedValueFlags;
|
||||||
use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
|
use crate::context::{CascadeInputs, ElementCascadeInputs, QuirksMode, SelectorFlagsMap};
|
||||||
use crate::context::{SharedStyleContext, StyleContext};
|
use crate::context::{SharedStyleContext, StyleContext};
|
||||||
use crate::data::ElementData;
|
use crate::data::ElementData;
|
||||||
use crate::dom::TElement;
|
use crate::dom::TElement;
|
||||||
|
@ -176,7 +176,9 @@ trait PrivateMatchMethods: TElement {
|
||||||
if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
|
if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
|
||||||
replace_rule_node(
|
replace_rule_node(
|
||||||
CascadeLevel::Transitions,
|
CascadeLevel::Transitions,
|
||||||
self.transition_rule().as_ref().map(|a| a.borrow_arc()),
|
self.transition_rule(&context.shared)
|
||||||
|
.as_ref()
|
||||||
|
.map(|a| a.borrow_arc()),
|
||||||
primary_rules,
|
primary_rules,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -184,7 +186,9 @@ trait PrivateMatchMethods: TElement {
|
||||||
if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
|
if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
|
||||||
replace_rule_node(
|
replace_rule_node(
|
||||||
CascadeLevel::Animations,
|
CascadeLevel::Animations,
|
||||||
self.animation_rule().as_ref().map(|a| a.borrow_arc()),
|
self.animation_rule(&context.shared)
|
||||||
|
.as_ref()
|
||||||
|
.map(|a| a.borrow_arc()),
|
||||||
primary_rules,
|
primary_rules,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -194,14 +198,11 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If there is no transition rule in the ComputedValues, it returns None.
|
/// If there is no transition rule in the ComputedValues, it returns None.
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
fn after_change_style(
|
fn after_change_style(
|
||||||
&self,
|
&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
primary_style: &Arc<ComputedValues>,
|
primary_style: &Arc<ComputedValues>,
|
||||||
) -> Option<Arc<ComputedValues>> {
|
) -> Option<Arc<ComputedValues>> {
|
||||||
use crate::context::CascadeInputs;
|
|
||||||
|
|
||||||
let rule_node = primary_style.rules();
|
let rule_node = primary_style.rules();
|
||||||
let without_transition_rules = context
|
let without_transition_rules = context
|
||||||
.shared
|
.shared
|
||||||
|
@ -314,6 +315,29 @@ trait PrivateMatchMethods: TElement {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn might_need_transitions_update(
|
||||||
|
&self,
|
||||||
|
context: &StyleContext<Self>,
|
||||||
|
old_style: Option<&ComputedValues>,
|
||||||
|
new_style: &ComputedValues,
|
||||||
|
) -> bool {
|
||||||
|
let old_style = match old_style {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_box_style = new_style.get_box();
|
||||||
|
if !self.has_css_transitions(context.shared) && !new_box_style.specifies_transitions() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_box_style.clone_display().is_none() || old_style.clone_display().is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a SequentialTask for resolving descendants in a SMIL display
|
/// Create a SequentialTask for resolving descendants in a SMIL display
|
||||||
/// property animation if the display property changed from none.
|
/// property animation if the display property changed from none.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -374,10 +398,12 @@ trait PrivateMatchMethods: TElement {
|
||||||
tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
|
tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
let before_change_style = if self
|
let before_change_style = if self.might_need_transitions_update(
|
||||||
.might_need_transitions_update(old_values.as_ref().map(|s| &**s), new_values)
|
context,
|
||||||
{
|
old_values.as_ref().map(|s| &**s),
|
||||||
let after_change_style = if self.has_css_transitions() {
|
new_values,
|
||||||
|
) {
|
||||||
|
let after_change_style = if self.has_css_transitions(context.shared) {
|
||||||
self.after_change_style(context, new_values)
|
self.after_change_style(context, new_values)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -442,6 +468,16 @@ trait PrivateMatchMethods: TElement {
|
||||||
// map because this call will do a RwLock::read().
|
// map because this call will do a RwLock::read().
|
||||||
let needs_animations_update =
|
let needs_animations_update =
|
||||||
self.needs_animations_update(context, old_values.as_ref().map(|s| &**s), new_values);
|
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 this_opaque = self.as_node().opaque();
|
||||||
let shared_context = context.shared;
|
let shared_context = context.shared;
|
||||||
|
@ -471,28 +507,43 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
animation_set.update_transitions_for_new_style(
|
animation_set.update_transitions_for_new_style(
|
||||||
|
might_need_transitions_update,
|
||||||
&shared_context,
|
&shared_context,
|
||||||
this_opaque,
|
this_opaque,
|
||||||
old_values.as_ref(),
|
old_values.as_ref(),
|
||||||
new_values,
|
after_change_style.as_ref().unwrap_or(new_values),
|
||||||
);
|
);
|
||||||
|
|
||||||
animation_set.apply_active_animations(shared_context, new_values);
|
|
||||||
|
|
||||||
// We clear away any finished transitions, but retain animations, because they
|
// We clear away any finished transitions, but retain animations, because they
|
||||||
// might still be used for proper calculation of `animation-fill-mode`.
|
// 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.
|
||||||
animation_set
|
animation_set
|
||||||
.transitions
|
.transitions
|
||||||
.retain(|transition| transition.state != AnimationState::Finished);
|
.retain(|transition| transition.state != AnimationState::Finished);
|
||||||
|
|
||||||
// If the ElementAnimationSet is empty, and don't store it in order to
|
// If the ElementAnimationSet is empty, and don't store it in order to
|
||||||
// save memory and to avoid extra processing later.
|
// save memory and to avoid extra processing later.
|
||||||
|
let changed_animations = animation_set.dirty;
|
||||||
if !animation_set.is_empty() {
|
if !animation_set.is_empty() {
|
||||||
|
animation_set.dirty = false;
|
||||||
shared_context
|
shared_context
|
||||||
.animation_states
|
.animation_states
|
||||||
.write()
|
.write()
|
||||||
.insert(this_opaque, animation_set);
|
.insert(this_opaque, animation_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have modified animation or transitions, we recascade style for this node.
|
||||||
|
if changed_animations {
|
||||||
|
let mut resolver = StyleResolverForElement::new(
|
||||||
|
*self,
|
||||||
|
context,
|
||||||
|
RuleInclusion::All,
|
||||||
|
PseudoElementResolution::IfApplicable,
|
||||||
|
);
|
||||||
|
let new_primary = resolver.resolve_style_with_default_parents();
|
||||||
|
*new_values = new_primary.primary.style.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes and applies non-redundant damage.
|
/// Computes and applies non-redundant damage.
|
||||||
|
|
|
@ -2828,11 +2828,12 @@ pub mod style_structs {
|
||||||
/// Returns whether there are any transitions specified.
|
/// Returns whether there are any transitions specified.
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
pub fn specifies_transitions(&self) -> bool {
|
pub fn specifies_transitions(&self) -> bool {
|
||||||
// TODO(mrobinson): This should check the combined duration and not just
|
(0..self.transition_property_count()).any(|index| {
|
||||||
// the duration.
|
let combined_duration =
|
||||||
self.transition_duration_iter()
|
self.transition_duration_mod(index).seconds().max(0.) +
|
||||||
.take(self.transition_property_count())
|
self.transition_delay_mod(index).seconds();
|
||||||
.any(|t| t.seconds() > 0.)
|
combined_duration > 0.
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if animation properties are equal between styles, but without
|
/// Returns true if animation properties are equal between styles, but without
|
||||||
|
|
|
@ -587,6 +587,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
style: &PrimaryStyle,
|
style: &PrimaryStyle,
|
||||||
validation_data_holder: Option<&mut StyleSharingTarget<E>>,
|
validation_data_holder: Option<&mut StyleSharingTarget<E>>,
|
||||||
dom_depth: usize,
|
dom_depth: usize,
|
||||||
|
shared_context: &SharedStyleContext,
|
||||||
) {
|
) {
|
||||||
let parent = match element.traversal_parent() {
|
let parent = match element.traversal_parent() {
|
||||||
Some(element) => element,
|
Some(element) => element,
|
||||||
|
@ -619,7 +620,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
// * Our computed style can still be affected by animations after we no
|
// * Our computed style can still be affected by animations after we no
|
||||||
// longer match any animation rules, since removing animations involves
|
// longer match any animation rules, since removing animations involves
|
||||||
// a sequential task and an additional traversal.
|
// a sequential task and an additional traversal.
|
||||||
if element.has_animations() {
|
if element.has_animations(shared_context) {
|
||||||
debug!("Failing to insert to the cache: running animations");
|
debug!("Failing to insert to the cache: running animations");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -700,6 +701,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
bloom_filter,
|
bloom_filter,
|
||||||
nth_index_cache,
|
nth_index_cache,
|
||||||
selector_flags_map,
|
selector_flags_map,
|
||||||
|
shared_context,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -711,6 +713,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
bloom: &StyleBloom<E>,
|
bloom: &StyleBloom<E>,
|
||||||
nth_index_cache: &mut NthIndexCache,
|
nth_index_cache: &mut NthIndexCache,
|
||||||
selector_flags_map: &mut SelectorFlagsMap<E>,
|
selector_flags_map: &mut SelectorFlagsMap<E>,
|
||||||
|
shared_context: &SharedStyleContext,
|
||||||
) -> Option<ResolvedElementStyles> {
|
) -> Option<ResolvedElementStyles> {
|
||||||
debug_assert!(!target.is_in_native_anonymous_subtree());
|
debug_assert!(!target.is_in_native_anonymous_subtree());
|
||||||
|
|
||||||
|
@ -770,7 +773,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if target.element.has_animations() {
|
if target.element.has_animations(shared_context) {
|
||||||
trace!("Miss: Has Animations");
|
trace!("Miss: Has Animations");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,7 +433,7 @@ where
|
||||||
implemented_pseudo.as_ref(),
|
implemented_pseudo.as_ref(),
|
||||||
self.element.style_attribute(),
|
self.element.style_attribute(),
|
||||||
self.element.smil_override(),
|
self.element.smil_override(),
|
||||||
self.element.animation_rules(),
|
self.element.animation_rules(self.context.shared),
|
||||||
self.rule_inclusion,
|
self.rule_inclusion,
|
||||||
&mut applicable_declarations,
|
&mut applicable_declarations,
|
||||||
&mut matching_context,
|
&mut matching_context,
|
||||||
|
|
|
@ -613,6 +613,7 @@ where
|
||||||
&new_styles.primary,
|
&new_styles.primary,
|
||||||
Some(&mut target),
|
Some(&mut target),
|
||||||
traversal_data.current_dom_depth,
|
traversal_data.current_dom_depth,
|
||||||
|
&context.shared,
|
||||||
);
|
);
|
||||||
|
|
||||||
new_styles
|
new_styles
|
||||||
|
@ -669,6 +670,7 @@ where
|
||||||
&new_styles.primary,
|
&new_styles.primary,
|
||||||
None,
|
None,
|
||||||
traversal_data.current_dom_depth,
|
traversal_data.current_dom_depth,
|
||||||
|
&context.shared,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[animation-base-response-003.html]
|
|
||||||
[Identical elements are all responsive to font-size animation]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -89,66 +89,3 @@
|
||||||
[Web Animations: property <opacity> from [initial\] to [0.2\] at (0.3) should be [0.76\]]
|
[Web Animations: property <opacity> from [initial\] to [0.2\] at (0.3) should be [0.76\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <opacity> from [inherit\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <opacity> from [initial\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <opacity> from [inherit\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <opacity> from [unset\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <opacity> from [0\] to [1\] at (-0.3) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <opacity> from [inherit\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <opacity> from [initial\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <opacity> from [unset\] to [0.2\] at (-0.3) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <opacity> from [unset\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <opacity> from [0\] to [1\] at (1.5) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <opacity> from [initial\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <opacity> from [0\] to [1\] at (-0.3) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <opacity> from [initial\] to [0.2\] at (-0.3) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <opacity> from [unset\] to [0.2\] at (1.5) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <opacity> from [initial\] to [0.2\] at (-0.3) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <opacity> from [0\] to [1\] at (-0.3) should be [0\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <opacity> from [unset\] to [0.2\] at (-0.3) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <opacity> from [unset\] to [0.2\] at (-0.3) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Animations: property <opacity> from [initial\] to [0.2\] at (-0.3) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions: property <opacity> from [0\] to [1\] at (1.5) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[CSS Transitions with transition: all: property <opacity> from [0\] to [1\] at (1.5) should be [1\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[non-rendered-element-001.html]
|
|
||||||
[Transitions do not run for an element newly rendered]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[transition-base-response-003.html]
|
|
||||||
[Identical elements are all responsive to font-size transition]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[animation-base-response-003.html]
|
|
||||||
[Identical elements are all responsive to font-size animation]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[non-rendered-element-001.html]
|
|
||||||
[Transitions do not run for an element newly rendered]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[transition-base-response-003.html]
|
|
||||||
[Identical elements are all responsive to font-size transition]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue