/* * 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); JS_DECLARE_ALLOCATOR(Animation); public: static JS::NonnullGCPtr create(JS::Realm&, JS::GCPtr, Optional>); static WebIDL::ExceptionOr> construct_impl(JS::Realm&, JS::GCPtr, Optional>); FlyString const& id() const { return m_id; } void set_id(FlyString value) { m_id = move(value); } JS::GCPtr effect() const { return m_effect; } void set_effect(JS::GCPtr); JS::GCPtr timeline() const { return m_timeline; } void set_timeline(JS::GCPtr); 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 JS::NonnullGCPtr ready() const { return *current_ready_promise()->promise(); } // https://www.w3.org/TR/web-animations-1/#dom-animation-finished JS::NonnullGCPtr finished() const { return *current_finished_promise()->promise(); } bool is_finished() const { return m_is_finished; } JS::GCPtr onfinish(); void set_onfinish(JS::GCPtr); JS::GCPtr oncancel(); void set_oncancel(JS::GCPtr); JS::GCPtr onremove(); void set_onremove(JS::GCPtr); 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; JS::GCPtr document_for_timing() const; void notify_timeline_time_did_change(); void effect_timing_changed(Badge); virtual bool is_css_animation() const { return false; } virtual AnimationClass animation_class() const { return AnimationClass::None; } virtual Optional class_specific_composite_order(JS::NonnullGCPtr) const { return {}; } unsigned int global_animation_list_order() const { return m_global_animation_list_order; } 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 associated_effect_end() const; 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(); JS::NonnullGCPtr current_ready_promise() const; JS::NonnullGCPtr 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 JS::GCPtr m_effect; // https://www.w3.org/TR/web-animations-1/#dom-animation-timeline JS::GCPtr 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 JS::GCPtr m_current_ready_promise; // https://www.w3.org/TR/web-animations-1/#current-finished-promise mutable JS::GCPtr 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 }; Optional m_pending_finish_microtask_id; Optional m_saved_play_time; Optional m_saved_pause_time; }; }