Bug 1415042 - Wait for two requestAnimationFrames to ensure that animations have been tried to send to the compositor in test_animation_performance_warning.html. r=birtles
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 07 Nov 2017 14:49:59 +0900
changeset 443727 1dc0be215168ca6a808b3050b00ededa26cefcd7
parent 443726 b023fdb6af095e8404f44f2cec33c9ae9a8231f3
child 443728 d73110cb1e936db8e5e29f6ae1c286ff201e2f82
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, 1341294, 1193394
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_animation_performance_warning.html. r=birtles To do that, ideally we can wait for MozAfterPaint, but it's sometimes fired before we do a paint process (bug 1341294), we should avoid using it until that bug is fixed. So, we wait for two requestAnimationFrames instead. The callback for requestAnimationFrame is called before styling process, so we need to wait for one more frame there. For now a Promise in waitForFrame is processed after paint process, so these tests would have been working fine with waiting for a single requestAnimationFrame. But after bug 1193394, the Promise will be processed right after requestAnimationFrame, thus these tests will fail with a single requestAnimationFrame. MozReview-Commit-ID: 4pbofZ3DUm3
dom/animation/test/chrome/test_animation_performance_warning.html
--- a/dom/animation/test/chrome/test_animation_performance_warning.html
+++ b/dom/animation/test/chrome/test_animation_performance_warning.html
@@ -37,16 +37,24 @@ 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;
@@ -176,17 +184,17 @@ function testBasicOperation() {
           runningOnCompositor: true
         }
       ]
     },
   ].forEach(subtest => {
     promise_test(t => {
       var animation = addDivAndAnimate(t, { class: 'compositable' },
                                        subtest.frames, 100 * MS_PER_SEC);
-      return animation.ready.then(() => {
+      return waitForPaints().then(() => {
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
           subtest.expected);
       });
     }, subtest.desc);
   });
 }
 
