From 61b444d538721f9d62fd8e5f551863f04acd167d Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Fri, 27 Dec 2024 17:08:36 -0500 Subject: [PATCH] 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 --- Libraries/LibWeb/DOM/Document.cpp | 139 +++++++++++++++--------------- 1 file changed, 69 insertions(+), 70 deletions(-) diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 75f0645571d..28976e33349 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -2360,85 +2360,84 @@ void Document::dispatch_events_for_animation_if_necessary(GC::Refphase(); 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) { - append_pending_animation_event({ - .event = CSS::AnimationEvent::create( - owning_element->realm(), - name, - { - { .bubbles = true }, - css_animation.id(), - elapsed_time, - }), - .animation = css_animation, - .target = *target, - .scheduled_event_time = HighResolutionTime::unsafe_shared_current_time(), - }); - }; + auto dispatch_event = [&](FlyString const& name, double elapsed_time) { + append_pending_animation_event({ + .event = CSS::AnimationEvent::create( + owning_element->realm(), + name, + { + { .bubbles = true }, + css_animation.id(), + elapsed_time, + }), + .animation = css_animation, + .target = *target, + .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) - auto interval_start = max(min(-effect->start_delay(), effect->active_duration()), 0.0); + // - interval start = max(min(-start delay, active duration), 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) - auto interval_end = max(min(effect->end_time() - effect->start_delay(), effect->active_duration()), 0.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); - switch (previous_phase) { - case Animations::AnimationEffect::Phase::Before: - [[fallthrough]]; - case Animations::AnimationEffect::Phase::Idle: - if (current_phase == Animations::AnimationEffect::Phase::Active) { - dispatch_event(HTML::EventNames::animationstart, interval_start); - } else if (current_phase == Animations::AnimationEffect::Phase::After) { - dispatch_event(HTML::EventNames::animationstart, interval_start); - 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() ? 0.0 : iteration_duration_variant.get(); - 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; + switch (previous_phase) { + case Animations::AnimationEffect::Phase::Before: + [[fallthrough]]; + case Animations::AnimationEffect::Phase::Idle: + if (current_phase == Animations::AnimationEffect::Phase::Active) { + dispatch_event(HTML::EventNames::animationstart, interval_start); + } else if (current_phase == Animations::AnimationEffect::Phase::After) { + dispatch_event(HTML::EventNames::animationstart, interval_start); + 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: - 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); + // 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() ? 0.0 : iteration_duration_variant.get(); + 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_current_iteration(current_iteration); }