🐛not rebind non-native global properties (#2733)

This commit is contained in:
Kuitos 2023-10-18 01:52:52 -05:00 committed by GitHub
parent aa4a75c5a6
commit 3524195989
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 825 additions and 66 deletions

View File

@ -13,11 +13,14 @@ writeFileSync(
globalsFilePath, globalsFilePath,
`// generated from https://github.com/sindresorhus/globals/blob/main/globals.json es2015 part `// generated from https://github.com/sindresorhus/globals/blob/main/globals.json es2015 part
// only init its values while Proxy is supported // only init its values while Proxy is supported
export const globals = window.Proxy ? ${JSON.stringify( export const globalsInES2015 = window.Proxy ? ${JSON.stringify(
Object.keys(globals.es2015), Object.keys(globals.es2015),
null, null,
2, 2,
)}.filter(p => /* just keep the available properties in current window context */ p in window) : [];`, )}.filter(p => /* just keep the available properties in current window context */ p in window) : [];
export const globalsInBrowser = ${JSON.stringify(Object.keys(globals.browser), null, 2)};
`,
); );
export default { export default {

View File

@ -3,17 +3,17 @@
* @since 2021-04-12 * @since 2021-04-12
*/ */
import { getTargetValue } from '../common'; import { rebindTarget2Fn } from '../common';
describe('getTargetValue', () => { describe('getTargetValue', () => {
it('should work well', () => { it('should work well', () => {
const a1 = getTargetValue(window, undefined); const a1 = rebindTarget2Fn(window, undefined);
expect(a1).toEqual(undefined); expect(a1).toEqual(undefined);
const a2 = getTargetValue(window, null); const a2 = rebindTarget2Fn(window, null);
expect(a2).toEqual(null); expect(a2).toEqual(null);
const a3 = getTargetValue(window, function bindThis(this: any) { const a3 = rebindTarget2Fn(window, function bindThis(this: any) {
return this; return this;
}); });
const a3returns = a3(); const a3returns = a3();
@ -24,7 +24,7 @@ describe('getTargetValue', () => {
function prototypeAddedAfterFirstInvocation(this: any, field: string) { function prototypeAddedAfterFirstInvocation(this: any, field: string) {
this.field = field; this.field = field;
} }
const notConstructableFunction = getTargetValue(window, prototypeAddedAfterFirstInvocation); const notConstructableFunction = rebindTarget2Fn(window, prototypeAddedAfterFirstInvocation);
// `this` of not constructable function will be bound automatically, and it can not be changed by calling with special `this` // `this` of not constructable function will be bound automatically, and it can not be changed by calling with special `this`
const result = {}; const result = {};
notConstructableFunction.call(result, '123'); notConstructableFunction.call(result, '123');
@ -32,7 +32,7 @@ describe('getTargetValue', () => {
expect(window.field).toEqual('123'); expect(window.field).toEqual('123');
prototypeAddedAfterFirstInvocation.prototype.addedFn = () => {}; prototypeAddedAfterFirstInvocation.prototype.addedFn = () => {};
const constructableFunction = getTargetValue(window, prototypeAddedAfterFirstInvocation); const constructableFunction = rebindTarget2Fn(window, prototypeAddedAfterFirstInvocation);
// `this` coule be available if it be predicated as a constructable function // `this` coule be available if it be predicated as a constructable function
const result2 = {}; const result2 = {};
constructableFunction.call(result2, '456'); constructableFunction.call(result2, '456');
@ -40,7 +40,7 @@ describe('getTargetValue', () => {
// window.field not be affected // window.field not be affected
expect(window.field).toEqual('123'); expect(window.field).toEqual('123');
// reference should be stable after first running // reference should be stable after first running
expect(constructableFunction).toBe(getTargetValue(window, prototypeAddedAfterFirstInvocation)); expect(constructableFunction).toBe(rebindTarget2Fn(window, prototypeAddedAfterFirstInvocation));
}); });
it('should work well while value have a readonly prototype on its prototype chain', () => { it('should work well while value have a readonly prototype on its prototype chain', () => {
@ -56,7 +56,7 @@ describe('getTargetValue', () => {
Object.setPrototypeOf(callableFunction, functionWithReadonlyPrototype); Object.setPrototypeOf(callableFunction, functionWithReadonlyPrototype);
const boundFn = getTargetValue(window, callableFunction); const boundFn = rebindTarget2Fn(window, callableFunction);
expect(boundFn.prototype).toBe(callableFunction.prototype); expect(boundFn.prototype).toBe(callableFunction.prototype);
}); });
@ -71,9 +71,9 @@ describe('getTargetValue', () => {
}, },
}); });
const boundFn1 = getTargetValue(window, callableFunction1); const boundFn1 = rebindTarget2Fn(window, callableFunction1);
const boundFn2 = getTargetValue(window, callableFunction2); const boundFn2 = rebindTarget2Fn(window, callableFunction2);
const boundFn3 = getTargetValue(window, callableFunction3); const boundFn3 = rebindTarget2Fn(window, callableFunction3);
expect(boundFn1.toString()).toBe(callableFunction1.toString()); expect(boundFn1.toString()).toBe(callableFunction1.toString());
expect(boundFn2.toString()).toBe(callableFunction2.toString()); expect(boundFn2.toString()).toBe(callableFunction2.toString());

View File

@ -293,16 +293,35 @@ it('document should work well with MutationObserver', (done) => {
docProxy.document.body.innerHTML = '<div></div>'; docProxy.document.body.innerHTML = '<div></div>';
}); });
it('bounded function should not be rebounded', () => { it('native window function calling should always be bound with window', () => {
const proxy = new ProxySandbox('bound-fn-test').proxy as any; window.mockNativeWindowFunction = function mockNativeWindowFunction(this: any) {
const fn = () => {}; if (this !== undefined && this !== window) {
const boundedFn = fn.bind(null); throw new Error('Illegal Invocation!');
proxy.fn1 = fn; }
proxy.fn2 = boundedFn;
expect(proxy.fn1 === fn).toBeFalsy(); return 'success';
expect(proxy.fn2 === boundedFn).toBeTruthy(); };
expect(isBoundedFunction(proxy.fn1)).toBeTruthy();
const { proxy } = new ProxySandbox('mustBeBoundWithWindowReference');
expect(proxy.mockNativeWindowFunction()).toBe('success');
});
it('native bounded function should not be rebounded', () => {
const proxy = new ProxySandbox('bound-native-fn-test').proxy as any;
const boundedFn = atob.bind(null);
proxy.atob = boundedFn;
expect(proxy.atob === boundedFn).toBeTruthy();
expect(isBoundedFunction(proxy.atob)).toBeTruthy();
});
it('non-native function should not be rebounded', () => {
const proxy = new ProxySandbox('non-native-fn-bound-test').proxy as any;
function test() {}
proxy.fn = test;
expect(proxy.fn === test).toBeTruthy();
expect(isBoundedFunction(proxy.fn)).toBeFalsy();
}); });
it('frozen property should not be overwrite', () => { it('frozen property should not be overwrite', () => {
@ -332,12 +351,8 @@ it('frozen property should not be overwrite', () => {
it('the prototype should be kept while we create a function with prototype on proxy', () => { it('the prototype should be kept while we create a function with prototype on proxy', () => {
const proxy = new ProxySandbox('new-function').proxy as any; const proxy = new ProxySandbox('new-function').proxy as any;
proxy.CustomEvent = CustomEvent;
function test() {} expect(proxy.CustomEvent.prototype).toBe(CustomEvent.prototype);
proxy.fn = test;
expect(proxy.fn === test).toBeFalsy();
expect(proxy.fn.prototype).toBe(test.prototype);
}); });
it('some native window property was defined with getter in safari and firefox, and they will check the caller source', () => { it('some native window property was defined with getter in safari and firefox, and they will check the caller source', () => {
@ -399,19 +414,6 @@ it('should get current running sandbox proxy correctly', async () => {
}); });
}); });
it('native window function calling should always be bound with window', () => {
window.nativeWindowFunction = function nativeWindowFunction(this: any) {
if (this !== undefined && this !== window) {
throw new Error('Illegal Invocation!');
}
return 'success';
};
const { proxy } = new ProxySandbox('mustBeBoundWithWindowReference');
expect(proxy.nativeWindowFunction()).toBe('success');
});
describe('should work well while the property existed in global context before', () => { describe('should work well while the property existed in global context before', () => {
it('should not write value while the readonly property existed in global context but not in sandbox', () => { it('should not write value while the readonly property existed in global context but not in sandbox', () => {
Object.defineProperty(window, 'readonlyPropertyInGlobalContext', { Object.defineProperty(window, 'readonlyPropertyInGlobalContext', {

View File

@ -26,60 +26,60 @@ export function clearCurrentRunningApp() {
const functionBoundedValueMap = new WeakMap<CallableFunction, CallableFunction>(); const functionBoundedValueMap = new WeakMap<CallableFunction, CallableFunction>();
export function getTargetValue(target: any, value: any): any { export function rebindTarget2Fn(target: any, fn: any): any {
/* /*
isCallable && !isBoundedFunction && !isConstructable window.consolewindow.atob Illegal invocation isCallable && !isBoundedFunction && !isConstructable window.consolewindow.atob Illegal invocation
prototype prototype
@warning edge case lodash.isFunction iframe top window @warning edge case lodash.isFunction iframe top window
*/ */
if (isCallable(value) && !isBoundedFunction(value) && !isConstructable(value)) { if (isCallable(fn) && !isBoundedFunction(fn) && !isConstructable(fn)) {
const cachedBoundFunction = functionBoundedValueMap.get(value); const cachedBoundFunction = functionBoundedValueMap.get(fn);
if (cachedBoundFunction) { if (cachedBoundFunction) {
return cachedBoundFunction; return cachedBoundFunction;
} }
const boundValue = Function.prototype.bind.call(value, target); const boundValue = Function.prototype.bind.call(fn, target);
// some callable function has custom fields, we need to copy the own props to boundValue. such as moment function. // some callable function has custom fields, we need to copy the own props to boundValue. such as moment function.
Object.getOwnPropertyNames(value).forEach((key) => { Object.getOwnPropertyNames(fn).forEach((key) => {
// boundValue might be a proxy, we need to check the key whether exist in it // boundValue might be a proxy, we need to check the key whether exist in it
if (!boundValue.hasOwnProperty(key)) { if (!boundValue.hasOwnProperty(key)) {
Object.defineProperty(boundValue, key, Object.getOwnPropertyDescriptor(value, key)!); Object.defineProperty(boundValue, key, Object.getOwnPropertyDescriptor(fn, key)!);
} }
}); });
// copy prototype if bound function not have but target one have // copy prototype if bound function not have but target one have
// as prototype is non-enumerable mostly, we need to copy it from target function manually // as prototype is non-enumerable mostly, we need to copy it from target function manually
if (value.hasOwnProperty('prototype') && !boundValue.hasOwnProperty('prototype')) { if (fn.hasOwnProperty('prototype') && !boundValue.hasOwnProperty('prototype')) {
// we should not use assignment operator to set boundValue prototype like `boundValue.prototype = value.prototype` // we should not use assignment operator to set boundValue prototype like `boundValue.prototype = fn.prototype`
// as the assignment will also look up prototype chain while it hasn't own prototype property, // as the assignment will also look up prototype chain while it hasn't own prototype property,
// when the lookup succeed, the assignment will throw an TypeError like `Cannot assign to read only property 'prototype' of function` if its descriptor configured with writable false or just have a getter accessor // when the lookup succeed, the assignment will throw an TypeError like `Cannot assign to read only property 'prototype' of function` if its descriptor configured with writable false or just have a getter accessor
// see https://github.com/umijs/qiankun/issues/1121 // see https://github.com/umijs/qiankun/issues/1121
Object.defineProperty(boundValue, 'prototype', { value: value.prototype, enumerable: false, writable: true }); Object.defineProperty(boundValue, 'prototype', { value: fn.prototype, enumerable: false, writable: true });
} }
// Some util, like `function isNative() { return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) }` relies on the original `toString()` result // Some util, like `function isNative() { return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) }` relies on the original `toString()` result
// but bound functions will always return "function() {[native code]}" for `toString`, which is misleading // but bound functions will always return "function() {[native code]}" for `toString`, which is misleading
if (typeof value.toString === 'function') { if (typeof fn.toString === 'function') {
const valueHasInstanceToString = value.hasOwnProperty('toString') && !boundValue.hasOwnProperty('toString'); const valueHasInstanceToString = fn.hasOwnProperty('toString') && !boundValue.hasOwnProperty('toString');
const boundValueHasPrototypeToString = boundValue.toString === Function.prototype.toString; const boundValueHasPrototypeToString = boundValue.toString === Function.prototype.toString;
if (valueHasInstanceToString || boundValueHasPrototypeToString) { if (valueHasInstanceToString || boundValueHasPrototypeToString) {
const originToStringDescriptor = Object.getOwnPropertyDescriptor( const originToStringDescriptor = Object.getOwnPropertyDescriptor(
valueHasInstanceToString ? value : Function.prototype, valueHasInstanceToString ? fn : Function.prototype,
'toString', 'toString',
); );
Object.defineProperty(boundValue, 'toString', { Object.defineProperty(boundValue, 'toString', {
...originToStringDescriptor, ...originToStringDescriptor,
...(originToStringDescriptor?.get ? null : { value: () => value.toString() }), ...(originToStringDescriptor?.get ? null : { value: () => fn.toString() }),
}); });
} }
} }
functionBoundedValueMap.set(value, boundValue); functionBoundedValueMap.set(fn, boundValue);
return boundValue; return boundValue;
} }
return value; return fn;
} }

View File

@ -1,6 +1,6 @@
// generated from https://github.com/sindresorhus/globals/blob/main/globals.json es2015 part // generated from https://github.com/sindresorhus/globals/blob/main/globals.json es2015 part
// only init its values while Proxy is supported // only init its values while Proxy is supported
export const globals = window.Proxy export const globalsInES2015 = window.Proxy
? [ ? [
'Array', 'Array',
'ArrayBuffer', 'ArrayBuffer',
@ -61,3 +61,743 @@ export const globals = window.Proxy
'WeakSet', 'WeakSet',
].filter((p) => /* just keep the available properties in current window context */ p in window) ].filter((p) => /* just keep the available properties in current window context */ p in window)
: []; : [];
export const globalsInBrowser = [
'AbortController',
'AbortSignal',
'addEventListener',
'alert',
'AnalyserNode',
'Animation',
'AnimationEffectReadOnly',
'AnimationEffectTiming',
'AnimationEffectTimingReadOnly',
'AnimationEvent',
'AnimationPlaybackEvent',
'AnimationTimeline',
'applicationCache',
'ApplicationCache',
'ApplicationCacheErrorEvent',
'atob',
'Attr',
'Audio',
'AudioBuffer',
'AudioBufferSourceNode',
'AudioContext',
'AudioDestinationNode',
'AudioListener',
'AudioNode',
'AudioParam',
'AudioProcessingEvent',
'AudioScheduledSourceNode',
'AudioWorkletGlobalScope',
'AudioWorkletNode',
'AudioWorkletProcessor',
'BarProp',
'BaseAudioContext',
'BatteryManager',
'BeforeUnloadEvent',
'BiquadFilterNode',
'Blob',
'BlobEvent',
'blur',
'BroadcastChannel',
'btoa',
'BudgetService',
'ByteLengthQueuingStrategy',
'Cache',
'caches',
'CacheStorage',
'cancelAnimationFrame',
'cancelIdleCallback',
'CanvasCaptureMediaStreamTrack',
'CanvasGradient',
'CanvasPattern',
'CanvasRenderingContext2D',
'ChannelMergerNode',
'ChannelSplitterNode',
'CharacterData',
'clearInterval',
'clearTimeout',
'clientInformation',
'ClipboardEvent',
'ClipboardItem',
'close',
'closed',
'CloseEvent',
'Comment',
'CompositionEvent',
'CompressionStream',
'confirm',
'console',
'ConstantSourceNode',
'ConvolverNode',
'CountQueuingStrategy',
'createImageBitmap',
'Credential',
'CredentialsContainer',
'crypto',
'Crypto',
'CryptoKey',
'CSS',
'CSSConditionRule',
'CSSFontFaceRule',
'CSSGroupingRule',
'CSSImportRule',
'CSSKeyframeRule',
'CSSKeyframesRule',
'CSSMatrixComponent',
'CSSMediaRule',
'CSSNamespaceRule',
'CSSPageRule',
'CSSPerspective',
'CSSRotate',
'CSSRule',
'CSSRuleList',
'CSSScale',
'CSSSkew',
'CSSSkewX',
'CSSSkewY',
'CSSStyleDeclaration',
'CSSStyleRule',
'CSSStyleSheet',
'CSSSupportsRule',
'CSSTransformValue',
'CSSTranslate',
'CustomElementRegistry',
'customElements',
'CustomEvent',
'DataTransfer',
'DataTransferItem',
'DataTransferItemList',
'DecompressionStream',
'defaultstatus',
'defaultStatus',
'DelayNode',
'DeviceMotionEvent',
'DeviceOrientationEvent',
'devicePixelRatio',
'dispatchEvent',
'document',
'Document',
'DocumentFragment',
'DocumentType',
'DOMError',
'DOMException',
'DOMImplementation',
'DOMMatrix',
'DOMMatrixReadOnly',
'DOMParser',
'DOMPoint',
'DOMPointReadOnly',
'DOMQuad',
'DOMRect',
'DOMRectList',
'DOMRectReadOnly',
'DOMStringList',
'DOMStringMap',
'DOMTokenList',
'DragEvent',
'DynamicsCompressorNode',
'Element',
'ErrorEvent',
'event',
'Event',
'EventSource',
'EventTarget',
'external',
'fetch',
'File',
'FileList',
'FileReader',
'find',
'focus',
'FocusEvent',
'FontFace',
'FontFaceSetLoadEvent',
'FormData',
'FormDataEvent',
'frameElement',
'frames',
'GainNode',
'Gamepad',
'GamepadButton',
'GamepadEvent',
'getComputedStyle',
'getSelection',
'HashChangeEvent',
'Headers',
'history',
'History',
'HTMLAllCollection',
'HTMLAnchorElement',
'HTMLAreaElement',
'HTMLAudioElement',
'HTMLBaseElement',
'HTMLBodyElement',
'HTMLBRElement',
'HTMLButtonElement',
'HTMLCanvasElement',
'HTMLCollection',
'HTMLContentElement',
'HTMLDataElement',
'HTMLDataListElement',
'HTMLDetailsElement',
'HTMLDialogElement',
'HTMLDirectoryElement',
'HTMLDivElement',
'HTMLDListElement',
'HTMLDocument',
'HTMLElement',
'HTMLEmbedElement',
'HTMLFieldSetElement',
'HTMLFontElement',
'HTMLFormControlsCollection',
'HTMLFormElement',
'HTMLFrameElement',
'HTMLFrameSetElement',
'HTMLHeadElement',
'HTMLHeadingElement',
'HTMLHRElement',
'HTMLHtmlElement',
'HTMLIFrameElement',
'HTMLImageElement',
'HTMLInputElement',
'HTMLLabelElement',
'HTMLLegendElement',
'HTMLLIElement',
'HTMLLinkElement',
'HTMLMapElement',
'HTMLMarqueeElement',
'HTMLMediaElement',
'HTMLMenuElement',
'HTMLMetaElement',
'HTMLMeterElement',
'HTMLModElement',
'HTMLObjectElement',
'HTMLOListElement',
'HTMLOptGroupElement',
'HTMLOptionElement',
'HTMLOptionsCollection',
'HTMLOutputElement',
'HTMLParagraphElement',
'HTMLParamElement',
'HTMLPictureElement',
'HTMLPreElement',
'HTMLProgressElement',
'HTMLQuoteElement',
'HTMLScriptElement',
'HTMLSelectElement',
'HTMLShadowElement',
'HTMLSlotElement',
'HTMLSourceElement',
'HTMLSpanElement',
'HTMLStyleElement',
'HTMLTableCaptionElement',
'HTMLTableCellElement',
'HTMLTableColElement',
'HTMLTableElement',
'HTMLTableRowElement',
'HTMLTableSectionElement',
'HTMLTemplateElement',
'HTMLTextAreaElement',
'HTMLTimeElement',
'HTMLTitleElement',
'HTMLTrackElement',
'HTMLUListElement',
'HTMLUnknownElement',
'HTMLVideoElement',
'IDBCursor',
'IDBCursorWithValue',
'IDBDatabase',
'IDBFactory',
'IDBIndex',
'IDBKeyRange',
'IDBObjectStore',
'IDBOpenDBRequest',
'IDBRequest',
'IDBTransaction',
'IDBVersionChangeEvent',
'IdleDeadline',
'IIRFilterNode',
'Image',
'ImageBitmap',
'ImageBitmapRenderingContext',
'ImageCapture',
'ImageData',
'indexedDB',
'innerHeight',
'innerWidth',
'InputEvent',
'IntersectionObserver',
'IntersectionObserverEntry',
'Intl',
'isSecureContext',
'KeyboardEvent',
'KeyframeEffect',
'KeyframeEffectReadOnly',
'length',
'localStorage',
'location',
'Location',
'locationbar',
'matchMedia',
'MediaDeviceInfo',
'MediaDevices',
'MediaElementAudioSourceNode',
'MediaEncryptedEvent',
'MediaError',
'MediaKeyMessageEvent',
'MediaKeySession',
'MediaKeyStatusMap',
'MediaKeySystemAccess',
'MediaList',
'MediaMetadata',
'MediaQueryList',
'MediaQueryListEvent',
'MediaRecorder',
'MediaSettingsRange',
'MediaSource',
'MediaStream',
'MediaStreamAudioDestinationNode',
'MediaStreamAudioSourceNode',
'MediaStreamEvent',
'MediaStreamTrack',
'MediaStreamTrackEvent',
'menubar',
'MessageChannel',
'MessageEvent',
'MessagePort',
'MIDIAccess',
'MIDIConnectionEvent',
'MIDIInput',
'MIDIInputMap',
'MIDIMessageEvent',
'MIDIOutput',
'MIDIOutputMap',
'MIDIPort',
'MimeType',
'MimeTypeArray',
'MouseEvent',
'moveBy',
'moveTo',
'MutationEvent',
'MutationObserver',
'MutationRecord',
'name',
'NamedNodeMap',
'NavigationPreloadManager',
'navigator',
'Navigator',
'NavigatorUAData',
'NetworkInformation',
'Node',
'NodeFilter',
'NodeIterator',
'NodeList',
'Notification',
'OfflineAudioCompletionEvent',
'OfflineAudioContext',
'offscreenBuffering',
'OffscreenCanvas',
'OffscreenCanvasRenderingContext2D',
'onabort',
'onafterprint',
'onanimationend',
'onanimationiteration',
'onanimationstart',
'onappinstalled',
'onauxclick',
'onbeforeinstallprompt',
'onbeforeprint',
'onbeforeunload',
'onblur',
'oncancel',
'oncanplay',
'oncanplaythrough',
'onchange',
'onclick',
'onclose',
'oncontextmenu',
'oncuechange',
'ondblclick',
'ondevicemotion',
'ondeviceorientation',
'ondeviceorientationabsolute',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'ondurationchange',
'onemptied',
'onended',
'onerror',
'onfocus',
'ongotpointercapture',
'onhashchange',
'oninput',
'oninvalid',
'onkeydown',
'onkeypress',
'onkeyup',
'onlanguagechange',
'onload',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onlostpointercapture',
'onmessage',
'onmessageerror',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onoffline',
'ononline',
'onpagehide',
'onpageshow',
'onpause',
'onplay',
'onplaying',
'onpointercancel',
'onpointerdown',
'onpointerenter',
'onpointerleave',
'onpointermove',
'onpointerout',
'onpointerover',
'onpointerup',
'onpopstate',
'onprogress',
'onratechange',
'onrejectionhandled',
'onreset',
'onresize',
'onscroll',
'onsearch',
'onseeked',
'onseeking',
'onselect',
'onstalled',
'onstorage',
'onsubmit',
'onsuspend',
'ontimeupdate',
'ontoggle',
'ontransitionend',
'onunhandledrejection',
'onunload',
'onvolumechange',
'onwaiting',
'onwheel',
'open',
'openDatabase',
'opener',
'Option',
'origin',
'OscillatorNode',
'outerHeight',
'outerWidth',
'OverconstrainedError',
'PageTransitionEvent',
'pageXOffset',
'pageYOffset',
'PannerNode',
'parent',
'Path2D',
'PaymentAddress',
'PaymentRequest',
'PaymentRequestUpdateEvent',
'PaymentResponse',
'performance',
'Performance',
'PerformanceEntry',
'PerformanceLongTaskTiming',
'PerformanceMark',
'PerformanceMeasure',
'PerformanceNavigation',
'PerformanceNavigationTiming',
'PerformanceObserver',
'PerformanceObserverEntryList',
'PerformancePaintTiming',
'PerformanceResourceTiming',
'PerformanceTiming',
'PeriodicWave',
'Permissions',
'PermissionStatus',
'personalbar',
'PhotoCapabilities',
'Plugin',
'PluginArray',
'PointerEvent',
'PopStateEvent',
'postMessage',
'Presentation',
'PresentationAvailability',
'PresentationConnection',
'PresentationConnectionAvailableEvent',
'PresentationConnectionCloseEvent',
'PresentationConnectionList',
'PresentationReceiver',
'PresentationRequest',
'print',
'ProcessingInstruction',
'ProgressEvent',
'PromiseRejectionEvent',
'prompt',
'PushManager',
'PushSubscription',
'PushSubscriptionOptions',
'queueMicrotask',
'RadioNodeList',
'Range',
'ReadableByteStreamController',
'ReadableStream',
'ReadableStreamBYOBReader',
'ReadableStreamBYOBRequest',
'ReadableStreamDefaultController',
'ReadableStreamDefaultReader',
'registerProcessor',
'RemotePlayback',
'removeEventListener',
'reportError',
'Request',
'requestAnimationFrame',
'requestIdleCallback',
'resizeBy',
'ResizeObserver',
'ResizeObserverEntry',
'resizeTo',
'Response',
'RTCCertificate',
'RTCDataChannel',
'RTCDataChannelEvent',
'RTCDtlsTransport',
'RTCIceCandidate',
'RTCIceGatherer',
'RTCIceTransport',
'RTCPeerConnection',
'RTCPeerConnectionIceEvent',
'RTCRtpContributingSource',
'RTCRtpReceiver',
'RTCRtpSender',
'RTCSctpTransport',
'RTCSessionDescription',
'RTCStatsReport',
'RTCTrackEvent',
'screen',
'Screen',
'screenLeft',
'ScreenOrientation',
'screenTop',
'screenX',
'screenY',
'ScriptProcessorNode',
'scroll',
'scrollbars',
'scrollBy',
'scrollTo',
'scrollX',
'scrollY',
'SecurityPolicyViolationEvent',
'Selection',
'self',
'ServiceWorker',
'ServiceWorkerContainer',
'ServiceWorkerRegistration',
'sessionStorage',
'setInterval',
'setTimeout',
'ShadowRoot',
'SharedWorker',
'SourceBuffer',
'SourceBufferList',
'speechSynthesis',
'SpeechSynthesisEvent',
'SpeechSynthesisUtterance',
'StaticRange',
'status',
'statusbar',
'StereoPannerNode',
'stop',
'Storage',
'StorageEvent',
'StorageManager',
'structuredClone',
'styleMedia',
'StyleSheet',
'StyleSheetList',
'SubmitEvent',
'SubtleCrypto',
'SVGAElement',
'SVGAngle',
'SVGAnimatedAngle',
'SVGAnimatedBoolean',
'SVGAnimatedEnumeration',
'SVGAnimatedInteger',
'SVGAnimatedLength',
'SVGAnimatedLengthList',
'SVGAnimatedNumber',
'SVGAnimatedNumberList',
'SVGAnimatedPreserveAspectRatio',
'SVGAnimatedRect',
'SVGAnimatedString',
'SVGAnimatedTransformList',
'SVGAnimateElement',
'SVGAnimateMotionElement',
'SVGAnimateTransformElement',
'SVGAnimationElement',
'SVGCircleElement',
'SVGClipPathElement',
'SVGComponentTransferFunctionElement',
'SVGDefsElement',
'SVGDescElement',
'SVGDiscardElement',
'SVGElement',
'SVGEllipseElement',
'SVGFEBlendElement',
'SVGFEColorMatrixElement',
'SVGFEComponentTransferElement',
'SVGFECompositeElement',
'SVGFEConvolveMatrixElement',
'SVGFEDiffuseLightingElement',
'SVGFEDisplacementMapElement',
'SVGFEDistantLightElement',
'SVGFEDropShadowElement',
'SVGFEFloodElement',
'SVGFEFuncAElement',
'SVGFEFuncBElement',
'SVGFEFuncGElement',
'SVGFEFuncRElement',
'SVGFEGaussianBlurElement',
'SVGFEImageElement',
'SVGFEMergeElement',
'SVGFEMergeNodeElement',
'SVGFEMorphologyElement',
'SVGFEOffsetElement',
'SVGFEPointLightElement',
'SVGFESpecularLightingElement',
'SVGFESpotLightElement',
'SVGFETileElement',
'SVGFETurbulenceElement',
'SVGFilterElement',
'SVGForeignObjectElement',
'SVGGElement',
'SVGGeometryElement',
'SVGGradientElement',
'SVGGraphicsElement',
'SVGImageElement',
'SVGLength',
'SVGLengthList',
'SVGLinearGradientElement',
'SVGLineElement',
'SVGMarkerElement',
'SVGMaskElement',
'SVGMatrix',
'SVGMetadataElement',
'SVGMPathElement',
'SVGNumber',
'SVGNumberList',
'SVGPathElement',
'SVGPatternElement',
'SVGPoint',
'SVGPointList',
'SVGPolygonElement',
'SVGPolylineElement',
'SVGPreserveAspectRatio',
'SVGRadialGradientElement',
'SVGRect',
'SVGRectElement',
'SVGScriptElement',
'SVGSetElement',
'SVGStopElement',
'SVGStringList',
'SVGStyleElement',
'SVGSVGElement',
'SVGSwitchElement',
'SVGSymbolElement',
'SVGTextContentElement',
'SVGTextElement',
'SVGTextPathElement',
'SVGTextPositioningElement',
'SVGTitleElement',
'SVGTransform',
'SVGTransformList',
'SVGTSpanElement',
'SVGUnitTypes',
'SVGUseElement',
'SVGViewElement',
'TaskAttributionTiming',
'Text',
'TextDecoder',
'TextDecoderStream',
'TextEncoder',
'TextEncoderStream',
'TextEvent',
'TextMetrics',
'TextTrack',
'TextTrackCue',
'TextTrackCueList',
'TextTrackList',
'TimeRanges',
'toolbar',
'top',
'Touch',
'TouchEvent',
'TouchList',
'TrackEvent',
'TransformStream',
'TransformStreamDefaultController',
'TransitionEvent',
'TreeWalker',
'UIEvent',
'URL',
'URLSearchParams',
'ValidityState',
'visualViewport',
'VisualViewport',
'VTTCue',
'WaveShaperNode',
'WebAssembly',
'WebGL2RenderingContext',
'WebGLActiveInfo',
'WebGLBuffer',
'WebGLContextEvent',
'WebGLFramebuffer',
'WebGLProgram',
'WebGLQuery',
'WebGLRenderbuffer',
'WebGLRenderingContext',
'WebGLSampler',
'WebGLShader',
'WebGLShaderPrecisionFormat',
'WebGLSync',
'WebGLTexture',
'WebGLTransformFeedback',
'WebGLUniformLocation',
'WebGLVertexArrayObject',
'WebSocket',
'WheelEvent',
'window',
'Window',
'Worker',
'WritableStream',
'WritableStreamDefaultController',
'WritableStreamDefaultWriter',
'XMLDocument',
'XMLHttpRequest',
'XMLHttpRequestEventTarget',
'XMLHttpRequestUpload',
'XMLSerializer',
'XPathEvaluator',
'XPathExpression',
'XPathResult',
'XSLTProcessor',
];

View File

@ -4,7 +4,7 @@
*/ */
import type { SandBox } from '../../interfaces'; import type { SandBox } from '../../interfaces';
import { SandBoxType } from '../../interfaces'; import { SandBoxType } from '../../interfaces';
import { getTargetValue } from '../common'; import { rebindTarget2Fn } from '../common';
function isPropConfigurable(target: WindowProxy, prop: PropertyKey) { function isPropConfigurable(target: WindowProxy, prop: PropertyKey) {
const descriptor = Object.getOwnPropertyDescriptor(target, prop); const descriptor = Object.getOwnPropertyDescriptor(target, prop);
@ -125,7 +125,7 @@ export default class LegacySandbox implements SandBox {
} }
const value = (rawWindow as any)[p]; const value = (rawWindow as any)[p];
return getTargetValue(rawWindow, value); return rebindTarget2Fn(rawWindow, value);
}, },
// trap in operator // trap in operator

View File

@ -1,14 +1,14 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import { without } from 'lodash';
/** /**
* @author Kuitos * @author Kuitos
* @since 2020-3-31 * @since 2020-3-31
*/ */
import { without } from 'lodash';
import type { SandBox } from '../interfaces'; import type { SandBox } from '../interfaces';
import { SandBoxType } from '../interfaces'; import { SandBoxType } from '../interfaces';
import { isPropertyFrozen, nativeGlobal, nextTask } from '../utils'; import { isPropertyFrozen, nativeGlobal, nextTask } from '../utils';
import { clearCurrentRunningApp, getCurrentRunningApp, getTargetValue, setCurrentRunningApp } from './common'; import { clearCurrentRunningApp, getCurrentRunningApp, rebindTarget2Fn, setCurrentRunningApp } from './common';
import { globals } from './globals'; import { globalsInBrowser, globalsInES2015 } from './globals';
type SymbolTarget = 'target' | 'globalContext'; type SymbolTarget = 'target' | 'globalContext';
@ -24,6 +24,13 @@ function uniq(array: Array<string | symbol>) {
}, Object.create(null)); }, Object.create(null));
} }
const cachedGlobalsInBrowser = globalsInBrowser
.concat(process.env.NODE_ENV === 'test' ? ['mockNativeWindowFunction'] : [])
.reduce<Record<string, true>>((acc, key) => ({ ...acc, [key]: true }), Object.create(null));
function isNativeGlobalProp(prop: string): boolean {
return prop in cachedGlobalsInBrowser;
}
// zone.js will overwrite Object.defineProperty // zone.js will overwrite Object.defineProperty
const rawObjectDefineProperty = Object.defineProperty; const rawObjectDefineProperty = Object.defineProperty;
@ -58,7 +65,9 @@ const mockGlobalThis = 'mockGlobalThis';
const accessingSpiedGlobals = ['document', 'top', 'parent', 'eval']; const accessingSpiedGlobals = ['document', 'top', 'parent', 'eval'];
const overwrittenGlobals = ['window', 'self', 'globalThis', 'hasOwnProperty'].concat(inTest ? [mockGlobalThis] : []); const overwrittenGlobals = ['window', 'self', 'globalThis', 'hasOwnProperty'].concat(inTest ? [mockGlobalThis] : []);
export const cachedGlobals = Array.from( export const cachedGlobals = Array.from(
new Set(without(globals.concat(overwrittenGlobals).concat('requestAnimationFrame'), ...accessingSpiedGlobals)), new Set(
without(globalsInES2015.concat(overwrittenGlobals).concat('requestAnimationFrame'), ...accessingSpiedGlobals),
),
); );
// transform cachedGlobals to object for faster element check // transform cachedGlobals to object for faster element check
@ -297,14 +306,20 @@ export default class ProxySandbox implements SandBox {
return value; return value;
} }
// non-native property return directly to avoid rebind
if (!isNativeGlobalProp(p as string) && !useNativeWindowForBindingsProps.has(p)) {
return 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' /* 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: See this code:
const proxy = new Proxy(window, {}); const proxy = new Proxy(window, {});
// in nest sandbox fetch will be bind to proxy rather than window in master
const proxyFetch = fetch.bind(proxy); const proxyFetch = fetch.bind(proxy);
proxyFetch('https://qiankun.com'); proxyFetch('https://qiankun.com');
*/ */
const boundTarget = useNativeWindowForBindingsProps.get(p) ? nativeGlobal : globalContext; const boundTarget = useNativeWindowForBindingsProps.get(p) ? nativeGlobal : globalContext;
return getTargetValue(boundTarget, value); return rebindTarget2Fn(boundTarget, value);
}, },
// trap in operator // trap in operator

View File

@ -2,8 +2,7 @@
* @author Kuitos * @author Kuitos
* @since 2019-05-15 * @since 2019-05-15
*/ */
import { isFunction, memoize, once, snakeCase } from 'lodash';
import { isFunction, once, snakeCase, memoize } from 'lodash';
import type { FrameworkConfiguration } from './interfaces'; import type { FrameworkConfiguration } from './interfaces';
import { version } from './version'; import { version } from './version';