🐛 avoid illegal invocation error while using some dom api in nest sandbox (#1768)

This commit is contained in:
Kuitos 2021-10-15 16:55:32 +08:00 committed by GitHub
parent ab64a7047b
commit df24f02e92
4 changed files with 47 additions and 8 deletions

View File

@ -383,3 +383,30 @@ it('native window function calling should always be bound with window', () => {
const { proxy } = new ProxySandbox('mustBeBoundWithWindowReference'); const { proxy } = new ProxySandbox('mustBeBoundWithWindowReference');
expect(proxy.nativeWindowFunction()).toBe('success'); expect(proxy.nativeWindowFunction()).toBe('success');
}); });
describe('should work with nest sandbox', () => {
it('specified dom api should bound with native window', () => {
const { proxy: sandboxProxy } = new ProxySandbox('sandbox');
const { proxy: nestProxy } = new ProxySandbox('dom-api', sandboxProxy as typeof window);
function mockDomAPI(this: any) {
if (this !== window) {
throw new TypeError('Illegal invocation!');
}
return true;
}
nestProxy.mockDomAPIInBlackList = mockDomAPI;
// must use a new function to avoid cache
nestProxy.mockDomAPINotInBlackList = function fnCp(this: any) {
return mockDomAPI.call(this);
};
expect(nestProxy.mockDomAPIInBlackList()).toBeTruthy();
expect(() => {
nestProxy.mockDomAPINotInBlackList();
}).toThrowError(/Illegal invocation!/);
});
});

View File

@ -4,6 +4,7 @@
*/ */
import type { Freer } from '../../../interfaces'; import type { Freer } from '../../../interfaces';
import { nativeGlobal } from '../../../utils';
import { getCurrentRunningApp } from '../../common'; import { getCurrentRunningApp } from '../../common';
import type { ContainerConfig } from './common'; import type { ContainerConfig } from './common';
import { import {
@ -21,8 +22,6 @@ declare global {
} }
// Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪 // Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪
// eslint-disable-next-line no-new-func
const nativeGlobal: Window = new Function('return this')();
Object.defineProperty(nativeGlobal, '__proxyAttachContainerConfigMap__', { enumerable: false, writable: true }); Object.defineProperty(nativeGlobal, '__proxyAttachContainerConfigMap__', { enumerable: false, writable: true });
// Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record // Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record

View File

@ -5,9 +5,13 @@
*/ */
import type { SandBox } from '../interfaces'; import type { SandBox } from '../interfaces';
import { SandBoxType } from '../interfaces'; import { SandBoxType } from '../interfaces';
import { nextTask } from '../utils'; import { nativeGlobal, nextTask } from '../utils';
import { getTargetValue, setCurrentRunningApp } from './common'; import { getTargetValue, setCurrentRunningApp } from './common';
type SymbolTarget = 'target' | 'globalContext';
type FakeWindow = Window & Record<PropertyKey, any>;
/** /**
* fastest(at most time) unique array method * fastest(at most time) unique array method
* @see https://jsperf.com/array-filter-unique/30 * @see https://jsperf.com/array-filter-unique/30
@ -57,9 +61,10 @@ const unscopables = {
Float32Array: true, Float32Array: true,
}; };
type SymbolTarget = 'target' | 'globalContext'; const useNativeWindowForBindingsProps = new Map<PropertyKey, boolean>([
['fetch', true],
type FakeWindow = Window & Record<PropertyKey, any>; ['mockDomAPIInBlackList', process.env.NODE_ENV === 'test'],
]);
function createFakeWindow(globalContext: Window) { function createFakeWindow(globalContext: Window) {
// map always has the fastest performance in has check scenario // map always has the fastest performance in has check scenario
@ -269,13 +274,19 @@ export default class ProxySandbox implements SandBox {
return eval; return eval;
} }
// eslint-disable-next-line no-nested-ternary
const value = propertiesWithGetter.has(p) const value = propertiesWithGetter.has(p)
? (globalContext as any)[p] ? (globalContext as any)[p]
: p in target : p in target
? (target as any)[p] ? (target as any)[p]
: (globalContext as any)[p]; : (globalContext as any)[p];
return getTargetValue(globalContext, value); /* Some dom api must be bound to native window, otherwise it would cause exception like 'TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation'
See this code:
const proxy = new Proxy(window, {});
const proxyFetch = fetch.bind(proxy);
proxyFetch('https://qiankun.com');
*/
const boundTarget = useNativeWindowForBindingsProps.get(p) ? nativeGlobal : globalContext;
return getTargetValue(boundTarget, value);
}, },
// trap in operator // trap in operator

View File

@ -109,6 +109,8 @@ export function getWrapperId(id: string) {
return `__qiankun_microapp_wrapper_for_${snakeCase(id)}__`; return `__qiankun_microapp_wrapper_for_${snakeCase(id)}__`;
} }
export const nativeGlobal = new Function('return this')();
/** 校验子应用导出的 生命周期 对象是否正确 */ /** 校验子应用导出的 生命周期 对象是否正确 */
export function validateExportLifecycle(exports: any) { export function validateExportLifecycle(exports: any) {
const { bootstrap, mount, unmount } = exports ?? {}; const { bootstrap, mount, unmount } = exports ?? {};