diff --git a/src/server/dom.ts b/src/server/dom.ts index bff62e055d..a790fd8bd4 100644 --- a/src/server/dom.ts +++ b/src/server/dom.ts @@ -647,10 +647,10 @@ export class ElementHandle extends js.JSHandle { return; } if (state === 'stable') { - const rafCount = this._page._delegate.rafCountForStablePosition(); - const poll = await this._evaluateHandleInUtility(([injected, node, rafCount]) => { - return injected.waitForDisplayedAtStablePosition(node, rafCount, false /* waitForEnabled */); - }, rafCount); + const rafCount = this._page._delegate.rafCountForStablePosition(); + const poll = await this._evaluateHandleInUtility(([injected, node, rafOptions]) => { + return injected.waitForDisplayedAtStablePosition(node, rafOptions, false /* waitForEnabled */); + }, { rafCount, useTimeout: !!process.env.PW_USE_TIMEOUT_FOR_RAF }); const pollHandler = new InjectedScriptPollHandler(progress, poll); assertDone(throwRetargetableDOMError(await pollHandler.finish())); return; @@ -694,10 +694,10 @@ export class ElementHandle extends js.JSHandle { progress.log(` waiting for element to be visible, enabled and not moving`); else progress.log(` waiting for element to be visible and not moving`); - const rafCount = this._page._delegate.rafCountForStablePosition(); - const poll = this._evaluateHandleInUtility(([injected, node, { rafCount, waitForEnabled }]) => { - return injected.waitForDisplayedAtStablePosition(node, rafCount, waitForEnabled); - }, { rafCount, waitForEnabled }); + const rafCount = this._page._delegate.rafCountForStablePosition(); + const poll = this._evaluateHandleInUtility(([injected, node, { rafOptions, waitForEnabled }]) => { + return injected.waitForDisplayedAtStablePosition(node, rafOptions, waitForEnabled); + }, { rafOptions: { rafCount, useTimeout: !!process.env.PW_USE_TIMEOUT_FOR_RAF }, waitForEnabled }); const pollHandler = new InjectedScriptPollHandler(progress, await poll); const result = await pollHandler.finish(); if (waitForEnabled) diff --git a/src/server/injected/injectedScript.ts b/src/server/injected/injectedScript.ts index df10789f6a..f8034989d3 100644 --- a/src/server/injected/injectedScript.ts +++ b/src/server/injected/injectedScript.ts @@ -484,13 +484,13 @@ export class InjectedScript { input.dispatchEvent(new Event('change', { 'bubbles': true })); } - waitForDisplayedAtStablePosition(node: Node, rafCount: number, waitForEnabled: boolean): InjectedScriptPoll<'error:notconnected' | 'done'> { + waitForDisplayedAtStablePosition(node: Node, rafOptions: { rafCount: number, useTimeout?: boolean }, waitForEnabled: boolean): InjectedScriptPoll<'error:notconnected' | 'done'> { let lastRect: { x: number, y: number, width: number, height: number } | undefined; let counter = 0; let samePositionCounter = 0; let lastTime = 0; - return this.pollRaf((progress, continuePolling) => { + const predicate = (progress: InjectedScriptProgress, continuePolling: symbol) => { // First raf happens in the same animation frame as evaluation, so it does not produce // any client rect difference compared to synchronous call. We skip the synchronous call // and only force layout during actual rafs as a small optimisation. @@ -505,7 +505,7 @@ export class InjectedScript { // Drop frames that are shorter than 16ms - WebKit Win bug. const time = performance.now(); - if (rafCount > 1 && time - lastTime < 15) + if (rafOptions.rafCount > 1 && time - lastTime < 15) return continuePolling; lastTime = time; @@ -518,7 +518,7 @@ export class InjectedScript { ++samePositionCounter; else samePositionCounter = 0; - const isStable = samePositionCounter >= rafCount; + const isStable = samePositionCounter >= rafOptions.rafCount; const isStableForLogs = isStable || !lastRect; lastRect = rect; @@ -537,7 +537,11 @@ export class InjectedScript { else if (isDisabled) progress.logRepeating(` element is disabled - waiting...`); return continuePolling; - }); + }; + if (rafOptions.useTimeout) + return this.pollInterval(16, predicate); + else + return this.pollRaf(predicate); } checkHitTargetAt(node: Node, point: { x: number, y: number }): 'error:notconnected' | 'done' | { hitTargetDescription: string } {