diff --git a/docs/api/README.md b/docs/api/README.md index c2a5094..83c5d50 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -396,7 +396,7 @@ A criterion for judging whether the business is closely related: Look at - Usage - init global state, and return actions for communication. It is recommended to use in master, and slave get actions through props。 + init global state, and return actions for communication. It is recommended to use in master, and slave get actions through props. - Return diff --git a/src/sandbox/__tests__/proxySandbox.test.ts b/src/sandbox/__tests__/proxySandbox.test.ts index 9fefdf9..962e3b5 100644 --- a/src/sandbox/__tests__/proxySandbox.test.ts +++ b/src/sandbox/__tests__/proxySandbox.test.ts @@ -4,7 +4,7 @@ */ import { isBoundedFunction } from '../../utils'; -import { getCurrentRunningSandboxProxy } from '../common'; +import { getCurrentRunningApp } from '../common'; import ProxySandbox from '../proxySandbox'; declare global { @@ -234,17 +234,21 @@ test('document and eval accessing should modify the attachDocProxySymbol value e const proxy4 = new ProxySandbox('eval-access-test2').proxy; const d1 = proxy1.document; - expect(getCurrentRunningSandboxProxy()).toBe(proxy1); + expect(getCurrentRunningApp()?.window).toBe(proxy1); + expect(getCurrentRunningApp()?.name).toBe('doc-access-test1'); const d2 = proxy2.document; - expect(getCurrentRunningSandboxProxy()).toBe(proxy2); + expect(getCurrentRunningApp()?.window).toBe(proxy2); + expect(getCurrentRunningApp()?.name).toBe('doc-access-test2'); expect(d1).toBe(d2); expect(d1).toBe(document); const eval1 = proxy3.eval; - expect(getCurrentRunningSandboxProxy()).toBe(proxy3); + expect(getCurrentRunningApp()?.window).toBe(proxy3); + expect(getCurrentRunningApp()?.name).toBe('eval-access-test1'); const eval2 = proxy4.eval; - expect(getCurrentRunningSandboxProxy()).toBe(proxy4); + expect(getCurrentRunningApp()?.window).toBe(proxy4); + expect(getCurrentRunningApp()?.name).toBe('eval-access-test2'); expect(eval1).toBe(eval2); // eslint-disable-next-line no-eval @@ -257,10 +261,11 @@ test('document attachDocProxySymbol mark should be remove before next task', (do // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars const d1 = proxy.document; - expect(getCurrentRunningSandboxProxy()).toBe(proxy); + expect(getCurrentRunningApp()?.window).toBe(proxy); + expect(getCurrentRunningApp()?.name).toBe('doc-symbol'); setTimeout(() => { - expect(getCurrentRunningSandboxProxy()).toBeNull(); + expect(getCurrentRunningApp()).toBeNull(); done(); }); }); @@ -357,11 +362,12 @@ it('should get current running sandbox proxy correctly', async () => { const { proxy } = new ProxySandbox('running'); await Promise.resolve().then(() => { - expect(getCurrentRunningSandboxProxy()).toBeNull(); + expect(getCurrentRunningApp()).toBeNull(); // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars const unused = proxy.accessing; - expect(getCurrentRunningSandboxProxy()).toBe(proxy); + expect(getCurrentRunningApp()?.window).toBe(proxy); + expect(getCurrentRunningApp()?.name).toBe('running'); }); }); diff --git a/src/sandbox/common.ts b/src/sandbox/common.ts index 681fb1a..36ef7b8 100644 --- a/src/sandbox/common.ts +++ b/src/sandbox/common.ts @@ -7,21 +7,26 @@ import { isBoundedFunction, isCallable, isConstructable } from '../utils'; declare global { interface Window { - __currentRunningSandboxProxy__: WindowProxy | null; + __currentRunningAppInSandbox__: { name: string; window: WindowProxy } | null; } } // 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, '__currentRunningSandboxProxy__', { enumerable: false, writable: true }); -export function getCurrentRunningSandboxProxy() { - return nativeGlobal.__currentRunningSandboxProxy__; +Object.defineProperty(nativeGlobal, '__currentRunningAppInSandbox__', { enumerable: false, writable: true }); + +/** + * get the app that running tasks at current tick + * @warning this method only works with proxy sandbox, right now it is for internal use only. + */ +export function getCurrentRunningApp() { + return nativeGlobal.__currentRunningAppInSandbox__; } -export function setCurrentRunningSandboxProxy(proxy: WindowProxy | null) { - // set currentRunningSandboxProxy to global window, as its only use case is for document.createElement from now on, which hijacked by a global way - nativeGlobal.__currentRunningSandboxProxy__ = proxy; +export function setCurrentRunningApp(instance: { name: string; window: WindowProxy } | null) { + // set currentRunningApp and it's proxySandbox to global window, as its only use case is for document.createElement from now on, which hijacked by a global way + nativeGlobal.__currentRunningAppInSandbox__ = instance; } const functionBoundedValueMap = new WeakMap(); diff --git a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts index fadb29a..ab4ee4e 100644 --- a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts +++ b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts @@ -4,7 +4,7 @@ */ import type { Freer } from '../../../interfaces'; -import { getCurrentRunningSandboxProxy } from '../../common'; +import { getCurrentRunningApp } from '../../common'; import type { ContainerConfig } from './common'; import { isHijackingTag, @@ -27,7 +27,7 @@ function patchDocumentCreateElement() { ): HTMLElement { const element = rawDocumentCreateElement.call(this, tagName, options); if (isHijackingTag(tagName)) { - const currentRunningSandboxProxy = getCurrentRunningSandboxProxy(); + const { window: currentRunningSandboxProxy } = getCurrentRunningApp() || {}; if (currentRunningSandboxProxy) { const proxyContainerConfig = proxyAttachContainerConfigMap.get(currentRunningSandboxProxy); if (proxyContainerConfig) { diff --git a/src/sandbox/proxySandbox.ts b/src/sandbox/proxySandbox.ts index 1a1c554..6d35bcd 100644 --- a/src/sandbox/proxySandbox.ts +++ b/src/sandbox/proxySandbox.ts @@ -6,7 +6,7 @@ import type { SandBox } from '../interfaces'; import { SandBoxType } from '../interfaces'; import { nextTask } from '../utils'; -import { getTargetValue, setCurrentRunningSandboxProxy } from './common'; +import { getTargetValue, setCurrentRunningApp } from './common'; /** * fastest(at most time) unique array method @@ -122,6 +122,16 @@ function createFakeWindow(global: Window) { let activeSandboxCount = 0; +function registerRunningApp(name: string, proxy: Window) { + setCurrentRunningApp({ name, window: proxy }); + // FIXME if you have any other good ideas + // remove the mark in next tick, thus we can identify whether it in micro app or not + // this approach is just a workaround, it could not cover all complex cases, such as the micro app runs in the same task context with master in some case + nextTask(() => { + setCurrentRunningApp(null); + }); +} + /** * 基于 Proxy 实现的沙箱 */ @@ -177,6 +187,7 @@ export default class ProxySandbox implements SandBox { const proxy = new Proxy(fakeWindow, { set: (target: FakeWindow, p: PropertyKey, value: any): boolean => { if (this.sandboxRunning) { + registerRunningApp(name, proxy); // We must kept its description while the property existed in rawWindow before if (!target.hasOwnProperty(p) && rawWindow.hasOwnProperty(p)) { const descriptor = Object.getOwnPropertyDescriptor(rawWindow, p); @@ -216,13 +227,7 @@ export default class ProxySandbox implements SandBox { get(target: FakeWindow, p: PropertyKey): any { if (p === Symbol.unscopables) return unscopables; - - setCurrentRunningSandboxProxy(proxy); - // FIXME if you have any other good ideas - // remove the mark in next tick, thus we can identify whether it in micro app or not - // this approach is just a workaround, it could not cover all complex cases, such as the micro app runs in the same task context with master in some case - nextTask(() => setCurrentRunningSandboxProxy(null)); - + registerRunningApp(name, proxy); // avoid who using window.window or window.self to escape the sandbox environment to touch the really window // see https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js#L13 if (p === 'window' || p === 'self') { @@ -323,6 +328,7 @@ export default class ProxySandbox implements SandBox { }, deleteProperty(target: FakeWindow, p: string | number | symbol): boolean { + registerRunningApp(name, proxy); if (target.hasOwnProperty(p)) { // @ts-ignore delete target[p];