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