Skip to content

add support for shadow doms (open & closed mode) #954

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 12 commits into
base: main
Choose a base branch
from

Conversation

seanmcguire12
Copy link
Member

@seanmcguire12 seanmcguire12 commented Aug 9, 2025

why

what changed

  • This PR adds some injected JS which
    1. intercepts Element.prototype.attachShadow early and stashes closed mode shadow roots in a WeakMap
    2. provides a 'backdoor' for safely accessing these closed roots without mutating anything in the actual DOM
  • to access the 'backdoor', this PR adds a custom locator engine selectors.register('stagehand', …)
  • engine does a DFS over:
    • regular DOM nodes,
    • open shadow roots via el.shadowRoot,
    • closed roots via window.__stagehand__.getClosedRoot(el)
    • returns a regular playwright locator

note

  • all the logic here is behind the experimental flag in the stagehand constructor, so that we can give people access without breaking existing behaviour
  • this means that this feature is not available on the API (yet), and you'll need to set experimental: true in order to use it

test plan

  • added 8 different evals. They are primarily aimed at testing shadow dom interactions with the various types of shadow DOMs (open & closed mode) and iframes (OOPIFs & SPIFs)
  • will also run:
    • regression evals
    • act evals
    • extract evals
    • observe evals

Copy link

changeset-bot bot commented Aug 9, 2025

🦋 Changeset detected

Latest commit: fd7338a

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@seanmcguire12 seanmcguire12 force-pushed the sean/stg-659-add-support-for-shadow-doms branch from bd4a3ab to be7f776 Compare August 12, 2025 00:19
@seanmcguire12 seanmcguire12 marked this pull request as ready for review August 12, 2025 01:22
@seanmcguire12 seanmcguire12 added act These changes pertain to the act function extract These changes pertain to the extract function observe These changes pertain to the observe function targeted-extract These changes pertain to targeted extract labels Aug 12, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Summary

This PR introduces comprehensive Shadow DOM support to Stagehand, enabling interaction with elements inside both open and closed shadow roots. The implementation addresses multiple user-reported issues where Stagehand previously returned 'not-supported' errors for shadow DOM elements.

The solution uses a multi-layered approach: (1) JavaScript injection that intercepts Element.prototype.attachShadow early in the page lifecycle to capture closed shadow roots in a WeakMap, (2) a global window.__stagehand__ backdoor API providing safe access to closed shadow roots without DOM mutations, (3) a custom Playwright selector engine 'stagehand' that performs depth-first search traversal across regular DOM nodes, open shadow roots via el.shadowRoot, and closed roots via the backdoor API.

The feature integrates throughout the codebase by adding experimental flag support to all handlers (ActHandler, ExtractHandler, ObserveHandler), modifying XPath generation to include shadow root markers using '//' syntax, enhancing accessibility tree building to traverse shadow boundaries, and adding specialized error classes for shadow DOM failures. Eight comprehensive evaluation tests validate different combinations of shadow DOM modes (open/closed) with iframe contexts (OOPIF/SPIF).

The implementation is gated behind the experimental: true flag in the Stagehand constructor to prevent breaking existing behavior and is not yet available on the API. This architectural choice allows users to opt into the enhanced functionality while maintaining backward compatibility for production environments.

Confidence score: 3/5

  • This PR introduces significant complexity with experimental shadow DOM support that could cause issues if not thoroughly tested in production scenarios
  • Score reflects the experimental nature of the feature and potential edge cases in shadow DOM traversal, especially with the global window object modification and WeakMap approach
  • Pay close attention to lib/StagehandPage.ts where shadow DOM detection logic may incorrectly identify elements, and evaluate files that lack proper return statements for failure cases

17 files reviewed, 10 comments

Edit Code Review Bot Settings | Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
act These changes pertain to the act function extract These changes pertain to the extract function observe These changes pertain to the observe function targeted-extract These changes pertain to targeted extract
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant