Scrubbing of detached DOM elements, prevents memory leaks (#5265)

* Scrubbing of detached DOM elements, prevents memory leaks and fixes #5263

* Make linter happy
This commit is contained in:
Iván Sánchez Ortega 2017-02-02 10:57:57 +01:00 committed by Per Liedman
parent 5603a87c70
commit 14c5f1602c
8 changed files with 59 additions and 12 deletions

View File

@ -161,6 +161,11 @@ Map.include({
}, },
_clearControlPos: function () { _clearControlPos: function () {
for (var i in this._controlCorners) {
DomUtil.remove(this._controlCorners[i]);
}
DomUtil.remove(this._controlContainer); DomUtil.remove(this._controlContainer);
delete this._controlCorners;
delete this._controlContainer;
} }
}); });

View File

@ -23,7 +23,6 @@ export var _pointersCount = 0;
// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
export function addPointerListener(obj, type, handler, id) { export function addPointerListener(obj, type, handler, id) {
if (type === 'touchstart') { if (type === 'touchstart') {
_addPointerStart(obj, handler, id); _addPointerStart(obj, handler, id);

View File

@ -37,6 +37,8 @@ export function on(obj, types, fn, context) {
return this; return this;
} }
var eventsKey = '_leaflet_events';
// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
// Removes a previously added listener function. If no function is specified, // Removes a previously added listener function. If no function is specified,
// it will remove all the listeners of that particular DOM event from the element. // it will remove all the listeners of that particular DOM event from the element.
@ -46,25 +48,30 @@ export function on(obj, types, fn, context) {
// @alternative // @alternative
// @function off(el: HTMLElement, eventMap: Object, context?: Object): this // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
// @alternative
// @function off(el: HTMLElement): this
// Removes all known event listeners
export function off(obj, types, fn, context) { export function off(obj, types, fn, context) {
if (typeof types === 'object') { if (typeof types === 'object') {
for (var type in types) { for (var type in types) {
removeOne(obj, type, types[type], fn); removeOne(obj, type, types[type], fn);
} }
} else { } else if (types) {
types = Util.splitWords(types); types = Util.splitWords(types);
for (var i = 0, len = types.length; i < len; i++) { for (var i = 0, len = types.length; i < len; i++) {
removeOne(obj, types[i], fn, context); removeOne(obj, types[i], fn, context);
} }
} else {
for (var j in obj[eventsKey]) {
removeOne(obj, j, obj[eventsKey][j]);
}
delete obj[eventsKey];
} }
return this;
} }
var eventsKey = '_leaflet_events';
function addOne(obj, type, fn, context) { function addOne(obj, type, fn, context) {
var id = type + Util.stamp(fn) + (context ? '_' + Util.stamp(context) : ''); var id = type + Util.stamp(fn) + (context ? '_' + Util.stamp(context) : '');

View File

@ -67,6 +67,13 @@ export var Canvas = Renderer.extend({
this._ctx = container.getContext('2d'); this._ctx = container.getContext('2d');
}, },
_destroyContainer: function () {
delete this._ctx;
L.DomUtil.remove(this._container);
L.DomEvent.off(this._container);
delete this._container;
},
_updatePaths: function () { _updatePaths: function () {
if (this._postponeUpdatePaths) { return; } if (this._postponeUpdatePaths) { return; }

View File

@ -58,8 +58,8 @@ export var Renderer = Layer.extend({
}, },
onRemove: function () { onRemove: function () {
DomUtil.remove(this._container);
this.off('update', this._updatePaths, this); this.off('update', this._updatePaths, this);
this._destroyContainer();
}, },
getEvents: function () { getEvents: function () {

View File

@ -61,6 +61,13 @@ export var SVG = Renderer.extend({
this._container.appendChild(this._rootGroup); this._container.appendChild(this._rootGroup);
}, },
_destroyContainer: function () {
L.DomUtil.remove(this._container);
L.DomEvent.off(this._container);
delete this._container;
delete this._rootGroup;
},
_onZoomStart: function () { _onZoomStart: function () {
// Drag-then-pinch interactions might mess up the center and zoom. // Drag-then-pinch interactions might mess up the center and zoom.
// In this case, the easiest way to prevent this is re-do the renderer // In this case, the easiest way to prevent this is re-do the renderer

View File

@ -716,9 +716,18 @@ export var Map = Evented.extend({
this.fire('unload'); this.fire('unload');
} }
for (var i in this._layers) { var i;
for (i in this._layers) {
this._layers[i].remove(); this._layers[i].remove();
} }
for (i in this._panes) {
L.DomUtil.remove(this._panes[i]);
}
this._layers = [];
this._panes = [];
delete this._mapPane;
delete this._renderer;
return this; return this;
}, },
@ -1507,12 +1516,12 @@ export var Map = Evented.extend({
this.on('zoomanim', function (e) { this.on('zoomanim', function (e) {
var prop = DomUtil.TRANSFORM, var prop = DomUtil.TRANSFORM,
transform = proxy.style[prop]; transform = this._proxy.style[prop];
DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); DomUtil.setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
// workaround for case when transform is the same and so transitionend event is not fired // workaround for case when transform is the same and so transitionend event is not fired
if (transform === proxy.style[prop] && this._animatingZoom) { if (transform === this._proxy.style[prop] && this._animatingZoom) {
this._onZoomTransitionEnd(); this._onZoomTransitionEnd();
} }
}, this); }, this);
@ -1520,8 +1529,15 @@ export var Map = Evented.extend({
this.on('load moveend', function () { this.on('load moveend', function () {
var c = this.getCenter(), var c = this.getCenter(),
z = this.getZoom(); z = this.getZoom();
DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1)); DomUtil.setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
}, this); }, this);
this._on('unload', this._destroyAnimProxy, this);
},
_destroyAnimProxy: function () {
L.DomUtil.remove(this._proxy);
delete this._proxy;
}, },
_catchTransitionEnd: function (e) { _catchTransitionEnd: function (e) {

View File

@ -25,6 +25,7 @@ export var BoxZoom = Handler.extend({
this._map = map; this._map = map;
this._container = map._container; this._container = map._container;
this._pane = map._panes.overlayPane; this._pane = map._panes.overlayPane;
map.on('unload', this._destroy, this);
}, },
addHooks: function () { addHooks: function () {
@ -39,6 +40,11 @@ export var BoxZoom = Handler.extend({
return this._moved; return this._moved;
}, },
_destroy: function () {
L.DomUtil.remove(this._pane);
delete this._pane;
},
_resetState: function () { _resetState: function () {
this._moved = false; this._moved = false;
}, },