diff --git a/Tests/LibWeb/Text/expected/WebAnimations/misc/DocumentTimeline.txt b/Tests/LibWeb/Text/expected/WebAnimations/misc/DocumentTimeline.txt
new file mode 100644
index 0000000000..c98c32c778
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/WebAnimations/misc/DocumentTimeline.txt
@@ -0,0 +1,3 @@
+new timeline time equals document timeline time: true
+new timeline originTime defaults to 0: true
+new timeline originTime offsets from document timeline: true
diff --git a/Tests/LibWeb/Text/input/WebAnimations/misc/DocumentTimeline.html b/Tests/LibWeb/Text/input/WebAnimations/misc/DocumentTimeline.html
new file mode 100644
index 0000000000..36e6f3a797
--- /dev/null
+++ b/Tests/LibWeb/Text/input/WebAnimations/misc/DocumentTimeline.html
@@ -0,0 +1,18 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/Animations/DocumentTimeline.cpp b/Userland/Libraries/LibWeb/Animations/DocumentTimeline.cpp
index cb1d2403f6..9571c47c43 100644
--- a/Userland/Libraries/LibWeb/Animations/DocumentTimeline.cpp
+++ b/Userland/Libraries/LibWeb/Animations/DocumentTimeline.cpp
@@ -20,9 +20,14 @@ JS_DEFINE_ALLOCATOR(DocumentTimeline);
JS::NonnullGCPtr DocumentTimeline::create(JS::Realm& realm, DOM::Document& document, HighResolutionTime::DOMHighResTimeStamp origin_time)
{
auto timeline = realm.heap().allocate(realm, realm, document, origin_time);
- auto* window_or_worker = dynamic_cast(&realm.global_object());
- VERIFY(window_or_worker);
- timeline->set_current_time(window_or_worker->performance()->now());
+ auto current_time = document.last_animation_frame_timestamp();
+ if (!current_time.has_value()) {
+ // The document hasn't processed an animation frame yet, so just use the exact current time
+ auto* window_or_worker = dynamic_cast(&realm.global_object());
+ VERIFY(window_or_worker);
+ current_time = window_or_worker->performance()->now();
+ }
+ timeline->set_current_time(current_time);
return timeline;
}
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
index de28809bea..3a1616ce74 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -4233,6 +4233,7 @@ void Document::update_animations_and_send_events(Optional const& timesta
// - Running the update an animation’s finished state procedure for any animations whose current time has been
// updated.
// - Queueing animation events for any such animations.
+ m_last_animation_frame_timestamp = timestamp;
for (auto const& timeline : m_associated_animation_timelines)
timeline->set_current_time(timestamp);
diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h
index 6108a6cbf9..f049f717df 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.h
+++ b/Userland/Libraries/LibWeb/DOM/Document.h
@@ -596,6 +596,7 @@ public:
void restore_the_history_object_state(JS::NonnullGCPtr entry);
JS::NonnullGCPtr timeline();
+ auto const& last_animation_frame_timestamp() const { return m_last_animation_frame_timestamp; }
void associate_with_timeline(JS::NonnullGCPtr);
void disassociate_with_timeline(JS::NonnullGCPtr);
@@ -889,6 +890,7 @@ private:
// https://www.w3.org/TR/web-animations-1/#document-default-document-timeline
JS::GCPtr m_default_timeline;
+ Optional m_last_animation_frame_timestamp;
// https://www.w3.org/TR/web-animations-1/#pending-animation-event-queue
Vector m_pending_animation_event_queue;