@@ -8,25 +8,59 @@ interface PostMessageEvent extends MessageEvent {
88 }
99}
1010
11+ type FrameElement = HTMLIFrameElement | HTMLFrameElement
12+
13+ const frameBySource = new WeakMap < object , FrameElement > ( )
14+
15+ function isFrameElement ( element : Element | null ) : element is FrameElement {
16+ return (
17+ element instanceof HTMLIFrameElement || element instanceof HTMLFrameElement
18+ )
19+ }
20+
1121function findFrameBySource ( source : MessageEventSource | null ) {
22+ if ( ! source ) {
23+ return
24+ }
25+
26+ const cachedFrame = frameBySource . get ( source )
27+ if (
28+ cachedFrame &&
29+ cachedFrame . isConnected &&
30+ cachedFrame . contentWindow === source
31+ ) {
32+ return cachedFrame
33+ }
34+
35+ // This also works for frames inside closed shadow roots when same-origin.
36+ try {
37+ const frame = ( source as Window ) . frameElement
38+ if ( isFrameElement ( frame ) ) {
39+ frameBySource . set ( source , frame )
40+ return frame
41+ }
42+ } catch {
43+ // Accessing frameElement on a cross-origin WindowProxy throws.
44+ }
45+
1246 const roots : Array < Document | ShadowRoot > = [ document ]
1347
1448 while ( roots . length ) {
1549 const root = roots . pop ( ) !
16- const frame = Array . from (
17- root . querySelectorAll < HTMLIFrameElement | HTMLFrameElement > (
18- 'iframe, frame'
19- )
20- ) . find ( ( { contentWindow } ) => contentWindow === source )
50+ const walker = document . createTreeWalker ( root , NodeFilter . SHOW_ELEMENT )
51+ let element = walker . nextNode ( ) as Element | null
2152
22- if ( frame ) {
23- return frame
24- }
53+ while ( element ) {
54+ if ( isFrameElement ( element ) && element . contentWindow === source ) {
55+ frameBySource . set ( source , element )
56+ return element
57+ }
2558
26- for ( const element of Array . from ( root . querySelectorAll ( '*' ) ) ) {
2759 if ( element . shadowRoot ) {
2860 roots . push ( element . shadowRoot )
2961 }
62+
63+ element = walker . nextNode ( ) as Element | null
3064 }
3165 }
3266}
0 commit comments