1
1
import {
2
2
assignFilteredGlobalDescriptorsFromPropertyDescriptorMap ,
3
3
Connector ,
4
+ CallableEvaluate ,
4
5
createBlueConnector ,
5
6
createRedConnector ,
6
7
DistortionCallback ,
@@ -10,6 +11,7 @@ import {
10
11
linkIntrinsics ,
11
12
PropertyKeys ,
12
13
toSafeWeakMap ,
14
+ SUPPORTS_SHADOW_REALM ,
13
15
VirtualEnvironment ,
14
16
} from '@locker/near-membrane-base' ;
15
17
@@ -34,10 +36,15 @@ const ObjectCtor = Object;
34
36
const TypeErrorCtor = TypeError ;
35
37
const WeakMapCtor = WeakMap ;
36
38
const { prototype : DocumentProto } = Document ;
39
+ const { bind : FunctionProtoBind } = Function . prototype ;
37
40
const { prototype : NodeProto } = Node ;
38
41
const { remove : ElementProtoRemove , setAttribute : ElementProtoSetAttribute } = Element . prototype ;
39
42
const { appendChild : NodeProtoAppendChild } = NodeProto ;
40
- const { assign : ObjectAssign } = ObjectCtor ;
43
+ const {
44
+ assign : ObjectAssign ,
45
+ create : ObjectCreate ,
46
+ getOwnPropertyDescriptors : ObjectGetOwnPropertyDescriptors ,
47
+ } = ObjectCtor ;
41
48
// eslint-disable-next-line @typescript-eslint/naming-convention
42
49
const { __lookupGetter__ : ObjectProtoLookupGetter } = ObjectCtor . prototype as any ;
43
50
const { apply : ReflectApply } = Reflect ;
@@ -66,8 +73,13 @@ const NodeProtoLastChildGetter: Getter = ReflectApply(ObjectProtoLookupGetter, N
66
73
const blueDocumentToBlueCreateHooksCallbackMap = toSafeWeakMap (
67
74
new WeakMapCtor < Document , Connector > ( )
68
75
) ;
76
+ // @ts -ignore: Prevent cannot find name 'ShadowRealm' error.
77
+ const ShadowRealmCtor = SUPPORTS_SHADOW_REALM ? ShadowRealm : undefined ;
78
+ const ShadowRealmProtoEvaluate : CallableEvaluate | undefined = ShadowRealmCtor ?. prototype ?. evaluate ;
79
+ const defaultGlobalOwnKeysRegistry = { __proto__ : null } ;
69
80
70
81
let defaultGlobalOwnKeys : PropertyKeys | null = null ;
82
+ let defaultGlobalPropertyDescriptorMap : PropertyDescriptorMap | null = null ;
71
83
72
84
function createDetachableIframe ( doc : Document ) : HTMLIFrameElement {
73
85
const iframe : HTMLIFrameElement = ReflectApply ( DocumentProtoCreateElement , doc , [ 'iframe' ] ) ;
@@ -90,7 +102,7 @@ function createIframeVirtualEnvironment(
90
102
if ( typeof globalObject !== 'object' || globalObject === null ) {
91
103
throw new TypeErrorCtor ( 'Missing global object virtualization target.' ) ;
92
104
}
93
- const blueRefs = getCachedGlobalObjectReferences ( globalObject ) ;
105
+ const blueRefs = getCachedGlobalObjectReferences ( globalObject ) ! ;
94
106
if ( typeof blueRefs !== 'object' || blueRefs === null ) {
95
107
throw new TypeErrorCtor ( 'Invalid virtualization target.' ) ;
96
108
}
@@ -180,4 +192,103 @@ function createIframeVirtualEnvironment(
180
192
return env ;
181
193
}
182
194
183
- export default createIframeVirtualEnvironment ;
195
+ function createShadowRealmVirtualEnvironment (
196
+ globalObject : WindowProxy & typeof globalThis ,
197
+ globalObjectShape : object | null ,
198
+ providedOptions ?: BrowserEnvironmentOptions
199
+ ) : VirtualEnvironment {
200
+ if ( typeof globalObject !== 'object' || globalObject === null ) {
201
+ throw new TypeErrorCtor ( 'Missing global object virtualization target.' ) ;
202
+ }
203
+ const {
204
+ distortionCallback,
205
+ endowments,
206
+ instrumentation,
207
+ // eslint-disable-next-line prefer-object-spread
208
+ } = ObjectAssign ( { __proto__ : null } , providedOptions ) ;
209
+
210
+ // If a globalObjectShape has been explicitly specified, reset the
211
+ // defaultGlobalPropertyDescriptorMap to null. This will ensure that
212
+ // the provided globalObjectShape is used to re-create the cached
213
+ // defaultGlobalPropertyDescriptorMap.
214
+ if ( globalObjectShape !== null ) {
215
+ defaultGlobalPropertyDescriptorMap = null ;
216
+ }
217
+ if ( defaultGlobalPropertyDescriptorMap === null ) {
218
+ let sourceShapeOrOneTimeWindow = globalObjectShape ! ;
219
+ let sourceIsIframe = false ;
220
+ if ( globalObjectShape === null ) {
221
+ const oneTimeIframe = createDetachableIframe ( globalObject . document ) ;
222
+ sourceShapeOrOneTimeWindow = ReflectApply (
223
+ HTMLIFrameElementProtoContentWindowGetter ,
224
+ oneTimeIframe ,
225
+ [ ]
226
+ ) ! ;
227
+ sourceIsIframe = true ;
228
+ }
229
+ defaultGlobalOwnKeys = getFilteredGlobalOwnKeys ( sourceShapeOrOneTimeWindow ) ;
230
+ if ( sourceIsIframe ) {
231
+ ReflectApply ( ElementProtoRemove , sourceShapeOrOneTimeWindow , [ ] ) ;
232
+ }
233
+ defaultGlobalPropertyDescriptorMap = {
234
+ __proto__ : null ,
235
+ } as unknown as PropertyDescriptorMap ;
236
+ assignFilteredGlobalDescriptorsFromPropertyDescriptorMap (
237
+ defaultGlobalPropertyDescriptorMap ,
238
+ ObjectGetOwnPropertyDescriptors ( globalObject )
239
+ ) ;
240
+ for ( let i = 0 , { length } = defaultGlobalOwnKeys ; i < length ; i += 1 ) {
241
+ defaultGlobalOwnKeysRegistry [ defaultGlobalOwnKeys [ i ] ] = true ;
242
+ }
243
+ for ( const key in defaultGlobalPropertyDescriptorMap ) {
244
+ if ( ! ( key in defaultGlobalOwnKeysRegistry ) ) {
245
+ delete defaultGlobalPropertyDescriptorMap [ key ] ;
246
+ }
247
+ }
248
+ }
249
+ const blueRefs = getCachedGlobalObjectReferences ( globalObject ) ! ;
250
+ // Create a new environment.
251
+ const env = new VirtualEnvironment ( {
252
+ blueConnector : createBlueConnector ( globalObject ) ,
253
+ distortionCallback,
254
+ instrumentation,
255
+ redConnector : createRedConnector (
256
+ ReflectApply ( FunctionProtoBind , ShadowRealmProtoEvaluate , [ new ShadowRealmCtor ( ) ] )
257
+ ) ,
258
+ } ) ;
259
+ linkIntrinsics ( env , globalObject ) ;
260
+ // window
261
+ env . link ( 'globalThis' ) ;
262
+ // Set globalThis.__proto__ in the sandbox to a proxy of
263
+ // globalObject.__proto__ and with this, the entire
264
+ // structure around window proto chain should be covered.
265
+ env . remapProto ( globalObject , blueRefs . WindowProto ) ;
266
+ let unsafeBlueDescMap : PropertyDescriptorMap = defaultGlobalPropertyDescriptorMap ;
267
+ if ( globalObject !== window ) {
268
+ unsafeBlueDescMap = { __proto__ : null } as unknown as PropertyDescriptorMap ;
269
+ assignFilteredGlobalDescriptorsFromPropertyDescriptorMap (
270
+ unsafeBlueDescMap ,
271
+ ObjectGetOwnPropertyDescriptors ( globalObject )
272
+ ) ;
273
+ for ( const key in unsafeBlueDescMap ) {
274
+ if ( ! ( key in defaultGlobalOwnKeysRegistry ) ) {
275
+ delete unsafeBlueDescMap [ key ] ;
276
+ }
277
+ }
278
+ }
279
+ env . remapProperties ( blueRefs . window , unsafeBlueDescMap ) ;
280
+ if ( endowments ) {
281
+ const filteredEndowments : PropertyDescriptorMap = { } ;
282
+ assignFilteredGlobalDescriptorsFromPropertyDescriptorMap ( filteredEndowments , endowments ) ;
283
+ removeWindowDescriptors ( filteredEndowments ) ;
284
+ env . remapProperties ( blueRefs . window , filteredEndowments ) ;
285
+ }
286
+ // We remap `blueRefs.WindowPropertiesProto` to an empty object because it
287
+ // is "magical" in that it provides access to elements by id.
288
+ env . remapProto ( blueRefs . WindowProto , ObjectCreate ( blueRefs . EventTargetProto ) ) ;
289
+ return env ;
290
+ }
291
+
292
+ export default SUPPORTS_SHADOW_REALM
293
+ ? createShadowRealmVirtualEnvironment
294
+ : createIframeVirtualEnvironment ;
0 commit comments