LibWeb: Implement Document::remove_replaced_animations()

This commit is contained in:
Matthew Olsson 2024-02-03 18:21:29 -07:00 committed by Andreas Kling
parent fe848487db
commit 10fddb99fc
Notes: sideshowbarker 2024-07-17 04:10:16 +09:00
6 changed files with 105 additions and 1 deletions

View File

@ -345,6 +345,20 @@ bool Animation::is_replaceable() const
return true;
}
void Animation::set_replace_state(Bindings::AnimationReplaceState value)
{
m_replace_state = value;
if (value == Bindings::AnimationReplaceState::Removed) {
// Remove the associated effect from its target, if applicable
if (m_effect && m_effect->target())
m_effect->target()->disassociate_with_effect(*m_effect);
// Remove this animation from its timeline
m_timeline->disassociate_with_animation(*this);
}
}
// https://www.w3.org/TR/web-animations-1/#dom-animation-play
WebIDL::ExceptionOr<void> Animation::play()
{

View File

@ -54,6 +54,7 @@ public:
bool is_replaceable() const;
Bindings::AnimationReplaceState replace_state() const { return m_replace_state; }
void set_replace_state(Bindings::AnimationReplaceState value);
// https://www.w3.org/TR/web-animations-1/#dom-animation-pending
bool pending() const { return m_pending_play_task == TaskState::Scheduled || m_pending_pause_task == TaskState::Scheduled; }

View File

@ -142,6 +142,7 @@ public:
Optional<double> transformed_progress() const;
virtual DOM::Element* target() const { return {}; }
virtual bool is_keyframe_effect() const { return false; }
protected:
AnimationEffect(JS::Realm&);

View File

@ -96,6 +96,8 @@ public:
KeyFrameSet const* key_frame_set() { return m_key_frame_set; }
void set_key_frame_set(RefPtr<KeyFrameSet const> key_frame_set) { m_key_frame_set = key_frame_set; }
virtual bool is_keyframe_effect() const override { return true; }
private:
KeyframeEffect(JS::Realm&);
virtual ~KeyframeEffect() override;

View File

@ -3820,7 +3820,92 @@ void Document::update_animations_and_send_events(Optional<double> const& timesta
// https://www.w3.org/TR/web-animations-1/#remove-replaced-animations
void Document::remove_replaced_animations()
{
// FIXME: Implement this
// When asked to remove replaced animations for a Document, doc, then for every animation, animation, that:
// - has an associated animation effect whose effect target is a descendant of doc, and
// - is replaceable, and
// - has a replace state of active, and
// - for which there exists for each target property of every animation effect associated with animation, an
// animation effect associated with a replaceable animation with a higher composite order than animation that
// includes the same target property
Vector<JS::NonnullGCPtr<Animations::Animation>> replaceable_animations;
for (auto const& timeline : m_associated_animation_timelines) {
for (auto const& animation : timeline->associated_animations()) {
if (!animation->effect() || !animation->effect()->target() || &animation->effect()->target()->document() != this)
continue;
if (!animation->is_replaceable())
continue;
if (animation->replace_state() != Bindings::AnimationReplaceState::Active)
continue;
// Composite order is only defined for KeyframeEffects
if (!animation->effect()->is_keyframe_effect())
continue;
replaceable_animations.append(animation);
}
}
quick_sort(replaceable_animations, [](JS::NonnullGCPtr<Animations::Animation>& a, JS::NonnullGCPtr<Animations::Animation>& b) {
VERIFY(a->effect()->is_keyframe_effect());
VERIFY(b->effect()->is_keyframe_effect());
auto& a_effect = *static_cast<Animations::KeyframeEffect*>(a->effect().ptr());
auto& b_effect = *static_cast<Animations::KeyframeEffect*>(b->effect().ptr());
return Animations::KeyframeEffect::composite_order(a_effect, b_effect) < 0;
});
// Lower value = higher priority
HashMap<CSS::PropertyID, size_t> highest_property_composite_orders;
for (int i = replaceable_animations.size() - 1; i >= 0; i--) {
auto animation = replaceable_animations[i];
bool has_any_highest_priority_property = false;
for (auto const& property : animation->effect()->target_properties()) {
if (!highest_property_composite_orders.contains(property)) {
has_any_highest_priority_property = true;
highest_property_composite_orders.set(property, i);
}
}
if (!has_any_highest_priority_property) {
// perform the following steps:
// - Set animations replace state to removed.
animation->set_replace_state(Bindings::AnimationReplaceState::Removed);
// - Create an AnimationPlaybackEvent, removeEvent.
// - Set removeEvents type attribute to remove.
// - Set removeEvents currentTime attribute to the current time of animation.
// - Set removeEvents timelineTime attribute to the current time of the timeline with which animation is
// associated.
Animations::AnimationPlaybackEventInit init;
init.current_time = animation->current_time();
init.timeline_time = animation->timeline()->current_time();
auto remove_event = Animations::AnimationPlaybackEvent::create(realm(), HTML::EventNames::remove, init);
// - If animation has a document for timing, then append removeEvent to its document for timing's pending
// animation event queue along with its target, animation. For the scheduled event time, use the result of
// applying the procedure to convert timeline time to origin-relative time to the current time of the
// timeline with which animation is associated.
if (auto document = animation->document_for_timing()) {
PendingAnimationEvent pending_animation_event {
remove_event,
animation,
animation->timeline()->convert_a_timeline_time_to_an_origin_relative_time(init.timeline_time),
};
document->append_pending_animation_event(pending_animation_event);
}
// Otherwise, queue a task to dispatch removeEvent 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(), [animation, remove_event]() {
animation->dispatch_event(remove_event);
});
}
}
}
}
// https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem-filter

View File

@ -81,6 +81,7 @@ namespace Web::HTML::EventNames {
__ENUMERATE_HTML_EVENT(ratechange) \
__ENUMERATE_HTML_EVENT(readystatechange) \
__ENUMERATE_HTML_EVENT(rejectionhandled) \
__ENUMERATE_HTML_EVENT(remove) \
__ENUMERATE_HTML_EVENT(removetrack) \
__ENUMERATE_HTML_EVENT(reset) \
__ENUMERATE_HTML_EVENT(resize) \