Commit d455934d authored by Fredrik Söderquist's avatar Fredrik Söderquist Committed by Commit Bot

Clamp the presentation time to a usable range

If the time container is seeked to a large enough value, we will end up
truncating it to SMILTime::Latest(), which is the largest value that
isn't one of the two special values ("indefinite" and "unresolved").
When trying to derive other values from this value - like if we have an
interval begin at it - we can end up in a loop since any the result of
any additions will yield the same value, leading to the element being
rescheduled at the same point in time, hanging UpdateIntervals().
This mechanism can also be used to implement the "once" animation-policy
in a slightly nicer way. This will be done as a follow-up.

Bug: 1039886
Change-Id: If13d7d7d3c44c4f586d15852eb05105879f44918
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2030885Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#737260}
parent 3ce9f202
...@@ -77,7 +77,9 @@ class SMILTimeContainer::TimingUpdate { ...@@ -77,7 +77,9 @@ class SMILTimeContainer::TimingUpdate {
MovePolicy policy) MovePolicy policy)
: target_time_(target_time), : target_time_(target_time),
policy_(policy), policy_(policy),
time_container_(&time_container) {} time_container_(&time_container) {
DCHECK_LE(target_time_, time_container_->max_presentation_time_);
}
~TimingUpdate(); ~TimingUpdate();
const SMILTime& Time() const { return time_container_->latest_update_time_; } const SMILTime& Time() const { return time_container_->latest_update_time_; }
...@@ -151,7 +153,10 @@ static constexpr base::TimeDelta kAnimationPolicyOnceDuration = ...@@ -151,7 +153,10 @@ static constexpr base::TimeDelta kAnimationPolicyOnceDuration =
base::TimeDelta::FromSeconds(3); base::TimeDelta::FromSeconds(3);
SMILTimeContainer::SMILTimeContainer(SVGSVGElement& owner) SMILTimeContainer::SMILTimeContainer(SVGSVGElement& owner)
: frame_scheduling_state_(kIdle), // We can't seek beyond this time, because at Latest() any additions will
// yield the same value.
: max_presentation_time_(SMILTime::Latest() - SMILTime::Epsilon()),
frame_scheduling_state_(kIdle),
started_(false), started_(false),
paused_(false), paused_(false),
should_dispatch_events_(!SVGImage::IsInSVGImage(&owner)), should_dispatch_events_(!SVGImage::IsInSVGImage(&owner)),
...@@ -242,7 +247,7 @@ SMILTime SMILTimeContainer::Elapsed() const { ...@@ -242,7 +247,7 @@ SMILTime SMILTimeContainer::Elapsed() const {
SMILTime elapsed = presentation_time_ + SMILTime elapsed = presentation_time_ +
SMILTime::FromMicroseconds(time_offset.InMicroseconds()); SMILTime::FromMicroseconds(time_offset.InMicroseconds());
DCHECK_GE(elapsed, SMILTime()); DCHECK_GE(elapsed, SMILTime());
return elapsed; return ClampPresentationTime(elapsed);
} }
void SMILTimeContainer::ResetDocumentTime() { void SMILTimeContainer::ResetDocumentTime() {
...@@ -326,8 +331,13 @@ void SMILTimeContainer::Unpause() { ...@@ -326,8 +331,13 @@ void SMILTimeContainer::Unpause() {
ScheduleWakeUp(base::TimeDelta(), kSynchronizeAnimations); ScheduleWakeUp(base::TimeDelta(), kSynchronizeAnimations);
} }
SMILTime SMILTimeContainer::ClampPresentationTime(
SMILTime presentation_time) const {
return std::min(presentation_time, max_presentation_time_);
}
void SMILTimeContainer::SetElapsed(SMILTime elapsed) { void SMILTimeContainer::SetElapsed(SMILTime elapsed) {
presentation_time_ = elapsed; presentation_time_ = ClampPresentationTime(elapsed);
if (!GetDocument().IsActive()) if (!GetDocument().IsActive())
return; return;
...@@ -512,6 +522,8 @@ void SMILTimeContainer::UpdateAnimationsAndScheduleFrameIfNeeded( ...@@ -512,6 +522,8 @@ void SMILTimeContainer::UpdateAnimationsAndScheduleFrameIfNeeded(
} }
SMILTime SMILTimeContainer::NextProgressTime(SMILTime presentation_time) const { SMILTime SMILTimeContainer::NextProgressTime(SMILTime presentation_time) const {
if (presentation_time == max_presentation_time_)
return SMILTime::Unresolved();
SMILTime next_progress_time = SMILTime::Unresolved(); SMILTime next_progress_time = SMILTime::Unresolved();
for (const auto& entry : priority_queue_) { for (const auto& entry : priority_queue_) {
next_progress_time = std::min( next_progress_time = std::min(
......
...@@ -118,6 +118,7 @@ class SMILTimeContainer final : public GarbageCollected<SMILTimeContainer> { ...@@ -118,6 +118,7 @@ class SMILTimeContainer final : public GarbageCollected<SMILTimeContainer> {
void ServiceOnNextFrame(); void ServiceOnNextFrame();
void ScheduleWakeUp(base::TimeDelta delay_time, FrameSchedulingState); void ScheduleWakeUp(base::TimeDelta delay_time, FrameSchedulingState);
bool HasPendingSynchronization() const; bool HasPendingSynchronization() const;
SMILTime ClampPresentationTime(SMILTime presentation_time) const;
void UpdateDocumentOrderIndexes(); void UpdateDocumentOrderIndexes();
...@@ -127,6 +128,9 @@ class SMILTimeContainer final : public GarbageCollected<SMILTimeContainer> { ...@@ -127,6 +128,9 @@ class SMILTimeContainer final : public GarbageCollected<SMILTimeContainer> {
// The latest "restart" time for the time container's timeline. If the // The latest "restart" time for the time container's timeline. If the
// timeline has not been manipulated (seeked, paused) this will be zero. // timeline has not been manipulated (seeked, paused) this will be zero.
SMILTime presentation_time_; SMILTime presentation_time_;
// The maximum possible presentation time. When this time is reached
// animations will stop.
SMILTime max_presentation_time_;
// The state all SVGSMILElements should be at. // The state all SVGSMILElements should be at.
SMILTime latest_update_time_; SMILTime latest_update_time_;
// The time on the document timeline corresponding to |presentation_time_|. // The time on the document timeline corresponding to |presentation_time_|.
......
<!DOCTYPE html>
<html class="reftest-wait">
<title>Seeking the time container to a large value does not cause a crash (or hang)</title>
<svg>
<rect height="100" width="100" fill="blue">
<animateTransform begin="18446744073709551557" dur="2" repeatCount="indefinite"
attributeName="transform" type="rotate" from="0,15,15" to="360,15,15"/>
</rect>
</svg>
<script>
let svg = document.querySelector("svg");
svg.setCurrentTime(18446744073709551557);
let html = document.documentElement;
html.addEventListener("TestRendered", () => html.classList.remove("reftest-wait"));
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment