From cbb8a2fea8324a39b4013a7ec657d5e58424b5b5 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 14 Apr 2026 15:34:38 +0100 Subject: [PATCH] fix(clock): reset ticks on first install to avoid realTime drift Between `inject()` (which starts the realTime ticker) and the user's `install(time)` call, `_now.ticks` accumulates drift. Since `performance.now()` returns `ticks`, this drift leaked into `performance.now()` values observed by the page. Reset `ticks` to 0 on the first `install()` (when origin has not been set yet) so the monotonic counter starts from a pristine state. Exposed by the flaky test `library/page-clock.spec.ts:282 replaces global performance.timeOrigin` on Firefox. --- packages/injected/src/clock.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/injected/src/clock.ts b/packages/injected/src/clock.ts index e081a2c873379..69fe461163b8f 100644 --- a/packages/injected/src/clock.ts +++ b/packages/injected/src/clock.ts @@ -98,7 +98,7 @@ export class ClockController { install(time: number) { this._replayLogOnce(); - this._innerSetTime(asWallTime(time)); + this._innerInstall(asWallTime(time)); } setSystemTime(time: number) { @@ -136,6 +136,15 @@ export class ClockController { this._now.origin = this._now.time; } + private _innerInstall(time: WallTime) { + // On a fresh install, reset the monotonic counter so that drift + // accumulated by the realTime ticker before the user called install() + // does not leak into performance.now(). + if (this._now.origin < 0) + this._now.ticks = 0 as Ticks; + this._innerSetTime(time); + } + private _innerSetFixedTime(time: WallTime) { this._innerSetTime(time); this._now.isFixedTime = true; @@ -440,7 +449,7 @@ export class ClockController { lastLogTime = time; if (type === 'install') { - this._innerSetTime(asWallTime(param!)); + this._innerInstall(asWallTime(param!)); } else if (type === 'fastForward' || type === 'runFor') { this._advanceNow(shiftTicks(this._now.ticks, param!)); } else if (type === 'pauseAt') {