Bug 1646799 - setInterval(..., 0) is not clamped, unlike setTimeout(..., 0), r=peterv

Differential Revision: https://phabricator.services.mozilla.com/D84395
This commit is contained in:
Olli Pettay 2020-07-24 15:29:07 +00:00
Родитель 08df665966
Коммит 9d7df9a34d
3 изменённых файлов: 38 добавлений и 13 удалений

Просмотреть файл

@ -299,8 +299,7 @@ bool TimeoutManager::IsInvalidFiringId(uint32_t aFiringId) const {
return !mFiringIdStack.Contains(aFiringId);
}
// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
// uses 5.
// The number of nested timeouts before we start clamping. HTML says 5.
#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5u
TimeDuration TimeoutManager::CalculateDelay(Timeout* aTimeout) const {

Просмотреть файл

@ -5960,12 +5960,8 @@ bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
// timeouts from repeatedly opening poups.
timeout->mPopupState = PopupBlocker::openAbused;
bool trackNestingLevel = !timeout->mIsInterval;
uint32_t nestingLevel;
if (trackNestingLevel) {
nestingLevel = TimeoutManager::GetNestingLevel();
TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
}
uint32_t nestingLevel = TimeoutManager::GetNestingLevel();
TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
const char* reason = GetTimeoutReasonString(timeout);
@ -6016,9 +6012,7 @@ bool nsGlobalWindowInner::RunTimeoutHandler(Timeout* aTimeout,
// point anyway, and the script context should have already reported
// the script error in the usual way - so we just drop it.
if (trackNestingLevel) {
TimeoutManager::SetNestingLevel(nestingLevel);
}
TimeoutManager::SetNestingLevel(nestingLevel);
mTimeoutManager->EndRunningTimeout(last_running_timeout);
timeout->mRunning = false;

Просмотреть файл

@ -56,6 +56,36 @@ function delayByInterval(iterations) {
});
}
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;
@ -92,7 +122,7 @@ async function runTests() {
await clearNestingLevel();
// Verfy a setTimeout() chain will continue to clamp past the first
// Verify a setTimeout() chain will continue to clamp past the first
// expected iteration.
const expectedDelay = (1 + expectedClampIteration) * clampDelayMS;
@ -105,7 +135,7 @@ async function runTests() {
await clearNestingLevel();
// Verfy setInterval() will continue to clamp past the first expected
// Verify setInterval() will continue to clamp past the first expected
// iteration.
start = performance.now();
await delayByTimeoutChain(2 * expectedClampIteration);
@ -114,6 +144,8 @@ async function runTests() {
ok(delta >= expectedDelay, "setInterval() continued to clamp: " + stop + " - " + start + " = " + delta);
await testNestedIntervals();
SimpleTest.finish();
}