Bug 1334583 - Pass a separate timeOrigin and startTime for compositor animations; r?hiro
By passing the startTime as a TimeDuration we are able to represent times in the
distant past (and with the same range as we can represent on the main thread so
that if we do encounter range errors in future, they should not differ between
the main thread and the compositor).
This patch includes a crashtest. I have verified that, without the code changes
included in this patch, this crashtest fails on debug builds on OSX.
MozReview-Commit-ID: EDuKLzfEC0K
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1334583-1.html
@@ -0,0 +1,9 @@
+<div style="width: 200px; height: 200px; background: purple" id=div>
+</div>
+<script>
+const animation = div.animate(
+ [ { transform: "scale(1)" },
+ { transform: "scale(2)" } ],
+ { iterations: Infinity, duration: 512 } );
+animation.currentTime = 2147483647;
+</script>
--- a/dom/animation/test/crashtests/crashtests.list
+++ b/dom/animation/test/crashtests/crashtests.list
@@ -20,10 +20,11 @@ pref(dom.animations-api.core.enabled,tru
pref(dom.animations-api.core.enabled,true) load 1324554-1.html
pref(dom.animations-api.core.enabled,true) load 1325193-1.html
pref(dom.animations-api.core.enabled,true) load 1330190-1.html
pref(dom.animations-api.core.enabled,true) load 1330190-2.html
pref(dom.animations-api.core.enabled,true) load 1330513-1.html
pref(dom.animations-api.core.enabled,true) load 1333539-1.html
pref(dom.animations-api.core.enabled,true) load 1333539-2.html
pref(dom.animations-api.core.enabled,true) load 1333418-1.html
+pref(dom.animations-api.core.enabled,true) load 1334583-1.html
pref(dom.animations-api.core.enabled,true) load 1343589-1.html
pref(dom.animations-api.core.enabled,true) load 1359658-1.html
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -154,25 +154,28 @@ AnimationHelper::SampleAnimationForEachN
// Process in order, since later aAnimations override earlier ones.
for (size_t i = 0, iEnd = aAnimations.Length(); i < iEnd; ++i) {
Animation& animation = aAnimations[i];
AnimData& animData = aAnimationData[i];
activeAnimations = true;
- MOZ_ASSERT(!animation.startTime().IsNull() ||
+ MOZ_ASSERT((!animation.originTime().IsNull() &&
+ animation.startTime().type() != MaybeTimeDuration::Tnull_t) ||
animation.isNotPlaying(),
- "Failed to resolve start time of play-pending animations");
+ "If we are playing, we should have an origin time and a start"
+ " time");
// If the animation is not currently playing , e.g. paused or
// finished, then use the hold time to stay at the same position.
TimeDuration elapsedDuration = animation.isNotPlaying()
? animation.holdTime()
- : (aTime - animation.startTime())
- .MultDouble(animation.playbackRate());
+ : (aTime - animation.originTime() -
+ animation.startTime().get_TimeDuration())
+ .MultDouble(animation.playbackRate());
TimingParams timing;
timing.mDuration.emplace(animation.duration());
timing.mDelay = animation.delay();
timing.mEndDelay = animation.endDelay();
timing.mIterations = animation.iterations();
timing.mIterationStart = animation.iterationStart();
timing.mDirection =
static_cast<dom::PlaybackDirection>(animation.direction());
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -288,22 +288,25 @@ Layer::StartPendingAnimations(const Time
{
bool updated = false;
for (size_t animIdx = 0, animEnd = layer->mAnimations.Length();
animIdx < animEnd; animIdx++) {
Animation& anim = layer->mAnimations[animIdx];
// If the animation is play-pending, resolve the start time.
// This mirrors the calculation in Animation::StartTimeFromReadyTime.
- if (anim.startTime().IsNull() && !anim.isNotPlaying()) {
+ if (anim.startTime().type() == MaybeTimeDuration::Tnull_t &&
+ !anim.originTime().IsNull() &&
+ !anim.isNotPlaying()) {
+ TimeDuration readyTime = aReadyTime - anim.originTime();
anim.startTime() =
anim.playbackRate() == 0
- ? aReadyTime
- : aReadyTime - anim.holdTime().MultDouble(1.0 /
- anim.playbackRate());
+ ? readyTime
+ : readyTime - anim.holdTime().MultDouble(1.0 /
+ anim.playbackRate());
updated = true;
}
}
if (updated) {
layer->Mutated();
}
});
}
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -157,16 +157,21 @@ union TransformFunction {
Scale;
Skew;
SkewX;
SkewY;
Translation;
TransformMatrix;
};
+union MaybeTimeDuration {
+ null_t;
+ TimeDuration;
+};
+
union Animatable {
null_t;
float;
TransformFunction[];
};
struct AnimationSegment {
Animatable startState;
@@ -190,25 +195,30 @@ struct TransformData {
};
union AnimationData {
null_t;
TransformData;
};
struct Animation {
- TimeStamp startTime;
+ // The zero time of this Animation's timeline. May be null if isNotPlaying is
+ // true.
+ TimeStamp originTime;
+ // The start time is relative to the originTime. This allows us to represent
+ // start times in the distant past that cannot be expressed using a TimeStamp.
+ MaybeTimeDuration startTime;
TimeDuration delay;
TimeDuration endDelay;
// The value of the animation's current time at the moment it was sent to the
// compositor. This value will be used for below cases:
// 1) Animations that are play-pending. Initially these animations will have a
- // null |startTime|. Once the animation is read to start (i.e. painting has
- // finished), we calculate an appropriate value of |startTime| such that
- // playback begins from |holdTime|.
+ // null |startTime|. Once the animation is ready to start (i.e. painting
+ // has finished), we calculate an appropriate value of |startTime| such
+ // that playback begins from |holdTime|.
// 2) Not playing animations (e.g. paused and finished animations). In this
// case the |holdTime| represents the current time the animation will
// maintain.
TimeDuration holdTime;
TimeDuration duration;
// For each frame, the interpolation point is computed based on the
// startTime, the direction, the duration, and the current time.
// The segments must uniquely cover the portion from 0.0 to 1.0
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -521,25 +521,32 @@ AddAnimationForProperty(nsIFrame* aFrame
aAnimation->GetEffect() &&
aAnimation->GetEffect()->AsTransition()) {
// We update startValue from the replaced transition only if the effect is
// an ElementPropertyTransition.
aAnimation->GetEffect()->AsTransition()->
UpdateStartValueFromReplacedTransition();
}
+ animation->originTime() = !aAnimation->GetTimeline()
+ ? TimeStamp()
+ : aAnimation->GetTimeline()->
+ ToTimeStamp(TimeDuration());
+
+ Nullable<TimeDuration> startTime = aAnimation->GetCurrentOrPendingStartTime();
+ if (startTime.IsNull()) {
+ animation->startTime() = null_t();
+ } else {
+ animation->startTime() = startTime.Value();
+ }
+
+ animation->holdTime() = aAnimation->GetCurrentTime().Value();
+
const ComputedTiming computedTiming =
aAnimation->GetEffect()->GetComputedTiming();
- Nullable<TimeDuration> startTime = aAnimation->GetCurrentOrPendingStartTime();
- animation->startTime() = startTime.IsNull() || !aAnimation->GetTimeline()
- ? TimeStamp()
- : aAnimation->GetTimeline()->
- ToTimeStamp(startTime.Value());
- animation->holdTime() = aAnimation->GetCurrentTime().Value();
-
animation->delay() = timing.mDelay;
animation->endDelay() = timing.mEndDelay;
animation->duration() = computedTiming.mDuration;
animation->iterations() = computedTiming.mIterations;
animation->iterationStart() = computedTiming.mIterationStart;
animation->direction() = static_cast<uint8_t>(timing.mDirection);
animation->fillMode() = static_cast<uint8_t>(computedTiming.mFill);
animation->property() = aProperty.mProperty;