test_restyles_in_smil_animation.html (4909B)
1 <!doctype html> 2 <head> 3 <meta charset=utf-8> 4 <title>Tests restyles in smil animation</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <script src="/tests/SimpleTest/paint_listener.js"></script> 7 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> 8 </head> 9 <body> 10 11 <div id="target-div"> 12 <svg> 13 <rect id="svg-rect" width="100%" height="100%" fill="lime"/> 14 </svg> 15 </div> 16 17 <script> 18 "use strict"; 19 20 // Waits for |frameCount| requestAnimationFrame callbacks. 21 // Returns the number of frame actually waited. 22 function waitForAnimationFrames(frameCount) { 23 return new Promise(function(resolve, reject) { 24 let previousTime = document.timeline.currentTime; 25 let framesWaited = 0; 26 function handleFrame() { 27 // SMIL uses a time resolution of 1ms but our software-based vsync timer 28 // sometimes produces ticks with an interval of less than 1ms. In such 29 // cases we will skip restyling for SMIL animations since the SMIL time 30 // will not change. 31 // 32 // In the following we attempt to detect such situations and wait an 33 // additional frame when we detect it. However, the detection is not 34 // completely accurate since it uses the timeline time which is based on 35 // the navigation start time whereas the SMIL start time is based on the 36 // refresh driver time. 37 // 38 // To account for this inaccuracy the Promise returned by this method 39 // resolves with the number of frames waited with the additional frames. 40 // This can be used by the call site to add a suitable tolerance to the 41 // number of restylings it expects to happen. For example, if a call site 42 // is anticipating each animation frame to cause restyling, then the 43 // number of restylings, x, it should expect is frameCount <= x <= 44 // framesWaited. 45 const difference = document.timeline.currentTime - previousTime; 46 framesWaited++; 47 if (difference >= 1.0 && --frameCount <= 0) { 48 resolve(framesWaited); 49 return; 50 } 51 52 previousTime = document.timeline.currentTime; 53 window.requestAnimationFrame(handleFrame); // wait another frame 54 } 55 window.requestAnimationFrame(handleFrame); 56 }); 57 } 58 59 // Returns an object consisting of observed styling count and the number of 60 // frames actually waited because we detected a possibly overflapping SMIL 61 // time. 62 function observeStyling(frameCount) { 63 let priorAnimationTriggeredRestyles = SpecialPowers.DOMWindowUtils.animationTriggeredRestyles; 64 65 return new Promise(function(resolve) { 66 return waitForAnimationFrames(frameCount).then(framesWaited => { 67 const restyleCount = SpecialPowers.DOMWindowUtils.animationTriggeredRestyles - priorAnimationTriggeredRestyles; 68 resolve({ 69 stylingCount: restyleCount, 70 framesWaited: framesWaited, 71 }); 72 }); 73 }); 74 } 75 76 function ensureElementRemoval(aElement) { 77 return new Promise(function(resolve) { 78 aElement.remove(); 79 waitForAllPaintsFlushed(resolve); 80 }); 81 } 82 83 function waitForPaintFlushed() { 84 return new Promise(function(resolve) { 85 waitForAllPaintsFlushed(resolve); 86 }); 87 } 88 89 SimpleTest.waitForExplicitFinish(); 90 91 add_task(async function smil_is_in_display_none_subtree() { 92 await waitForPaintFlushed(); 93 94 var animate = 95 document.createElementNS("http://www.w3.org/2000/svg", "animate"); 96 animate.setAttribute("attributeType", "XML"); 97 animate.setAttribute("attributeName", "fill"); 98 animate.setAttribute("values", "red;lime"); 99 animate.setAttribute("dur", "1s"); 100 animate.setAttribute("repeatCount", "indefinite"); 101 document.getElementById("svg-rect").appendChild(animate); 102 103 await waitForAnimationFrames(2); 104 105 let result = await observeStyling(5); 106 // FIXME: Bug 866411: SMIL animations sometimes skip restyles when the target 107 // element is newly associated with an nsIFrame. 108 ok(result.stylingCount >= 4 && 109 result.stylingCount <= result.framesWaited, 110 `should restyle in most frames (got ${result.stylingCount} restyles ` + 111 `over ${result.framesWaited} frames, expected 4~${result.framesWaited})`); 112 113 var div = document.getElementById("target-div"); 114 115 div.style.display = "none"; 116 getComputedStyle(div).display; 117 await waitForPaintFlushed(); 118 119 result = await observeStyling(5); 120 is(result.stylingCount, 0, "should never restyle if display:none"); 121 122 div.style.display = ""; 123 getComputedStyle(div).display; 124 await waitForAnimationFrames(2); 125 126 result = await observeStyling(5); 127 // FIXME: Bug 866411: SMIL animations sometimes skip restyles when the target 128 // element is newly associated with an nsIFrame. 129 ok(result.stylingCount >= 4 && 130 result.stylingCount <= result.framesWaited, 131 `should restyle again (got ${result.stylingCount} restyles over ` + 132 `${result.framesWaited} frames, expected 4~${result.framesWaited})`); 133 134 await ensureElementRemoval(animate); 135 }); 136 </script> 137 </body>