Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/Animations/Animation.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2023-2024, Matthew Olsson <mattco@serenityos.org>.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <LibJS/Runtime/PromiseCapability.h>
10
#include <LibWeb/Bindings/AnimationPrototype.h>
11
#include <LibWeb/DOM/EventTarget.h>
12
13
namespace Web::Animations {
14
15
// Sorted by composite order:
16
// https://www.w3.org/TR/css-animations-2/#animation-composite-order
17
enum class AnimationClass {
18
    CSSAnimationWithOwningElement,
19
    CSSTransition,
20
    CSSAnimationWithoutOwningElement,
21
    None,
22
};
23
24
// https://www.w3.org/TR/web-animations-1/#the-animation-interface
25
class Animation : public DOM::EventTarget {
26
    WEB_PLATFORM_OBJECT(Animation, DOM::EventTarget);
27
    JS_DECLARE_ALLOCATOR(Animation);
28
29
public:
30
    static JS::NonnullGCPtr<Animation> create(JS::Realm&, JS::GCPtr<AnimationEffect>, Optional<JS::GCPtr<AnimationTimeline>>);
31
    static WebIDL::ExceptionOr<JS::NonnullGCPtr<Animation>> construct_impl(JS::Realm&, JS::GCPtr<AnimationEffect>, Optional<JS::GCPtr<AnimationTimeline>>);
32
33
0
    FlyString const& id() const { return m_id; }
34
0
    void set_id(FlyString value) { m_id = move(value); }
35
36
0
    JS::GCPtr<AnimationEffect> effect() const { return m_effect; }
37
    void set_effect(JS::GCPtr<AnimationEffect>);
38
39
0
    JS::GCPtr<AnimationTimeline> timeline() const { return m_timeline; }
40
    void set_timeline(JS::GCPtr<AnimationTimeline>);
41
42
0
    Optional<double> const& start_time() const { return m_start_time; }
43
    void set_start_time(Optional<double> const&);
44
45
    Optional<double> current_time() const;
46
    WebIDL::ExceptionOr<void> set_current_time(Optional<double> const&);
47
48
0
    double playback_rate() const { return m_playback_rate; }
49
    WebIDL::ExceptionOr<void> set_playback_rate(double value);
50
51
    Bindings::AnimationPlayState play_state() const;
52
53
    bool is_relevant() const;
54
55
    bool is_replaceable() const;
56
0
    Bindings::AnimationReplaceState replace_state() const { return m_replace_state; }
57
    void set_replace_state(Bindings::AnimationReplaceState value);
58
59
    // https://www.w3.org/TR/web-animations-1/#dom-animation-pending
60
0
    bool pending() const { return m_pending_play_task == TaskState::Scheduled || m_pending_pause_task == TaskState::Scheduled; }
61
62
    // https://www.w3.org/TR/web-animations-1/#dom-animation-ready
63
0
    JS::NonnullGCPtr<JS::Object> ready() const { return *current_ready_promise()->promise(); }
64
65
    // https://www.w3.org/TR/web-animations-1/#dom-animation-finished
66
0
    JS::NonnullGCPtr<JS::Object> finished() const { return *current_finished_promise()->promise(); }
67
0
    bool is_finished() const { return m_is_finished; }
68
69
    JS::GCPtr<WebIDL::CallbackType> onfinish();
70
    void set_onfinish(JS::GCPtr<WebIDL::CallbackType>);
71
    JS::GCPtr<WebIDL::CallbackType> oncancel();
72
    void set_oncancel(JS::GCPtr<WebIDL::CallbackType>);
73
    JS::GCPtr<WebIDL::CallbackType> onremove();
74
    void set_onremove(JS::GCPtr<WebIDL::CallbackType>);
75
76
    enum class AutoRewind {
77
        Yes,
78
        No,
79
    };
80
    enum class ShouldInvalidate {
81
        Yes,
82
        No,
83
    };
84
    void cancel(ShouldInvalidate = ShouldInvalidate::Yes);
85
    WebIDL::ExceptionOr<void> finish();
86
    WebIDL::ExceptionOr<void> play();
87
    WebIDL::ExceptionOr<void> play_an_animation(AutoRewind);
88
    WebIDL::ExceptionOr<void> pause();
89
    WebIDL::ExceptionOr<void> update_playback_rate(double);
90
    WebIDL::ExceptionOr<void> reverse();
91
    void persist();
92
93
    Optional<double> convert_an_animation_time_to_timeline_time(Optional<double>) const;
94
    Optional<double> convert_a_timeline_time_to_an_origin_relative_time(Optional<double>) const;
95
96
    JS::GCPtr<DOM::Document> document_for_timing() const;
97
    void notify_timeline_time_did_change();
98
99
    void effect_timing_changed(Badge<AnimationEffect>);
100
101
0
    virtual bool is_css_animation() const { return false; }
102
0
    virtual bool is_css_transition() const { return false; }
103
104
0
    JS::GCPtr<DOM::Element> owning_element() const { return m_owning_element; }
105
0
    void set_owning_element(JS::GCPtr<DOM::Element> value) { m_owning_element = value; }
106
107
0
    virtual AnimationClass animation_class() const { return AnimationClass::None; }
108
0
    virtual Optional<int> class_specific_composite_order(JS::NonnullGCPtr<Animation>) const { return {}; }
109
110
0
    unsigned int global_animation_list_order() const { return m_global_animation_list_order; }
111
112
0
    auto release_saved_cancel_time() { return move(m_saved_cancel_time); }
113
114
protected:
115
    Animation(JS::Realm&);
116
117
    virtual void initialize(JS::Realm&) override;
118
    virtual void visit_edges(Cell::Visitor&) override;
119
120
private:
121
    enum class TaskState {
122
        None,
123
        Scheduled,
124
    };
125
126
    enum class DidSeek {
127
        Yes,
128
        No,
129
    };
130
131
    enum class SynchronouslyNotify {
132
        Yes,
133
        No,
134
    };
135
136
    double associated_effect_end() const;
137
    double effective_playback_rate() const;
138
139
    void apply_any_pending_playback_rate();
140
    WebIDL::ExceptionOr<void> silently_set_current_time(Optional<double>);
141
    void update_finished_state(DidSeek, SynchronouslyNotify);
142
    void reset_an_animations_pending_tasks();
143
144
    void run_pending_play_task();
145
    void run_pending_pause_task();
146
147
    JS::NonnullGCPtr<WebIDL::Promise> current_ready_promise() const;
148
    JS::NonnullGCPtr<WebIDL::Promise> current_finished_promise() const;
149
150
    void invalidate_effect();
151
152
    // https://www.w3.org/TR/web-animations-1/#dom-animation-id
153
    FlyString m_id;
154
155
    // https://www.w3.org/TR/web-animations-1/#global-animation-list
156
    unsigned int m_global_animation_list_order { 0 };
157
158
    // https://www.w3.org/TR/web-animations-1/#dom-animation-effect
159
    JS::GCPtr<AnimationEffect> m_effect;
160
161
    // https://www.w3.org/TR/web-animations-1/#dom-animation-timeline
162
    JS::GCPtr<AnimationTimeline> m_timeline;
163
164
    // https://www.w3.org/TR/web-animations-1/#animation-start-time
165
    Optional<double> m_start_time {};
166
167
    // https://www.w3.org/TR/web-animations-1/#animation-hold-time
168
    Optional<double> m_hold_time {};
169
170
    // https://www.w3.org/TR/web-animations-1/#previous-current-time
171
    Optional<double> m_previous_current_time {};
172
173
    // https://www.w3.org/TR/web-animations-1/#playback-rate
174
    double m_playback_rate { 1.0 };
175
176
    // https://www.w3.org/TR/web-animations-1/#pending-playback-rate
177
    Optional<double> m_pending_playback_rate {};
178
179
    // https://www.w3.org/TR/web-animations-1/#dom-animation-replacestate
180
    Bindings::AnimationReplaceState m_replace_state { Bindings::AnimationReplaceState::Active };
181
182
    // Note: The following promises are initialized lazily to avoid constructing them outside of an execution context
183
    // https://www.w3.org/TR/web-animations-1/#current-ready-promise
184
    mutable JS::GCPtr<WebIDL::Promise> m_current_ready_promise;
185
186
    // https://www.w3.org/TR/web-animations-1/#current-finished-promise
187
    mutable JS::GCPtr<WebIDL::Promise> m_current_finished_promise;
188
    bool m_is_finished { false };
189
190
    // https://www.w3.org/TR/web-animations-1/#pending-play-task
191
    TaskState m_pending_play_task { TaskState::None };
192
193
    // https://www.w3.org/TR/web-animations-1/#pending-pause-task
194
    TaskState m_pending_pause_task { TaskState::None };
195
196
    // https://www.w3.org/TR/css-animations-2/#owning-element-section
197
    JS::GCPtr<DOM::Element> m_owning_element;
198
199
    Optional<HTML::TaskID> m_pending_finish_microtask_id;
200
201
    Optional<double> m_saved_play_time;
202
    Optional<double> m_saved_pause_time;
203
    Optional<double> m_saved_cancel_time;
204
};
205
206
}