Skip to content

Commit 4de4482

Browse files
committed
fix: update near-membrane-dom to use ShadowRealm
1 parent 1550cb2 commit 4de4482

2 files changed

Lines changed: 90 additions & 23 deletions

File tree

packages/near-membrane-base/src/intrinsics.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,8 @@ export function assignFilteredGlobalDescriptorsFromPropertyDescriptorMap<
152152
return descMap;
153153
}
154154

155-
export function getFilteredGlobalOwnKeys(source: object): (string | symbol)[] {
155+
export function filterGlobalOwnKeys(ownKeys: (string | symbol)[] = []): (string | symbol)[] {
156156
const result: (string | symbol)[] = [];
157-
const ownKeys = ReflectOwnKeys(source);
158157
for (let i = 0, { length } = ownKeys; i < length; i += 1) {
159158
const ownKey = ownKeys[i];
160159
// Avoid overriding ECMAScript global names that correspond to global
@@ -169,6 +168,11 @@ export function getFilteredGlobalOwnKeys(source: object): (string | symbol)[] {
169168
return result;
170169
}
171170

171+
export function getFilteredGlobalOwnKeys(source: object): (string | symbol)[] {
172+
const ownKeys = ReflectOwnKeys(source);
173+
return filterGlobalOwnKeys(ownKeys);
174+
}
175+
172176
export function linkIntrinsics(
173177
env: VirtualEnvironment,
174178
globalObjectVirtualizationTarget: typeof globalThis

packages/near-membrane-dom/src/browser-realm.ts

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
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+
79149
export 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

Comments
 (0)