[feat]: add timeout support to locator resolvers - #3023
Open
seanmcguire12 wants to merge 4 commits into
Open
seanmcguire12 wants to merge 4 commits into
seanmcguire12 wants to merge 4 commits into
Conversation
|
seanmcguire12
changed the base branch from
main
to
add-locator-operation-context
September 23, 2026 20:06
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 17 files
Architecture diagram
sequenceDiagram
participant Client as Stagehand Client SDK
participant Locator as Locator (user-facing)
participant Progress as Progress (deadline context)
participant DeepLoc as DeepLocatorDelegate
participant FrameLoc as FrameLocator
participant Frame as Frame
participant SelResolver as FrameSelectorResolver
participant ExecCtx as ExecutionContextRegistry
participant CDPSession as CDPSession (browser)
participant Page as Page (frame registry)
Note over Client,Page: Locator Resolution with Shared Deadline
Client->>Locator: locator(selector).resolveNode(progress)
Note over Locator,Progress: NEW: Optional Progress context shared across all steps
Locator->>Progress: throwIfStopped()
alt No progress provided
Locator->>CDPSession: Runtime.enable + DOM.enable (no deadline)
else Progress provided
Locator->>Progress: run("enabling runtime", ...)
Progress-->>Locator: step completed within budget
Locator->>Progress: run("enabling DOM", ...)
Progress-->>Locator: step completed within budget
end
Locator->>SelResolver: resolveAtIndex(query, index, progress)
SelResolver->>Progress: throwIfStopped()
SelResolver->>ExecCtx: waitForLocatorWorld(session, frameId, 1000, progress)
Note over ExecCtx,CDPSession: Readiness wait with shared deadline
loop While locator world not ready
ExecCtx->>ExecCtx: Check cached extension/fallback world
alt World not cached
ExecCtx->>CDPSession: Runtime.enable (bounded by progress)
ExecCtx->>CDPSession: Runtime.evaluate (inspect helpers)
Note over ExecCtx: Each probe bounded by remaining budget
alt Timeout expired
ExecCtx->>Progress: throwIfStopped() -> TimeoutError
end
end
end
SelResolver->>SelResolver: resolveElements(helper, value, limit, progress)
loop For each match index
SelResolver->>Progress: run("evaluating selector", ...)
SelResolver->>CDPSession: Runtime.evaluate (resolve nth selector)
CDPSession-->>SelResolver: objectId
SelResolver->>CDPSession: DOM.requestNode (resolve nodeId)
alt Multiple matches resolved
SelResolver->>SelResolver: Release non-selected nodes via cleanup()
end
end
Note over DeepLoc,FrameLoc: Deep XPath / Frame traversal with shared deadline
Client->>DeepLoc: deepLocatorThroughIframes(page, root, xpath, progress)
DeepLoc->>DeepLoc: planDeepXPathTarget(xpath)
Note over DeepLoc: CHANGED: Trailing iframe in XPath stays in parent frame
alt XPath contains iframe hops
DeepLoc->>FrameLoc: frameLocator(iframeSelector).resolveFrame(progress)
FrameLoc->>Progress: throwIfStopped()
FrameLoc->>FrameLoc: Resolve parent frame (recursive with progress)
FrameLoc->>Frame: locator(iframeSelector).resolveNode(progress)
Frame->>CDPSession: DOM.describeNode (bounded by progress)
FrameLoc->>Page: listDirectChildFrameIdsFromRegistry(progress)
loop For each child frame
FrameLoc->>CDPSession: DOM.getFrameOwner (bounded by progress)
alt Match found
FrameLoc->>ExecCtx: waitForLocatorWorld(childSession, childFrameId, 200, progress, retry=false)
Note over FrameLoc,ExecCtx: Re-checks session ownership between attempts
alt OOPIF ownership changed
FrameLoc->>ExecCtx: Retry with new session
end
end
end
alt Deadline expired
FrameLoc->>Progress: TimeoutError
Progress->>FrameLoc: cleanup() releases iframe objectId
end
end
Note over Locator,Progress: Edge: late results / cleanup after timeout
alt Timeout fires while command pending
Progress->>Progress: Abort with TimeoutError
CDPSession-->>SelResolver: Late result arrives
SelResolver->>Progress: cleanup() releases late objectId
Note over SelResolver: Resolution does not resume post-timeout
end
Locator-->>Client: Resolved node {objectId, nodeId} or TimeoutError
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
seanmcguire12
force-pushed
the
share-operation-context-in-locator-helpers
branch
from
September 23, 2026 21:11
8366982 to
8a7192c
Compare
seanmcguire12
added this pull request to stack #3025
September 23, 2026 21:12
seanmcguire12
force-pushed
the
share-operation-context-in-locator-helpers
branch
from
September 25, 2026 22:06
8a7192c to
d82c7f7
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
why
resolving a the node/object ID that a
locatorpoints to can involve crossing iframes, waiting for browser helpers, & looking up the element. these steps need to share the caller's deadline so each frame or retry cannot restart the timeout.this pr connects that deadline to node/object ID resolution. the next PR in this stack will connect
locatoractions & reads to these helpers. public parameters for setting timeouts will come in a later PR.note:
this PR also renames
LocatorOperationtoProgressfor claritywhat changed
locatorOperation.tstoprogress.ts,LocatorOperationtoProgress, &runLocatorOperation()torunWithProgress()because it will be used by callers beyond locators.0disables the deadline.test plan
added tests in
locatorResolution.test.tsusing controlled time & browser responses to verify:Summary by cubic
Adds a shared deadline to
locatorresolution so iframe hops, readiness waits, and element lookup all share the caller's timeout instead of each restarting it. Also fixes locator failures when entering still-loading iframes and correctly resolves XPaths that end at an iframe element.What changed
LocatorOperationtoProgressandrunLocatorOperation()torunWithProgress(); callers beyond locators now use it.Progressthrough frame traversal and element lookup; callers without one keep current behavior, and0disables the deadline.Bug fixes
iframe[n]in the parent frame as the target element instead of descending into the child document.Written for commit d82c7f7. Summary will update on new commits.