Skip to content

Commit

Permalink
fix: update near-membrane-dom to use ShadowRealm
Browse files Browse the repository at this point in the history
  • Loading branch information
rwaldron committed Mar 24, 2022
1 parent c550f35 commit 84fd7f8
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 23 deletions.
8 changes: 6 additions & 2 deletions packages/near-membrane-base/src/intrinsics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,8 @@ export function assignFilteredGlobalDescriptorsFromPropertyDescriptorMap<
return descMap;
}

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

export function getFilteredGlobalOwnKeys(source: object): (string | symbol)[] {
const ownKeys = ReflectOwnKeys(source);
return filterGlobalOwnKeys(ownKeys);
}

export function linkIntrinsics(
env: VirtualEnvironment,
globalObjectVirtualizationTarget: typeof globalThis
Expand Down
105 changes: 84 additions & 21 deletions packages/near-membrane-dom/src/browser-realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
assignFilteredGlobalDescriptorsFromPropertyDescriptorMap,
createConnector,
createMembraneMarshall,
filterGlobalOwnKeys,
getFilteredGlobalOwnKeys,
linkIntrinsics,
DistortionCallback,
Expand Down Expand Up @@ -76,6 +77,75 @@ function createDetachableIframe(): HTMLIFrameElement {
return iframe;
}

interface BrowserEnvironmentOptions {
distortionCallback?: DistortionCallback;
endowments?: PropertyDescriptorMap;
keepAlive?: boolean;
instrumentation?: Instrumentation | undefined;
}

interface RealmController {
cleanup: () => {};
evaluator: typeof eval;
getGlobalOwnKeys: () => (string | symbol)[];
needsExplicitCleanUp: boolean;
}
// @ts-ignore: TypeScript doesn't know what a ShadowRealm is yet.
const { ShadowRealm: ShadowRealmCtor } = globalThis;
function createRealmController(options: BrowserEnvironmentOptions): RealmController {
let cleanup = () => {};
let evaluator: typeof eval;
let getGlobalOwnKeys: () => (string | symbol)[];
let needsExplicitCleanUp = false;

if (typeof ShadowRealmCtor === 'function') {
// @ts-ignore: TypeScript doesn't know what a ShadowRealm is yet.
const shadowRealm = new ShadowRealm();
evaluator = shadowRealm.evaluate.bind(shadowRealm);
const getGlobalThisOwnKeysFromShadowRealm = evaluator(`(callableKeysCallback) => {
let ownKeys = Reflect.ownKeys(globalThis);
Reflect.apply(callableKeysCallback, undefined, ownKeys);
};`);
let keys: (string | symbol)[];
getGlobalOwnKeys = () => {
getGlobalThisOwnKeysFromShadowRealm((...args: (string | symbol)[]) => {
keys = args;
});
return filterWindowKeys(filterGlobalOwnKeys(keys));
};
} else {
const { keepAlive } = options;
const iframe = createDetachableIframe();
const redWindow = ReflectApply(
HTMLIFrameElementProtoContentWindowGetter,
iframe,
[]
)!.window;
getGlobalOwnKeys = () => filterWindowKeys(getFilteredGlobalOwnKeys(redWindow));
const { document: redDocument } = redWindow;
cleanup = () => {
// Once we get the iframe info ready, and all mapped, we can proceed
// to detach the iframe only if the keepAlive option isn't true.
if (keepAlive) {
// TODO: Temporary hack to preserve the document reference in Firefox.
// https://bugzilla.mozilla.org/show_bug.cgi?id=543435
ReflectApply(DocumentProtoOpen, redDocument, []);
ReflectApply(DocumentProtoClose, redDocument, []);
} else {
ReflectApply(ElementProtoRemove, iframe, []);
}
};
evaluator = redWindow.eval;
needsExplicitCleanUp = true;
}
return {
cleanup,
evaluator,
getGlobalOwnKeys,
needsExplicitCleanUp,
} as RealmController;
}

export default function createVirtualEnvironment(
globalObjectShape: object | null,
globalObjectVirtualizationTarget: WindowProxy & typeof globalThis,
Expand All @@ -90,22 +160,25 @@ export default function createVirtualEnvironment(
) {
throw new TypeErrorCtor('Missing global object virtualization target.');
}
const options = ObjectAssign(
{
__proto__: null,
keepAlive: false,
},
providedOptions
);
const {
distortionCallback,
endowments,
instrumentation,
keepAlive = false,
keepAlive,
// eslint-disable-next-line prefer-object-spread
} = ObjectAssign({ __proto__: null }, providedOptions);
const iframe = createDetachableIframe();
const {
window: redWindow,
window: { document: redDocument },
} = ReflectApply(HTMLIFrameElementProtoContentWindowGetter, iframe, [])!;
} = options;
const realmController = createRealmController(options);
let globalOwnKeys;
if (globalObjectShape === null) {
if (defaultGlobalOwnKeys === null) {
defaultGlobalOwnKeys = filterWindowKeys(getFilteredGlobalOwnKeys(redWindow));
defaultGlobalOwnKeys = realmController.getGlobalOwnKeys();
}
globalOwnKeys = defaultGlobalOwnKeys;
} else {
Expand All @@ -117,7 +190,7 @@ export default function createVirtualEnvironment(
// https://bugs.chromium.org/p/chromium/issues/detail?id=1305302
const unforgeableGlobalThisKeys = keepAlive ? undefined : unforgeablePoisonedWindowKeys;
const blueConnector = createHooksCallback;
const redConnector = createConnector(redWindow.eval);
const redConnector = createConnector(realmController.evaluator);
// Extract the global references and descriptors before any interference.
const blueRefs = getCachedGlobalObjectReferences(globalObjectVirtualizationTarget);
// Create a new environment.
Expand Down Expand Up @@ -154,18 +227,8 @@ export default function createVirtualEnvironment(
// We intentionally skip remapping Window.prototype because there is nothing
// in it that needs to be remapped.
env.lazyRemap(blueRefs.EventTargetProto, blueRefs.EventTargetProtoOwnKeys);
// We don't remap `blueRefs.WindowPropertiesProto` because it is "magical"
// in that it provides access to elements by id.
//
// Once we get the iframe info ready, and all mapped, we can proceed
// to detach the iframe only if the keepAlive option isn't true.
if (keepAlive) {
// TODO: Temporary hack to preserve the document reference in Firefox.
// https://bugzilla.mozilla.org/show_bug.cgi?id=543435
ReflectApply(DocumentProtoOpen, redDocument, []);
ReflectApply(DocumentProtoClose, redDocument, []);
} else {
ReflectApply(ElementProtoRemove, iframe, []);
if (realmController.needsExplicitCleanUp) {
realmController.cleanup();
}
return env;
}

0 comments on commit 84fd7f8

Please sign in to comment.