diff options
Diffstat (limited to 'layout/generic/test/test_scroll_behavior.html')
-rw-r--r-- | layout/generic/test/test_scroll_behavior.html | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/layout/generic/test/test_scroll_behavior.html b/layout/generic/test/test_scroll_behavior.html new file mode 100644 index 000000000..4508e84c3 --- /dev/null +++ b/layout/generic/test/test_scroll_behavior.html @@ -0,0 +1,309 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Tests for CSSOM-View Smooth-Scroll DOM API Methods and MSD Animation</title> + <style> + #scroll_behavior_test_body { + width: 100000px; + height: 100000px; + } + .scroll_to_target { + position: absolute; + left: 20000px; + top: 10000px; + width: 200px; + height: 200px; + background-color: rgb(0, 0, 255); + } + </style> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/paint_listener.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + SimpleTest.waitForExplicitFinish(); + + function clamp(val, minVal, maxVal) { + return Math.max(minVal, Math.min(maxVal, val)); + } + + window.addEventListener("load", function(event) { + if (event.target != document) + return; + + // See bug 1062609 - these tests do not work with APZ yet. If APZ is + // enabled, end the tests early. + if (SpecialPowers.getBoolPref("layers.async-pan-zoom.enabled")) { + todo(false, "This test does not yet work with APZ."); + SimpleTest.finish(); + return; + } + + SpecialPowers.pushPrefEnv( + { 'set': [['layout.css.scroll-behavior.enabled', true]] }, + function () { + testScrollBehaviorInterruption(function() { + testScrollBehaviorFramerate(function() { + window.scrollTo(0,0); + SimpleTest.finish(); + }); + }); + } + ); + }, false); + + + function testScrollBehaviorInterruption(nextTest) { + // Take control of refresh driver + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0); + waitForAllPaintsFlushed(function() { + + window.scrollTo(10, 9); + ok(window.scrollX == 10 && window.scrollY == 9, + "instant scroll-behavior must be synchronous when setting initial position"); + + window.scrollTo(15, 16); + ok(window.scrollX == 15 && window.scrollY == 16, + "instant scroll-behavior must be synchronous when setting new position"); + + window.scrollTo({left: 100, top: 200, behavior: 'smooth'}); + ok(window.scrollX == 15 && window.scrollY == 16, + "smooth scroll-behavior must be asynchronous"); + + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(100); + waitForAllPaintsFlushed(function() { + ok(window.scrollX != 15 && window.scrollY != 16 + && window.scrollX != 100 && window.scrollY != 200, + "smooth scroll-behavior must be triggered by window.scrollTo"); + + window.scrollTo(50, 52); + ok(window.scrollX == 50 && window.scrollY == 52, + "instant scroll-behavior must interrupt smooth scroll-behavior animation"); + + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(100); + waitForAllPaintsFlushed(function() { + ok(window.scrollX == 50 && window.scrollY == 52, + "smooth scroll-behavior animation must stop after being interrupted"); + + // Release control of refresh driver + SpecialPowers.DOMWindowUtils.restoreNormalRefresh(); + waitForAllPaintsFlushed(nextTest); + }); + }); + }); + } + + function testScrollBehaviorFramerate(nextTest) { + /** + * CSSOM-View scroll-behavior smooth scroll animations must produce the + * same results indendently of frame-rate: + * + * - Reference samples of scroll position for each frame are captured from + * a smooth scroll at 120fps for variations in X-Distance, Y-Distance. + * - Test samples are captured from an animation with the same parameters + * at varying framerates. + * - Variance in position at each sampled interval is compared to the + * 120fps reference. To pass the test, the position of each test + * sample must match the reference position with a tolerance of one test + * sample frame's range of motion. This range of motion is calculated + * by the position delta of the reference samples one test frame duration + * before and after. + * - The duration of the reference sample animation and the test sample + * animation must match within 1 frame to pass the test. + * - The simulation driving the animation must converge and stop on the + * destination position for the test to pass. + */ + + // Use 120hz for reference samples + var referenceFrameRate = 120; + + var frameRates = [ 13, 60 ]; + var deltas = [ {x: 0, y: 0}, + {x: 1, y: 100}, + {x: -100, y: 50000} ]; + + var deltaIndex = 0; + + function testDeltas() { + if(deltaIndex >= deltas.length) { + nextTest(); + return; + } + var deltaX = deltas[deltaIndex].x; + var deltaY = deltas[deltaIndex].y; + deltaIndex++; + + // startX and startY must be at least as big as the greatest negative + // number in the deltas array in order to prevent the animation from + // being interrupted by scroll range boundaries. + var startX = 1000; + var startY = 1000; + var endX = startX + deltaX; + var endY = startY + deltaY; + var referenceTimeStep = Math.floor(1000 / referenceFrameRate); + + sampleAnimation(startX, startY, endX, endY, + referenceTimeStep, function(refSamples) { + + var referenceDuration = refSamples.length * referenceTimeStep; // ms + + var frameRateIndex=0; + + function testFrameRate() { + if(frameRateIndex < frameRates.length) { + var frameRate = frameRates[frameRateIndex++]; + var testTimeStep = Math.floor(1000 / frameRate); + + sampleAnimation(startX, startY, endX, endY, + testTimeStep, function(testSamples) { + var testDuration = testSamples.length * testTimeStep; // ms + + // Variance in duration of animation must be accurate to within one + // frame interval + var durationVariance = Math.max(0, Math.abs(testDuration - referenceDuration) - testTimeStep); + is(durationVariance, 0, 'Smooth scroll animation duration must not ' + + 'be framerate dependent at deltaX: ' + deltaX + ', deltaY: ' + + deltaY + ', frameRate: ' + frameRate + 'fps'); + + var maxVariance = 0; + testSamples.forEach(function(sample, sampleIndex) { + + var testToRef = refSamples.length / testSamples.length; + var refIndexThisFrame = clamp(Math.floor(sampleIndex * testToRef), + 0, refSamples.length - 1); + var refIndexPrevFrame = clamp(Math.floor((sampleIndex - 1) * testToRef), + 0, refSamples.length - 1); + var refIndexNextFrame = clamp(Math.floor((sampleIndex + 1) * testToRef), + 0, refSamples.length - 1); + + var refSampleThisFrame = refSamples[refIndexThisFrame]; + var refSamplePrevFrame = refSamples[refIndexPrevFrame]; + var refSampleNextFrame = refSamples[refIndexNextFrame]; + + var refXMin = Math.min(refSamplePrevFrame[0], + refSampleThisFrame[0], + refSampleNextFrame[0]); + + var refYMin = Math.min(refSamplePrevFrame[1], + refSampleThisFrame[1], + refSampleNextFrame[1]); + + var refXMax = Math.max(refSamplePrevFrame[0], + refSampleThisFrame[0], + refSampleNextFrame[0]); + + var refYMax = Math.max(refSamplePrevFrame[1], + refSampleThisFrame[1], + refSampleNextFrame[1]); + + // Varience is expected to be at most 1 pixel beyond the range, + // due to integer rounding of pixel position. + var positionTolerance = 1; // 1 pixel + + maxVariance = Math.max(maxVariance, + refXMin - sample[0] - positionTolerance, + sample[0] - refXMax - positionTolerance, + refYMin - sample[1] - positionTolerance, + sample[1] - refYMax - positionTolerance); + }); + + is(maxVariance, 0, 'Smooth scroll animated position must not be ' + + 'framerate dependent at deltaX: ' + deltaX + ', deltaY: ' + + deltaY + ', frameRate: ' + frameRate + 'fps'); + testFrameRate(); + }); + } else { + waitForAllPaintsFlushed(testDeltas); + } + } + + testFrameRate(); + + }); + } + + testDeltas(); + + } + + function sampleAnimation(startX, startY, endX, endY, timeStep, callback) { + // The animation must be stopped at the destination position for + // minStoppedFrames consecutive frames to detect that the animation has + // completed. + var minStoppedFrames = 15; // 15 frames + + // In case the simulation fails to converge, the test will time out after + // processing maxTime milliseconds of animation. + var maxTime = 10000; // 10 seconds + + var positionSamples = []; + + var frameCountAtDestination = 0; + + // Take control of refresh driver so we can synthesize + // various frame rates + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0); + waitForAllPaintsFlushed(function() { + + window.scrollTo(startX, startY); + window.scrollTo({left: endX, top: endY, behavior: 'smooth'}); + + var currentTime = 0; // ms + + function advanceTime() { + if(currentTime < maxTime && frameCountAtDestination < 15) { + + positionSamples.push([window.scrollX, window.scrollY]); + + currentTime += timeStep; + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(timeStep); + waitForAllPaintsFlushed(function() { + if (window.scrollX == endX && window.scrollY == endY) { + frameCountAtDestination++; + } else { + frameCountAtDestination = 0; + } + + advanceTime(); + }); + + } else { + isnot(frameCountAtDestination, 0, + 'Smooth scrolls must always end at their destination ' + + 'unless they are interrupted, at deltaX: ' + + (endX - startX) + ', deltaY: ' + (endY - startY)); + + window.scrollTo(0, 0); + + // Release control of refresh driver + SpecialPowers.DOMWindowUtils.restoreNormalRefresh(); + + waitForAllPaintsFlushed(function() { + + // We must not include the duplicated frames at the animation + // destination as the tests are dependant on the total duration of + // the animation to be accurate. + positionSamples.splice(1 - minStoppedFrames, + minStoppedFrames - 1); + + callback(positionSamples); + }); + } + } + + advanceTime(); + + }); + } + + </script> +</head> +<body> +<pre id="test"> +</pre> + +<div id="scroll_behavior_test_body"> + <div id="scroll_to_target" class="scroll_to_target"></div> +</body> +</html> |