1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/style/test/animation_utils.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,182 @@ 1.4 +function px_to_num(str) 1.5 +{ 1.6 + return Number(String(str).match(/^([\d.]+)px$/)[1]); 1.7 +} 1.8 + 1.9 +function bezier(x1, y1, x2, y2) { 1.10 + // Cubic bezier with control points (0, 0), (x1, y1), (x2, y2), and (1, 1). 1.11 + function x_for_t(t) { 1.12 + var omt = 1-t; 1.13 + return 3 * omt * omt * t * x1 + 3 * omt * t * t * x2 + t * t * t; 1.14 + } 1.15 + function y_for_t(t) { 1.16 + var omt = 1-t; 1.17 + return 3 * omt * omt * t * y1 + 3 * omt * t * t * y2 + t * t * t; 1.18 + } 1.19 + function t_for_x(x) { 1.20 + // Binary subdivision. 1.21 + var mint = 0, maxt = 1; 1.22 + for (var i = 0; i < 30; ++i) { 1.23 + var guesst = (mint + maxt) / 2; 1.24 + var guessx = x_for_t(guesst); 1.25 + if (x < guessx) 1.26 + maxt = guesst; 1.27 + else 1.28 + mint = guesst; 1.29 + } 1.30 + return (mint + maxt) / 2; 1.31 + } 1.32 + return function bezier_closure(x) { 1.33 + if (x == 0) return 0; 1.34 + if (x == 1) return 1; 1.35 + return y_for_t(t_for_x(x)); 1.36 + } 1.37 +} 1.38 + 1.39 +function step_end(nsteps) { 1.40 + return function step_end_closure(x) { 1.41 + return Math.floor(x * nsteps) / nsteps; 1.42 + } 1.43 +} 1.44 + 1.45 +function step_start(nsteps) { 1.46 + var stepend = step_end(nsteps); 1.47 + return function step_start_closure(x) { 1.48 + return 1.0 - stepend(1.0 - x); 1.49 + } 1.50 +} 1.51 + 1.52 +var gTF = { 1.53 + "ease": bezier(0.25, 0.1, 0.25, 1), 1.54 + "linear": function(x) { return x; }, 1.55 + "ease_in": bezier(0.42, 0, 1, 1), 1.56 + "ease_out": bezier(0, 0, 0.58, 1), 1.57 + "ease_in_out": bezier(0.42, 0, 0.58, 1), 1.58 + "step_start": step_start(1), 1.59 + "step_end": step_end(1), 1.60 +}; 1.61 + 1.62 +function is_approx(float1, float2, error, desc) { 1.63 + ok(Math.abs(float1 - float2) < error, 1.64 + desc + ": " + float1 + " and " + float2 + " should be within " + error); 1.65 +} 1.66 + 1.67 +// Checks if off-main thread animation (OMTA) is available, and if it is, runs 1.68 +// the provided callback function. If OMTA is not available or is not 1.69 +// functioning correctly, the second callback, aOnSkip, is run instead. 1.70 +// 1.71 +// This function also does an internal test to verify that OMTA is working at 1.72 +// all so that if OMTA is not functioning correctly when it is expected to 1.73 +// function only a single failure is produced. 1.74 +// 1.75 +// Since this function relies on various asynchronous operations, the caller is 1.76 +// responsible for calling SimpleTest.waitForExplicitFinish() before calling 1.77 +// this and SimpleTest.finish() within aTestFunction and aOnSkip. 1.78 +function runOMTATest(aTestFunction, aOnSkip) { 1.79 + const OMTAPrefKey = "layers.offmainthreadcomposition.async-animations"; 1.80 + var utils = SpecialPowers.DOMWindowUtils; 1.81 + var expectOMTA = utils.layerManagerRemote && 1.82 + // ^ Off-main thread animation cannot be used if off-main 1.83 + // thread composition (OMTC) is not available 1.84 + SpecialPowers.getBoolPref(OMTAPrefKey); 1.85 + 1.86 + isOMTAWorking().then(function(isWorking) { 1.87 + if (expectOMTA) { 1.88 + if (isWorking) { 1.89 + aTestFunction(); 1.90 + } else { 1.91 + // We only call this when we know it will fail as otherwise in the 1.92 + // regular success case we will end up inflating the "passed tests" 1.93 + // count by 1 1.94 + ok(isWorking, "OMTA is working as expected"); 1.95 + aOnSkip(); 1.96 + } 1.97 + } else { 1.98 + todo(isWorking, "OMTA is working"); 1.99 + aOnSkip(); 1.100 + } 1.101 + }).catch(function(err) { 1.102 + ok(false, err); 1.103 + aOnSkip(); 1.104 + }); 1.105 + 1.106 + function isOMTAWorking() { 1.107 + // Create keyframes rule 1.108 + const animationName = "a6ce3091ed85"; // Random name to avoid clashes 1.109 + var ruleText = "@keyframes " + animationName + 1.110 + " { from { opacity: 0.5 } to { opacity 0.5 } }"; 1.111 + var style = document.createElement("style"); 1.112 + style.appendChild(document.createTextNode(ruleText)); 1.113 + document.head.appendChild(style); 1.114 + 1.115 + // Create animation target 1.116 + var div = document.createElement("div"); 1.117 + document.body.appendChild(div); 1.118 + 1.119 + // Give the target geometry so it is eligible for layerization 1.120 + div.style.width = "100px"; 1.121 + div.style.height = "100px"; 1.122 + div.style.backgroundColor = "white"; 1.123 + 1.124 + // Common clean up code 1.125 + var cleanUp = function() { 1.126 + div.parentNode.removeChild(div); 1.127 + style.parentNode.removeChild(style); 1.128 + if (utils.isTestControllingRefreshes) { 1.129 + utils.restoreNormalRefresh(); 1.130 + } 1.131 + }; 1.132 + 1.133 + return waitForDocumentLoad() 1.134 + .then(loadPaintListener) 1.135 + .then(function() { 1.136 + // Put refresh driver under test control and trigger animation 1.137 + utils.advanceTimeAndRefresh(0); 1.138 + div.style.animation = animationName + " 10s"; 1.139 + 1.140 + // Trigger style flush 1.141 + div.clientTop; 1.142 + return waitForPaints(); 1.143 + }).then(function() { 1.144 + var opacity = utils.getOMTAStyle(div, "opacity"); 1.145 + cleanUp(); 1.146 + return Promise.resolve(opacity == 0.5); 1.147 + }).catch(function(err) { 1.148 + cleanUp(); 1.149 + return Promise.reject(err); 1.150 + }); 1.151 + } 1.152 + 1.153 + function waitForDocumentLoad() { 1.154 + return new Promise(function(resolve, reject) { 1.155 + if (document.readyState === "complete") { 1.156 + resolve(); 1.157 + } else { 1.158 + window.addEventListener("load", resolve); 1.159 + } 1.160 + }); 1.161 + } 1.162 + 1.163 + function waitForPaints() { 1.164 + return new Promise(function(resolve, reject) { 1.165 + waitForAllPaintsFlushed(resolve); 1.166 + }); 1.167 + } 1.168 + 1.169 + function loadPaintListener() { 1.170 + return new Promise(function(resolve, reject) { 1.171 + if (typeof(window.waitForAllPaints) !== "function") { 1.172 + var script = document.createElement("script"); 1.173 + script.onload = resolve; 1.174 + script.onerror = function() { 1.175 + reject(new Error("Failed to load paint listener")); 1.176 + }; 1.177 + script.src = "/tests/SimpleTest/paint_listener.js"; 1.178 + var firstScript = document.scripts[0]; 1.179 + firstScript.parentNode.insertBefore(script, firstScript); 1.180 + } else { 1.181 + resolve(); 1.182 + } 1.183 + }); 1.184 + } 1.185 +}