Skip to content

Fix race condition in DOM settling detection #817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/StagehandPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,9 @@ ${scriptContent} \
* `_waitForSettledDom` waits until the DOM is settled, and therefore is
* ready for actions to be taken.
*
* **Definition of settled**
* **Definition of "settled"**
* • No in-flight network requests (except WebSocket / Server-Sent-Events).
* • That idle state lasts for at least **500 ms** (the quiet-window).
* • That idle state lasts for at least **500 ms** (the "quiet-window").
*
* **How it works**
* 1. Subscribes to CDP Network and Page events for the main target and all
Expand Down Expand Up @@ -520,6 +520,7 @@ ${scriptContent} \

let quietTimer: NodeJS.Timeout | null = null;
let stalledRequestSweepTimer: NodeJS.Timeout | null = null;
let isResolved = false; // Flag to prevent multiple resolutions

const clearQuiet = () => {
if (quietTimer) {
Expand All @@ -529,7 +530,7 @@ ${scriptContent} \
};

const maybeQuiet = () => {
if (inflight.size === 0 && !quietTimer)
if (inflight.size === 0 && !quietTimer && !isResolved)
quietTimer = setTimeout(() => resolveDone(), 500);
};

Expand Down Expand Up @@ -613,6 +614,10 @@ ${scriptContent} \
}, timeout);

const resolveDone = () => {
// Prevent multiple resolutions of the same Promise
if (isResolved) return;
isResolved = true;

client.off("Network.requestWillBeSent", onRequest);
client.off("Network.loadingFinished", onFinish);
client.off("Network.loadingFailed", onFinish);
Expand Down Expand Up @@ -990,7 +995,7 @@ ${scriptContent} \
if (msg.includes("does not have a separate CDP session")) {
// Re-use / create the top-level session instead
const rootSession = await this.getCDPClient(this.page);
// cache the alias so we dont try again for this frame
// cache the alias so we don't try again for this frame
this.cdpClients.set(target, rootSession);
return rootSession;
}
Expand Down