ladybird/Libraries/LibWeb/Animations/DocumentTimeline.cpp
Jelle Raaijmakers aa563706ca
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
LibWeb: Rework AnimationTimeline's monotonically increasing property
Our previous implementation kept track of an AnimationTimeline being
monotonically increasing, by looking at new time values coming in and
setting `m_monotonically_increasing` to `false` whenever a new value
is before the previous known time value.

As far as I can tell, the spec doesn't really ask us to do so: it just
defines 'monotonically increasing' as a property of a timeline, i.e. it
guarantees that returned time values from `::current_time()` are always
greater than or equal to the last returned value.

This fixes a common crash seen when the last render opportunity lies
before the document's origin time, and `::set_current_time()` was
invoked with a negative value. This was especially visible in the
`Text/input/wpt-import/css/cssom/CSSStyleSheet-constructable.html` test.
2025-07-30 14:37:54 +02:00

93 lines
4.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGC/Heap.h>
#include <LibJS/Runtime/Realm.h>
#include <LibWeb/Animations/DocumentTimeline.h>
#include <LibWeb/Bindings/DocumentTimelinePrototype.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HighResolutionTime/Performance.h>
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::Animations {
GC_DEFINE_ALLOCATOR(DocumentTimeline);
GC::Ref<DocumentTimeline> DocumentTimeline::create(JS::Realm& realm, DOM::Document& document, HighResolutionTime::DOMHighResTimeStamp origin_time)
{
auto timeline = realm.create<DocumentTimeline>(realm, document, origin_time);
auto current_time = document.last_animation_frame_timestamp();
if (!current_time.has_value()) {
// The document hasn't processed an animation frame yet, we use the navigation start time, which is either the time
// that the previous document started to be unloaded or the creation time of the current document.
current_time = HighResolutionTime::relative_high_resolution_time(document.load_timing_info().navigation_start_time, realm.global_object());
}
timeline->set_current_time(current_time);
return timeline;
}
// https://www.w3.org/TR/web-animations-1/#dom-documenttimeline-documenttimeline
WebIDL::ExceptionOr<GC::Ref<DocumentTimeline>> DocumentTimeline::construct_impl(JS::Realm& realm, DocumentTimelineOptions options)
{
// Creates a new DocumentTimeline. The Document with which the timeline is associated is the Document associated
// with the Window that is the current global object.
auto& window = as<HTML::Window>(realm.global_object());
return create(realm, window.associated_document(), options.origin_time);
}
// https://www.w3.org/TR/web-animations-1/#ref-for-timeline-time-to-origin-relative-time
Optional<double> DocumentTimeline::convert_a_timeline_time_to_an_origin_relative_time(Optional<double> timeline_time)
{
// To convert a timeline time, timeline time, to an origin-relative time for a document timeline, timeline, return
// the sum of the timeline time and timelines origin time. If timeline is inactive, return an unresolved time
// value.
if (is_inactive() || !timeline_time.has_value())
return {};
return timeline_time.value() + m_origin_time;
}
// https://drafts.csswg.org/web-animations-1/#document-timeline
void DocumentTimeline::set_current_time(Optional<double> current_time)
{
// A document timeline is a type of timeline that is associated with a document and whose current time is calculated
// as a fixed offset from the now timestamp provided each time the update animations and send events procedure is
// run. This fixed offset is equal to the current time of the default document timeline when this timelines current
// time was zero, and is thus referred to as the document timelines origin time.
if (!current_time.has_value())
Base::set_current_time({});
else
Base::set_current_time(current_time.value() - m_origin_time);
// https://drafts.csswg.org/web-animations-1/#ref-for-active-timeline
// After a document timeline becomes active, it is monotonically increasing.
if (!m_is_monotonically_increasing && !is_inactive())
m_is_monotonically_increasing = true;
}
// https://www.w3.org/TR/web-animations-1/#document-timelines
bool DocumentTimeline::is_inactive() const
{
// A document timeline that is associated with a Document which is not an active document is also considered to be
// inactive.
return Base::is_inactive() || !associated_document()->is_active();
}
DocumentTimeline::DocumentTimeline(JS::Realm& realm, DOM::Document& document, HighResolutionTime::DOMHighResTimeStamp origin_time)
: AnimationTimeline(realm)
, m_origin_time(origin_time)
{
set_associated_document(document);
}
void DocumentTimeline::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(DocumentTimeline);
Base::initialize(realm);
}
}