tor-browser

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

test_timeout_clamp.html (4995B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=1378586
      5 -->
      6 <head>
      7  <meta charset="utf-8">
      8  <title>Test for Bug 1378586</title>
      9  <script src="/tests/SimpleTest/SimpleTest.js"></script>
     10  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     11 </head>
     12 <body>
     13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1378586">Mozilla Bug 1378586</a>
     14 
     15 <script>
     16 SimpleTest.waitForExplicitFinish();
     17 
     18 // We need to clear our nesting level periodically.  We do this by firing
     19 // a postMessage() to get a runnable on the event loop without any setTimeout()
     20 // nesting.
     21 function clearNestingLevel() {
     22  return new Promise(resolve => {
     23    window.addEventListener('message', () => {
     24      resolve();
     25    }, {once: true});
     26    postMessage('done', '*');
     27  });
     28 }
     29 
     30 function delayByTimeoutChain(iterations) {
     31  return new Promise(resolve => {
     32    let count = 0;
     33    function tick() {
     34      count += 1;
     35      if (count >= iterations) {
     36        resolve();
     37        return;
     38      }
     39      setTimeout(tick, 0);
     40    }
     41    setTimeout(tick, 0);
     42  });
     43 }
     44 
     45 function delayByInterval(iterations) {
     46  return new Promise(resolve => {
     47    let count = 0;
     48    function tick() {
     49      count += 1;
     50      if (count >= iterations) {
     51        resolve();
     52      }
     53    }
     54    setInterval(tick, 0);
     55  });
     56 }
     57 
     58 function testNestedIntervals() {
     59  return new Promise(resolve => {
     60    const runCount = 100;
     61    let counter = 0;
     62    let intervalId = 0;
     63    let prevInitTime = performance.now();
     64    let totalTime = 0;
     65    function intervalCallback() {
     66      let now = performance.now();
     67      let delay = now - prevInitTime;
     68      totalTime += delay;
     69      prevInitTime = now;
     70      clearInterval(intervalId);
     71      if (++counter < runCount) {
     72        intervalId = setInterval(intervalCallback, 0);
     73      } else {
     74        // Delays should be clamped to 4ms after the initial calls, but allow
     75        // some fuzziness, so divide by 2.
     76        let expectedTime = runCount * 4 / 2;
     77        ok(totalTime > expectedTime, "Should not run callbacks too fast.");
     78        resolve();
     79      }
     80    }
     81 
     82    // Use the timeout value defined in the spec, 4ms.
     83    SpecialPowers.pushPrefEnv({ 'set': [["dom.min_timeout_value", 4]]},
     84                              intervalCallback);
     85  });
     86 }
     87 
     88 // Use a very long clamp delay to make it easier to measure the change
     89 // in automation.  Some of our test servers are very slow and noisy.
     90 const clampDelayMS = 10000;
     91 
     92 // We expect that we will clamp on the 5th callback.  This should
     93 // be the same for both setTimeout() chains and setInterval().
     94 const expectedClampIteration = 5;
     95 
     96 async function runTests() {
     97  // Things like pushPrefEnv() can use setTimeout() internally which may give
     98  // us a nesting level.  Clear the nesting level to start so this doesn't
     99  // confuse the test.
    100  await clearNestingLevel();
    101 
    102  // Verify a setTimeout() chain clamps correctly
    103  let start = performance.now();
    104  await delayByTimeoutChain(expectedClampIteration);
    105  let stop = performance.now();
    106  let delta = stop - start;
    107 
    108  ok(delta >= clampDelayMS, "setTimeout() chain clamped: " + stop + " - " + start + " = " + delta);
    109  ok(delta < (2*clampDelayMS), "setTimeout() chain did not clamp twice");
    110 
    111  await clearNestingLevel();
    112 
    113  // Verify setInterval() clamps correctly
    114  start = performance.now();
    115  await delayByInterval(expectedClampIteration);
    116  stop = performance.now();
    117  delta = stop - start;
    118 
    119  ok(delta >= clampDelayMS, "setInterval() clamped: " + stop + " - " + start + " = " + delta);
    120  ok(delta < (2*clampDelayMS), "setInterval() did not clamp twice");
    121 
    122  await clearNestingLevel();
    123 
    124  // Verify a setTimeout() chain will continue to clamp past the first
    125  // expected iteration.
    126  const expectedDelay = (1 + expectedClampIteration) * clampDelayMS;
    127 
    128  start = performance.now();
    129  await delayByTimeoutChain(2 * expectedClampIteration);
    130  stop = performance.now();
    131  delta = stop - start;
    132 
    133  ok(delta >= expectedDelay, "setTimeout() chain continued to clamp: " + stop + " - " + start + " = " + delta);
    134 
    135  await clearNestingLevel();
    136 
    137  // Verify setInterval() will continue to clamp past the first expected
    138  // iteration.
    139  start = performance.now();
    140  await delayByTimeoutChain(2 * expectedClampIteration);
    141  stop = performance.now();
    142  delta = stop - start;
    143 
    144  ok(delta >= expectedDelay, "setInterval() continued to clamp: " + stop + " - " + start + " = " + delta);
    145 
    146  await testNestedIntervals();
    147 
    148  SimpleTest.finish();
    149 }
    150 
    151 // It appears that it's possible to get unlucky with time jittering and fail this test.
    152 // If start is jittered upwards, everything executes very quickly, and delta has
    153 // a very high midpoint, we may have taken between 10 and 10.002 seconds to execute; but
    154 // it will appear to be 9.998. Turn off jitter (and add logging) to test this.
    155 SpecialPowers.pushPrefEnv({ 'set': [
    156  ["dom.min_timeout_value", clampDelayMS],
    157  ["privacy.resistFingerprinting.reduceTimerPrecision.jitter", false],
    158  ]}, runTests);
    159 </script>
    160 
    161 </body>
    162 </html>