From df24f02e921ebebe608b7cd58374cecb8cdcd974 Mon Sep 17 00:00:00 2001 From: Kuitos Date: Fri, 15 Oct 2021 16:55:32 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20avoid=20illegal=20invocation=20e?= =?UTF-8?q?rror=20while=20using=20some=20dom=20api=20in=20nest=20sandbox?= =?UTF-8?q?=20(#1768)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sandbox/__tests__/proxySandbox.test.ts | 27 +++++++++++++++++++ .../dynamicAppend/forStrictSandbox.ts | 3 +-- src/sandbox/proxySandbox.ts | 23 +++++++++++----- src/utils.ts | 2 ++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/sandbox/__tests__/proxySandbox.test.ts b/src/sandbox/__tests__/proxySandbox.test.ts index 962e3b5..aa69e4e 100644 --- a/src/sandbox/__tests__/proxySandbox.test.ts +++ b/src/sandbox/__tests__/proxySandbox.test.ts @@ -383,3 +383,30 @@ it('native window function calling should always be bound with window', () => { const { proxy } = new ProxySandbox('mustBeBoundWithWindowReference'); 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!/); + }); +}); diff --git a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts index b8a492c..16cbd38 100644 --- a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts +++ b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts @@ -4,6 +4,7 @@ */ import type { Freer } from '../../../interfaces'; +import { nativeGlobal } from '../../../utils'; import { getCurrentRunningApp } from '../../common'; import type { ContainerConfig } from './common'; import { @@ -21,8 +22,6 @@ declare global { } // 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 }); // Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record diff --git a/src/sandbox/proxySandbox.ts b/src/sandbox/proxySandbox.ts index 2810b21..785839e 100644 --- a/src/sandbox/proxySandbox.ts +++ b/src/sandbox/proxySandbox.ts @@ -5,9 +5,13 @@ */ import type { SandBox } from '../interfaces'; import { SandBoxType } from '../interfaces'; -import { nextTask } from '../utils'; +import { nativeGlobal, nextTask } from '../utils'; import { getTargetValue, setCurrentRunningApp } from './common'; +type SymbolTarget = 'target' | 'globalContext'; + +type FakeWindow = Window & Record; + /** * fastest(at most time) unique array method * @see https://jsperf.com/array-filter-unique/30 @@ -57,9 +61,10 @@ const unscopables = { Float32Array: true, }; -type SymbolTarget = 'target' | 'globalContext'; - -type FakeWindow = Window & Record; +const useNativeWindowForBindingsProps = new Map([ + ['fetch', true], + ['mockDomAPIInBlackList', process.env.NODE_ENV === 'test'], +]); function createFakeWindow(globalContext: Window) { // map always has the fastest performance in has check scenario @@ -269,13 +274,19 @@ export default class ProxySandbox implements SandBox { return eval; } - // eslint-disable-next-line no-nested-ternary const value = propertiesWithGetter.has(p) ? (globalContext as any)[p] : p in target ? (target 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 diff --git a/src/utils.ts b/src/utils.ts index 0c37508..3926718 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -109,6 +109,8 @@ export function getWrapperId(id: string) { return `__qiankun_microapp_wrapper_for_${snakeCase(id)}__`; } +export const nativeGlobal = new Function('return this')(); + /** 校验子应用导出的 生命周期 对象是否正确 */ export function validateExportLifecycle(exports: any) { const { bootstrap, mount, unmount } = exports ?? {};