@@ -49,8 +49,21 @@ import getCustomSelector from './modules/getCustomSelector';
4949 evMsg . data . source &&
5050 evMsg . data . source === 'event-from-iframe'
5151 ) {
52- const obj = { ... evMsg . data . obj } ;
52+ // Verify the message actually came from a child iframe
5353 const aFrames = document . querySelectorAll ( 'iframe, frame' ) ;
54+ let isFromChildFrame = false ;
55+ for ( const frame of aFrames ) {
56+ if ( frame . contentWindow === evMsg . source ) {
57+ isFromChildFrame = true ;
58+ break ;
59+ }
60+ }
61+ if ( ! isFromChildFrame ) {
62+ return ; // Message didn't come from any of our iframes
63+ }
64+
65+ const obj = { ...evMsg . data . obj } ;
66+ const framePath = evMsg . data . framePath || [ ] ;
5467 for ( const frame of aFrames ) {
5568 if ( frame . contentWindow === evMsg . source ) {
5669 let frameSelector = null ;
@@ -81,7 +94,9 @@ import getCustomSelector from './modules/getCustomSelector';
8194 }
8295 }
8396 if ( frameSelector && obj . event ) {
84- obj . event . frame = frameSelector ;
97+ // Build the complete frame path: [outermost, ..., innermost]
98+ const completeFramePath = [ frameSelector , ...framePath ] ;
99+ obj . event . frame = completeFramePath . join ( ' >>> ' ) ;
85100 chrome . runtime . sendMessage ( obj ) ;
86101 }
87102 break ;
@@ -91,17 +106,90 @@ import getCustomSelector from './modules/getCustomSelector';
91106 } ) ;
92107 }
93108
109+ // intermediate frames: listen for messages from child iframes and forward to parent
110+ if ( window !== window . top ) {
111+ window . addEventListener ( 'message' , ( evMsg ) => {
112+ if (
113+ evMsg . data &&
114+ evMsg . data . source &&
115+ evMsg . data . source === 'event-from-iframe'
116+ ) {
117+ // Verify the message actually came from a child iframe
118+ const aFrames = document . querySelectorAll ( 'iframe, frame' ) ;
119+ let isFromChildFrame = false ;
120+ for ( const frame of aFrames ) {
121+ if ( frame . contentWindow === evMsg . source ) {
122+ isFromChildFrame = true ;
123+ break ;
124+ }
125+ }
126+ if ( ! isFromChildFrame ) {
127+ return ; // Message didn't come from any of our iframes
128+ }
129+
130+ // This is an intermediate frame - find the child iframe and add to path
131+ const framePath = evMsg . data . framePath || [ ] ;
132+
133+ for ( const frame of aFrames ) {
134+ if ( frame . contentWindow === evMsg . source ) {
135+ let frameSelector = null ;
136+ try {
137+ frameSelector = getCustomSelector ( frame ) ;
138+ } catch ( ex ) {
139+ // ignore
140+ }
141+ if ( ! frameSelector ) {
142+ try {
143+ frameSelector = getNodeSelector ( frame , {
144+ root : window . document ,
145+ idName : ( name ) => {
146+ return ! / ^ [ 0 - 9 ] + .* / i. test ( name ) ;
147+ } ,
148+ className : ( name ) => {
149+ return (
150+ ! name . includes ( 'focus' ) &&
151+ ! name . includes ( 'highlight' ) &&
152+ ! / ^ [ 0 - 9 ] + .* / i. test ( name )
153+ ) ;
154+ } ,
155+ } ) ;
156+ } catch ( ex ) {
157+ // ignore
158+ }
159+ }
160+
161+ // Forward to parent with this frame's selector added to path
162+ if ( frameSelector ) {
163+ framePath . push ( frameSelector ) ;
164+ }
165+
166+ window . parent . postMessage (
167+ {
168+ source : 'event-from-iframe' ,
169+ obj : evMsg . data . obj ,
170+ framePath : framePath ,
171+ } ,
172+ '*'
173+ ) ;
174+ break ;
175+ }
176+ }
177+ }
178+ } ) ;
179+ }
180+
94181 function eventInterceptopMainHandler ( ev ) {
95182 if ( ev && ev . type === 'mouseover' && ! mutationDetected ) {
96183 return ;
97184 }
98185 interceptEvents ( ev , window . document , null , ( obj ) => {
99186 if ( window !== window . top ) {
100- // If the event is inside a frame, send it to the top
187+ // If the event is inside a frame, send it to the parent
101188 window . parent . postMessage (
102189 {
103190 source : 'event-from-iframe' ,
104191 obj : { ...obj } ,
192+ framePath : [ ] , // Start with empty path, will be built as message bubbles up
105193 } ,
106194 '*'
107195 ) ;
0 commit comments