fix(driver): make fast visibility shadow-DOM aware#33737
Open
mschile wants to merge 6 commits intomschile/pensive-rubin-df92b9from
Open
fix(driver): make fast visibility shadow-DOM aware#33737mschile wants to merge 6 commits intomschile/pensive-rubin-df92b9from
mschile wants to merge 6 commits intomschile/pensive-rubin-df92b9from
Conversation
Resolves #33046 The fast visibility algorithm used document.elementFromPoint() and Element.contains() in visibleAtPoint(), neither of which crosses shadow boundaries — so subjects inside a shadow root were either missed at sample points or compared against a host they don't light-tree-contain. Pierce nested shadow roots from the document-level hit using the existing getShadowElementFromPoint() helper, and replace contains() with a shadow-aware ancestor walk via findParent(), which crosses shadow boundaries via getRootNode().host. Enables the shadow-DOM visibility suite under fast mode (previously gated to legacy only). 8 tests where the fast multi-sample and legacy center-point algorithms inherently diverge for shadow subjects (cover detection with covers narrower than the underneath, pointer-events: none across host boundaries, complex out-of-bounds overflow) are scoped-skipped under fast via an itSkipFast helper, with a comment pointing back to the issue for fixture-level follow-up.
`hasClippingAncestor` was walking via `parentElement`, which returns null at shadow root boundaries — so a shadow descendant's `overflow: hidden` ancestor in the host's light tree was invisible to the check. The clipping-ancestor guard then incorrectly returned false, and `scrollIntoView` ran on a subject that should have been left clipped, exposing content the test author intentionally hid. Switch to `getParentNode` (already used elsewhere in the driver for shadow-aware traversal) so the parent walk crosses shadow boundaries via `getRootNode().host`. Fixes the three driver-integration regressions surfaced by enabling the shadow-DOM visibility suite under fast mode: - is hidden when parent outside of shadow dom overflow hidden and out of bounds below - is hidden when parent outside of shadow dom overflow hidden-y and out of bounds - is hidden when parent outside of shadow dom has overflow scroll and out of bounds
cypress
|
||||||||||||||||||||||||||||
| Project |
cypress
|
| Branch Review |
mschile/pensive-black-034995
|
| Run status |
|
| Run duration | 18m 30s |
| Commit |
|
| Committer | Matthew Schile |
| View all properties for this run ↗︎ | |
| Test results | |
|---|---|
|
|
0
|
|
|
9
|
|
|
1112
|
|
|
0
|
|
|
24926
|
| View all changes introduced in this branch ↗︎ | |
Warning
No Report: Something went wrong and we could not generate a report for the Application Quality products.
…t visibility The clipping-ancestor guard in `fastIsHidden` only skipped programmatic scroll-into-view for ancestors with `overflow: hidden` or `clip`, treating `scroll` and `auto` as scrollable enough to expose otherwise out-of-bounds content. Per Cypress visibility semantics — "is the user seeing this right now?" — content that is clipped because the user has not scrolled the container should report hidden, the same way it does for `overflow: hidden`. Treating `scroll` and `auto` the same way fixes the Firefox shadow-DOM regression (the existing "is hidden when parent outside of shadow dom has overflow scroll and out of bounds" test passes in Chrome only by chance, because Chrome's `scrollIntoView` on an absolutely-positioned descendant of an overflow:scroll container behaves differently than Firefox's). Pull the four overflow values into a single set so the relationship is explicit.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8a3dea9. Configure here.
The previous clipping-ancestor guard skipped programmatic scrolling whenever any ancestor on the off-screen axis had clipping `overflow`. That was too coarse: a subject merely below the fold of an `overflow: hidden` (or `clip`/`scroll`/`auto`) container is not intentionally clipped — it is just outside the viewport — and `scrollIntoView` brings it into view without exposing anything the author wanted to hide. Conversely, a subject positioned outside the ancestor's box (e.g. `position: absolute; bottom: -100px`) is what the guard meant to protect. Replace `hasClippingAncestor` with `isClippedByAncestor`: it only reports clipping when the subject's rect actually falls outside the ancestor's rect on the same axis the subject is off-screen. Treats `scroll`/`auto` the same as `hidden`/`clip` because the user has not scrolled the container, so out-of-bounds content is hidden right now — this addresses the Cursor Bugbot concern by virtue of the in-bounds check, not by excluding scrollable values. Concrete effects: - Unblocks three previously-skipped shadow DOM overflow tests where the subject is in-bounds of an overflow ancestor but the ancestor is below the fold. - Keeps the Firefox CI fix for `overflow: scroll` out-of-bounds shadow subjects (they remain reported hidden). - No change in behavior for non-shadow visibility cases that already passed: the guard still only matters when the subject is outside the viewport, and only fires when the subject is also outside the clipping ancestor.
…ed under fast The earlier comment said fixtures were "authored to legacy semantics," which is true but doesn't pinpoint *why* fast cannot match. Replace with the actual mechanism for each remaining skip: the cover-detection mismatch (multi-sample corners vs center-only), `pointer-events: none` (browsers skip such elements in `elementFromPoint`, so no sample ever lands on the subject), and a clipping ancestor between the subject and its containing block (legacy's `canClipContent` ignores it via `offsetParent` rules). The corresponding non-shadow scenarios either avoid these patterns or aren't exercised by `visibility.cy.ts` — documented so the next person to look at the skips understands what work the fix would actually require.
The previous commit treated `overflow: scroll`/`auto` ancestors as clipping when the subject was outside their box. That broke an existing fast-mode test: setting `overflow-x: hidden` on body causes computed `overflow-y` to become `auto` (per CSS spec), which then fired the clipping check on the off-screen axis and prevented the scroll-into-view that elements below the fold need. `<body>` and `<html>` are the page's scroll container, not real clipping containers — programmatic scroll there is exactly what the user would do to bring an element below the fold into view, and it does not surface anything the test author intentionally hid. Skip them while walking clipping ancestors so the orthogonal-axis case works again. Local non-shadow visibility now reports 136 passing / 0 failing.
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.

