diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index fa49a3e94e..e5b7bb4f8b 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -67,6 +67,7 @@ export class InjectedScript { _evaluator: SelectorEvaluatorImpl; private _stableRafCount: number; private _browserName: string; + onGlobalListenersRemoved = new Set<() => void>(); constructor(stableRafCount: number, browserName: string, customEngines: { name: string, engine: SelectorEngine}[]) { this._evaluator = new SelectorEvaluatorImpl(new Map()); @@ -95,6 +96,8 @@ export class InjectedScript { this._stableRafCount = stableRafCount; this._browserName = browserName; + + this._setupGlobalListenersRemovalDetection(); } eval(expression: string): any { @@ -769,6 +772,31 @@ export class InjectedScript { return error; } + private _setupGlobalListenersRemovalDetection() { + const customEventName = '__playwright_global_listeners_check__'; + + let seenEvent = false; + const handleCustomEvent = () => seenEvent = true; + window.addEventListener(customEventName, handleCustomEvent); + + new MutationObserver(entries => { + const newDocumentElement = entries.some(entry => Array.from(entry.addedNodes).includes(document.documentElement)); + if (!newDocumentElement) + return; + + // New documentElement - let's check whether listeners are still here. + seenEvent = false; + window.dispatchEvent(new CustomEvent(customEventName)); + if (seenEvent) + return; + + // Listener did not fire. Reattach the listener and notify. + window.addEventListener(customEventName, handleCustomEvent); + for (const callback of this.onGlobalListenersRemoved) + callback(); + }).observe(document, { childList: true }); + } + expect(progress: InjectedScriptProgress, element: Element, options: FrameExpectParams, elements: Element[]): { matches: boolean, received?: any } { const injected = progress.injectedScript; const expression = options.expression; diff --git a/packages/playwright-core/src/server/supplements/injected/recorder.ts b/packages/playwright-core/src/server/supplements/injected/recorder.ts index 05d44bb2b1..fc14a197fd 100644 --- a/packages/playwright-core/src/server/supplements/injected/recorder.ts +++ b/packages/playwright-core/src/server/supplements/injected/recorder.ts @@ -116,17 +116,14 @@ export class Recorder { this._glassPaneShadow.appendChild(styleElement); this._refreshListenersIfNeeded(); - setInterval(() => { - this._refreshListenersIfNeeded(); - if (params.isUnderTest && !(this as any)._reportedReadyForTest) { - (this as any)._reportedReadyForTest = true; - console.error('Recorder script ready for test'); - } - }, 500); + injectedScript.onGlobalListenersRemoved.add(() => this._refreshListenersIfNeeded()); + globalThis._playwrightRefreshOverlay = () => { this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console }; globalThis._playwrightRefreshOverlay(); + if (params.isUnderTest) + console.error('Recorder script ready for test'); } private _refreshListenersIfNeeded() {