@@ -66,6 +66,10 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
6666 const frameSandboxMeta = new WeakMap ( ) ;
6767 const directExternalFrameWindowOrigins = new WeakMap ( ) ;
6868 const crossWindowProxyCache = new WeakMap ( ) ;
69+ const frameWindowFacades = new WeakMap ( ) ;
70+ const frameElementWindowFacades = new WeakMap ( ) ;
71+ const frameSrcdocMessageSources = new WeakMap ( ) ;
72+ const frameDocumentFacades = new WeakMap ( ) ;
6973 const postMessageWrappers = new WeakMap ( ) ;
7074 const frameTargetOriginMarker = Symbol . for ( 'zeroproxy.frame.targetOrigin' ) ;
7175 const networkContainmentMarker = Symbol . for ( 'zeroproxy.network.contained' ) ;
@@ -76,6 +80,7 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
7680 const rewrittenInlineScripts = new WeakSet ( ) ;
7781 const rewrittenStyleNodes = new WeakSet ( ) ;
7882 const documentWriteHookedWindows = new WeakSet ( ) ;
83+ const messageEventSourceHookedPrototypes = new WeakSet ( ) ;
7984 const windowMethodBindings = new Map ( ) ;
8085 const integrityBackupAttr = 'data-zp-integrity' ;
8186 const nonceBackupAttr = 'data-zp-target-nonce' ;
@@ -608,6 +613,7 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
608613 frameTargetOriginMarker,
609614 maskNativeFunction,
610615 isDirectExternalFrameElement,
616+ messageSourceFacadeFor,
611617 } ) ;
612618 const { installChildRewriteHelpers } = createChildRewriteHelpers ( {
613619 root,
@@ -629,6 +635,202 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
629635 frameSandboxMeta,
630636 isDirectExternalFrameElement,
631637 } ) ;
638+
639+ function frameDocumentURL ( frame , childDoc , childWin ) {
640+ try {
641+ if ( frame && Native . getAttribute . call ( frame , 'srcdoc' ) != null ) return 'about:srcdoc' ;
642+ } catch { }
643+ try {
644+ const target = urlMeta . get ( frame ) || Native . getAttribute . call ( frame , 'data-zp-target-url' ) || '' ;
645+ if ( target ) return target ;
646+ } catch { }
647+ try {
648+ const href = childWin && childWin . location && childWin . location . href ;
649+ if ( href ) return String ( href ) ;
650+ } catch { }
651+ try {
652+ const href = childDoc && childDoc . URL ;
653+ if ( href ) return String ( href ) ;
654+ } catch { }
655+ return 'about:blank' ;
656+ }
657+
658+ function isSrcdocFrame ( frame ) {
659+ try { return ! ! frame && Native . getAttribute . call ( frame , 'srcdoc' ) != null ; }
660+ catch { return false ; }
661+ }
662+
663+ function frameLocationFacadeFor ( frame , childDoc , childWin ) {
664+ const current = ( ) => {
665+ try { return new URL ( frameDocumentURL ( frame , childDoc , childWin ) ) ; }
666+ catch { return new URL ( 'about:blank' ) ; }
667+ } ;
668+ const locationFacade = {
669+ get href ( ) { return current ( ) . href ; } ,
670+ set href ( _v ) { } ,
671+ get protocol ( ) { return current ( ) . protocol ; } ,
672+ get host ( ) { return current ( ) . host ; } ,
673+ get hostname ( ) { return current ( ) . hostname ; } ,
674+ get port ( ) { return current ( ) . port ; } ,
675+ get pathname ( ) { return current ( ) . pathname ; } ,
676+ get search ( ) { return current ( ) . search ; } ,
677+ get hash ( ) { return current ( ) . hash ; } ,
678+ set hash ( _v ) { } ,
679+ get origin ( ) { return current ( ) . origin ; } ,
680+ assign ( _v ) { } ,
681+ replace ( _v ) { } ,
682+ reload ( ) { } ,
683+ toString ( ) { return current ( ) . href ; } ,
684+ valueOf ( ) { return current ( ) . href ; } ,
685+ [ Symbol . toPrimitive ] ( ) { return current ( ) . href ; }
686+ } ;
687+ try { Object . defineProperty ( locationFacade , Symbol . toStringTag , { value : 'Location' , enumerable : false , configurable : true } ) ; } catch { }
688+ maskMethods ( locationFacade , [ 'assign' , 'replace' , 'reload' , 'toString' , 'valueOf' ] ) ;
689+ maskNativeFunction ( locationFacade [ Symbol . toPrimitive ] , Symbol . toPrimitive ) ;
690+ try { Object . freeze ( locationFacade ) ; } catch { }
691+ return locationFacade ;
692+ }
693+
694+ function frameWindowValue ( frame , childWin , proxy , locationFacade , prop ) {
695+ if ( prop === Symbol . toStringTag ) return 'Window' ;
696+ if ( prop === 'window' || prop === 'self' || prop === 'globalThis' || prop === 'frames' ) return proxy ;
697+ if ( prop === 'location' ) return locationFacade ;
698+ if ( prop === 'origin' ) return locationFacade . origin ;
699+ if ( prop === 'postMessage' ) return postMessageWrapperFor ( childWin ) ;
700+ if ( prop === 'document' ) {
701+ try { return frameDocumentFacadeFor ( frame , childWin . document , childWin ) ; } catch { return undefined ; }
702+ }
703+ if ( prop === 'parent' || prop === 'top' ) {
704+ if ( isSrcdocFrame ( frame ) ) return root ;
705+ try {
706+ const value = childWin [ prop ] ;
707+ if ( ! value || value === childWin ) return proxy ;
708+ if ( value === root ) return root ;
709+ return frameWindowFacades . get ( value ) || value ;
710+ } catch {
711+ return root ;
712+ }
713+ }
714+ if ( prop === 'opener' ) {
715+ try {
716+ const value = childWin . opener ;
717+ if ( ! value ) return null ;
718+ if ( value === root ) return root ;
719+ return frameWindowFacades . get ( value ) || value ;
720+ } catch {
721+ return null ;
722+ }
723+ }
724+ const value = childWin [ prop ] ;
725+ return typeof value === 'function' && WINDOW_BOUND_METHODS . has ( prop ) ? value . bind ( childWin ) : value ;
726+ }
727+
728+ function frameWindowFacadeFor ( frame , childWin , forceFacade = false ) {
729+ if ( ! childWin ) return childWin ;
730+ if ( ! forceFacade && isSrcdocFrame ( frame ) ) {
731+ try { return frameSrcdocMessageSources . get ( frame ) || childWin ; }
732+ catch { return childWin ; }
733+ }
734+ try {
735+ const existing = frame && frameElementWindowFacades . get ( frame ) ;
736+ if ( existing ) return existing ;
737+ } catch { }
738+ if ( frameWindowFacades . has ( childWin ) ) return frameWindowFacades . get ( childWin ) ;
739+ let proxy ;
740+ const locationFacade = frameLocationFacadeFor ( frame , null , childWin ) ;
741+ proxy = new Proxy ( { } , {
742+ get ( _target , prop ) { return frameWindowValue ( frame , childWin , proxy , locationFacade , prop ) ; } ,
743+ set ( _target , prop , value ) {
744+ if ( prop === 'location' ) return true ;
745+ try { childWin [ prop ] = value ; return true ; } catch { return false ; }
746+ } ,
747+ has ( _target , prop ) {
748+ return prop === 'location' || prop === 'document' || prop === 'parent' || prop === 'top' || prop in childWin ;
749+ } ,
750+ getOwnPropertyDescriptor ( _target , prop ) {
751+ if ( prop === 'location' || prop === 'document' || prop === 'parent' || prop === 'top' ) {
752+ return { configurable : true , enumerable : true , get ( ) { return frameWindowValue ( frame , childWin , proxy , locationFacade , prop ) ; } } ;
753+ }
754+ try { return Reflect . getOwnPropertyDescriptor ( childWin , prop ) ; } catch { return undefined ; }
755+ } ,
756+ ownKeys ( ) {
757+ try { return Reflect . ownKeys ( childWin ) ; } catch { return [ ] ; }
758+ }
759+ } ) ;
760+ membraneRawTargets . set ( proxy , childWin ) ;
761+ frameWindowFacades . set ( childWin , proxy ) ;
762+ try { if ( frame ) frameElementWindowFacades . set ( frame , proxy ) ; } catch { }
763+ return proxy ;
764+ }
765+
766+ function frameDocumentValue ( frame , childDoc , childWin , windowFacade , locationFacade , prop ) {
767+ if ( prop === Symbol . toStringTag ) return 'HTMLDocument' ;
768+ if ( prop === 'defaultView' ) return windowFacade ;
769+ if ( prop === 'location' ) return locationFacade ;
770+ if ( prop === 'URL' || prop === 'documentURI' ) return locationFacade . href ;
771+ const value = childDoc [ prop ] ;
772+ return typeof value === 'function' ? value . bind ( childDoc ) : value ;
773+ }
774+
775+ function frameDocumentFacadeFor ( frame , childDoc , childWin ) {
776+ if ( ! childDoc ) return childDoc ;
777+ if ( frameDocumentFacades . has ( childDoc ) ) return frameDocumentFacades . get ( childDoc ) ;
778+ const rawWindow = childWin || childDoc . defaultView ;
779+ const windowFacade = frameWindowFacadeFor ( frame , rawWindow ) ;
780+ const locationFacade = frameLocationFacadeFor ( frame , childDoc , rawWindow ) ;
781+ const proxy = new Proxy ( { } , {
782+ get ( _target , prop ) { return frameDocumentValue ( frame , childDoc , rawWindow , windowFacade , locationFacade , prop ) ; } ,
783+ set ( _target , prop , value ) {
784+ try { childDoc [ prop ] = value ; return true ; } catch { return false ; }
785+ } ,
786+ has ( _target , prop ) {
787+ return prop === 'defaultView' || prop === 'URL' || prop === 'documentURI' || prop in childDoc ;
788+ } ,
789+ getOwnPropertyDescriptor ( _target , prop ) {
790+ if ( prop === 'defaultView' || prop === 'URL' || prop === 'documentURI' ) {
791+ return { configurable : true , enumerable : true , get ( ) { return frameDocumentValue ( frame , childDoc , rawWindow , windowFacade , locationFacade , prop ) ; } } ;
792+ }
793+ try { return Reflect . getOwnPropertyDescriptor ( childDoc , prop ) ; } catch { return undefined ; }
794+ } ,
795+ ownKeys ( ) {
796+ try { return Reflect . ownKeys ( childDoc ) ; } catch { return [ ] ; }
797+ }
798+ } ) ;
799+ membraneRawTargets . set ( proxy , childDoc ) ;
800+ frameDocumentFacades . set ( childDoc , proxy ) ;
801+ return proxy ;
802+ }
803+
804+ function messageSourceFacadeFor ( source , ev ) {
805+ if ( ! source ) return srcdocMessageSourceFacade ( ev ) ;
806+ if ( frameWindowFacades . has ( source ) ) return frameWindowFacades . get ( source ) ;
807+ try {
808+ const frames = document . querySelectorAll && document . querySelectorAll ( 'iframe,frame' ) ;
809+ if ( ! frames ) return source ;
810+ for ( let i = 0 ; i < frames . length ; i ++ ) {
811+ const facade = frames [ i ] . contentWindow ;
812+ const raw = membraneRawTargets . get ( facade ) || facade ;
813+ if ( raw === source ) return facade ;
814+ }
815+ } catch { }
816+ return srcdocMessageSourceFacade ( ev , source ) || source ;
817+ }
818+
819+ function srcdocMessageSourceFacade ( ev , source ) {
820+ try {
821+ if ( ! ev ) return null ;
822+ const frames = document . querySelectorAll && document . querySelectorAll ( 'iframe[srcdoc],frame[srcdoc]' ) ;
823+ if ( ! frames || frames . length !== 1 ) return null ;
824+ if ( source ) {
825+ const facade = frameWindowFacadeFor ( frames [ 0 ] , source , true ) ;
826+ frameSrcdocMessageSources . set ( frames [ 0 ] , facade ) ;
827+ return facade ;
828+ }
829+ return frames [ 0 ] . contentWindow || null ;
830+ } catch {
831+ return null ;
832+ }
833+ }
632834 try { Object . defineProperty ( root , frameTargetOriginMarker , { get ( ) { return virtualURL . origin ; } , enumerable : false , configurable : false } ) ; } catch { }
633835 installToStringMasking ( root ) ;
634836 define ( root , '__ZP_SET_BASE' , updateVirtualBase ) ;
@@ -800,6 +1002,14 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
8001002 if ( base === document && ( prop === 'URL' || prop === 'documentURI' ) ) return virtualURL . href ;
8011003 if ( base === document && prop === 'baseURI' ) return baseURL ;
8021004 if ( base === document && prop === 'referrer' ) return boot . documentReferrer || '' ;
1005+ if ( prop === 'source' && base && typeof base === 'object' ) {
1006+ try {
1007+ const rawSource = Reflect . get ( Object ( base ) , prop ) ;
1008+ const framedSource = messageSourceFacadeFor ( rawSource , base ) ;
1009+ if ( framedSource ) return framedSource ;
1010+ return rawSource ;
1011+ } catch { }
1012+ }
8031013 if ( isWindowLike ( base ) ) {
8041014 if ( prop === 'window' || prop === 'self' || prop === 'globalThis' || prop === 'frames' ) return base === scope || base === root ? scope : base ;
8051015 if ( prop === 'top' || prop === 'parent' || prop === 'opener' ) {
@@ -1403,6 +1613,7 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
14031613 const addEventListener = w && w . addEventListener && w . addEventListener . bind ( w ) ;
14041614 const removeEventListener = w && w . removeEventListener && w . removeEventListener . bind ( w ) ;
14051615 if ( ! addEventListener || ! removeEventListener ) return ;
1616+ installMessageEventSourceAccessor ( w ) ;
14061617 function wrap ( listener ) {
14071618 if ( ! listener || ( typeof listener !== 'function' && typeof listener . handleEvent !== 'function' ) ) return listener ;
14081619 if ( messageListenerWrappers . has ( listener ) ) return messageListenerWrappers . get ( listener ) ;
@@ -1427,6 +1638,19 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
14271638 } ) ;
14281639 }
14291640
1641+ function installMessageEventSourceAccessor ( w ) {
1642+ const proto = w && w . MessageEvent && w . MessageEvent . prototype ;
1643+ if ( ! proto ) return ;
1644+ if ( messageEventSourceHookedPrototypes . has ( proto ) ) return ;
1645+ const d = Object . getOwnPropertyDescriptor ( proto , 'source' ) ;
1646+ if ( ! d || typeof d . get !== 'function' ) return ;
1647+ messageEventSourceHookedPrototypes . add ( proto ) ;
1648+ defineAccessor ( proto , 'source' , function ( ) {
1649+ const raw = d . get . call ( this ) ;
1650+ return messageSourceFacadeFor ( raw , this ) || raw ;
1651+ } ) ;
1652+ }
1653+
14301654 function visibleResourceURL ( el , attrName ) {
14311655 return urlMeta . get ( el ) || Native . getAttribute . call ( el , 'data-zp-target-url' ) || Native . getAttribute . call ( el , attrName ) || '' ;
14321656 }
@@ -2752,6 +2976,8 @@ import { createWorkerFacades } from './runtime/workers/facades.mjs';
27522976 networkContainmentMarker,
27532977 isDirectExternalFrameElement,
27542978 installNetworkContainment,
2979+ frameWindowFacadeFor,
2980+ frameDocumentFacadeFor,
27552981 } ) ;
27562982 const nativeCreateElement = w === root ? Native . createElement : w . document . createElement . bind ( w . document ) ;
27572983 const nativeCreateElementNS = w === root ? Native . createElementNS : w . document . createElementNS && w . document . createElementNS . bind ( w . document ) ;
0 commit comments