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

Refactor SMILTimeContainer timing update

Introduce a small helper class TimingUpdate which carries the timing-
related state for a (timing) update of the animations. This helper
will be extended to handle event dispatch when seeking correctly.

Bug: 1039886
Change-Id: I3dbeb64b2cd7ae4d771f80f9232573a0838248df
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2017333Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#735059}
parent 5221cab7
...@@ -58,6 +58,44 @@ class AnimationTargetsMutationsForbidden { ...@@ -58,6 +58,44 @@ class AnimationTargetsMutationsForbidden {
#endif #endif
}; };
class SMILTimeContainer::TimingUpdate {
STACK_ALLOCATED();
public:
TimingUpdate(SMILTimeContainer& time_container, SMILTime target_time)
: target_time_(target_time), time_container_(time_container) {}
const SMILTime& Time() const { return time_container_->latest_update_time_; }
bool TryAdvanceTime(SMILTime next_time) {
if (time_container_->latest_update_time_ >= target_time_)
return false;
if (next_time > target_time_) {
time_container_->latest_update_time_ = target_time_;
return false;
}
time_container_->latest_update_time_ = next_time;
return true;
}
void RewindTimeToZero() { time_container_->latest_update_time_ = SMILTime(); }
const SMILTime& TargetTime() const { return target_time_; }
void HandleEvents(SVGSMILElement*, SVGSMILElement::EventDispatchMask);
private:
bool ShouldDispatchEvents() const {
return time_container_->should_dispatch_events_;
}
SMILTime target_time_;
Member<SMILTimeContainer> time_container_;
};
void SMILTimeContainer::TimingUpdate::HandleEvents(
SVGSMILElement* element,
SVGSMILElement::EventDispatchMask events_to_dispatch) {
if (ShouldDispatchEvents() && events_to_dispatch)
element->DispatchEvents(events_to_dispatch);
}
static constexpr base::TimeDelta kAnimationPolicyOnceDuration = static constexpr base::TimeDelta kAnimationPolicyOnceDuration =
base::TimeDelta::FromSeconds(3); base::TimeDelta::FromSeconds(3);
...@@ -206,7 +244,8 @@ void SMILTimeContainer::Start() { ...@@ -206,7 +244,8 @@ void SMILTimeContainer::Start() {
SynchronizeToDocumentTimeline(); SynchronizeToDocumentTimeline();
started_ = true; started_ = true;
UpdateAnimationsAndScheduleFrameIfNeeded(presentation_time_); TimingUpdate update(*this, presentation_time_);
UpdateAnimationsAndScheduleFrameIfNeeded(update);
} }
void SMILTimeContainer::Pause() { void SMILTimeContainer::Pause() {
...@@ -255,14 +294,9 @@ void SMILTimeContainer::SetElapsed(SMILTime elapsed) { ...@@ -255,14 +294,9 @@ void SMILTimeContainer::SetElapsed(SMILTime elapsed) {
if (!IsPaused()) if (!IsPaused())
SynchronizeToDocumentTimeline(); SynchronizeToDocumentTimeline();
// If we are rewinding the timeline, we need to start from 0 and then move TimingUpdate update(*this, presentation_time_);
// forward to the new presentation time. If we're moving forward we can just PrepareSeek(update);
// perform the update in the normal fashion. UpdateAnimationsAndScheduleFrameIfNeeded(update);
if (elapsed < latest_update_time_) {
ResetIntervals();
latest_update_time_ = SMILTime();
}
UpdateAnimationsAndScheduleFrameIfNeeded(elapsed);
} }
void SMILTimeContainer::ScheduleAnimationFrame(base::TimeDelta delay_time) { void SMILTimeContainer::ScheduleAnimationFrame(base::TimeDelta delay_time) {
...@@ -307,7 +341,8 @@ void SMILTimeContainer::WakeupTimerFired(TimerBase*) { ...@@ -307,7 +341,8 @@ void SMILTimeContainer::WakeupTimerFired(TimerBase*) {
DCHECK(IsTimelineRunning()); DCHECK(IsTimelineRunning());
ServiceOnNextFrame(); ServiceOnNextFrame();
} else { } else {
UpdateAnimationsAndScheduleFrameIfNeeded(Elapsed()); TimingUpdate update(*this, Elapsed());
UpdateAnimationsAndScheduleFrameIfNeeded(update);
} }
} }
...@@ -396,11 +431,12 @@ void SMILTimeContainer::ServiceAnimations() { ...@@ -396,11 +431,12 @@ void SMILTimeContainer::ServiceAnimations() {
// document, so this should be turned into a DCHECK. // document, so this should be turned into a DCHECK.
if (!GetDocument().IsActive()) if (!GetDocument().IsActive())
return; return;
UpdateAnimationsAndScheduleFrameIfNeeded(Elapsed()); TimingUpdate update(*this, Elapsed());
UpdateAnimationsAndScheduleFrameIfNeeded(update);
} }
void SMILTimeContainer::UpdateAnimationsAndScheduleFrameIfNeeded( void SMILTimeContainer::UpdateAnimationsAndScheduleFrameIfNeeded(
SMILTime elapsed) { TimingUpdate& update) {
DCHECK(GetDocument().IsActive()); DCHECK(GetDocument().IsActive());
DCHECK(!wakeup_timer_.IsActive()); DCHECK(!wakeup_timer_.IsActive());
// If the priority queue is empty, there are no timed elements to process and // If the priority queue is empty, there are no timed elements to process and
...@@ -408,17 +444,17 @@ void SMILTimeContainer::UpdateAnimationsAndScheduleFrameIfNeeded( ...@@ -408,17 +444,17 @@ void SMILTimeContainer::UpdateAnimationsAndScheduleFrameIfNeeded(
if (priority_queue_.IsEmpty()) if (priority_queue_.IsEmpty())
return; return;
AnimationTargetsMutationsForbidden scope(this); AnimationTargetsMutationsForbidden scope(this);
UpdateAnimationTimings(elapsed); UpdateTimedElements(update);
ApplyTimedEffects(elapsed); ApplyTimedEffects(update.TargetTime());
DCHECK(!wakeup_timer_.IsActive()); DCHECK(!wakeup_timer_.IsActive());
DCHECK(!HasPendingSynchronization()); DCHECK(!HasPendingSynchronization());
if (!IsTimelineRunning()) if (!IsTimelineRunning())
return; return;
SMILTime next_progress_time = NextProgressTime(elapsed); SMILTime next_progress_time = NextProgressTime(update.TargetTime());
if (!next_progress_time.IsFinite()) if (!next_progress_time.IsFinite())
return; return;
SMILTime delay_time = next_progress_time - elapsed; SMILTime delay_time = next_progress_time - update.TargetTime();
DCHECK(delay_time.IsFinite()); DCHECK(delay_time.IsFinite());
ScheduleAnimationFrame( ScheduleAnimationFrame(
base::TimeDelta::FromMicroseconds(delay_time.InMicroseconds())); base::TimeDelta::FromMicroseconds(delay_time.InMicroseconds()));
...@@ -435,6 +471,17 @@ SMILTime SMILTimeContainer::NextProgressTime(SMILTime presentation_time) const { ...@@ -435,6 +471,17 @@ SMILTime SMILTimeContainer::NextProgressTime(SMILTime presentation_time) const {
return next_progress_time; return next_progress_time;
} }
void SMILTimeContainer::PrepareSeek(TimingUpdate& update) {
// If we are rewinding the timeline, we need to start from 0 and then move
// forward to the new presentation time. If we're moving forward we can just
// perform the update in the normal fashion.
if (update.TargetTime() < update.Time()) {
ResetIntervals();
// TODO(fs): Clear resolved end times.
update.RewindTimeToZero();
}
}
void SMILTimeContainer::ResetIntervals() { void SMILTimeContainer::ResetIntervals() {
base::AutoReset<bool> updating_intervals_scope(&is_updating_intervals_, true); base::AutoReset<bool> updating_intervals_scope(&is_updating_intervals_, true);
AnimationTargetsMutationsForbidden scope(this); AnimationTargetsMutationsForbidden scope(this);
...@@ -446,7 +493,8 @@ void SMILTimeContainer::ResetIntervals() { ...@@ -446,7 +493,8 @@ void SMILTimeContainer::ResetIntervals() {
priority_queue_.ResetAllPriorities(SMILTime::Earliest()); priority_queue_.ResetAllPriorities(SMILTime::Earliest());
} }
void SMILTimeContainer::UpdateIntervals(SMILTime document_time) { void SMILTimeContainer::UpdateIntervals(TimingUpdate& update) {
const SMILTime document_time = update.Time();
DCHECK(document_time.IsFinite()); DCHECK(document_time.IsFinite());
DCHECK_GE(document_time, SMILTime()); DCHECK_GE(document_time, SMILTime());
DCHECK(!priority_queue_.IsEmpty()); DCHECK(!priority_queue_.IsEmpty());
...@@ -459,8 +507,7 @@ void SMILTimeContainer::UpdateIntervals(SMILTime document_time) { ...@@ -459,8 +507,7 @@ void SMILTimeContainer::UpdateIntervals(SMILTime document_time) {
SVGSMILElement* element = priority_queue_.MinElement(); SVGSMILElement* element = priority_queue_.MinElement();
element->UpdateInterval(document_time); element->UpdateInterval(document_time);
auto events_to_dispatch = element->UpdateActiveState(document_time); auto events_to_dispatch = element->UpdateActiveState(document_time);
if (should_dispatch_events_ && events_to_dispatch) update.HandleEvents(element, events_to_dispatch);
element->DispatchEvents(events_to_dispatch);
SMILTime next_interval_time = SMILTime next_interval_time =
element->ComputeNextIntervalTime(document_time); element->ComputeNextIntervalTime(document_time);
priority_queue_.Update(next_interval_time, element); priority_queue_.Update(next_interval_time, element);
...@@ -469,21 +516,12 @@ void SMILTimeContainer::UpdateIntervals(SMILTime document_time) { ...@@ -469,21 +516,12 @@ void SMILTimeContainer::UpdateIntervals(SMILTime document_time) {
} }
} }
void SMILTimeContainer::UpdateAnimationTimings(SMILTime presentation_time) { void SMILTimeContainer::UpdateTimedElements(TimingUpdate& update) {
DCHECK(GetDocument().IsActive());
// Flush any "late" interval updates. // Flush any "late" interval updates.
UpdateIntervals(latest_update_time_); UpdateIntervals(update);
while (latest_update_time_ < presentation_time) { while (update.TryAdvanceTime(priority_queue_.Min()))
const SMILTime interval_time = priority_queue_.Min(); UpdateIntervals(update);
if (interval_time <= presentation_time) {
latest_update_time_ = interval_time;
UpdateIntervals(latest_update_time_);
} else {
latest_update_time_ = presentation_time;
}
}
} }
void SMILTimeContainer::ApplyTimedEffects(SMILTime elapsed) { void SMILTimeContainer::ApplyTimedEffects(SMILTime elapsed) {
......
...@@ -107,10 +107,12 @@ class SMILTimeContainer final : public GarbageCollected<SMILTimeContainer> { ...@@ -107,10 +107,12 @@ class SMILTimeContainer final : public GarbageCollected<SMILTimeContainer> {
void AnimationPolicyTimerFired(TimerBase*); void AnimationPolicyTimerFired(TimerBase*);
ImageAnimationPolicy AnimationPolicy() const; ImageAnimationPolicy AnimationPolicy() const;
bool HandleAnimationPolicy(AnimationPolicyOnceAction); bool HandleAnimationPolicy(AnimationPolicyOnceAction);
void UpdateAnimationsAndScheduleFrameIfNeeded(SMILTime elapsed); class TimingUpdate;
void UpdateAnimationsAndScheduleFrameIfNeeded(TimingUpdate&);
void PrepareSeek(TimingUpdate&);
void ResetIntervals(); void ResetIntervals();
void UpdateIntervals(SMILTime presentation_time); void UpdateIntervals(TimingUpdate&);
void UpdateAnimationTimings(SMILTime elapsed); void UpdateTimedElements(TimingUpdate&);
void ApplyTimedEffects(SMILTime elapsed); void ApplyTimedEffects(SMILTime elapsed);
SMILTime NextProgressTime(SMILTime presentation_time) const; SMILTime NextProgressTime(SMILTime presentation_time) const;
void ServiceOnNextFrame(); void ServiceOnNextFrame();
......
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