Additional details
The fast visibility algorithm used
document.elementFromPoint()andElement.contains()invisibleAtPoint(), neither of which crosses shadow boundaries — so subjects inside a shadow root were either missed at sample points (host's box doesn't include the sample coord) or compared against a host they don't light-tree-contain. The wholevisibility_shadow_dom.cy.tssuite was gated tolegacymode only as a result.This PR pierces nested shadow roots from the document-level hit using the existing
getShadowElementFromPoint()helper, and replacescontains()with a shadow-aware ancestor walk viafindParent()(which crosses shadow boundaries viagetRootNode().host). Both helpers are pre-existing — no new utility code.The shadow-DOM visibility suite is now enabled under both modes. 8 tests where the fast multi-sample and legacy center-point algorithms inherently diverge for shadow subjects are scoped-skipped under fast via a tiny
itSkipFasthelper, with a comment pointing back to the issue for fixture-level follow-up. The skip is fast-only — legacy still runs all 43.The skipped scenarios fall into three buckets, each requiring fixture work rather than further algorithm changes:
pointer-events: noneacross shadow boundaries (2 tests). Non-shadow analogues pass under fast, but the inheritance through the host changes whatelementFromPointreturns inside the shadow tree.position: absolute/relative(4 tests).document.elementFromPointdoesn't return the host at the shadow content's rendered coordinates because the host's flow box doesn't include them; falling back toshadowRoot.elementFromPointwas tried but does not resolve these cases either.This PR is stacked on top of #33736 because it builds on that branch's
elementFromPointrelated changes.Steps to test
yarn workspace @packages/driver cypress:run -- --spec cypress/e2e/dom/visibility_shadow_dom.cy.ts— should report 78 passing, 8 pending, 0 failing.yarn workspace @packages/driver cypress:run -- --spec cypress/e2e/dom/visibility.cy.ts— non-shadow visibility behavior must remain unchanged from fix(driver): scroll off-screen elements into view in fast visibility algorithm #33736's baseline (no new failures attributable to this PR).How has the user experience changed?
Before: with
experimentalFastVisibility: true, visibility assertions on Shadow DOM subjects could give different results from non-shadow subjects (the documented "Current Limitations" in the migration guide). Test suites covering shadow components against fast visibility were effectively unsupported.After: shadow DOM subjects use the same algorithmic path as non-shadow subjects under fast visibility —
elementFromPointpierces shadow roots, and ancestor checks cross host boundaries.PR Tasks
cypress-documentation? #6435type definitions?Note
Medium Risk
Changes core
fastIsHiddenvisibility logic (hit-testing and scrolling/clipping) and could alter visibility outcomes in edge cases, especially with nested shadow roots and overflow scrolling. Test updates mitigate regressions but several shadow-specific scenarios are now explicitly skipped under fast mode.Overview
Makes the fast visibility algorithm shadow-DOM aware by piercing nested shadow roots during hit-testing (
getShadowElementFromPoint) and replacingcontains()with a shadow-boundary-crossing ancestor walk (findParent).Adjusts fast mode’s scrolling/clipping behavior by replacing the prior “any clipping ancestor” check with an
isClippedByAncestortest that (a) walks across shadow boundaries, (b) treatsauto/scrolloverflow as clipping, and (c) verifies the element is actually out-of-bounds of the ancestor before blockingscrollIntoView.Enables the shadow-DOM visibility E2E suite to run in both
fastandlegacymodes, adding aitSkipFasthelper to mark a small set of known-divergent cases as pending under fast while keeping legacy coverage intact.Reviewed by Cursor Bugbot for commit f5a46c4. Bugbot is set up for automated code reviews on this repo. Configure here.