@@ -251,17 +259,17 @@ function testKeyframesWithGeometricPrope
           }
         ]
       }
     },
   ].forEach(subtest => {
     promise_test(t => {
       var animation = addDivAndAnimate(t, { class: 'compositable' },
                                        subtest.frames, 100 * MS_PER_SEC);
-      return animation.ready.then(() => {
+      return waitForPaints().then(() => {
         // First, a transform animation is running on compositor.
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
           subtest.expected.withoutGeometric);
       }).then(() => {
         // Add a 'width' property.
         var keyframes = animation.effect.getKeyframes();
 
@@ -309,17 +317,17 @@ function testSetOfGeometricProperties() 
     promise_test(t => {
       const keyframes = {
         [propertyToIDL(property)]: [ '100px', '200px' ],
         transform: [ 'translate(0px)', 'translate(100px)' ]
       };
       var animation = addDivAndAnimate(t, { class: 'compositable' },
                                        keyframes, 100 * MS_PER_SEC);
 
-      return animation.ready.then(() => {
+      return waitForPaints().then(() => {
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
           [
             {
               property,
               runningOnCompositor: false
             },
             {
@@ -401,17 +409,17 @@ function testStyleChanges() {
           warning: 'CompositorAnimationWarningTransformBackfaceVisibilityHidden'
         }
       ]
     },
   ].forEach(subtest => {
     promise_test(t => {
       var animation = addDivAndAnimate(t, { class: 'compositable' },
                                        subtest.frames, 100 * MS_PER_SEC);
-      return animation.ready.then(() => {
+      return waitForPaints().then(() => {
         assert_all_properties_running_on_compositor(
           animation.effect.getProperties(),
           subtest.expected);
         animation.effect.target.style = subtest.style;
         return waitForFrame();
       }).then(() => {
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
@@ -448,17 +456,17 @@ function testIdChanges() {
   ].forEach(subtest => {
     promise_test(t => {
       if (subtest.createelement) {
         addDiv(t, { style: subtest.createelement });
       }
 
       var animation = addDivAndAnimate(t, { class: 'compositable' },
                                        subtest.frames, 100 * MS_PER_SEC);
-      return animation.ready.then(() => {
+      return waitForPaints().then(() => {
         assert_all_properties_running_on_compositor(
           animation.effect.getProperties(),
           subtest.expected);
         animation.effect.target.id = subtest.id;
         return waitForFrame();
       }).then(() => {
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
@@ -539,17 +547,17 @@ function testMultipleAnimations() {
       var div = addDiv(t, { class: 'compositable' });
       var animations = subtest.animations.map(anim => {
         var animation = div.animate(anim.frames, 100 * MS_PER_SEC);
 
         // Bind expected values to animation object.
         animation.expected = anim.expected;
         return animation;
       });
-      return waitForAllAnimations(animations).then(() => {
+      return waitForPaints().then(() => {
         animations.forEach(anim => {
           assert_all_properties_running_on_compositor(
             anim.effect.getProperties(),
             anim.expected);
         });
         div.style = subtest.style;
         return waitForFrame();
       }).then(() => {
@@ -677,17 +685,17 @@ function testMultipleAnimationsWithGeome
       var div = addDiv(t, { class: 'compositable' });
       var animations = subtest.animations.map(anim => {
         var animation = div.animate(anim.frames, 100 * MS_PER_SEC);
 
         // Bind expected values to animation object.
         animation.expected = anim.expected;
         return animation;
       });
-      return waitForAllAnimations(animations).then(() => {
+      return waitForPaints().then(() => {
         // First, all animations are running on compositor.
         animations.forEach(anim => {
           assert_animation_property_state_equals(
             anim.effect.getProperties(),
             anim.expected.withoutGeometric);
         });
       }).then(() => {
         // Add a 'width' property to animations[1].
@@ -800,17 +808,17 @@ function testMultipleAnimationsWithGeome
 
         // Bind expected values to animation object.
         animation.expected = anim.expected;
         return animation;
       });
 
       var widthAnimation;
 
-      return waitForAllAnimations(animations).then(() => {
+      return waitForPaints().then(() => {
         animations.forEach(anim => {
           assert_all_properties_running_on_compositor(
             anim.effect.getProperties(),
             anim.expected);
         });
       }).then(() => {
         // Append 'width' animation on the same element.
         widthAnimation = div.animate({ width: ['100px', '200px'] },
@@ -873,17 +881,17 @@ function testSmallElements() {
           runningOnCompositor: true
         }
       ]
     },
   ].forEach(subtest => {
     promise_test(t => {
     var div = addDiv(t, subtest.style);
     var animation = div.animate(subtest.frames, 100 * MS_PER_SEC);
-      return animation.ready.then(() => {
+      return waitForPaints().then(() => {
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
           subtest.expected);
       });
     }, subtest.desc);
   });
 }
 
@@ -893,17 +901,17 @@ function testSynchronizedAnimations() {
     const elemB = addDiv(t, { class: 'compositable' });
 
     const animA = elemA.animate({ transform: [ 'translate(0px)',
                                                'translate(100px)' ] },
                                 100 * MS_PER_SEC);
     const animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
 
-    return Promise.all([animA.ready, animB.ready])
+    return waitForPaints()
       .then(() => {
         assert_animation_property_state_equals(
           animA.effect.getProperties(),
           [ { property: 'transform',
               runningOnCompositor: false,
               warning: 'CompositorAnimationWarningTransformWithSyncGeometricAnimations'
           } ]);
       });
@@ -915,17 +923,17 @@ function testSynchronizedAnimations() {
     const elemB = addDiv(t, { class: 'compositable' });
 
     const animA = elemA.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
     const animB = elemB.animate({ transform: [ 'translate(0px)',
                                                'translate(100px)' ] },
                                 100 * MS_PER_SEC);
 
-    return Promise.all([animA.ready, animB.ready])
+    return waitForPaints()
       .then(() => {
         assert_animation_property_state_equals(
           animB.effect.getProperties(),
           [ { property: 'transform',
               runningOnCompositor: false,
               warning: 'CompositorAnimationWarningTransformWithSyncGeometricAnimations'
           } ]);
       });
@@ -986,17 +994,17 @@ function testSynchronizedAnimations() {
 
     const animA = elemA.animate({ transform: [ 'translate(0px)',
                                                'translate(100px)' ],
                                   opacity: [ 0, 1 ] },
                                 100 * MS_PER_SEC);
     const animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
 
-    return Promise.all([animA.ready, animB.ready])
+    return waitForPaints()
       .then(() => {
         assert_animation_property_state_equals(
           animA.effect.getProperties(),
           [ { property: 'transform',
               runningOnCompositor: false,
               warning: 'CompositorAnimationWarningTransformWithSyncGeometricAnimations'
             },
             { property: 'opacity',
@@ -1010,17 +1018,17 @@ function testSynchronizedAnimations() {
   promise_test(t => {
     const elemA = addDiv(t, { class: 'compositable' });
     const elemB = addDiv(t, { class: 'compositable' });
 
     const animA = elemA.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
     let animB;
 
-    return waitForFrame()
+    return waitForPaints()
       .then(() => {
         animB = elemB.animate({ transform: [ 'translate(0px)',
                                              'translate(100px)' ] },
                                 100 * MS_PER_SEC);
         return animB.ready;
       }).then(() => {
         assert_animation_property_state_equals(
           animB.effect.getProperties(),
@@ -1034,17 +1042,17 @@ function testSynchronizedAnimations() {
     const elemA = addDiv(t, { class: 'compositable' });
     const elemB = addDiv(t, { class: 'compositable' });
 
     const animA = elemA.animate({ transform: [ 'translate(0px)',
                                                'translate(100px)' ] },
                                 100 * MS_PER_SEC);
     let animB;
 
-    return waitForFrame()
+    return waitForPaints()
       .then(() => {
         animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
                               100 * MS_PER_SEC);
         return animB.ready;
       }).then(() => {
         assert_animation_property_state_equals(
           animA.effect.getProperties(),
           [ { property: 'transform',
@@ -1059,17 +1067,17 @@ function testSynchronizedAnimations() {
 
     const animA = elemA.animate({ transform: [ 'translate(0px)',
                                                'translate(100px)' ] },
                                 100 * MS_PER_SEC);
     const animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
     animB.pause();
 
-    return Promise.all([animA.ready, animB.ready])
+    return waitForPaints()
       .then(() => {
         assert_animation_property_state_equals(
           animA.effect.getProperties(),
           [ { property: 'transform', runningOnCompositor: true } ]);
       });
   }, 'Paused animations are not synchronized');
 
   promise_test(t => {
@@ -1080,17 +1088,17 @@ function testSynchronizedAnimations() {
                                                'translate(100px)' ] },
                                 100 * MS_PER_SEC);
     const animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
 
     // Seek one of the animations so that their start times will differ
     animA.currentTime = 5000;
 
-    return Promise.all([animA.ready, animB.ready])
+    return waitForPaints()
       .then(() => {
         assert_not_equals(animA.startTime, animB.startTime,
                           'Animations should have different start times');
         assert_animation_property_state_equals(
           animA.effect.getProperties(),
           [ { property: 'transform',
               runningOnCompositor: false,
               warning: 'CompositorAnimationWarningTransformWithSyncGeometricAnimations'
@@ -1104,17 +1112,17 @@ function testSynchronizedAnimations() {
     const elemB = addDiv(t, { class: 'compositable' });
 
     const animA = elemA.animate({ transform: [ 'translate(0px)',
                                                'translate(100px)' ] },
                                 100 * MS_PER_SEC);
     const animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
 
-    return Promise.all([animA.ready, animB.ready])
+    return waitForPaints()
       .then(() => {
         assert_animation_property_state_equals(
           animA.effect.getProperties(),
           [ { property: 'transform',
               runningOnCompositor: false } ]);
         // Restart animation
         animA.pause();
         animA.play();
@@ -1135,17 +1143,17 @@ function testSynchronizedAnimations() {
                                                'translate(100px)' ] },
                                 100 * MS_PER_SEC);
     const animB = elemB.animate({ marginLeft: [ '0px', '100px' ] },
                                 100 * MS_PER_SEC);
 
     // Clear target effect
     animB.effect.target = null;
 
-    return Promise.all([animA.ready, animB.ready])
+    return waitForPaints()
       .then(() => {
         assert_animation_property_state_equals(
           animA.effect.getProperties(),
           [ { property: 'transform',
               runningOnCompositor: true } ]);
       });
   }, 'A geometric animation with no target element is not synchronized');
 }
@@ -1168,17 +1176,17 @@ function start() {
   testSynchronizedAnimations();
 
   promise_test(t => {
     var animation = addDivAndAnimate(t,
                                      { class: 'compositable' },
                                      { transform: [ 'translate(0px)',
                                                     'translate(100px)'] },
                                      100 * MS_PER_SEC);
-    return animation.ready.then(() => {
+    return waitForPaints().then(() => {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: true } ]);
       animation.effect.target.style = 'width: 5200px; height: 5200px';
       return waitForFrame();
     }).then(() => {
       // viewport depends on test environment.
       var expectedWarning = new RegExp(
@@ -1203,17 +1211,17 @@ function start() {
   }, 'transform on too big element - area');
 
   promise_test(t => {
     var animation = addDivAndAnimate(t,
                                      { class: 'compositable' },
                                      { transform: [ 'translate(0px)',
                                                     'translate(100px)'] },
                                      100 * MS_PER_SEC);
-    return animation.ready.then(() => {
+    return waitForPaints().then(() => {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: true } ]);
       animation.effect.target.style = 'width: 5200px; height: 1px';
       return waitForFrame();
     }).then(() => {
       // viewport depends on test environment.
       var expectedWarning = new RegExp(
@@ -1249,17 +1257,17 @@ function start() {
     svg.appendChild(rect);
     document.body.appendChild(svg);
     t.add_cleanup(() => {
       svg.remove();
     });
 
     var animation = svg.animate(
       { transform: ['translate(0px)', 'translate(100px)'] }, 100 * MS_PER_SEC);
-    return animation.ready.then(() => {
+    return waitForPaints().then(() => {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: true } ]);
       svg.setAttribute('transform', 'translate(10, 20)');
       return waitForFrame();
     }).then(() => {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
@@ -1277,17 +1285,17 @@ function start() {
     });
   }, 'transform of nsIFrame with SVG transform');
 
   promise_test(t => {
     var div = addDiv(t, { class: 'compositable',
                           style: 'animation: fade 100s' });
     var cssAnimation = div.getAnimations()[0];
     var scriptAnimation = div.animate({ opacity: [ 1, 0 ] }, 100 * MS_PER_SEC);
-    return scriptAnimation.ready.then(() => {
+    return waitForPaints().then(() => {
       assert_animation_property_state_equals(
         cssAnimation.effect.getProperties(),
         [ { property: 'opacity', runningOnCompositor: true } ]);
       assert_animation_property_state_equals(
         scriptAnimation.effect.getProperties(),
         [ { property: 'opacity', runningOnCompositor: true } ]);
     });
   }, 'overridden animation');