Bug 1415042 - Wait for two requestAnimationFrames to ensure that animations have been tried to send to the compositor in test_running_on_compositor.html. r=birtles
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 07 Nov 2017 14:50:15 +0900
changeset 443729 917324392dd4c13def255b1ec2aa293fe99c99ee
parent 443728 d73110cb1e936db8e5e29f6ae1c286ff201e2f82
child 443730 55c1cb6938fc2a9a55345d4202548872259a28a6
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles
bugs1415042
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1415042 - Wait for two requestAnimationFrames to ensure that animations have been tried to send to the compositor in test_running_on_compositor.html. r=birtles MozReview-Commit-ID: FhzRLiKiLC
dom/animation/test/chrome/test_animation_performance_warning.html
dom/animation/test/chrome/test_running_on_compositor.html
dom/animation/test/testcommon.js
--- a/dom/animation/test/chrome/test_animation_performance_warning.html
+++ b/dom/animation/test/chrome/test_animation_performance_warning.html
@@ -37,24 +37,16 @@ Services.locale.setRequestedLocales(["en
 SpecialPowers.pushPrefEnv({ "set": [
                             // Need to set devPixelsPerPx explicitly to gain
                             // consistent pixel values in warning messages
                             // regardless of platform DPIs.
                             ["layout.css.devPixelsPerPx", 1],
                           ] },
                           start);
 
-// a fake function waiting for a paint.
-function waitForPaints() {
-  // FIXME: Bug 1415065 Instead waiting for two requestAnimationFrames, we
-  // should wait for MozAfterPaint once after MozAfterPaint is fired properly
-  // (bug 1341294).
-  return waitForAnimationFrames(2);
-}
-
 function compare_property_state(a, b) {
   if (a.property > b.property) {
     return -1;
   } else if (a.property < b.property) {
     return 1;
   }
   if (a.runningOnCompositor != b.runningOnCompositor) {
     return a.runningOnCompositor ? 1 : -1;
--- a/dom/animation/test/chrome/test_running_on_compositor.html
+++ b/dom/animation/test/chrome/test_running_on_compositor.html
@@ -63,17 +63,17 @@ function assert_animation_is_not_running
 }
 
 promise_test(t => {
   // FIXME: When we implement Element.animate, use that here instead of CSS
   // so that we remove any dependency on the CSS mapping.
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
        'Animation reports that it is running on the compositor'
        + ' during playback');
 
     div.style.animationPlayState = 'paused';
 
     return animation.ready;
   }).then(() => {
@@ -82,55 +82,55 @@ promise_test(t => {
        + ' when paused');
   });
 }, '');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: background 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
        'Animation reports that it is NOT running on the compositor'
        + ' for animation of "background"');
   });
 }, 'isRunningOnCompositor is false for animation of "background"');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: background_and_translate 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
        'Animation reports that it is running on the compositor'
         + ' when the animation has two properties, where one can run'
         + ' on the compositor, the other cannot');
   });
 }, 'isRunningOnCompositor is true if the animation has at least one ' +
    'property can run on compositor');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     animation.pause();
     return animation.ready;
   }).then(() => {
     assert_animation_is_not_running_on_compositor(animation,
        'Animation reports that it is NOT running on the compositor'
        + ' when animation.pause() is called');
   });
 }, 'isRunningOnCompositor is false when the animation.pause() is called');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     animation.finish();
     assert_animation_is_not_running_on_compositor(animation,
        'Animation reports that it is NOT running on the compositor'
        + ' immediately after animation.finish() is called');
     // Check that we don't set the flag back again on the next tick.
     return waitForFrame();
   }).then(() => {
     assert_animation_is_not_running_on_compositor(animation,
@@ -138,17 +138,17 @@ promise_test(t => {
        + ' on the next tick after animation.finish() is called');
   });
 }, 'isRunningOnCompositor is false when the animation.finish() is called');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     animation.currentTime = 100 * MS_PER_SEC;
     assert_animation_is_not_running_on_compositor(animation,
        'Animation reports that it is NOT running on the compositor'
        + ' immediately after manually seeking the animation to the end');
     // Check that we don't set the flag back again on the next tick.
     return waitForFrame();
   }).then(() => {
     assert_animation_is_not_running_on_compositor(animation,
@@ -157,17 +157,17 @@ promise_test(t => {
   });
 }, 'isRunningOnCompositor is false when manually seeking the animation to ' +
    'the end');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     animation.cancel();
     assert_animation_is_not_running_on_compositor(animation,
        'Animation reports that it is NOT running on the compositor'
        + ' immediately after animation.cancel() is called');
     // Check that we don't set the flag back again on the next tick.
     return waitForFrame();
   }).then(() => {
     assert_animation_is_not_running_on_compositor(animation,
@@ -177,17 +177,17 @@ promise_test(t => {
 }, 'isRunningOnCompositor is false when animation.cancel() is called');
 
 // This is to test that we don't simply clobber the flag when ticking
 // animations and then set it again during painting.
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     return new Promise(resolve => {
       window.requestAnimationFrame(() => {
         t.step(() => {
           assert_animation_is_running_on_compositor(animation,
             'Animation reports that it is running on the compositor'
              + ' in requestAnimationFrame callback');
         });
 
@@ -196,17 +196,17 @@ promise_test(t => {
     });
   });
 }, 'isRunningOnCompositor is true in requestAnimationFrame callback');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     return new Promise(resolve => {
       var observer = new MutationObserver(records => {
         var changedAnimation;
 
         records.forEach(record => {
           changedAnimation =
             record.changedAnimations.find(changedAnim => {
               return changedAnim == animation;
@@ -237,17 +237,17 @@ promise_test(t => {
 // an unthrottled sample.
 promise_test(t => {
   // Needs scrollbars to cause overflow.
   return SpecialPowers.pushPrefEnv({ set: [["ui.showHideScrollbars", 1]] })
   .then(() => {
     var div = addDiv(t, { style: 'animation: rotate 100s' });
     var animation = div.getAnimations()[0];
 
-    return animation.ready.then(() => {
+    return waitForPaints().then(() => {
       return new Promise(resolve => {
         var timeAtStart = window.performance.now();
         function handleFrame() {
           t.step(() => {
             assert_animation_is_running_on_compositor(animation,
               'Animation reports that it is running on the compositor'
                + ' in requestAnimationFrame callback');
           });
@@ -271,45 +271,45 @@ promise_test(t => {
 promise_test(t => {
   var div = addDiv(t, { style: 'transition: opacity 100s; opacity: 1' });
 
   getComputedStyle(div).opacity;
 
   div.style.opacity = 0;
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
        'Transition reports that it is running on the compositor'
        + ' during playback for opacity transition');
   });
 }, 'isRunningOnCompositor for transitions');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'animation: rotate-and-opacity 100s; ' +
                                'backface-visibility: hidden; ' +
                                'transform: none !important;' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
        'If an animation has a property that can run on the compositor and a '
        + 'property that cannot (due to Gecko limitations) but where the latter'
        + 'property is overridden in the CSS cascade, the animation should '
        + 'still report that it is running on the compositor');
   });
 }, 'isRunningOnCompositor is true when a property that would otherwise block ' +
    'running on the compositor is overridden in the CSS cascade');
 
 promise_test(t => {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
       'Animation reports that it is running on the compositor');
 
     animation.currentTime = 150 * MS_PER_SEC;
     animation.effect.timing.duration = 100 * MS_PER_SEC;
 
     assert_animation_is_not_running_on_compositor(animation,
        'Animation reports that it is NOT running on the compositor'
@@ -318,17 +318,17 @@ promise_test(t => {
 }, 'animation is immediately removed from compositor' +
    'when timing.duration is made shorter than the current time');
 
 promise_test(t => {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
       'Animation reports that it is running on the compositor');
 
     animation.currentTime = 500 * MS_PER_SEC;
 
     assert_animation_is_not_running_on_compositor(animation,
       'Animation reports that it is NOT running on the compositor'
       + ' when finished');
@@ -343,17 +343,17 @@ promise_test(t => {
 }, 'animation is added to compositor' +
    ' when timing.duration is made longer than the current time');
 
 promise_test(t => {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
       'Animation reports that it is running on the compositor');
 
     animation.effect.timing.endDelay = 100 * MS_PER_SEC;
 
     assert_animation_is_running_on_compositor(animation,
       'Animation reports that it is running on the compositor'
       + ' when endDelay is changed');
@@ -368,17 +368,17 @@ promise_test(t => {
 }, 'animation is removed from compositor' +
    ' when current time is made longer than the duration even during endDelay');
 
 promise_test(t => {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
       'Animation reports that it is running on the compositor');
 
     animation.effect.timing.endDelay = -200 * MS_PER_SEC;
     return waitForFrame();
   }).then(() => {
     assert_animation_is_not_running_on_compositor(animation,
       'Animation reports that it is NOT running on the compositor'
@@ -387,17 +387,17 @@ promise_test(t => {
 }, 'animation is removed from compositor' +
    ' when endTime is negative value');
 
 promise_test(t => {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
       'Animation reports that it is running on the compositor');
 
     animation.effect.timing.endDelay = -100 * MS_PER_SEC;
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
       'Animation reports that it is running on the compositor'
@@ -416,17 +416,17 @@ promise_test(t => {
   var effect = new KeyframeEffect(null,
                                   { opacity: [ 0, 1 ] },
                                   100 * MS_PER_SEC);
   var animation = new Animation(effect, document.timeline);
   animation.play();
 
   var div = addDiv(t);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
                   'Animation with null target reports that it is not running ' +
                   'on the compositor');
 
     animation.effect.target = div;
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -434,17 +434,17 @@ promise_test(t => {
                   'after setting a valid target');
   });
 }, 'animation is added to the compositor when setting a valid target');
 
 promise_test(t => {
   var div = addDiv(t);
   var animation = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
                   'Animation reports that it is running on the compositor');
 
     animation.effect.target = null;
     assert_animation_is_not_running_on_compositor(animation,
                   'Animation reports that it is NOT running on the ' +
                   'compositor after setting null target');
   });
@@ -452,17 +452,17 @@ promise_test(t => {
 
 promise_test(t => {
   var div = addDiv(t);
   var animation = div.animate({ opacity: [ 0, 1 ] },
                               { duration: 100 * MS_PER_SEC,
                                 delay: 100 * MS_PER_SEC,
                                 fill: 'backwards' });
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
                   'Animation with fill:backwards in delay phase reports ' +
                   'that it is running on the compositor');
 
     animation.currentTime = 100 * MS_PER_SEC;
     return waitForFrame();
  }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -475,17 +475,17 @@ promise_test(t => {
 promise_test(t => {
   var div = addDiv(t);
   var animation = div.animate([{ opacity: 1, offset: 0 },
                                { opacity: 1, offset: 0.99 },
                                { opacity: 0, offset: 1 }], 100 * MS_PER_SEC);
 
   var another = addDiv(t);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
                   'Opacity animation on a 100% opacity keyframe reports ' +
                   'that it is running on the compositor from the begining');
 
     animation.effect.target = another;
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -495,17 +495,17 @@ promise_test(t => {
   });
 }, '100% opacity animations with keeps running on the ' +
    'compositor after changing the target element');
 
 promise_test(t => {
   var div = addDiv(t);
   var animation = div.animate({ color: ['red', 'black'] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
                   'Color animation reports that it is not running on the ' +
                   'compositor');
 
     animation.effect.setKeyframes([{ opacity: 1, offset: 0 },
                                    { opacity: 1, offset: 0.99 },
                                    { opacity: 0, offset: 1 }]);
     return waitForFrame();
@@ -521,17 +521,17 @@ promise_test(t => {
   var div = addDiv(t);
   var animation = div.animate({ color: ['red', 'black'] }, 100 * MS_PER_SEC);
   var effect = new KeyframeEffect(div,
                                   [{ opacity: 1, offset: 0 },
                                    { opacity: 1, offset: 0.99 },
                                    { opacity: 0, offset: 1 }],
                                   100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
                   'Color animation reports that it is not running on the ' +
                   'compositor');
 
     animation.effect = effect;
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -542,17 +542,17 @@ promise_test(t => {
    'animation which cannot be run on the compositor, is running on the ' +
    'compositor');
 
 promise_test(t => {
   var div = addDiv(t, { style: "opacity: 1 ! important" });
 
   var animation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
       'Opacity animation on an element which has 100% opacity style with ' +
       '!important flag reports that it is not running on the compositor');
     // Clear important flag from the opacity style on the target element.
     div.style.setProperty("opacity", "1", "");
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -562,17 +562,17 @@ promise_test(t => {
 }, 'Clearing *important* opacity style on the target element sends the ' +
    'animation to the compositor');
 
 promise_test(t => {
   var div = addDiv(t);
   var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
   var higherAnimation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
 
-  return Promise.all([lowerAnimation.ready, higherAnimation.ready]).then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(higherAnimation,
                   'A higher-priority opacity animation on an element ' +
                   'reports that it is running on the compositor');
     assert_animation_is_running_on_compositor(lowerAnimation,
                   'A lower-priority opacity animation on the same ' +
                   'element also reports that it is running on the compositor');
   });
 }, 'Opacity animations on the same element run on the compositor');
@@ -583,34 +583,34 @@ promise_test(t => {
   getComputedStyle(div).opacity;
 
   div.style.opacity = 0;
   getComputedStyle(div).opacity;
 
   var transition = div.getAnimations()[0];
   var animation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
 
-  return Promise.all([transition.ready, animation.ready]).then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
                   'An opacity animation on an element reports that' +
                   'that it is running on the compositor');
     assert_animation_is_running_on_compositor(transition,
                   'An opacity transition on the same element reports that ' +
                   'it is running on the compositor');
   });
 }, 'Both of transition and script animation on the same element run on the ' +
    'compositor');
 
 promise_test(t => {
   var div = addDiv(t);
   var importantOpacityElement = addDiv(t, { style: "opacity: 1 ! important" });
 
   var animation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
                   'Opacity animation on an element reports ' +
                   'that it is running on the compositor');
 
     animation.effect.target = null;
     return waitForFrame();
   }).then(() => {
     assert_animation_is_not_running_on_compositor(animation,
@@ -630,17 +630,17 @@ promise_test(t => {
 
 promise_test(t => {
   var div = addDiv(t);
   var another = addDiv(t);
 
   var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
   var higherAnimation = another.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
 
-  return Promise.all([lowerAnimation.ready, higherAnimation.ready]).then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(lowerAnimation,
                   'An opacity animation on an element reports that ' +
                   'it is running on the compositor');
     assert_animation_is_running_on_compositor(higherAnimation,
                   'Opacity animation on a different element reports ' +
                   'that it is running on the compositor');
 
     lowerAnimation.effect.target = null;
@@ -667,17 +667,17 @@ promise_test(t => {
 
 promise_test(t => {
   var div = addDiv(t);
   var another = addDiv(t);
 
   var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
   var higherAnimation = another.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
 
-  return Promise.all([lowerAnimation.ready, higherAnimation.ready]).then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(lowerAnimation,
                   'An opacity animation on an element reports that ' +
                   'it is running on the compositor');
     assert_animation_is_running_on_compositor(higherAnimation,
                   'Opacity animation on a different element reports ' +
                   'that it is running on the compositor');
 
     higherAnimation.effect.target = null;
@@ -753,17 +753,17 @@ var delayPhaseTests = [
     },
   },
 ];
 
 delayPhaseTests.forEach(test => {
   promise_test(t => {
     var animation = test.setupAnimation(t);
 
-    return animation.ready.then(() => {
+    return waitForPaints().then(() => {
       assert_animation_is_running_on_compositor(animation,
          test.desc + ' reports that it is running on the '
          + 'compositor even though it is in the delay phase');
     });
   }, 'isRunningOnCompositor for ' + test.desc + ' is true even though ' +
      'it is in the delay phase');
 });
 
@@ -799,17 +799,17 @@ var delayPhaseWithTransformStyleTests = 
     },
   },
 ];
 
 delayPhaseWithTransformStyleTests.forEach(test => {
   promise_test(t => {
     var animation = test.setupAnimation(t);
 
-    return animation.ready.then(() => {
+    return waitForPaints().then(() => {
       assert_animation_is_running_on_compositor(animation,
          test.desc + ' reports that it is running on the '
          + 'compositor even though it is in the delay phase');
     }).then(() => {
       // Remove the initial transform style during delay phase.
       animation.effect.target.style.transform = 'none';
       return animation.ready;
     }).then(() => {
@@ -838,33 +838,33 @@ var startsWithNoneTests = [
     },
   },
 ];
 
 startsWithNoneTests.forEach(test => {
   promise_test(t => {
     var animation = test.setupAnimation(t);
 
-    return animation.ready.then(() => {
+    return waitForPaints().then(() => {
       assert_animation_is_running_on_compositor(animation,
          test.desc + ' reports that it is running on the '
          + 'compositor even though it is in transform:none segment');
     });
   }, 'isRunningOnCompositor for ' + test.desc + ' is true even though ' +
      'it is in transform:none segment');
 });
 
 promise_test(t => {
   var div = addDiv(t, { style: 'opacity: 1 ! important' });
 
   var animation = div.animate(
     { opacity: [0, 1] },
     { delay: 100 * MS_PER_SEC, duration: 100 * MS_PER_SEC });
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
       'Opacity animation on an element which has opacity:1 important style'
       + 'reports that it is not running on the compositor');
     // Clear the opacity style on the target element.
     div.style.setProperty("opacity", "1", "");
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -877,17 +877,17 @@ promise_test(t => {
 promise_test(t => {
   var opaqueDiv = addDiv(t, { style: 'opacity: 1 ! important' });
   var anotherDiv = addDiv(t);
 
   var animation = opaqueDiv.animate(
     { opacity: [0, 1] },
     { delay: 100 * MS_PER_SEC, duration: 100 * MS_PER_SEC });
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
       'Opacity animation on an element which has opacity:1 important style'
       + 'reports that it is not running on the compositor');
     // Changing target element to another element which has no opacity style.
     animation.effect.target = anotherDiv;
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -900,17 +900,17 @@ promise_test(t => {
 
 promise_test(t => {
   var animation =
     addDivAndAnimate(t,
                      {},
                      { width: ['100px', '200px'] },
                      { duration: 100 * MS_PER_SEC, delay: 100 * MS_PER_SEC });
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(animation,
       'Width animation reports that it is not running on the compositor '
       + 'in the delay phase');
     // Changing to property runnable on the compositor.
     animation.effect.setKeyframes({ opacity: [0, 1] });
     return waitForFrame();
   }).then(() => {
     assert_animation_is_running_on_compositor(animation,
@@ -925,17 +925,17 @@ promise_test(t => {
                                'opacity: 0 !important' });
   getComputedStyle(div).opacity;
 
   div.style.setProperty('opacity', '1', 'important');
   getComputedStyle(div).opacity;
 
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_running_on_compositor(animation,
        'Transition reports that it is running on the compositor even if the ' +
        'property is overridden by an !important rule');
   });
 }, 'Transitions override important rules');
 
 promise_test(t => {
   var div = addDiv(t, { style: 'transition: opacity 100s; ' +
@@ -944,17 +944,17 @@ promise_test(t => {
 
   div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
   div.style.setProperty('opacity', '1', 'important');
   getComputedStyle(div).opacity;
 
   var [transition, animation] = div.getAnimations();
 
-  return Promise.all([transition.ready, animation.ready]).then(() => {
+  return waitForPaints().then(() => {
     assert_animation_is_not_running_on_compositor(transition,
        'Transition suppressed by an animation which is overridden by an ' +
        '!important rule reports that it is NOT running on the compositor');
     assert_animation_is_not_running_on_compositor(animation,
        'Animation overridden by an !important rule reports that it is ' +
        'NOT running on the compositor');
   });
 }, 'Neither transition nor animation does run on the compositor if the ' +
--- a/dom/animation/test/testcommon.js
+++ b/dom/animation/test/testcommon.js
@@ -362,8 +362,18 @@ function addSVGElement(target, tag, attr
  */
 function getDistance(target, prop, v1, v2) {
   if (!target) {
     return 0.0;
   }
   return SpecialPowers.DOMWindowUtils
            .computeAnimationDistance(target, prop, v1, v2);
 }
+
+/*
+ * A promise wrapper for waiting MozAfterPaint.
+ */
+function waitForPaints() {
+  // FIXME: Bug 1415065. Instead waiting for two requestAnimationFrames, we
+  // should wait for MozAfterPaint once after MozAfterPaint is fired properly
+  // (bug 1341294).
+  return waitForAnimationFrames(2);
+}