Bug 1361260 - Incorporate playbackRate when calculating the start time of a pending compositor animation; r?hiro
MozReview-Commit-ID: FBmT5ImBcYJ
--- a/dom/animation/test/mozilla/file_deferred_start.html
+++ b/dom/animation/test/mozilla/file_deferred_start.html
@@ -74,23 +74,26 @@ promise_test(function(t) {
}, 'Animation.ready is resolved for an empty animation');
// Test that compositor animations with delays get synced correctly
//
// NOTE: It is important that we DON'T use
// SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes
// us through a different code path.
promise_test(function(t) {
+ assert_false(SpecialPowers.DOMWindowUtils.isTestControllingRefreshes,
+ 'Test should run without the refresh driver being under'
+ + ' test control');
+
// This test only applies to compositor animations
if (!isOMTAEnabled()) {
return;
}
- const div = addDiv(t);
- div.classList.add('target');
+ const div = addDiv(t, { class: 'target' });
// As with the above test, any stray paints can cause this test to produce
// a false negative (that is, pass when it should fail). To avoid this we
// first wait for the document to load, then wait until it is idle, then wait
// for paints and only then do we commence the test. Even doing that, this
// test can sometimes pass when it should not due to a stray paint. Most of
// the time, however, it will correctly fail so hopefully even if we do
// occasionally produce a false negative on one platform, another platform
@@ -98,34 +101,81 @@ promise_test(function(t) {
return waitForDocLoad().then(() => waitForIdle())
.then(() => waitForPaints())
.then(() => {
div.animate({ transform: [ 'translate(0px)', 'translate(100px)' ] },
{ duration: 400 * MS_PER_SEC,
delay: -200 * MS_PER_SEC });
return waitForPaints();
}).then(() => {
- var transformStr =
+ const transformStr =
SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
-
- var matrixComponents =
- transformStr.startsWith('matrix(')
- ? transformStr.substring('matrix('.length, transformStr.length-1)
- .split(',')
- .map(component => Number(component))
- : [];
- assert_equals(matrixComponents.length, 6,
- 'Got a valid transform matrix on the compositor'
- + ' (got: "' + transformStr + '")');
+ const translateX = getTranslateXFromTransform(transformStr);
// If the delay has been applied we should be about half-way through
// the animation. However, if we applied it twice we will be at the
// end of the animation already so check that we are roughly half way
// through.
- const translateX = matrixComponents[4];
assert_between_inclusive(translateX, 40, 75,
'Animation is about half-way through on the compositor');
});
}, 'Starting an animation with a delay starts from the correct point');
+// Test that compositor animations with a playback rate start at the
+// appropriate point.
+//
+// NOTE: As with the previous test, it is important that we DON'T use
+// SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes
+// us through a different code path.
+promise_test(function(t) {
+ assert_false(SpecialPowers.DOMWindowUtils.isTestControllingRefreshes,
+ 'Test should run without the refresh driver being under'
+ + ' test control');
+
+ // This test only applies to compositor animations
+ if (!isOMTAEnabled()) {
+ return;
+ }
+
+ const div = addDiv(t, { class: 'target' });
+
+ // Wait for idle state (see notes in previous test).
+ return waitForDocLoad().then(() => waitForIdle())
+ .then(() => waitForPaints())
+ .then(() => {
+ const animation =
+ div.animate({ transform: [ 'translate(0px)', 'translate(100px)' ] },
+ 200 * MS_PER_SEC);
+ animation.currentTime = 100 * MS_PER_SEC;
+ animation.playbackRate = 0.1;
+ return waitForPaints();
+ }).then(() => {
+ const transformStr =
+ SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
+ const translateX = getTranslateXFromTransform(transformStr);
+
+ // We pass the playback rate to the compositor independently and we have
+ // tests to ensure that it is correctly applied there. However, if, when
+ // we resolve the start time of the pending animation, we fail to
+ // incorporate the playback rate, we will end up starting from the wrong
+ // point and the current time calculated on the compositor will be wrong.
+ assert_between_inclusive(translateX, 25, 75,
+ 'Animation is about half-way through on the compositor');
+ });
+}, 'Starting an animation with a playbackRate starts from the correct point');
+
+function getTranslateXFromTransform(transformStr) {
+ const matrixComponents =
+ transformStr.startsWith('matrix(')
+ ? transformStr.substring('matrix('.length, transformStr.length-1)
+ .split(',')
+ .map(component => Number(component))
+ : [];
+ assert_equals(matrixComponents.length, 6,
+ 'Got a valid transform matrix on the compositor'
+ + ' (got: "' + transformStr + '")');
+
+ return matrixComponents[4];
+}
+
done();
</script>
</body>
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -287,18 +287,23 @@ Layer::StartPendingAnimations(const Time
[&aReadyTime](Layer *layer)
{
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()) {
- anim.startTime() = aReadyTime - anim.holdTime();
+ anim.startTime() =
+ anim.playbackRate() == 0
+ ? aReadyTime
+ : aReadyTime - anim.holdTime().MultDouble(1.0 /
+ anim.playbackRate());
updated = true;
}
}
if (updated) {
layer->Mutated();
}
});
}