@@ -169,11 +169,14 @@ export async function buildBackendIdMaps(
169
169
170
170
let iframeNode : DOMNode | undefined ;
171
171
const locate = ( n : DOMNode ) : boolean => {
172
- if ( n . backendNodeId === backendNodeId ) return ( iframeNode = n ) , true ;
173
- return (
174
- ( n . children ?. some ( locate ) ?? false ) ||
175
- ( n . contentDocument ? locate ( n . contentDocument ) : false )
176
- ) ;
172
+ if ( n . backendNodeId === backendNodeId ) {
173
+ iframeNode = n ;
174
+ return true ;
175
+ }
176
+ if ( n . shadowRoots ?. some ( locate ) ) return true ;
177
+ if ( n . children ?. some ( locate ) ) return true ;
178
+ if ( n . contentDocument && locate ( n . contentDocument ) ) return true ;
179
+ return false ;
177
180
} ;
178
181
179
182
if ( ! locate ( root ) || ! iframeNode ?. contentDocument ) {
@@ -725,20 +728,43 @@ export async function getFrameRootXpath(
725
728
const handle = await frame . frameElement ( ) ;
726
729
// Evaluate the element's absolute XPath within the page context
727
730
return handle . evaluate ( ( node : Element ) => {
728
- const pos = ( el : Element ) => {
731
+ function stepFor ( el : Element ) : string {
732
+ const tag = el . tagName . toLowerCase ( ) ;
729
733
let i = 1 ;
730
734
for (
731
735
let sib = el . previousElementSibling ;
732
736
sib ;
733
737
sib = sib . previousElementSibling
734
- )
735
- if ( sib . tagName === el . tagName ) i += 1 ;
736
- return i ;
737
- } ;
738
+ ) {
739
+ if ( sib . tagName . toLowerCase ( ) === tag ) i ++ ;
740
+ }
741
+ return `${ tag } [${ i } ]` ;
742
+ }
743
+
738
744
const segs : string [ ] = [ ] ;
739
- for ( let el : Element | null = node ; el ; el = el . parentElement )
740
- segs . unshift ( `${ el . tagName . toLowerCase ( ) } [${ pos ( el ) } ]` ) ;
741
- return `/${ segs . join ( "/" ) } ` ;
745
+ let el : Element | null = node ;
746
+
747
+ while ( el ) {
748
+ segs . unshift ( stepFor ( el ) ) ;
749
+ if ( el . parentElement ) {
750
+ el = el . parentElement ;
751
+ continue ;
752
+ }
753
+
754
+ // top of this tree: check if we’re inside a shadow root
755
+ const root = el . getRootNode ( ) ; // Document or ShadowRoot
756
+ if ( ( root as ShadowRoot ) . host ) {
757
+ // Insert a shadow hop marker so the final path contains “//”
758
+ segs . unshift ( "" ) ;
759
+ el = ( root as ShadowRoot ) . host ;
760
+ continue ;
761
+ }
762
+
763
+ break ;
764
+ }
765
+
766
+ // Leading '/' + join; empty tokens become “//” between segments
767
+ return "/" + segs . join ( "/" ) ;
742
768
} ) ;
743
769
}
744
770
0 commit comments