LibWeb: Simplify Animation::update_finished_state a bit

This removes the two boolean hack in favor of using the existing
mechanism to remove queued tasks. It also exposes the element
invalidation behavior for call sites that don't necessarily want to
update the finished state, but still need to invalidate the associated
target.
This commit is contained in:
Matthew Olsson 2024-02-10 20:52:58 -07:00 committed by Andreas Kling
parent 1e37ba5515
commit 06a8674eec
Notes: sideshowbarker 2024-07-18 22:57:59 +09:00
3 changed files with 37 additions and 31 deletions

View file

@ -630,6 +630,8 @@ WebIDL::ExceptionOr<void> Animation::silently_set_current_time(Optional<double>
// https://www.w3.org/TR/web-animations-1/#update-an-animations-finished-state
void Animation::update_finished_state(DidSeek did_seek, SynchronouslyNotify synchronously_notify)
{
auto& realm = this->realm();
// 1. Let the unconstrained current time be the result of calculating the current time substituting an unresolved
// time value for the hold time if did seek is false. If did seek is true, the unconstrained current time is
// equal to the current time.
@ -706,30 +708,23 @@ void Animation::update_finished_state(DidSeek did_seek, SynchronouslyNotify sync
if (current_finished_state && !m_is_finished) {
// 1. Let finish notification steps refer to the following procedure:
JS::SafeFunction<void()> finish_notification_steps = [&]() {
if (m_should_abort_finish_notification_microtask) {
m_should_abort_finish_notification_microtask = false;
m_has_finish_notification_microtask_scheduled = false;
return;
}
// 1. If animations play state is not equal to finished, abort these steps.
if (play_state() != Bindings::AnimationPlayState::Finished)
return;
// 2. Resolve animations current finished promise object with animation.
{
HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm()) };
WebIDL::resolve_promise(realm(), current_finished_promise(), this);
HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm) };
WebIDL::resolve_promise(realm, current_finished_promise(), this);
}
m_is_finished = true;
// 3. Create an AnimationPlaybackEvent, finishEvent.
// 4. Set finishEvents type attribute to finish.
// 5. Set finishEvents currentTime attribute to the current time of animation.
auto& realm = this->realm();
AnimationPlaybackEventInit init;
init.current_time = current_time();
auto finish_event = AnimationPlaybackEvent::create(realm, "finish"_fly_string, init);
auto finish_event = AnimationPlaybackEvent::create(realm, HTML::EventNames::finish, init);
// 6. Set finishEvents timelineTime attribute to the current time of the timeline with which animation is
// associated. If animation is not associated with a timeline, or the timeline is inactive, let
@ -750,44 +745,47 @@ void Animation::update_finished_state(DidSeek did_seek, SynchronouslyNotify sync
// Otherwise, queue a task to dispatch finishEvent at animation. The task source for this task is the DOM
// manipulation task source.
else {
HTML::queue_global_task(HTML::Task::Source::DOMManipulation, realm.global_object(), [this, finish_event]() {
// Manually create a task so its ID can be saved
auto& document = verify_cast<HTML::Window>(realm.global_object()).associated_document();
auto task = HTML::Task::create(HTML::Task::Source::DOMManipulation, &document, [this, finish_event]() {
dispatch_event(finish_event);
});
m_pending_finish_microtask_id = task->id();
HTML::main_thread_event_loop().task_queue().add(move(task));
}
m_has_finish_notification_microtask_scheduled = false;
};
// 2. If synchronously notify is true, cancel any queued microtask to run the finish notification steps for this
// animation, and run the finish notification steps immediately.
if (synchronously_notify == SynchronouslyNotify::Yes) {
m_should_abort_finish_notification_microtask = false;
if (m_pending_finish_microtask_id.has_value()) {
HTML::main_thread_event_loop().task_queue().remove_tasks_matching([id = move(m_pending_finish_microtask_id)](auto const& task) {
return task.id() == id;
});
}
finish_notification_steps();
m_should_abort_finish_notification_microtask = true;
}
// Otherwise, if synchronously notify is false, queue a microtask to run finish notification steps for
// animation unless there is already a microtask queued to run those steps for animation.
else {
if (!m_has_finish_notification_microtask_scheduled)
HTML::queue_a_microtask({}, move(finish_notification_steps));
m_has_finish_notification_microtask_scheduled = true;
m_should_abort_finish_notification_microtask = false;
else if (!m_pending_finish_microtask_id.has_value()) {
auto& document = verify_cast<HTML::Window>(realm.global_object()).associated_document();
auto task = HTML::Task::create(HTML::Task::Source::DOMManipulation, &document, move(finish_notification_steps));
m_pending_finish_microtask_id = task->id();
HTML::main_thread_event_loop().task_queue().add(move(task));
}
}
// 6. If current finished state is false and animations current finished promise is already resolved, set
// animations current finished promise to a new promise in the relevant Realm of animation.
if (!current_finished_state && m_is_finished) {
m_current_finished_promise = WebIDL::create_promise(realm());
{
HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm) };
m_current_finished_promise = WebIDL::create_promise(realm);
}
m_is_finished = false;
}
// Invalidate the style of our target element, if applicable
if (m_effect) {
if (auto target = m_effect->target())
target->invalidate_style();
}
invalidate_effect();
}
// Step 12 of https://www.w3.org/TR/web-animations-1/#playing-an-animation-section
@ -881,6 +879,14 @@ JS::NonnullGCPtr<WebIDL::Promise> Animation::current_finished_promise() const
return *m_current_finished_promise;
}
void Animation::invalidate_effect()
{
if (m_effect) {
if (auto target = m_effect->target())
target->invalidate_style();
}
}
Animation::Animation(JS::Realm& realm)
: DOM::EventTarget(realm)
{

View file

@ -120,6 +120,8 @@ private:
JS::NonnullGCPtr<WebIDL::Promise> current_ready_promise() const;
JS::NonnullGCPtr<WebIDL::Promise> current_finished_promise() const;
void invalidate_effect();
// https://www.w3.org/TR/web-animations-1/#dom-animation-id
FlyString m_id;
@ -164,10 +166,7 @@ private:
// https://www.w3.org/TR/web-animations-1/#pending-pause-task
TaskState m_pending_pause_task { TaskState::None };
// Flags used to manage the finish notification microtask and ultimately prevent more than one finish notification
// microtask from being queued at any given time
bool m_should_abort_finish_notification_microtask { false };
bool m_has_finish_notification_microtask_scheduled { false };
Optional<int> m_pending_finish_microtask_id;
};
}

View file

@ -49,6 +49,7 @@ namespace Web::HTML::EventNames {
__ENUMERATE_HTML_EVENT(emptied) \
__ENUMERATE_HTML_EVENT(ended) \
__ENUMERATE_HTML_EVENT(error) \
__ENUMERATE_HTML_EVENT(finish) \
__ENUMERATE_HTML_EVENT(focus) \
__ENUMERATE_HTML_EVENT(formdata) \
__ENUMERATE_HTML_EVENT(hashchange) \