22 assignFilteredGlobalDescriptorsFromPropertyDescriptorMap ,
33 createConnector ,
44 createMembraneMarshall ,
5+ filterGlobalOwnKeys ,
56 getFilteredGlobalOwnKeys ,
67 linkIntrinsics ,
78 DistortionCallback ,
@@ -76,6 +77,75 @@ function createDetachableIframe(): HTMLIFrameElement {
7677 return iframe ;
7778}
7879
80+ interface BrowserEnvironmentOptions {
81+ distortionCallback ?: DistortionCallback ;
82+ endowments ?: PropertyDescriptorMap ;
83+ keepAlive ?: boolean ;
84+ instrumentation ?: InstrumentationHooks ;
85+ }
86+
87+ interface RealmController {
88+ cleanup : ( ) => { } ;
89+ evaluator : typeof eval ;
90+ getGlobalOwnKeys : ( ) => ( string | symbol ) [ ] ;
91+ needsExplicitCleanUp : boolean ;
92+ }
93+ // @ts -ignore: TypeScript doesn't know what a ShadowRealm is yet.
94+ const { ShadowRealm : ShadowRealmCtor } = globalThis ;
95+ function createRealmController ( options : BrowserEnvironmentOptions ) : RealmController {
96+ let cleanup = ( ) => { } ;
97+ let evaluator : typeof eval ;
98+ let getGlobalOwnKeys : ( ) => ( string | symbol ) [ ] ;
99+ let needsExplicitCleanUp = false ;
100+
101+ if ( typeof ShadowRealmCtor === 'function' ) {
102+ // @ts -ignore: TypeScript doesn't know what a ShadowRealm is yet.
103+ const shadowRealm = new ShadowRealm ( ) ;
104+ evaluator = shadowRealm . evaluate . bind ( shadowRealm ) ;
105+ const getGlobalThisOwnKeysFromShadowRealm = evaluator ( `(callableKeysCallback) => {
106+ let ownKeys = Reflect.ownKeys(globalThis);
107+ Reflect.apply(callableKeysCallback, undefined, ownKeys);
108+ };` ) ;
109+ let keys : ( string | symbol ) [ ] ;
110+ getGlobalOwnKeys = ( ) => {
111+ getGlobalThisOwnKeysFromShadowRealm ( ( ...args : ( string | symbol ) [ ] ) => {
112+ keys = args ;
113+ } ) ;
114+ return filterWindowKeys ( filterGlobalOwnKeys ( keys ) ) ;
115+ } ;
116+ } else {
117+ const { keepAlive } = options ;
118+ const iframe = createDetachableIframe ( ) ;
119+ const redWindow = ReflectApply (
120+ HTMLIFrameElementProtoContentWindowGetter ,
121+ iframe ,
122+ [ ]
123+ ) ! . window ;
124+ getGlobalOwnKeys = ( ) => filterWindowKeys ( getFilteredGlobalOwnKeys ( redWindow ) ) ;
125+ const { document : redDocument } = redWindow ;
126+ cleanup = ( ) => {
127+ // Once we get the iframe info ready, and all mapped, we can proceed
128+ // to detach the iframe only if the keepAlive option isn't true.
129+ if ( keepAlive ) {
130+ // TODO: Temporary hack to preserve the document reference in Firefox.
131+ // https://bugzilla.mozilla.org/show_bug.cgi?id=543435
132+ ReflectApply ( DocumentProtoOpen , redDocument , [ ] ) ;
133+ ReflectApply ( DocumentProtoClose , redDocument , [ ] ) ;
134+ } else {
135+ ReflectApply ( ElementProtoRemove , iframe , [ ] ) ;
136+ }
137+ } ;
138+ evaluator = redWindow . eval ;
139+ needsExplicitCleanUp = true ;
140+ }
141+ return {
142+ cleanup,
143+ evaluator,
144+ getGlobalOwnKeys,
145+ needsExplicitCleanUp,
146+ } as RealmController ;
147+ }
148+
79149export default function createVirtualEnvironment (
80150 globalObjectShape : object | null ,
81151 globalObjectVirtualizationTarget : WindowProxy & typeof globalThis ,
@@ -90,22 +160,25 @@ export default function createVirtualEnvironment(
90160 ) {
91161 throw new TypeErrorCtor ( 'Missing global object virtualization target.' ) ;
92162 }
163+ const options = ObjectAssign (
164+ {
165+ __proto__ : null ,
166+ keepAlive : false ,
167+ } ,
168+ providedOptions
169+ ) ;
93170 const {
94171 distortionCallback,
95172 endowments,
96173 instrumentation,
97- keepAlive = false ,
174+ keepAlive,
98175 // eslint-disable-next-line prefer-object-spread
99- } = ObjectAssign ( { __proto__ : null } , providedOptions ) ;
100- const iframe = createDetachableIframe ( ) ;
101- const {
102- window : redWindow ,
103- window : { document : redDocument } ,
104- } = ReflectApply ( HTMLIFrameElementProtoContentWindowGetter , iframe , [ ] ) ! ;
176+ } = options ;
177+ const realmController = createRealmController ( options ) ;
105178 let globalOwnKeys ;
106179 if ( globalObjectShape === null ) {
107180 if ( defaultGlobalOwnKeys === null ) {
108- defaultGlobalOwnKeys = filterWindowKeys ( getFilteredGlobalOwnKeys ( redWindow ) ) ;
181+ defaultGlobalOwnKeys = realmController . getGlobalOwnKeys ( ) ;
109182 }
110183 globalOwnKeys = defaultGlobalOwnKeys ;
111184 } else {
@@ -117,7 +190,7 @@ export default function createVirtualEnvironment(
117190 // https://bugs.chromium.org/p/chromium/issues/detail?id=1305302
118191 const unforgeableGlobalThisKeys = keepAlive ? undefined : unforgeablePoisonedWindowKeys ;
119192 const blueConnector = createHooksCallback ;
120- const redConnector = createConnector ( redWindow . eval ) ;
193+ const redConnector = createConnector ( realmController . evaluator ) ;
121194 // Extract the global references and descriptors before any interference.
122195 const blueRefs = getCachedGlobalObjectReferences ( globalObjectVirtualizationTarget ) ;
123196 // Create a new environment.
@@ -154,18 +227,8 @@ export default function createVirtualEnvironment(
154227 // We intentionally skip remapping Window.prototype because there is nothing
155228 // in it that needs to be remapped.
156229 env . lazyRemap ( blueRefs . EventTargetProto , blueRefs . EventTargetProtoOwnKeys ) ;
157- // We don't remap `blueRefs.WindowPropertiesProto` because it is "magical"
158- // in that it provides access to elements by id.
159- //
160- // Once we get the iframe info ready, and all mapped, we can proceed
161- // to detach the iframe only if the keepAlive option isn't true.
162- if ( keepAlive ) {
163- // TODO: Temporary hack to preserve the document reference in Firefox.
164- // https://bugzilla.mozilla.org/show_bug.cgi?id=543435
165- ReflectApply ( DocumentProtoOpen , redDocument , [ ] ) ;
166- ReflectApply ( DocumentProtoClose , redDocument , [ ] ) ;
167- } else {
168- ReflectApply ( ElementProtoRemove , iframe , [ ] ) ;
230+ if ( realmController . needsExplicitCleanUp ) {
231+ realmController . cleanup ( ) ;
169232 }
170233 return env ;
171234}
0 commit comments