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 draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 07 Nov 2017 14:50:15 +0900
changeset 693936 e3410c47b18b5913852994f5216241b75f1a2730
parent 693935 2be10b46bb1b4a9cfc3e51f447b041161c26f04c
child 739207 5e8c4551a755c2f35c52b3c806773878f35b3be7
push id87989
push userhikezoe@mozilla.com
push dateTue, 07 Nov 2017 05:53:46 +0000
reviewersbirtles
bugs1415042
milestone58.0a1
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);
+}