LibWeb/DOM: Allow the animationiteration event to be fired

This event is fired while both the previous and the current phase are
active.

This prevents this test from timing out:
- css/css-animations/animationevent-types.txt
This commit is contained in:
Lucas CHOLLET 2024-12-27 17:08:36 -05:00 committed by Andreas Kling
commit 61b444d538
Notes: github-actions[bot] 2024-12-30 10:09:05 +00:00

View file

@ -2360,85 +2360,84 @@ void Document::dispatch_events_for_animation_if_necessary(GC::Ref<Animations::An
auto current_phase = effect->phase(); auto current_phase = effect->phase();
auto current_iteration = effect->current_iteration().value_or(0.0); auto current_iteration = effect->current_iteration().value_or(0.0);
if (previous_phase != current_phase) { auto owning_element = css_animation.owning_element();
auto owning_element = css_animation.owning_element();
auto dispatch_event = [&](FlyString const& name, double elapsed_time) { auto dispatch_event = [&](FlyString const& name, double elapsed_time) {
append_pending_animation_event({ append_pending_animation_event({
.event = CSS::AnimationEvent::create( .event = CSS::AnimationEvent::create(
owning_element->realm(), owning_element->realm(),
name, name,
{ {
{ .bubbles = true }, { .bubbles = true },
css_animation.id(), css_animation.id(),
elapsed_time, elapsed_time,
}), }),
.animation = css_animation, .animation = css_animation,
.target = *target, .target = *target,
.scheduled_event_time = HighResolutionTime::unsafe_shared_current_time(), .scheduled_event_time = HighResolutionTime::unsafe_shared_current_time(),
}); });
}; };
// For calculating the elapsedTime of each event, the following definitions are used: // For calculating the elapsedTime of each event, the following definitions are used:
// - interval start = max(min(-start delay, active duration), 0) // - interval start = max(min(-start delay, active duration), 0)
auto interval_start = max(min(-effect->start_delay(), effect->active_duration()), 0.0); auto interval_start = max(min(-effect->start_delay(), effect->active_duration()), 0.0);
// - interval end = max(min(associated effect end - start delay, active duration), 0) // - interval end = max(min(associated effect end - start delay, active duration), 0)
auto interval_end = max(min(effect->end_time() - effect->start_delay(), effect->active_duration()), 0.0); auto interval_end = max(min(effect->end_time() - effect->start_delay(), effect->active_duration()), 0.0);
switch (previous_phase) { switch (previous_phase) {
case Animations::AnimationEffect::Phase::Before: case Animations::AnimationEffect::Phase::Before:
[[fallthrough]]; [[fallthrough]];
case Animations::AnimationEffect::Phase::Idle: case Animations::AnimationEffect::Phase::Idle:
if (current_phase == Animations::AnimationEffect::Phase::Active) { if (current_phase == Animations::AnimationEffect::Phase::Active) {
dispatch_event(HTML::EventNames::animationstart, interval_start); dispatch_event(HTML::EventNames::animationstart, interval_start);
} else if (current_phase == Animations::AnimationEffect::Phase::After) { } else if (current_phase == Animations::AnimationEffect::Phase::After) {
dispatch_event(HTML::EventNames::animationstart, interval_start); dispatch_event(HTML::EventNames::animationstart, interval_start);
dispatch_event(HTML::EventNames::animationend, interval_end); dispatch_event(HTML::EventNames::animationend, interval_end);
}
break;
case Animations::AnimationEffect::Phase::Active:
if (current_phase == Animations::AnimationEffect::Phase::Before) {
dispatch_event(HTML::EventNames::animationend, interval_start);
} else if (current_phase == Animations::AnimationEffect::Phase::Active) {
auto previous_current_iteration = effect->previous_current_iteration();
if (previous_current_iteration != current_iteration) {
// The elapsed time for an animationiteration event is defined as follows:
// 1. Let previous current iteration be the current iteration from the previous animation frame.
// 2. If previous current iteration is greater than current iteration, let iteration boundary be current iteration + 1,
// otherwise let it be current iteration.
auto iteration_boundary = previous_current_iteration > current_iteration ? current_iteration + 1 : current_iteration;
// 3. The elapsed time is the result of evaluating (iteration boundary - iteration start) × iteration duration).
auto iteration_duration_variant = effect->iteration_duration();
auto iteration_duration = iteration_duration_variant.has<String>() ? 0.0 : iteration_duration_variant.get<double>();
auto elapsed_time = (iteration_boundary - effect->iteration_start()) * iteration_duration;
dispatch_event(HTML::EventNames::animationiteration, elapsed_time);
}
} else if (current_phase == Animations::AnimationEffect::Phase::After) {
dispatch_event(HTML::EventNames::animationend, interval_end);
}
break;
case Animations::AnimationEffect::Phase::After:
if (current_phase == Animations::AnimationEffect::Phase::Active) {
dispatch_event(HTML::EventNames::animationstart, interval_end);
} else if (current_phase == Animations::AnimationEffect::Phase::Before) {
dispatch_event(HTML::EventNames::animationstart, interval_end);
dispatch_event(HTML::EventNames::animationend, interval_start);
}
break;
} }
break;
case Animations::AnimationEffect::Phase::Active:
if (current_phase == Animations::AnimationEffect::Phase::Before) {
dispatch_event(HTML::EventNames::animationend, interval_start);
} else if (current_phase == Animations::AnimationEffect::Phase::Active) {
auto previous_current_iteration = effect->previous_current_iteration();
if (previous_current_iteration != current_iteration) {
// The elapsed time for an animationiteration event is defined as follows:
if (current_phase == Animations::AnimationEffect::Phase::Idle && previous_phase != Animations::AnimationEffect::Phase::Idle && previous_phase != Animations::AnimationEffect::Phase::After) { // 1. Let previous current iteration be the current iteration from the previous animation frame.
// FIXME: Calculate a non-zero time when the animation is cancelled by means other than calling cancel()
auto cancel_time = animation->release_saved_cancel_time().value_or(0.0); // 2. If previous current iteration is greater than current iteration, let iteration boundary be current iteration + 1,
dispatch_event(HTML::EventNames::animationcancel, cancel_time); // otherwise let it be current iteration.
auto iteration_boundary = previous_current_iteration > current_iteration ? current_iteration + 1 : current_iteration;
// 3. The elapsed time is the result of evaluating (iteration boundary - iteration start) × iteration duration).
auto iteration_duration_variant = effect->iteration_duration();
auto iteration_duration = iteration_duration_variant.has<String>() ? 0.0 : iteration_duration_variant.get<double>();
auto elapsed_time = (iteration_boundary - effect->iteration_start()) * iteration_duration;
dispatch_event(HTML::EventNames::animationiteration, elapsed_time);
}
} else if (current_phase == Animations::AnimationEffect::Phase::After) {
dispatch_event(HTML::EventNames::animationend, interval_end);
} }
break;
case Animations::AnimationEffect::Phase::After:
if (current_phase == Animations::AnimationEffect::Phase::Active) {
dispatch_event(HTML::EventNames::animationstart, interval_end);
} else if (current_phase == Animations::AnimationEffect::Phase::Before) {
dispatch_event(HTML::EventNames::animationstart, interval_end);
dispatch_event(HTML::EventNames::animationend, interval_start);
}
break;
} }
if (current_phase == Animations::AnimationEffect::Phase::Idle && previous_phase != Animations::AnimationEffect::Phase::Idle && previous_phase != Animations::AnimationEffect::Phase::After) {
// FIXME: Calculate a non-zero time when the animation is cancelled by means other than calling cancel()
auto cancel_time = animation->release_saved_cancel_time().value_or(0.0);
dispatch_event(HTML::EventNames::animationcancel, cancel_time);
}
effect->set_previous_phase(current_phase); effect->set_previous_phase(current_phase);
effect->set_previous_current_iteration(current_iteration); effect->set_previous_current_iteration(current_iteration);
} }