mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
Add support for remaining animation and transition events
Fixes #21564.
This commit is contained in:
parent
47642e0eee
commit
77aa3721c5
28 changed files with 493 additions and 68 deletions
|
@ -2,6 +2,7 @@ DOMContentLoaded
|
||||||
abort
|
abort
|
||||||
activate
|
activate
|
||||||
addtrack
|
addtrack
|
||||||
|
animationcancel
|
||||||
animationend
|
animationend
|
||||||
animationiteration
|
animationiteration
|
||||||
animationstart
|
animationstart
|
||||||
|
@ -132,6 +133,7 @@ track
|
||||||
transitioncancel
|
transitioncancel
|
||||||
transitionend
|
transitionend
|
||||||
transitionrun
|
transitionrun
|
||||||
|
transitionstart
|
||||||
unhandledrejection
|
unhandledrejection
|
||||||
unload
|
unload
|
||||||
url
|
url
|
||||||
|
|
|
@ -78,12 +78,15 @@ impl Animations {
|
||||||
let mut sets = self.sets.write();
|
let mut sets = self.sets.write();
|
||||||
|
|
||||||
for set in sets.values_mut() {
|
for set in sets.values_mut() {
|
||||||
|
self.start_pending_animations(set, now, pipeline_id);
|
||||||
|
|
||||||
// When necessary, iterate our running animations to the next iteration.
|
// When necessary, iterate our running animations to the next iteration.
|
||||||
for animation in set.animations.iter_mut() {
|
for animation in set.animations.iter_mut() {
|
||||||
if animation.iterate_if_necessary(now) {
|
if animation.iterate_if_necessary(now) {
|
||||||
self.add_animation_event(
|
self.add_animation_event(
|
||||||
animation,
|
animation,
|
||||||
TransitionOrAnimationEventType::AnimationIteration,
|
TransitionOrAnimationEventType::AnimationIteration,
|
||||||
|
now,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -97,7 +100,6 @@ impl Animations {
|
||||||
|
|
||||||
/// Processes any new animations that were discovered after reflow. Collect messages
|
/// Processes any new animations that were discovered after reflow. Collect messages
|
||||||
/// that trigger events for any animations that changed state.
|
/// that trigger events for any animations that changed state.
|
||||||
/// TODO(mrobinson): The specification dictates that this should happen before reflow.
|
|
||||||
pub(crate) fn do_post_reflow_update(&self, window: &Window, now: f64) {
|
pub(crate) fn do_post_reflow_update(&self, window: &Window, now: f64) {
|
||||||
let pipeline_id = window.pipeline_id();
|
let pipeline_id = window.pipeline_id();
|
||||||
let mut sets = self.sets.write();
|
let mut sets = self.sets.write();
|
||||||
|
@ -140,6 +142,39 @@ impl Animations {
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Walk through the list of pending animations and start all of the ones that
|
||||||
|
/// have left the delay phase.
|
||||||
|
fn start_pending_animations(
|
||||||
|
&self,
|
||||||
|
set: &mut ElementAnimationSet,
|
||||||
|
now: f64,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
) {
|
||||||
|
for animation in set.animations.iter_mut() {
|
||||||
|
if animation.state == AnimationState::Pending && animation.started_at <= now {
|
||||||
|
animation.state = AnimationState::Running;
|
||||||
|
self.add_animation_event(
|
||||||
|
animation,
|
||||||
|
TransitionOrAnimationEventType::AnimationStart,
|
||||||
|
now,
|
||||||
|
pipeline_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for transition in set.transitions.iter_mut() {
|
||||||
|
if transition.state == AnimationState::Pending && transition.start_time <= now {
|
||||||
|
transition.state = AnimationState::Running;
|
||||||
|
self.add_transition_event(
|
||||||
|
transition,
|
||||||
|
TransitionOrAnimationEventType::TransitionStart,
|
||||||
|
now,
|
||||||
|
pipeline_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Walk through the list of running animations and remove all of the ones that
|
/// Walk through the list of running animations and remove all of the ones that
|
||||||
/// have ended.
|
/// have ended.
|
||||||
fn finish_running_animations(
|
fn finish_running_animations(
|
||||||
|
@ -154,6 +189,7 @@ impl Animations {
|
||||||
self.add_animation_event(
|
self.add_animation_event(
|
||||||
animation,
|
animation,
|
||||||
TransitionOrAnimationEventType::AnimationEnd,
|
TransitionOrAnimationEventType::AnimationEnd,
|
||||||
|
now,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -192,7 +228,17 @@ impl Animations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(mrobinson): We need to send animationcancel events.
|
for animation in &set.animations {
|
||||||
|
if animation.state == AnimationState::Canceled {
|
||||||
|
self.add_animation_event(
|
||||||
|
animation,
|
||||||
|
TransitionOrAnimationEventType::AnimationCancel,
|
||||||
|
now,
|
||||||
|
pipeline_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
set.clear_canceled_animations();
|
set.clear_canceled_animations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,14 +309,21 @@ impl Animations {
|
||||||
now: f64,
|
now: f64,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
) {
|
) {
|
||||||
|
// Calculate the `elapsed-time` property of the event and take the absolute
|
||||||
|
// value to prevent -0 values.
|
||||||
let elapsed_time = match event_type {
|
let elapsed_time = match event_type {
|
||||||
TransitionOrAnimationEventType::TransitionRun |
|
TransitionOrAnimationEventType::TransitionRun |
|
||||||
|
TransitionOrAnimationEventType::TransitionStart => transition
|
||||||
|
.property_animation
|
||||||
|
.duration
|
||||||
|
.min((-transition.delay).max(0.)),
|
||||||
TransitionOrAnimationEventType::TransitionEnd => transition.property_animation.duration,
|
TransitionOrAnimationEventType::TransitionEnd => transition.property_animation.duration,
|
||||||
TransitionOrAnimationEventType::TransitionCancel => {
|
TransitionOrAnimationEventType::TransitionCancel => {
|
||||||
(now - transition.start_time).max(0.)
|
(now - transition.start_time).max(0.)
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
}
|
||||||
|
.abs();
|
||||||
|
|
||||||
self.pending_events
|
self.pending_events
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -291,6 +344,7 @@ impl Animations {
|
||||||
&self,
|
&self,
|
||||||
animation: &Animation,
|
animation: &Animation,
|
||||||
event_type: TransitionOrAnimationEventType,
|
event_type: TransitionOrAnimationEventType,
|
||||||
|
now: f64,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
) {
|
) {
|
||||||
let num_iterations = match animation.iteration_state {
|
let num_iterations = match animation.iteration_state {
|
||||||
|
@ -298,11 +352,25 @@ impl Animations {
|
||||||
KeyframesIterationState::Infinite(current) => current,
|
KeyframesIterationState::Infinite(current) => current,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let active_duration = match animation.iteration_state {
|
||||||
|
KeyframesIterationState::Finite(_, max) => max * animation.duration,
|
||||||
|
KeyframesIterationState::Infinite(_) => std::f64::MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate the `elapsed-time` property of the event and take the absolute
|
||||||
|
// value to prevent -0 values.
|
||||||
let elapsed_time = match event_type {
|
let elapsed_time = match event_type {
|
||||||
|
TransitionOrAnimationEventType::AnimationStart => {
|
||||||
|
(-animation.delay).max(0.).min(active_duration)
|
||||||
|
},
|
||||||
TransitionOrAnimationEventType::AnimationIteration |
|
TransitionOrAnimationEventType::AnimationIteration |
|
||||||
TransitionOrAnimationEventType::AnimationEnd => num_iterations * animation.duration,
|
TransitionOrAnimationEventType::AnimationEnd => num_iterations * animation.duration,
|
||||||
|
TransitionOrAnimationEventType::AnimationCancel => {
|
||||||
|
(num_iterations * animation.duration) + (now - animation.started_at).max(0.)
|
||||||
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
}
|
||||||
|
.abs();
|
||||||
|
|
||||||
self.pending_events
|
self.pending_events
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -333,10 +401,13 @@ impl Animations {
|
||||||
|
|
||||||
let event_atom = match event.event_type {
|
let event_atom = match event.event_type {
|
||||||
TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"),
|
TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"),
|
||||||
|
TransitionOrAnimationEventType::AnimationStart => atom!("animationstart"),
|
||||||
|
TransitionOrAnimationEventType::AnimationCancel => atom!("animationcancel"),
|
||||||
TransitionOrAnimationEventType::AnimationIteration => atom!("animationiteration"),
|
TransitionOrAnimationEventType::AnimationIteration => atom!("animationiteration"),
|
||||||
TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"),
|
TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"),
|
||||||
TransitionOrAnimationEventType::TransitionEnd => atom!("transitionend"),
|
TransitionOrAnimationEventType::TransitionEnd => atom!("transitionend"),
|
||||||
TransitionOrAnimationEventType::TransitionRun => atom!("transitionrun"),
|
TransitionOrAnimationEventType::TransitionRun => atom!("transitionrun"),
|
||||||
|
TransitionOrAnimationEventType::TransitionStart => atom!("transitionstart"),
|
||||||
};
|
};
|
||||||
let parent = EventInit {
|
let parent = EventInit {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
|
@ -381,25 +452,39 @@ pub enum TransitionOrAnimationEventType {
|
||||||
/// "The transitionrun event occurs when a transition is created (i.e., when it
|
/// "The transitionrun event occurs when a transition is created (i.e., when it
|
||||||
/// is added to the set of running transitions)."
|
/// is added to the set of running transitions)."
|
||||||
TransitionRun,
|
TransitionRun,
|
||||||
|
/// "The transitionstart event occurs when a transition’s delay phase ends."
|
||||||
|
TransitionStart,
|
||||||
/// "The transitionend event occurs at the completion of the transition. In the
|
/// "The transitionend event occurs at the completion of the transition. In the
|
||||||
/// case where a transition is removed before completion, such as if the
|
/// case where a transition is removed before completion, such as if the
|
||||||
/// transition-property is removed, then the event will not fire."
|
/// transition-property is removed, then the event will not fire."
|
||||||
TransitionEnd,
|
TransitionEnd,
|
||||||
/// "The transitioncancel event occurs when a transition is canceled."
|
/// "The transitioncancel event occurs when a transition is canceled."
|
||||||
TransitionCancel,
|
TransitionCancel,
|
||||||
|
/// "The animationstart event occurs at the start of the animation. If there is
|
||||||
|
/// an animation-delay then this event will fire once the delay period has expired."
|
||||||
|
AnimationStart,
|
||||||
/// "The animationiteration event occurs at the end of each iteration of an
|
/// "The animationiteration event occurs at the end of each iteration of an
|
||||||
/// animation, except when an animationend event would fire at the same time."
|
/// animation, except when an animationend event would fire at the same time."
|
||||||
AnimationIteration,
|
AnimationIteration,
|
||||||
/// "The animationend event occurs when the animation finishes"
|
/// "The animationend event occurs when the animation finishes"
|
||||||
AnimationEnd,
|
AnimationEnd,
|
||||||
|
/// "The animationcancel event occurs when the animation stops running in a way
|
||||||
|
/// that does not fire an animationend event..."
|
||||||
|
AnimationCancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransitionOrAnimationEventType {
|
impl TransitionOrAnimationEventType {
|
||||||
/// Whether or not this event is a transition-related event.
|
/// Whether or not this event is a transition-related event.
|
||||||
pub fn is_transition_event(&self) -> bool {
|
pub fn is_transition_event(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Self::TransitionRun | Self::TransitionEnd | Self::TransitionCancel => true,
|
Self::TransitionRun |
|
||||||
Self::AnimationEnd | Self::AnimationIteration => false,
|
Self::TransitionEnd |
|
||||||
|
Self::TransitionCancel |
|
||||||
|
Self::TransitionStart => true,
|
||||||
|
Self::AnimationEnd |
|
||||||
|
Self::AnimationIteration |
|
||||||
|
Self::AnimationStart |
|
||||||
|
Self::AnimationCancel => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,17 +139,27 @@ impl PropertyAnimation {
|
||||||
/// This structure represents the state of an animation.
|
/// This structure represents the state of an animation.
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
||||||
pub enum AnimationState {
|
pub enum AnimationState {
|
||||||
|
/// The animation has been created, but is not running yet. This state
|
||||||
|
/// is also used when an animation is still in the first delay phase.
|
||||||
|
Pending,
|
||||||
|
/// This animation is currently running.
|
||||||
|
Running,
|
||||||
/// This animation is paused. The inner field is the percentage of progress
|
/// This animation is paused. The inner field is the percentage of progress
|
||||||
/// when it was paused, from 0 to 1.
|
/// when it was paused, from 0 to 1.
|
||||||
Paused(f64),
|
Paused(f64),
|
||||||
/// This animation is currently running.
|
|
||||||
Running,
|
|
||||||
/// This animation has finished.
|
/// This animation has finished.
|
||||||
Finished,
|
Finished,
|
||||||
/// This animation has been canceled.
|
/// This animation has been canceled.
|
||||||
Canceled,
|
Canceled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AnimationState {
|
||||||
|
/// Whether or not this state requires its owning animation to be ticked.
|
||||||
|
fn needs_to_be_ticked(&self) -> bool {
|
||||||
|
*self == AnimationState::Running || *self == AnimationState::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This structure represents a keyframes animation current iteration state.
|
/// This structure represents a keyframes animation current iteration state.
|
||||||
///
|
///
|
||||||
/// If the iteration count is infinite, there's no other state, otherwise we
|
/// If the iteration count is infinite, there's no other state, otherwise we
|
||||||
|
@ -174,7 +184,8 @@ pub struct Animation {
|
||||||
/// The internal animation from the style system.
|
/// The internal animation from the style system.
|
||||||
pub keyframes_animation: KeyframesAnimation,
|
pub keyframes_animation: KeyframesAnimation,
|
||||||
|
|
||||||
/// The time this animation started at.
|
/// The time this animation started at, which is the current value of the animation
|
||||||
|
/// timeline when this animation was created plus any animation delay.
|
||||||
pub started_at: f64,
|
pub started_at: f64,
|
||||||
|
|
||||||
/// The duration of this animation.
|
/// The duration of this animation.
|
||||||
|
@ -273,9 +284,11 @@ impl Animation {
|
||||||
/// canceled due to changes in the style.
|
/// canceled due to changes in the style.
|
||||||
pub fn has_ended(&self, time: f64) -> bool {
|
pub fn has_ended(&self, time: f64) -> bool {
|
||||||
match self.state {
|
match self.state {
|
||||||
AnimationState::Canceled | AnimationState::Paused(_) => return false,
|
|
||||||
AnimationState::Finished => return true,
|
|
||||||
AnimationState::Running => {},
|
AnimationState::Running => {},
|
||||||
|
AnimationState::Finished => return true,
|
||||||
|
AnimationState::Pending | AnimationState::Canceled | AnimationState::Paused(_) => {
|
||||||
|
return false
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.iteration_over(time) {
|
if !self.iteration_over(time) {
|
||||||
|
@ -312,24 +325,11 @@ impl Animation {
|
||||||
let old_direction = self.current_direction;
|
let old_direction = self.current_direction;
|
||||||
let old_state = self.state.clone();
|
let old_state = self.state.clone();
|
||||||
let old_iteration_state = self.iteration_state.clone();
|
let old_iteration_state = self.iteration_state.clone();
|
||||||
|
|
||||||
*self = other.clone();
|
*self = other.clone();
|
||||||
|
|
||||||
let mut new_started_at = old_started_at;
|
self.started_at = old_started_at;
|
||||||
|
self.current_direction = old_direction;
|
||||||
// If we're unpausing the animation, fake the start time so we seem to
|
|
||||||
// restore it.
|
|
||||||
//
|
|
||||||
// If the animation keeps paused, keep the old value.
|
|
||||||
//
|
|
||||||
// If we're pausing the animation, compute the progress value.
|
|
||||||
match (&mut self.state, old_state) {
|
|
||||||
(&mut Running, Paused(progress)) => new_started_at = now - (self.duration * progress),
|
|
||||||
(&mut Paused(ref mut new), Paused(old)) => *new = old,
|
|
||||||
(&mut Paused(ref mut progress), Running) => {
|
|
||||||
*progress = (now - old_started_at) / old_duration
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't update the iteration count, just the iteration limit.
|
// Don't update the iteration count, just the iteration limit.
|
||||||
// TODO: see how changing the limit affects rendering in other browsers.
|
// TODO: see how changing the limit affects rendering in other browsers.
|
||||||
|
@ -342,8 +342,37 @@ impl Animation {
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current_direction = old_direction;
|
// Don't pause or restart animations that should remain finished.
|
||||||
self.started_at = new_started_at;
|
// We call mem::replace because `has_ended(...)` looks at `Animation::state`.
|
||||||
|
let new_state = std::mem::replace(&mut self.state, Running);
|
||||||
|
if old_state == Finished && self.has_ended(now) {
|
||||||
|
self.state = Finished;
|
||||||
|
} else {
|
||||||
|
self.state = new_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're unpausing the animation, fake the start time so we seem to
|
||||||
|
// restore it.
|
||||||
|
//
|
||||||
|
// If the animation keeps paused, keep the old value.
|
||||||
|
//
|
||||||
|
// If we're pausing the animation, compute the progress value.
|
||||||
|
match (&mut self.state, &old_state) {
|
||||||
|
(&mut Pending, &Paused(progress)) => {
|
||||||
|
self.started_at = now - (self.duration * progress);
|
||||||
|
},
|
||||||
|
(&mut Paused(ref mut new), &Paused(old)) => *new = old,
|
||||||
|
(&mut Paused(ref mut progress), &Running) => {
|
||||||
|
*progress = (now - old_started_at) / old_duration
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to detect when we should skip straight to the running phase to
|
||||||
|
// avoid sending multiple animationstart events.
|
||||||
|
if self.state == Pending && self.started_at <= now && old_state != Pending {
|
||||||
|
self.state = Running;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the given style to reflect the values specified by this `Animation`
|
/// Update the given style to reflect the values specified by this `Animation`
|
||||||
|
@ -360,7 +389,7 @@ impl Animation {
|
||||||
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::Finished => {
|
AnimationState::Running | AnimationState::Pending | AnimationState::Finished => {
|
||||||
context.current_time_for_animations
|
context.current_time_for_animations
|
||||||
},
|
},
|
||||||
AnimationState::Paused(progress) => started_at + duration * progress,
|
AnimationState::Paused(progress) => started_at + duration * progress,
|
||||||
|
@ -551,9 +580,12 @@ pub struct Transition {
|
||||||
pub node: OpaqueNode,
|
pub node: OpaqueNode,
|
||||||
|
|
||||||
/// The start time of this transition, which is the current value of the animation
|
/// The start time of this transition, which is the current value of the animation
|
||||||
/// timeline when this transition created.
|
/// timeline when this transition was created plus any animation delay.
|
||||||
pub start_time: f64,
|
pub start_time: f64,
|
||||||
|
|
||||||
|
/// The delay used for this transition.
|
||||||
|
pub delay: f64,
|
||||||
|
|
||||||
/// The internal style `PropertyAnimation` for this transition.
|
/// The internal style `PropertyAnimation` for this transition.
|
||||||
pub property_animation: PropertyAnimation,
|
pub property_animation: PropertyAnimation,
|
||||||
|
|
||||||
|
@ -724,26 +756,25 @@ impl ElementAnimationSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not this state needs animation ticks for its transitions
|
/// Whether or not this state needs animation ticks for its transitions
|
||||||
/// or animations. New animations don't need ticks until they are no
|
/// or animations.
|
||||||
/// longer marked as new.
|
|
||||||
pub fn needs_animation_ticks(&self) -> bool {
|
pub fn needs_animation_ticks(&self) -> bool {
|
||||||
self.animations
|
self.animations
|
||||||
.iter()
|
.iter()
|
||||||
.any(|animation| animation.state == AnimationState::Running && !animation.is_new) ||
|
.any(|animation| animation.state.needs_to_be_ticked()) ||
|
||||||
self.transitions.iter().any(|transition| {
|
self.transitions
|
||||||
transition.state == AnimationState::Running && !transition.is_new
|
.iter()
|
||||||
})
|
.any(|transition| transition.state.needs_to_be_ticked())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of running animations and transitions for this `ElementAnimationSet`.
|
/// The number of running animations and transitions for this `ElementAnimationSet`.
|
||||||
pub fn running_animation_and_transition_count(&self) -> usize {
|
pub fn running_animation_and_transition_count(&self) -> usize {
|
||||||
self.animations
|
self.animations
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|animation| animation.state == AnimationState::Running)
|
.filter(|animation| animation.state.needs_to_be_ticked())
|
||||||
.count() +
|
.count() +
|
||||||
self.transitions
|
self.transitions
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|transition| transition.state == AnimationState::Running)
|
.filter(|transition| transition.state.needs_to_be_ticked())
|
||||||
.count()
|
.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -870,8 +901,9 @@ impl ElementAnimationSet {
|
||||||
let mut new_transition = Transition {
|
let mut new_transition = Transition {
|
||||||
node: opaque_node,
|
node: opaque_node,
|
||||||
start_time: now + delay,
|
start_time: now + delay,
|
||||||
|
delay,
|
||||||
property_animation,
|
property_animation,
|
||||||
state: AnimationState::Running,
|
state: AnimationState::Pending,
|
||||||
is_new: true,
|
is_new: true,
|
||||||
reversing_adjusted_start_value,
|
reversing_adjusted_start_value,
|
||||||
reversing_shortening_factor: 1.0,
|
reversing_shortening_factor: 1.0,
|
||||||
|
@ -1040,7 +1072,7 @@ pub fn maybe_start_animations<E>(
|
||||||
|
|
||||||
let state = match box_style.animation_play_state_mod(i) {
|
let state = match box_style.animation_play_state_mod(i) {
|
||||||
AnimationPlayState::Paused => AnimationState::Paused(0.),
|
AnimationPlayState::Paused => AnimationState::Paused(0.),
|
||||||
AnimationPlayState::Running => AnimationState::Running,
|
AnimationPlayState::Running => AnimationState::Pending,
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_animation = Animation {
|
let new_animation = Animation {
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[animationevent-types.html]
|
|
||||||
expected: TIMEOUT
|
|
||||||
[animationstart event is instanceof AnimationEvent]
|
|
||||||
expected: TIMEOUT
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[Element-getAnimations.tentative.html]
|
[Element-getAnimations.tentative.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26626
|
||||||
[getAnimations for CSS Animations with animation-name: none]
|
[getAnimations for CSS Animations with animation-name: none]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[animationevent-pseudoelement.html]
|
[animationevent-pseudoelement.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/10316
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[AnimationEvent should have the correct pseudoElement memeber]
|
[AnimationEvent should have the correct pseudoElement memeber]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
[animationevent-types.html]
|
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
|
||||||
expected: TIMEOUT
|
|
||||||
[animationstart event is instanceof AnimationEvent]
|
|
||||||
expected: TIMEOUT
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[events-006.html]
|
[events-006.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/10316
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[transition padding-left on ::after]
|
[transition padding-left on ::after]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-animation-from-to.html]
|
[variable-animation-from-to.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verify CSS variable value before animation]
|
[Verify CSS variable value before animation]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-animation-over-transition.html]
|
[variable-animation-over-transition.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verify CSS variable value before animation]
|
[Verify CSS variable value before animation]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
[variable-animation-substitute-into-keyframe-shorthand.html]
|
[variable-animation-substitute-into-keyframe-shorthand.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-animation-substitute-into-keyframe-transform.html]
|
[variable-animation-substitute-into-keyframe-transform.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
[Verify transform before animation]
|
[Verify transform before animation]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
[variable-animation-substitute-into-keyframe.html]
|
[variable-animation-substitute-into-keyframe.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-animation-substitute-within-keyframe-fallback.html]
|
[variable-animation-substitute-within-keyframe-fallback.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
[Verify color after animation]
|
[Verify color after animation]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-animation-substitute-within-keyframe-multiple.html]
|
[variable-animation-substitute-within-keyframe-multiple.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
[Verify color after animation]
|
[Verify color after animation]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-animation-substitute-within-keyframe.html]
|
[variable-animation-substitute-within-keyframe.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
[Verify color after animation]
|
[Verify color after animation]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-animation-to-only.html]
|
[variable-animation-to-only.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verify CSS variable value after animation]
|
[Verify CSS variable value after animation]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-transitions-from-no-value.html]
|
[variable-transitions-from-no-value.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verify CSS variable value after transition]
|
[Verify CSS variable value after transition]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-transitions-to-no-value.html]
|
[variable-transitions-to-no-value.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verify CSS variable value after transition]
|
[Verify CSS variable value after transition]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-transitions-transition-property-variable-before-value.html]
|
[variable-transitions-transition-property-variable-before-value.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verify CSS variable value after transition]
|
[Verify CSS variable value after transition]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[variable-transitions-value-before-transition-property-variable.html]
|
[variable-transitions-value-before-transition-property-variable.html]
|
||||||
bug: https://github.com/servo/servo/issues/21564
|
bug: https://github.com/servo/servo/issues/26625
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Verify CSS variable value after transition]
|
[Verify CSS variable value after transition]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
prefs: ["layout.animations.test.enabled:false",
|
||||||
|
"dom.testbinding.enabled:false"]
|
|
@ -0,0 +1,3 @@
|
||||||
|
prefs: ["layout.animations.test.enabled:false",
|
||||||
|
"dom.testbinding.enabled:false"]
|
||||||
|
|
|
@ -12849,6 +12849,15 @@
|
||||||
},
|
},
|
||||||
"css": {
|
"css": {
|
||||||
"animations": {
|
"animations": {
|
||||||
|
"animation-events.html": [
|
||||||
|
"0975aa64ec47ca4b4c8fc1e0a40414a51719ad67",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
"timeout": "long"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
"animation-fill-mode.html": [
|
"animation-fill-mode.html": [
|
||||||
"4cfaab9fbce0adccd83f592935e63fa8ff58a1cf",
|
"4cfaab9fbce0adccd83f592935e63fa8ff58a1cf",
|
||||||
[
|
[
|
||||||
|
@ -12884,6 +12893,13 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"transition-events.html": [
|
||||||
|
"b561fc8353276e6bdd13a9d1b965f57733ecd19b",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"transition-raf.html": [
|
"transition-raf.html": [
|
||||||
"c38404503408e04b3c75b42df18ec3a7ec0819f5",
|
"c38404503408e04b3c75b42df18ec3a7ec0819f5",
|
||||||
[
|
[
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
prefs: ["layout.animations.test.enabled:false",
|
||||||
|
"dom.testbinding.enabled:false"]
|
|
@ -0,0 +1,3 @@
|
||||||
|
prefs: ["layout.animations.test.enabled:false",
|
||||||
|
"dom.testbinding.enabled:false"]
|
||||||
|
|
160
tests/wpt/mozilla/tests/css/animations/animation-events.html
Normal file
160
tests/wpt/mozilla/tests/css/animations/animation-events.html
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSS animation event dispatch</title>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim {
|
||||||
|
from { margin-left: 0px; }
|
||||||
|
to { margin-left: 100px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// This series of tests is a forked version of the Web Platform Test
|
||||||
|
// /css/css-animations/event-dispatch.tentative that do not make use
|
||||||
|
// of the Web Animations API, since Servo doesn't yet support Web Animations.
|
||||||
|
|
||||||
|
function waitForFrame() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
window.requestAnimationFrame(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// All animation events should be received on the next animation frame.
|
||||||
|
const animationEventsTimeout = () => {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
setTimeout(resolve, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupAnimation = (t, animationStyle) => {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.setAttribute('style', 'animation: ' + animationStyle);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
if (t && typeof t.add_cleanup === 'function') {
|
||||||
|
t.add_cleanup(function() {
|
||||||
|
if (div.parentNode) {
|
||||||
|
div.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const watcher = new EventWatcher(t, div, [ 'animationstart',
|
||||||
|
'animationiteration',
|
||||||
|
'animationend',
|
||||||
|
'animationcancel' ],
|
||||||
|
animationEventsTimeout);
|
||||||
|
|
||||||
|
return { watcher, div };
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['animationstart' ],
|
||||||
|
{
|
||||||
|
record: 'all',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_equals(events[0].elapsedTime, 0.0);
|
||||||
|
}, 'animationstart');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher } = setupAnimation(t, 'anim 0.1s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['animationstart', 'animationend'],
|
||||||
|
{
|
||||||
|
record: 'all',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_equals(events[0].elapsedTime, 0);
|
||||||
|
assert_equals(events[0].animationName, "anim");
|
||||||
|
|
||||||
|
assert_approx_equals(events[1].elapsedTime, 0.1, 0.001);
|
||||||
|
assert_equals(events[1].animationName, "anim");
|
||||||
|
}, 'animationstart and animationend');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher } = setupAnimation(t, 'anim 1s 0.5s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['animationstart', 'animationend'], { record: 'all', }
|
||||||
|
);
|
||||||
|
assert_approx_equals(events[0].elapsedTime, 0, 0.01);
|
||||||
|
assert_equals(events[0].animationName, "anim");
|
||||||
|
|
||||||
|
assert_approx_equals(events[1].elapsedTime, 1, 0.01);
|
||||||
|
assert_equals(events[1].animationName, "anim");
|
||||||
|
}, 'animationstart and animationend with positive delay');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher } = setupAnimation(t, 'anim 100s -99.99s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['animationstart', 'animationend'], { record: 'all', }
|
||||||
|
);
|
||||||
|
assert_approx_equals(events[0].elapsedTime, 99.99, 0.1);
|
||||||
|
assert_equals(events[0].animationName, "anim");
|
||||||
|
|
||||||
|
assert_approx_equals(events[1].elapsedTime, 99.99, 0.1);
|
||||||
|
assert_equals(events[1].animationName, "anim");
|
||||||
|
}, 'animationstart and animationend with negative delay');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher } = setupAnimation(t, 'anim 100s -200s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['animationstart', 'animationend'], { record: 'all', }
|
||||||
|
);
|
||||||
|
assert_approx_equals(events[0].elapsedTime, 99.99, 0.1);
|
||||||
|
assert_equals(events[0].animationName, "anim");
|
||||||
|
|
||||||
|
assert_approx_equals(events[1].elapsedTime, 99.99, 0.1);
|
||||||
|
assert_equals(events[1].animationName, "anim");
|
||||||
|
}, 'animationstart and animationend with negative delay larger than active duration');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher, div } = setupAnimation(t, 'anim 100s');
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
div.style.animation = "";
|
||||||
|
|
||||||
|
await watcher.wait_for('animationcancel');
|
||||||
|
}, 'animationcancel');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher, div } = setupAnimation(t, 'anim 100s 50s');
|
||||||
|
|
||||||
|
// Wait for two animation frames. One is not enough in some browser engines.
|
||||||
|
await waitForFrame();
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
div.style.animation = "";
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['animationcancel'], { record: 'all', }
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_equals(events[0].elapsedTime, 0);
|
||||||
|
}, 'animationcancel with positive delay');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher, div } = setupAnimation(t, 'anim 100s -50s');
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
div.style.animation = "";
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['animationcancel'], { record: 'all', }
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_approx_equals(events[0].elapsedTime, 50, 0.1);
|
||||||
|
}, 'animationcancel with negative delay');
|
||||||
|
|
||||||
|
</script>
|
131
tests/wpt/mozilla/tests/css/animations/transition-events.html
Normal file
131
tests/wpt/mozilla/tests/css/animations/transition-events.html
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSS transition event dispatch</title>
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#event-dispatch">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// This series of tests is a forked version of the Web Platform Test
|
||||||
|
// /css/css-transitions/event-dispatch.tentative that do not make use
|
||||||
|
// of the Web Animations API, since Servo doesn't yet support Web Animations.
|
||||||
|
|
||||||
|
|
||||||
|
// All transition events should be received on the next animation frame.
|
||||||
|
function transitionEventsTimeout() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
window.requestAnimationFrame(resolve);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupTransition = (t, transitionStyle) => {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.setAttribute('style', 'transition: ' + transitionStyle);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
if (t && typeof t.add_cleanup === 'function') {
|
||||||
|
t.add_cleanup(function() {
|
||||||
|
if (div.parentNode) {
|
||||||
|
div.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const watcher = new EventWatcher(t, div, [ 'transitionrun',
|
||||||
|
'transitionstart',
|
||||||
|
'transitionend',
|
||||||
|
'transitioncancel' ],
|
||||||
|
transitionEventsTimeout);
|
||||||
|
getComputedStyle(div).marginLeft;
|
||||||
|
|
||||||
|
div.style.marginLeft = '100px';
|
||||||
|
|
||||||
|
return { watcher, div };
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher } = setupTransition(t, 'margin-left 100s 100s');
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['transitionrun' ],
|
||||||
|
{
|
||||||
|
record: 'all',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_equals(events[0].elapsedTime, 0.0);
|
||||||
|
}, 'transitionrun');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher } = setupTransition(t, 'margin-left 100s');
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['transitionrun', 'transitionstart', ],
|
||||||
|
{
|
||||||
|
record: 'all',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_equals(events[0].elapsedTime, 0.0);
|
||||||
|
assert_equals(events[1].elapsedTime, 0.0);
|
||||||
|
}, 'transitionrun, transitionstart');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher, div } = setupTransition(t, 'margin-left 0.1s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['transitionrun', 'transitionstart', 'transitionend'],
|
||||||
|
{
|
||||||
|
record: 'all',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_equals(events[0].elapsedTime, 0);
|
||||||
|
assert_equals(events[0].propertyName, "margin-left");
|
||||||
|
|
||||||
|
assert_equals(events[1].elapsedTime, 0);
|
||||||
|
assert_equals(events[1].propertyName, "margin-left");
|
||||||
|
|
||||||
|
assert_approx_equals(events[2].elapsedTime, 0.1, 0.01);
|
||||||
|
assert_equals(events[2].propertyName, "margin-left");
|
||||||
|
|
||||||
|
}, 'transitionrun, transitionstart, transitionend');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher, div } = setupTransition(t, 'margin-left 1s 0.5s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['transitionrun', 'transitionstart', 'transitionend'],
|
||||||
|
{
|
||||||
|
record: 'all',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_equals(events[0].elapsedTime, 0);
|
||||||
|
assert_equals(events[0].propertyName, "margin-left");
|
||||||
|
|
||||||
|
assert_equals(events[1].elapsedTime, 0);
|
||||||
|
assert_equals(events[1].propertyName, "margin-left");
|
||||||
|
|
||||||
|
assert_equals(events[2].elapsedTime, 1);
|
||||||
|
assert_equals(events[2].propertyName, "margin-left");
|
||||||
|
}, 'transitionrun, transitionstart, transitionend with positive delay');
|
||||||
|
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { watcher, div } = setupTransition(t, 'margin-left 100s -99.99s');
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(
|
||||||
|
['transitionrun', 'transitionstart', 'transitionend'],
|
||||||
|
{
|
||||||
|
record: 'all',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_approx_equals(events[0].elapsedTime, 99.99, 0.1);
|
||||||
|
assert_equals(events[0].propertyName, "margin-left");
|
||||||
|
|
||||||
|
assert_approx_equals(events[1].elapsedTime, 99.99, 0.1);
|
||||||
|
assert_equals(events[1].propertyName, "margin-left");
|
||||||
|
|
||||||
|
assert_approx_equals(events[2].elapsedTime, 99.99, 0.1);
|
||||||
|
assert_equals(events[2].propertyName, "margin-left");
|
||||||
|
}, 'transitionrun, transitionstart, transitionend with negative delay');
|
||||||
|
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue