| /* This is the helper function to run animation tests: |
| |
| Test page requirements: |
| - The body must contain an empty div with id "result" |
| - Call this function directly from the <script> inside the test page |
| |
| Function parameters: |
| expected [required]: an array of arrays defining a set of CSS properties that must have given values at specific times (see below) |
| callbacks [optional]: a function to be executed immediately after animation starts; |
| or, an object in the form {time: function} containing functions to be |
| called at the specified times (in seconds) during animation. |
| trigger [optional]: a function to trigger transitions at the start of the test |
| |
| Each sub-array must contain these items in this order: |
| - the time in seconds at which to snapshot the CSS property |
| - the id of the element on which to get the CSS property value [1] |
| - the name of the CSS property to get [2] |
| - the expected value for the CSS property |
| - the tolerance to use when comparing the effective CSS property value with its expected value |
| |
| [1] If a single string is passed, it is the id of the element to test. If an array with 2 elements is passed they |
| are the ids of 2 elements, whose values are compared for equality. In this case the expected value is ignored |
| but the tolerance is used in the comparison. If the second element is prefixed with "static:", no animation on that |
| element is required, allowing comparison with an unanimated "expected value" element. |
| |
| If a string with a '.' is passed, this is an element in an iframe. The string before the dot is the iframe id |
| and the string after the dot is the element name in that iframe. |
| |
| [2] If the CSS property name is "webkitTransform", expected value must be an array of 1 or more numbers corresponding to the matrix elements, |
| or a string which will be compared directly (useful if the expected value is "none") |
| If the CSS property name is "webkitTransform.N", expected value must be a number corresponding to the Nth element of the matrix |
| |
| */ |
| |
| // Set to true to log debug information in failing tests. Note that these logs |
| // contain timestamps, so are non-deterministic and will introduce flakiness if |
| // any expected results include failures. |
| var ENABLE_ERROR_LOGGING = false; |
| |
| function isCloseEnough(actual, desired, tolerance) |
| { |
| var diff = Math.abs(actual - desired); |
| return diff <= tolerance; |
| } |
| |
| function roundNumber(num, decimalPlaces) |
| { |
| return Math.round(num * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces); |
| } |
| |
| function matrixStringToArray(s) |
| { |
| if (s == "none") |
| return [ 1, 0, 0, 1, 0, 0 ]; |
| var m = s.split("("); |
| m = m[1].split(")"); |
| return m[0].split(","); |
| } |
| |
| function parseCrossFade(s) |
| { |
| var matches = s.match("-webkit-cross-fade\\((.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\)"); |
| |
| if (!matches) |
| return null; |
| |
| return {"from": matches[1], "to": matches[2], "percent": parseFloat(matches[3])} |
| } |
| |
| function parseBasicShape(s) |
| { |
| var shapeFunction = s.match(/(\w+)\((.+)\)/); |
| if (!shapeFunction) |
| return null; |
| |
| var matches; |
| switch (shapeFunction[1]) { |
| case "rectangle": |
| matches = s.match("rectangle\\((.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\,\\s*(.*)\\)"); |
| break; |
| case "circle": |
| matches = s.match("circle\\((.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\)"); |
| break; |
| case "ellipse": |
| matches = s.match("ellipse\\((.*)\\s*,\\s*(.*)\\s*,\\s*(.*)\\,\\s*(.*)\\)"); |
| break; |
| case "polygon": |
| matches = s.match("polygon\\(nonzero, (.*)\\s+(.*)\\s*,\\s*(.*)\\s+(.*)\\s*,\\s*(.*)\\s+(.*)\\s*,\\s*(.*)\\s+(.*)\\)"); |
| break; |
| default: |
| return null; |
| } |
| |
| if (!matches) |
| return null; |
| |
| matches.shift(); |
| |
| // Normalize percentage values. |
| for (var i = 0; i < matches.length; ++i) { |
| var param = matches[i]; |
| matches[i] = parseFloat(matches[i]); |
| if (param.indexOf('%') != -1) |
| matches[i] = matches[i] / 100; |
| } |
| |
| return {"shape": shapeFunction[1], "params": matches}; |
| } |
| |
| function basicShapeParametersMatch(paramList1, paramList2, tolerance) |
| { |
| if (paramList1.shape != paramList2.shape |
| || paramList1.params.length != paramList2.params.length) |
| return false; |
| for (var i = 0; i < paramList1.params.length; ++i) { |
| var param1 = paramList1.params[i], |
| param2 = paramList2.params[i]; |
| var match = isCloseEnough(param1, param2, tolerance); |
| if (!match) |
| return false; |
| } |
| return true; |
| } |
| |
| // Return an array of numeric filter params in 0-1. |
| function getFilterParameters(s) |
| { |
| var filterResult = s.match(/(\w+)\((.+)\)/); |
| if (!filterResult) |
| throw new Error("There's no filter in \"" + s + "\""); |
| var filterParams = filterResult[2]; |
| if (filterResult[1] == "custom") { |
| if (!window.getCustomFilterParameters) |
| throw new Error("getCustomFilterParameters not found. Did you include custom-filter-parser.js?"); |
| return getCustomFilterParameters(filterParams); |
| } |
| var paramList = filterParams.split(' '); // FIXME: the spec may allow comma separation at some point. |
| |
| // Normalize percentage values. |
| for (var i = 0; i < paramList.length; ++i) { |
| var param = paramList[i]; |
| paramList[i] = parseFloat(paramList[i]); |
| if (param.indexOf('%') != -1) |
| paramList[i] = paramList[i] / 100; |
| } |
| |
| return paramList; |
| } |
| |
| function customFilterParameterMatch(param1, param2, tolerance) |
| { |
| if (param1.type != "parameter") { |
| // Checking for shader uris and other keywords. They need to be exactly the same. |
| return (param1.type == param2.type && param1.value == param2.value); |
| } |
| |
| if (param1.name != param2.name || param1.value.length != param2.value.length) |
| return false; |
| |
| for (var j = 0; j < param1.value.length; ++j) { |
| var val1 = param1.value[j], |
| val2 = param2.value[j]; |
| if (val1.type != val2.type) |
| return false; |
| switch (val1.type) { |
| case "function": |
| if (val1.name != val2.name) |
| return false; |
| if (val1.arguments.length != val2.arguments.length) { |
| console.error("Arguments length mismatch: ", val1.arguments.length, "/", val2.arguments.length); |
| return false; |
| } |
| for (var t = 0; t < val1.arguments.length; ++t) { |
| if (val1.arguments[t].type != "number" || val2.arguments[t].type != "number") |
| return false; |
| if (!isCloseEnough(val1.arguments[t].value, val2.arguments[t].value, tolerance)) |
| return false; |
| } |
| break; |
| case "number": |
| if (!isCloseEnough(val1.value, val2.value, tolerance)) |
| return false; |
| break; |
| default: |
| console.error("Unsupported parameter type ", val1.type); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| function filterParametersMatch(paramList1, paramList2, tolerance) |
| { |
| if (paramList1.length != paramList2.length) |
| return false; |
| for (var i = 0; i < paramList1.length; ++i) { |
| var param1 = paramList1[i], |
| param2 = paramList2[i]; |
| if (typeof param1 == "object") { |
| // This is a custom filter parameter. |
| if (!customFilterParameterMatch(param1, param2, tolerance)) |
| return false; |
| continue; |
| } |
| var match = isCloseEnough(param1, param2, tolerance); |
| if (!match) |
| return false; |
| } |
| return true; |
| } |
| |
| function checkExpectedValue(expected, index) |
| { |
| log('Checking expectation: ' + JSON.stringify(expected[index])); |
| var time = expected[index][0]; |
| var elementId = expected[index][1]; |
| var property = expected[index][2]; |
| var expectedValue = expected[index][3]; |
| var tolerance = expected[index][4]; |
| |
| // Check for a pair of element Ids |
| var compareElements = false; |
| var element2Static = false; |
| var elementId2; |
| if (typeof elementId != "string") { |
| if (elementId.length != 2) |
| return; |
| |
| elementId2 = elementId[1]; |
| elementId = elementId[0]; |
| |
| if (elementId2.indexOf("static:") == 0) { |
| elementId2 = elementId2.replace("static:", ""); |
| element2Static = true; |
| } |
| |
| compareElements = true; |
| } |
| |
| // Check for a dot separated string |
| var iframeId; |
| if (!compareElements) { |
| var array = elementId.split('.'); |
| if (array.length == 2) { |
| iframeId = array[0]; |
| elementId = array[1]; |
| } |
| } |
| |
| var computedValue, computedValue2; |
| if (compareElements) { |
| computedValue = getPropertyValue(property, elementId, iframeId); |
| computedValue2 = getPropertyValue(property, elementId2, iframeId); |
| |
| if (comparePropertyValue(property, computedValue, computedValue2, tolerance)) |
| result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + |
| "\" elements at " + time + "s are close enough to each other" + "<br>"; |
| else |
| result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + |
| "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 + |
| "\" which are not close enough to each other" + "<br>"; |
| } else { |
| var elementName; |
| if (iframeId) |
| elementName = iframeId + '.' + elementId; |
| else |
| elementName = elementId; |
| |
| computedValue = getPropertyValue(property, elementId, iframeId); |
| |
| if (comparePropertyValue(property, computedValue, expectedValue, tolerance)) |
| result += "PASS - \"" + property + "\" property for \"" + elementName + "\" element at " + time + |
| "s saw something close to: " + expectedValue + "<br>"; |
| else |
| result += "FAIL - \"" + property + "\" property for \"" + elementName + "\" element at " + time + |
| "s expected: " + expectedValue + " but saw: " + computedValue + "<br>"; |
| } |
| } |
| |
| function compareRGB(rgb, expected, tolerance) |
| { |
| return (isCloseEnough(parseInt(rgb[0]), expected[0], tolerance) && |
| isCloseEnough(parseInt(rgb[1]), expected[1], tolerance) && |
| isCloseEnough(parseInt(rgb[2]), expected[2], tolerance)); |
| } |
| |
| function checkExpectedTransitionValue(expected, index) |
| { |
| log('Checking expectation: ' + JSON.stringify(expected[index])); |
| var time = expected[index][0]; |
| var elementId = expected[index][1]; |
| var property = expected[index][2]; |
| var expectedValue = expected[index][3]; |
| var tolerance = expected[index][4]; |
| var postCompletionCallback = expected[index][5]; |
| |
| var computedValue; |
| var pass = false; |
| var transformRegExp = /^-webkit-transform(\.\d+)?$/; |
| if (transformRegExp.test(property)) { |
| computedValue = window.getComputedStyle(document.getElementById(elementId)).webkitTransform; |
| if (typeof expectedValue == "string") |
| pass = (computedValue == expectedValue); |
| else if (typeof expectedValue == "number") { |
| var m = computedValue.split("("); |
| var m = m[1].split(","); |
| pass = isCloseEnough(parseFloat(m[parseInt(property.substring(18))]), expectedValue, tolerance); |
| } else { |
| var m = computedValue.split("("); |
| var m = m[1].split(","); |
| for (i = 0; i < expectedValue.length; ++i) { |
| pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance); |
| if (!pass) |
| break; |
| } |
| } |
| } else if (property == "fill" || property == "stroke") { |
| computedValue = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property).rgbColor; |
| if (compareRGB([computedValue.red.cssText, computedValue.green.cssText, computedValue.blue.cssText], expectedValue, tolerance)) |
| pass = true; |
| else { |
| // We failed. Make sure computed value is something we can read in the error message |
| computedValue = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property).cssText; |
| } |
| } else if (property == "stop-color" || property == "flood-color" || property == "lighting-color") { |
| computedValue = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property); |
| // The computedValue cssText is rgb(num, num, num) |
| var components = computedValue.cssText.split("(")[1].split(")")[0].split(","); |
| if (compareRGB(components, expectedValue, tolerance)) |
| pass = true; |
| else { |
| // We failed. Make sure computed value is something we can read in the error message |
| computedValue = computedValue.cssText; |
| } |
| } else if (property == "lineHeight") { |
| computedValue = parseInt(window.getComputedStyle(document.getElementById(elementId)).lineHeight); |
| pass = isCloseEnough(computedValue, expectedValue, tolerance); |
| } else if (property == "background-image" |
| || property == "border-image-source" |
| || property == "border-image" |
| || property == "list-style-image" |
| || property == "-webkit-mask-image" |
| || property == "-webkit-mask-box-image") { |
| if (property == "border-image" || property == "-webkit-mask-image" || property == "-webkit-mask-box-image") |
| property += "-source"; |
| |
| computedValue = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property).cssText; |
| computedCrossFade = parseCrossFade(computedValue); |
| |
| if (!computedCrossFade) { |
| pass = false; |
| } else { |
| pass = isCloseEnough(computedCrossFade.percent, expectedValue, tolerance); |
| } |
| } else if (property == "object-position") { |
| computedValue = window.getComputedStyle(document.getElementById(elementId)).objectPosition; |
| var actualArray = computedValue.split(" "); |
| var expectedArray = expectedValue.split(" "); |
| if (actualArray.length != expectedArray.length) { |
| pass = false; |
| } else { |
| for (i = 0; i < expectedArray.length; ++i) { |
| pass = isCloseEnough(parseFloat(actualArray[i]), parseFloat(expectedArray[i]), tolerance); |
| if (!pass) |
| break; |
| } |
| } |
| } else { |
| var computedStyle = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property); |
| if (computedStyle.cssValueType == CSSValue.CSS_VALUE_LIST) { |
| var values = []; |
| for (var i = 0; i < computedStyle.length; ++i) { |
| switch (computedStyle[i].cssValueType) { |
| case CSSValue.CSS_PRIMITIVE_VALUE: |
| values.push(computedStyle[i].getFloatValue(CSSPrimitiveValue.CSS_NUMBER)); |
| break; |
| case CSSValue.CSS_CUSTOM: |
| // arbitrarily pick shadow-x and shadow-y |
| if (property == 'box-shadow' || property == 'text-shadow') { |
| var text = computedStyle[i].cssText; |
| // Shadow cssText looks like "rgb(0, 0, 255) 0px -3px 10px 0px" |
| var shadowPositionRegExp = /\)\s*(-?\d+)px\s*(-?\d+)px/; |
| var match = shadowPositionRegExp.exec(text); |
| var shadowXY = [parseInt(match[1]), parseInt(match[2])]; |
| values.push(shadowXY[0]); |
| values.push(shadowXY[1]); |
| } else |
| values.push(computedStyle[i].cssText); |
| break; |
| } |
| } |
| computedValue = values.join(','); |
| pass = true; |
| for (var i = 0; i < values.length; ++i) |
| pass &= isCloseEnough(values[i], expectedValue[i], tolerance); |
| } else if (computedStyle.cssValueType == CSSValue.CSS_PRIMITIVE_VALUE) { |
| switch (computedStyle.primitiveType) { |
| case CSSPrimitiveValue.CSS_STRING: |
| case CSSPrimitiveValue.CSS_IDENT: |
| computedValue = computedStyle.getStringValue(); |
| pass = computedValue == expectedValue; |
| break; |
| case CSSPrimitiveValue.CSS_RGBCOLOR: |
| var rgbColor = computedStyle.getRGBColorValue(); |
| computedValue = [rgbColor.red.getFloatValue(CSSPrimitiveValue.CSS_NUMBER), |
| rgbColor.green.getFloatValue(CSSPrimitiveValue.CSS_NUMBER), |
| rgbColor.blue.getFloatValue(CSSPrimitiveValue.CSS_NUMBER)]; // alpha is not exposed to JS |
| pass = true; |
| for (var i = 0; i < 3; ++i) |
| pass &= isCloseEnough(computedValue[i], expectedValue[i], tolerance); |
| break; |
| case CSSPrimitiveValue.CSS_RECT: |
| computedValue = computedStyle.getRectValue(); |
| computedValue = [computedValue.top.getFloatValue(CSSPrimitiveValue.CSS_NUMBER), |
| computedValue.right.getFloatValue(CSSPrimitiveValue.CSS_NUMBER), |
| computedValue.bottom.getFloatValue(CSSPrimitiveValue.CSS_NUMBER), |
| computedValue.left.getFloatValue(CSSPrimitiveValue.CSS_NUMBER)]; |
| pass = true; |
| for (var i = 0; i < 4; ++i) |
| pass &= isCloseEnough(computedValue[i], expectedValue[i], tolerance); |
| break; |
| case CSSPrimitiveValue.CSS_PERCENTAGE: |
| computedValue = parseFloat(computedStyle.cssText); |
| pass = isCloseEnough(computedValue, expectedValue, tolerance); |
| break; |
| default: |
| computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); |
| pass = isCloseEnough(computedValue, expectedValue, tolerance); |
| } |
| } |
| } |
| |
| if (pass) |
| result += "PASS - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s saw something close to: " + expectedValue + "<br>"; |
| else |
| result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s expected: " + expectedValue + " but saw: " + computedValue + "<br>"; |
| |
| if (postCompletionCallback) |
| result += postCompletionCallback(); |
| } |
| |
| |
| function getPropertyValue(property, elementId, iframeId) |
| { |
| var computedValue; |
| var element; |
| if (iframeId) |
| element = document.getElementById(iframeId).contentDocument.getElementById(elementId); |
| else |
| element = document.getElementById(elementId); |
| |
| if (property == "lineHeight") |
| computedValue = parseInt(window.getComputedStyle(element).lineHeight); |
| else if (property == "backgroundImage" |
| || property == "borderImageSource" |
| || property == "listStyleImage" |
| || property == "webkitMaskImage" |
| || property == "webkitMaskBoxImage" |
| || property == "webkitFilter" |
| || property == "webkitClipPath" |
| || property == "webkitShapeInside" |
| || !property.indexOf("webkitTransform")) { |
| computedValue = window.getComputedStyle(element)[property.split(".")[0]]; |
| } else { |
| var computedStyle = window.getComputedStyle(element).getPropertyCSSValue(property); |
| try { |
| computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); |
| } catch (e) { |
| computedValue = computedStyle.getStringValue(); |
| } |
| } |
| |
| return computedValue; |
| } |
| |
| function comparePropertyValue(property, computedValue, expectedValue, tolerance) |
| { |
| var result = true; |
| |
| if (!property.indexOf("webkitTransform")) { |
| if (typeof expectedValue == "string") |
| result = (computedValue == expectedValue); |
| else if (typeof expectedValue == "number") { |
| var m = matrixStringToArray(computedValue); |
| result = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance); |
| } else { |
| var m = matrixStringToArray(computedValue); |
| for (i = 0; i < expectedValue.length; ++i) { |
| result = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance); |
| if (!result) |
| break; |
| } |
| } |
| } else if (property == "webkitFilter") { |
| var filterParameters = getFilterParameters(computedValue); |
| var filter2Parameters = getFilterParameters(expectedValue); |
| result = filterParametersMatch(filterParameters, filter2Parameters, tolerance); |
| } else if (property == "webkitClipPath" || property == "webkitShapeInside") { |
| var clipPathParameters = parseBasicShape(computedValue); |
| var clipPathParameters2 = parseBasicShape(expectedValue); |
| if (!clipPathParameters || !clipPathParameters2) |
| result = false; |
| result = basicShapeParametersMatch(clipPathParameters, clipPathParameters2, tolerance); |
| } else if (property == "backgroundImage" |
| || property == "borderImageSource" |
| || property == "listStyleImage" |
| || property == "webkitMaskImage" |
| || property == "webkitMaskBoxImage") { |
| var computedCrossFade = parseCrossFade(computedValue); |
| |
| if (!computedCrossFade) { |
| result = false; |
| } else { |
| if (typeof expectedValue == "string") { |
| var computedCrossFade2 = parseCrossFade(expectedValue); |
| result = isCloseEnough(computedCrossFade.percent, computedCrossFade2.percent, tolerance) && computedCrossFade.from == computedCrossFade2.from && computedCrossFade.to == computedCrossFade2.to; |
| } else { |
| result = isCloseEnough(computedCrossFade.percent, expectedValue, tolerance) |
| } |
| } |
| } else { |
| if (typeof expectedValue == "string") |
| result = (computedValue == expectedValue); |
| else |
| result = isCloseEnough(computedValue, expectedValue, tolerance); |
| } |
| return result; |
| } |
| |
| function endTest() |
| { |
| log('Ending test'); |
| var resultElement = useResultElement ? document.getElementById('result') : document.documentElement; |
| if (ENABLE_ERROR_LOGGING && result.indexOf('FAIL') >= 0) |
| result += '<br>Log:<br>' + logMessages.join('<br>'); |
| resultElement.innerHTML = result; |
| |
| if (window.testRunner) |
| testRunner.notifyDone(); |
| } |
| |
| function runChecksWithRAF(checks) |
| { |
| var finished = true; |
| var time = performance.now() - animStartTime; |
| |
| log('RAF callback, animation time: ' + time); |
| for (var k in checks) { |
| var checkTime = Number(k); |
| if (checkTime > time) { |
| finished = false; |
| break; |
| } |
| log('Running checks for time: ' + checkTime + ', delay: ' + (time - checkTime)); |
| checks[k].forEach(function(check) { check(); }); |
| delete checks[k]; |
| } |
| |
| if (finished) |
| endTest(); |
| else |
| requestAnimationFrame(runChecksWithRAF.bind(null, checks)); |
| } |
| |
| function runChecksWithPauseAPI(checks) { |
| for (var k in checks) { |
| var timeMs = Number(k); |
| log('Pausing at time: ' + timeMs + ', active animations: ' + internals.numberOfActiveAnimations()); |
| internals.pauseAnimations(timeMs / 1000); |
| checks[k].forEach(function(check) { check(); }); |
| } |
| endTest(); |
| } |
| |
| function startTest(checks) |
| { |
| if (hasPauseAnimationAPI) |
| runChecksWithPauseAPI(checks); |
| else { |
| result += 'Warning this test is running in real-time and may be flaky.<br>'; |
| runChecksWithRAF(checks); |
| } |
| } |
| |
| var logMessages = []; |
| var useResultElement = false; |
| var result = ""; |
| var hasPauseAnimationAPI; |
| var animStartTime; |
| var isTransitionsTest = false; |
| |
| var shouldBeTransitioning = 'should-be-transitioning'; |
| var shouldNotBeTransitioning = 'should-not-be-transitioning'; |
| |
| function log(message) |
| { |
| logMessages.push(performance.now() + ' - ' + message); |
| } |
| |
| function waitForAnimationsToStart(callback) |
| { |
| if (!window.internals || internals.numberOfActiveAnimations() > 0) { |
| callback(); |
| } else { |
| setTimeout(waitForAnimationsToStart.bind(this, callback), 0); |
| } |
| } |
| |
| // FIXME: disablePauseAnimationAPI and doPixelTest |
| function runAnimationTest(expected, callbacks, trigger, disablePauseAnimationAPI, doPixelTest, startTestImmediately) |
| { |
| log('runAnimationTest'); |
| if (!expected) |
| throw "Expected results are missing!"; |
| |
| hasPauseAnimationAPI = 'internals' in window; |
| if (disablePauseAnimationAPI) |
| hasPauseAnimationAPI = false; |
| |
| var checks = {}; |
| |
| if (typeof callbacks == 'function') { |
| checks[0] = [callbacks]; |
| } else for (var time in callbacks) { |
| timeMs = Math.round(time * 1000); |
| checks[timeMs] = [callbacks[time]]; |
| } |
| |
| for (var i = 0; i < expected.length; i++) { |
| var expectation = expected[i]; |
| var timeMs = Math.round(expectation[0] * 1000); |
| if (!checks[timeMs]) |
| checks[timeMs] = []; |
| if (isTransitionsTest) |
| checks[timeMs].push(checkExpectedTransitionValue.bind(null, expected, i)); |
| else |
| checks[timeMs].push(checkExpectedValue.bind(null, expected, i)); |
| } |
| |
| var doPixelTest = Boolean(doPixelTest); |
| useResultElement = doPixelTest; |
| |
| if (window.testRunner) { |
| if (doPixelTest) { |
| testRunner.dumpAsTextWithPixelResults(); |
| } else { |
| testRunner.dumpAsText(); |
| } |
| testRunner.waitUntilDone(); |
| } |
| |
| var started = false; |
| |
| function begin() { |
| if (!started) { |
| log('First ' + event + ' event fired'); |
| started = true; |
| if (trigger) { |
| trigger(); |
| document.documentElement.offsetTop |
| } |
| waitForAnimationsToStart(function() { |
| log('Finished waiting for animations to start'); |
| animStartTime = performance.now(); |
| startTest(checks); |
| }); |
| } |
| } |
| |
| var startTestImmediately = Boolean(startTestImmediately); |
| if (startTestImmediately) { |
| begin(); |
| } else { |
| var target = isTransitionsTest ? window : document; |
| var event = isTransitionsTest ? 'load' : 'webkitAnimationStart'; |
| target.addEventListener(event, begin, false); |
| } |
| } |
| |
| /* This is the helper function to run transition tests: |
| |
| Test page requirements: |
| - The body must contain an empty div with id "result" |
| - Call this function directly from the <script> inside the test page |
| |
| Function parameters: |
| expected [required]: an array of arrays defining a set of CSS properties that must have given values at specific times (see below) |
| trigger [optional]: a function to be executed just before the test starts (none by default) |
| callbacks [optional]: an object in the form {timeS: function} specifing callbacks to be made during the test |
| doPixelTest [optional]: whether to dump pixels during the test (false by default) |
| |
| Each sub-array must contain these items in this order: |
| - the time in seconds at which to snapshot the CSS property |
| - the id of the element on which to get the CSS property value |
| - the name of the CSS property to get [1] |
| - the expected value for the CSS property |
| - the tolerance to use when comparing the effective CSS property value with its expected value |
| |
| [1] If the CSS property name is "-webkit-transform", expected value must be an array of 1 or more numbers corresponding to the matrix elements, |
| or a string which will be compared directly (useful if the expected value is "none") |
| If the CSS property name is "-webkit-transform.N", expected value must be a number corresponding to the Nth element of the matrix |
| |
| */ |
| function runTransitionTest(expected, trigger, callbacks, doPixelTest) { |
| isTransitionsTest = true; |
| runAnimationTest(expected, callbacks, trigger, false, doPixelTest); |
| } |