/* * Copyright (c) 2023-2024, Matthew Olsson . * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include namespace Web::Animations { // Sorted by composite order: // https://www.w3.org/TR/css-animations-2/#animation-composite-order enum class AnimationClass { CSSAnimationWithOwningElement, CSSTransition, CSSAnimationWithoutOwningElement, None, }; // https://www.w3.org/TR/web-animations-1/#the-animation-interface class Animation : public DOM::EventTarget { WEB_PLATFORM_OBJECT(Animation, DOM::EventTarget); GC_DECLARE_ALLOCATOR(Animation); public: static GC::Ref create(JS::Realm&, GC::Ptr, Optional>); static WebIDL::ExceptionOr> construct_impl(JS::Realm&, GC::Ptr, Optional>); FlyString const& id() const { return m_id; } void set_id(FlyString value) { m_id = move(value); } GC::Ptr effect() const { return m_effect; } void set_effect(GC::Ptr); GC::Ptr timeline() const { return m_timeline; } void set_timeline(GC::Ptr); Optional const& start_time() const { return m_start_time; } void set_start_time(Optional const&); Optional current_time() const; WebIDL::ExceptionOr set_current_time(Optional const&); double playback_rate() const { return m_playback_rate; } WebIDL::ExceptionOr set_playback_rate(double value); Bindings::AnimationPlayState play_state() const; bool is_relevant() const; 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; } // https://www.w3.org/TR/web-animations-1/#dom-animation-ready GC::Ref ready() const { return current_ready_promise(); } // https://www.w3.org/TR/web-animations-1/#dom-animation-finished GC::Ref finished() const { return current_finished_promise(); } bool is_finished() const { return m_is_finished; } GC::Ptr onfinish(); void set_onfinish(GC::Ptr); GC::Ptr oncancel(); void set_oncancel(GC::Ptr); GC::Ptr onremove(); void set_onremove(GC::Ptr); enum class AutoRewind { Yes, No, }; enum class ShouldInvalidate { Yes, No, }; void cancel(ShouldInvalidate = ShouldInvalidate::Yes); WebIDL::ExceptionOr finish(); WebIDL::ExceptionOr play(); WebIDL::ExceptionOr play_an_animation(AutoRewind); WebIDL::ExceptionOr pause(); WebIDL::ExceptionOr update_playback_rate(double); WebIDL::ExceptionOr reverse(); void persist(); Optional convert_an_animation_time_to_timeline_time(Optional) const; Optional convert_a_timeline_time_to_an_origin_relative_time(Optional) const; GC::Ptr document_for_timing() const; void notify_timeline_time_did_change(); void effect_timing_changed(Badge); virtual bool is_css_animation() const { return false; } virtual bool is_css_transition() const { return false; } GC::Ptr owning_element() const { return m_owning_element; } void set_owning_element(GC::Ptr value) { m_owning_element = value; } virtual AnimationClass animation_class() const { return AnimationClass::None; } virtual Optional class_specific_composite_order(GC::Ref) const { return {}; } unsigned int global_animation_list_order() const { return m_global_animation_list_order; } auto release_saved_cancel_time() { return move(m_saved_cancel_time); } double associated_effect_end() const; protected: Animation(JS::Realm&); virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; private: enum class TaskState { None, Scheduled, }; enum class DidSeek { Yes, No, }; enum class SynchronouslyNotify { Yes, No, }; double effective_playback_rate() const; void apply_any_pending_playback_rate(); WebIDL::ExceptionOr silently_set_current_time(Optional); void update_finished_state(DidSeek, SynchronouslyNotify); void reset_an_animations_pending_tasks(); void run_pending_play_task(); void run_pending_pause_task(); GC::Ref current_ready_promise() const; GC::Ref current_finished_promise() const; void invalidate_effect(); // https://www.w3.org/TR/web-animations-1/#dom-animation-id FlyString m_id; // https://www.w3.org/TR/web-animations-1/#global-animation-list unsigned int m_global_animation_list_order { 0 }; // https://www.w3.org/TR/web-animations-1/#dom-animation-effect GC::Ptr m_effect; // https://www.w3.org/TR/web-animations-1/#dom-animation-timeline GC::Ptr m_timeline; // https://www.w3.org/TR/web-animations-1/#animation-start-time Optional m_start_time {}; // https://www.w3.org/TR/web-animations-1/#animation-hold-time Optional m_hold_time {}; // https://www.w3.org/TR/web-animations-1/#previous-current-time Optional m_previous_current_time {}; // https://www.w3.org/TR/web-animations-1/#playback-rate double m_playback_rate { 1.0 }; // https://www.w3.org/TR/web-animations-1/#pending-playback-rate Optional m_pending_playback_rate {}; // https://www.w3.org/TR/web-animations-1/#dom-animation-replacestate Bindings::AnimationReplaceState m_replace_state { Bindings::AnimationReplaceState::Active }; // Note: The following promises are initialized lazily to avoid constructing them outside of an execution context // https://www.w3.org/TR/web-animations-1/#current-ready-promise mutable GC::Ptr m_current_ready_promise; // https://www.w3.org/TR/web-animations-1/#current-finished-promise mutable GC::Ptr m_current_finished_promise; bool m_is_finished { false }; // https://www.w3.org/TR/web-animations-1/#pending-play-task TaskState m_pending_play_task { TaskState::None }; // https://www.w3.org/TR/web-animations-1/#pending-pause-task TaskState m_pending_pause_task { TaskState::None }; // https://www.w3.org/TR/css-animations-2/#owning-element-section GC::Ptr m_owning_element; Optional m_pending_finish_microtask_id; Optional m_saved_play_time; Optional m_saved_pause_time; Optional m_saved_cancel_time; }; }