tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_scroll_behavior.html (9995B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <meta charset="utf-8">
      5  <title>Tests for CSSOM-View Smooth-Scroll DOM API Methods and MSD Animation</title>
      6  <style>
      7    #scroll_behavior_test_body {
      8      width: 100000px;
      9      height: 100000px;
     10    }
     11    .scroll_to_target {
     12      position: absolute;
     13      left: 20000px;
     14      top: 10000px;
     15      width: 200px;
     16      height: 200px;
     17      background-color: rgb(0, 0, 255);
     18    }
     19  </style>
     20  <script src="/tests/SimpleTest/SimpleTest.js"></script>
     21  <script src="/tests/SimpleTest/paint_listener.js"></script>
     22  <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
     23  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     24  <script type="application/javascript">
     25 
     26  SimpleTest.waitForExplicitFinish();
     27 
     28  function clamp(val, minVal, maxVal) {
     29    return Math.max(minVal, Math.min(maxVal, val));
     30  }
     31 
     32  window.addEventListener("load", async function(event) {
     33    if (event.target != document) {
     34      return;
     35    }
     36    await testScrollBehaviorInterruption();
     37    await testScrollBehaviorFramerate();
     38    window.scrollTo(0,0);
     39    SimpleTest.finish();
     40  });
     41 
     42  async function testScrollBehaviorInterruption() {
     43    // Take control of refresh driver
     44    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0);
     45    await promiseApzFlushedRepaints();
     46 
     47    window.scrollTo(10, 9);
     48    ok(window.scrollX == 10 && window.scrollY == 9,
     49      "instant scroll-behavior must be synchronous when setting initial position");
     50 
     51    window.scrollTo(15, 16);
     52    ok(window.scrollX == 15 && window.scrollY == 16,
     53      "instant scroll-behavior must be synchronous when setting new position");
     54 
     55    window.scrollTo({left: 100, top: 200, behavior: 'smooth'});
     56    ok(window.scrollX == 15 && window.scrollY == 16,
     57      "smooth scroll-behavior must be asynchronous");
     58 
     59    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(100);
     60    await promiseApzFlushedRepaints();
     61 
     62    ok(window.scrollX != 15 && window.scrollY != 16
     63       && window.scrollX != 100 && window.scrollY != 200,
     64      "smooth scroll-behavior must be triggered by window.scrollTo");
     65 
     66    window.scrollTo(50, 52);
     67    ok(window.scrollX == 50 && window.scrollY == 52,
     68      "instant scroll-behavior must interrupt smooth scroll-behavior animation");
     69 
     70    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(100);
     71    await promiseApzFlushedRepaints();
     72 
     73    ok(window.scrollX == 50 && window.scrollY == 52,
     74      "smooth scroll-behavior animation must stop after being interrupted");
     75 
     76    // Release control of refresh driver
     77    SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
     78    await promiseApzFlushedRepaints();
     79  }
     80 
     81  async function testScrollBehaviorFramerate() {
     82    /**
     83     *  CSSOM-View scroll-behavior smooth scroll animations must produce the
     84     *  same results indendently of frame-rate:
     85     *
     86     *   - Reference samples of scroll position for each frame are captured from
     87     *     a smooth scroll at 120fps for variations in X-Distance, Y-Distance.
     88     *   - Test samples are captured from an animation with the same parameters
     89     *     at varying framerates.
     90     *   - Variance in position at each sampled interval is compared to the
     91     *     120fps reference.  To pass the test, the position of each test
     92     *     sample must match the reference position with a tolerance of one test
     93     *     sample frame's range of motion.  This range of motion is calculated
     94     *     by the position delta of the reference samples one test frame duration
     95     *     before and after.
     96     *   - The duration of the reference sample animation and the test sample
     97     *     animation must match within 1 frame to pass the test.
     98     *   - The simulation driving the animation must converge and stop on the
     99     *     destination position for the test to pass.
    100     */
    101 
    102    // Use 120hz for reference samples
    103    var referenceFrameRate = 120;
    104 
    105    var frameRates = [ 13, 60 ];
    106    var deltas = [ {x: 0, y: 0},
    107                   {x: 1, y: 100},
    108                   {x: -100, y: 50000} ];
    109 
    110    for (var deltaIndex = 0; deltaIndex < deltas.length; deltaIndex++) {
    111      var deltaX = deltas[deltaIndex].x;
    112      var deltaY = deltas[deltaIndex].y;
    113 
    114      // startX and startY must be at least as big as the greatest negative
    115      // number in the deltas array in order to prevent the animation from
    116      // being interrupted by scroll range boundaries.
    117      var startX = 1000;
    118      var startY = 1000;
    119      var endX = startX + deltaX;
    120      var endY = startY + deltaY;
    121      var referenceTimeStep = Math.floor(1000 / referenceFrameRate);
    122 
    123      let refSamples = await sampleAnimation(startX, startY, endX, endY,
    124                      referenceTimeStep);
    125 
    126      var referenceDuration = refSamples.length * referenceTimeStep; // ms
    127 
    128      for (var frameRateIndex = 0; frameRateIndex < frameRates.length; frameRateIndex++) {
    129        var frameRate = frameRates[frameRateIndex];
    130 
    131        var testTimeStep = Math.floor(1000 / frameRate);
    132 
    133        let testSamples = await sampleAnimation(startX, startY, endX, endY,
    134                        testTimeStep);
    135        var testDuration = testSamples.length * testTimeStep; // ms
    136 
    137        // Variance in duration of animation must be accurate to within one
    138        // frame interval
    139        var durationVariance = Math.max(0, Math.abs(testDuration - referenceDuration) - testTimeStep);
    140        is(durationVariance, 0, 'Smooth scroll animation duration must not '
    141           + 'be framerate dependent at deltaX: ' + deltaX + ', deltaY: '
    142           + deltaY + ', frameRate: ' + frameRate + 'fps');
    143 
    144        var maxVariance = 0;
    145        testSamples.forEach(function(sample, sampleIndex) {
    146 
    147          var testToRef = refSamples.length / testSamples.length;
    148          var refIndexThisFrame = clamp(Math.floor(sampleIndex * testToRef),
    149                                        0, refSamples.length - 1);
    150          var refIndexPrevFrame = clamp(Math.floor((sampleIndex - 1) * testToRef),
    151                                        0, refSamples.length - 1);
    152          var refIndexNextFrame = clamp(Math.floor((sampleIndex + 1) * testToRef),
    153                                        0, refSamples.length - 1);
    154 
    155          var refSampleThisFrame = refSamples[refIndexThisFrame];
    156          var refSamplePrevFrame = refSamples[refIndexPrevFrame];
    157          var refSampleNextFrame = refSamples[refIndexNextFrame];
    158 
    159          var refXMin = Math.min(refSamplePrevFrame[0],
    160                                refSampleThisFrame[0],
    161                                refSampleNextFrame[0]);
    162 
    163          var refYMin = Math.min(refSamplePrevFrame[1],
    164                                refSampleThisFrame[1],
    165                                refSampleNextFrame[1]);
    166 
    167          var refXMax = Math.max(refSamplePrevFrame[0],
    168                                refSampleThisFrame[0],
    169                                refSampleNextFrame[0]);
    170 
    171          var refYMax = Math.max(refSamplePrevFrame[1],
    172                                refSampleThisFrame[1],
    173                                refSampleNextFrame[1]);
    174 
    175          // Varience is expected to be at most 1 pixel beyond the range,
    176          // due to integer rounding of pixel position.
    177          var positionTolerance = 1; // 1 pixel
    178 
    179          maxVariance = Math.max(maxVariance,
    180                                 refXMin - sample[0] - positionTolerance,
    181                                 sample[0] - refXMax - positionTolerance,
    182                                 refYMin - sample[1] - positionTolerance,
    183                                 sample[1] - refYMax - positionTolerance);
    184        });
    185 
    186        is(maxVariance, 0, 'Smooth scroll animated position must not be '
    187           + 'framerate dependent at deltaX: ' + deltaX + ', deltaY: '
    188           + deltaY + ', frameRate: ' + frameRate + 'fps');
    189      }
    190 
    191      await promiseApzFlushedRepaints();
    192    }
    193  }
    194 
    195  async function sampleAnimation(startX, startY, endX, endY, timeStep) {
    196    // The animation must be stopped at the destination position for
    197    // minStoppedFrames consecutive frames to detect that the animation has
    198    // completed.
    199    var minStoppedFrames = 15; // 15 frames
    200 
    201    // In case the simulation fails to converge, the test will time out after
    202    // processing maxTime milliseconds of animation.
    203    var maxTime = 10000; // 10 seconds
    204 
    205    var positionSamples = [];
    206 
    207    var frameCountAtDestination = 0;
    208 
    209    // Take control of refresh driver so we can synthesize
    210    // various frame rates
    211    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0);
    212    await promiseApzFlushedRepaints();
    213 
    214    window.scrollTo(startX, startY);
    215    window.scrollTo({left: endX, top: endY, behavior: 'smooth'});
    216 
    217    var currentTime = 0; // ms
    218 
    219    while (currentTime < maxTime && frameCountAtDestination < 15) {
    220      positionSamples.push([window.scrollX, window.scrollY]);
    221 
    222      currentTime += timeStep;
    223      SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(timeStep);
    224      await promiseApzFlushedRepaints();
    225      if (window.scrollX == endX && window.scrollY == endY) {
    226        frameCountAtDestination++;
    227      } else {
    228        frameCountAtDestination = 0;
    229      }
    230    }
    231 
    232    isnot(frameCountAtDestination, 0,
    233          'Smooth scrolls must always end at their destination '
    234          + 'unless they are interrupted, at deltaX: '
    235          + (endX - startX) + ', deltaY: ' + (endY - startY));
    236 
    237    window.scrollTo(0, 0);
    238 
    239    // Release control of refresh driver
    240    SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
    241 
    242    await promiseApzFlushedRepaints();
    243 
    244    // We must not include the duplicated frames at the animation
    245    // destination as the tests are dependant on the total duration of
    246    // the animation to be accurate.
    247    positionSamples.splice(1 - minStoppedFrames,
    248                           minStoppedFrames - 1);
    249 
    250    return positionSamples;
    251  }
    252 
    253  </script>
    254 </head>
    255 <body>
    256 <pre id="test">
    257 </pre>
    258 
    259 <div id="scroll_behavior_test_body">
    260      <div id="scroll_to_target" class="scroll_to_target"></div>
    261 </body>
    262 </html>