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

Commit

Permalink
Create intermediaries for global objects
Browse files Browse the repository at this point in the history
  This is in order to reduce memory leaks in JSDOM
  as it shares prototypes between environments

  Those objects are:

    - Event
    - EventTarget
    - XMLHttpRequestEventTarget
    - HTMLCanvasElement
  • Loading branch information
FreeleX committed Jun 26, 2018
1 parent 0a2f6ff commit 043f18d
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
13 changes: 12 additions & 1 deletion lib/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import {findEventTasks} from '../common/events';
import {patchTimer} from '../common/timers';
import {bindArguments, patchClass, patchMacroTask, patchMethod, patchOnProperties, patchPrototype, scheduleMacroTaskWithCurrentZone, ZONE_SYMBOL_ADD_EVENT_LISTENER, ZONE_SYMBOL_REMOVE_EVENT_LISTENER, zoneSymbol} from '../common/utils';
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';

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

global['Event'] = createConstructorFunctionWrapper(global['Event']);
global['EventTarget'] = createConstructorFunctionWrapper(global['EventTarget']);

if (global['XMLHttpRequestEventTarget'] && global['XMLHttpRequestEventTarget'].prototype) {
const XMLHttpRequestEventTargetProtoProto = createShallowObjectCopy(ObjectGetPrototypeOf(global['XMLHttpRequestEventTarget'].prototype));
global['XMLHttpRequestEventTarget'] = createConstructorFunctionWrapper(global['XMLHttpRequestEventTarget']);
(Object as any).setPrototypeOf(global['XMLHttpRequestEventTarget'].prototype, XMLHttpRequestEventTargetProtoProto);
}

patchEvent(global, api);
eventTargetPatch(global, api);
// patch XMLHttpRequestEventTarget's addEventListener/removeEventListener
Expand All @@ -78,6 +87,8 @@ Zone.__load_patch('on_property', (global: any, Zone: ZoneType, api: _ZonePrivate
});

Zone.__load_patch('canvas', (global: any) => {
global['HTMLCanvasElement'] = createConstructorFunctionWrapper(global['HTMLCanvasElement']);

const HTMLCanvasElement = global['HTMLCanvasElement'];
if (typeof HTMLCanvasElement !== 'undefined' && HTMLCanvasElement.prototype &&
HTMLCanvasElement.prototype.toBlob) {
Expand Down
27 changes: 27 additions & 0 deletions lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,30 @@ export function isIEOrEdge() {
} catch (error) {
}
}

export function createShallowObjectCopy(obj: Object) {
const wrapper = ObjectCreate(obj);

Object.getOwnPropertyNames(obj).forEach((key: string) => {
const desc = Object.getOwnPropertyDescriptor(obj, key);
if (desc) {
Object.defineProperty(wrapper, key, desc);
}
});

return wrapper;
}

export function createConstructorFunctionWrapper(fn: Function | ObjectConstructor) {
if (typeof fn !== 'function') {
return fn;
}

const wrappedFn = function(...args: any[]) {
return new (fn as any)(...args);
}

wrappedFn.prototype = createShallowObjectCopy(fn.prototype);

return wrappedFn;
}

0 comments on commit 043f18d

Please sign in to comment.