Skip to content

feat: CORS iframe + closed shadow DOM parity#436

Draft
aryanku-dev wants to merge 3 commits into
masterfrom
feat/cors-iframes-and-shadow-dom
Draft

feat: CORS iframe + closed shadow DOM parity#436
aryanku-dev wants to merge 3 commits into
masterfrom
feat/cors-iframes-and-shadow-dom

Conversation

@aryanku-dev
Copy link
Copy Markdown

Summary

Brings percy-selenium-dotnet to parity with the canonical Percy CORS iframe + closed shadow DOM feature set.

Implemented

  • Nested cross-origin iframe capture (depth-capped, cycle-guarded)
  • data-percy-ignore attribute opt-out
  • ignoreIframeSelectors option
  • Post-switch URL re-check via IsUnsupportedIframeSrc
  • PercyContextLostException recovery merges PartialCapture
  • Closed shadow DOM via CDP (ExposeClosedShadowRoots)
  • Inlined C# helpers

Skipped

  • ElementInternals preflight (Feature 8): N/A — selenium-dotnet has no before-page-load hook.
  • @percy/sdk-utils bump (Feature 9): not applicable to .NET; helpers inlined.

Reference

Mirrored from percy/percy-nightwatch#869 (PER-7292-add-cors-iframe-support); CDP from percy/percy-playwright#609.

Test plan

  • Full repo test suite passed locally (dotnet test)
  • Manual smoke: cross-origin iframes
  • Manual smoke: closed shadow roots in Chrome

🤖 Generated with Claude Code via /percy-sdk-sync

aryanku-dev and others added 3 commits May 11, 2026 15:15
…ore, and recovery controls

Replace the single-level processFrame helper with a recursive
ProcessFrameTree pipeline that mirrors the canonical Percy CORS iframe
spec from percy/percy-nightwatch#869. Adds:

- DEFAULT_MAX_FRAME_DEPTH/MAX_ALLOWED_FRAME_DEPTH, ClampFrameDepth,
  NormalizeIgnoreSelectors, ResolveMaxFrameDepth, ResolveIgnoreSelectors
  inlined since .NET has no @percy/sdk-utils equivalent.
- ENUMERATE_IFRAMES_SCRIPT + IframeInfo to collect iframe metadata in
  one round-trip per frame context.
- ShouldSkipIframe centralizes filtering: data-percy-ignore attribute,
  ignoreIframeSelectors matches, unsupported / srcdoc / same-origin
  frames, and frames without data-percy-element-id are dropped early.
- ProcessFrameTree recurses cross-origin descendants up to MaxFrameDepth,
  with a HashSet ancestor-URL chain to break cyclic A->B->A graphs.
- Post-switch URL re-check: after SwitchTo().Frame, the loaded
  document.URL is rechecked against IsUnsupportedIframeSrc to handle
  late navigations.
- PercyContextLostException carries the partial capture so a failed
  SwitchTo().ParentFrame() unwind still surfaces every frame serialized
  up to the failure.

CaptureCorsIframes wires the helpers into getSerializedDom, replacing
the prior ad-hoc top-level iframe loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors percy/percy-playwright#609. Walks the CDP DOM tree (DOM.getDocument
with depth=-1 and pierce=true), collects backendNodeId pairs for every
closed shadow root, resolves both sides via DOM.resolveNode, then uses
Runtime.callFunctionOn to register each shadow root in a window-bound
WeakMap (window.__percyClosedShadowRoots) that PercyDOM.serialize() reads
during cloning. Nodes inside child frame documents are skipped because
their execution contexts don't share the WeakMap.

Wired into Snapshot() before serialization and into the responsive
capture reload path so the WeakMap survives page.reload(). No-op on
non-Chrome drivers and when ExecuteCdpCommand isn't available, so
existing Firefox / non-Chrome test paths are unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers the inlined helpers (GetOrigin, IsUnsupportedIframeSrc,
ClampFrameDepth, NormalizeIgnoreSelectors), the ShouldSkipIframe skip
matrix (data-percy-ignore, ignoreIframeSelectors, unsupported src,
srcdoc, same-origin, missing percyElementId, and immediate-parent
origin comparison), and the PercyContextLostException carrier.

Tests rely on the existing InternalsVisibleTo("Percy.Test") in
AssemblyInfo.cs and use reflection for the private IframeInfo /
ShouldSkipIframe symbols so the production API stays sealed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant