Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 043f18d

Browse files
committed
Create intermediaries for global objects
This is in order to reduce memory leaks in JSDOM as it shares prototypes between environments Those objects are: - Event - EventTarget - XMLHttpRequestEventTarget - HTMLCanvasElement
1 parent 0a2f6ff commit 043f18d

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

lib/browser/browser.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import {findEventTasks} from '../common/events';
1414
import {patchTimer} from '../common/timers';
15-
import {bindArguments, patchClass, patchMacroTask, patchMethod, patchOnProperties, patchPrototype, scheduleMacroTaskWithCurrentZone, ZONE_SYMBOL_ADD_EVENT_LISTENER, ZONE_SYMBOL_REMOVE_EVENT_LISTENER, zoneSymbol} from '../common/utils';
15+
import {bindArguments, patchClass, patchMacroTask, patchMethod, patchOnProperties, patchPrototype, scheduleMacroTaskWithCurrentZone, ZONE_SYMBOL_ADD_EVENT_LISTENER, ZONE_SYMBOL_REMOVE_EVENT_LISTENER, zoneSymbol, createConstructorFunctionWrapper, createShallowObjectCopy, ObjectGetPrototypeOf} from '../common/utils';
1616

1717
import {propertyPatch} from './define-property';
1818
import {eventTargetPatch, patchEvent} from './event-target';
@@ -58,6 +58,15 @@ Zone.__load_patch('EventTarget', (global: any, Zone: ZoneType, api: _ZonePrivate
5858
(Zone as any)[SYMBOL_BLACK_LISTED_EVENTS] = global[SYMBOL_BLACK_LISTED_EVENTS];
5959
}
6060

61+
global['Event'] = createConstructorFunctionWrapper(global['Event']);
62+
global['EventTarget'] = createConstructorFunctionWrapper(global['EventTarget']);
63+
64+
if (global['XMLHttpRequestEventTarget'] && global['XMLHttpRequestEventTarget'].prototype) {
65+
const XMLHttpRequestEventTargetProtoProto = createShallowObjectCopy(ObjectGetPrototypeOf(global['XMLHttpRequestEventTarget'].prototype));
66+
global['XMLHttpRequestEventTarget'] = createConstructorFunctionWrapper(global['XMLHttpRequestEventTarget']);
67+
(Object as any).setPrototypeOf(global['XMLHttpRequestEventTarget'].prototype, XMLHttpRequestEventTargetProtoProto);
68+
}
69+
6170
patchEvent(global, api);
6271
eventTargetPatch(global, api);
6372
// patch XMLHttpRequestEventTarget's addEventListener/removeEventListener
@@ -78,6 +87,8 @@ Zone.__load_patch('on_property', (global: any, Zone: ZoneType, api: _ZonePrivate
7887
});
7988

8089
Zone.__load_patch('canvas', (global: any) => {
90+
global['HTMLCanvasElement'] = createConstructorFunctionWrapper(global['HTMLCanvasElement']);
91+
8192
const HTMLCanvasElement = global['HTMLCanvasElement'];
8293
if (typeof HTMLCanvasElement !== 'undefined' && HTMLCanvasElement.prototype &&
8394
HTMLCanvasElement.prototype.toBlob) {

lib/common/utils.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,3 +484,30 @@ export function isIEOrEdge() {
484484
} catch (error) {
485485
}
486486
}
487+
488+
export function createShallowObjectCopy(obj: Object) {
489+
const wrapper = ObjectCreate(obj);
490+
491+
Object.getOwnPropertyNames(obj).forEach((key: string) => {
492+
const desc = Object.getOwnPropertyDescriptor(obj, key);
493+
if (desc) {
494+
Object.defineProperty(wrapper, key, desc);
495+
}
496+
});
497+
498+
return wrapper;
499+
}
500+
501+
export function createConstructorFunctionWrapper(fn: Function | ObjectConstructor) {
502+
if (typeof fn !== 'function') {
503+
return fn;
504+
}
505+
506+
const wrappedFn = function(...args: any[]) {
507+
return new (fn as any)(...args);
508+
}
509+
510+
wrappedFn.prototype = createShallowObjectCopy(fn.prototype);
511+
512+
return wrappedFn;
513+
}

0 commit comments

Comments
 (0)