Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit f6972c4

Browse files
author
Marcel Gerber
committed
Merge pull request #10868 from adobe/pflynn/listener-leak-canary
Listener leak "canary" warning
2 parents 40ad8e9 + 9ec3d09 commit f6972c4

4 files changed

Lines changed: 46 additions & 3 deletions

File tree

src/extensions/default/JavaScriptCodeHints/ParameterHintManager.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,14 +389,22 @@ define(function (require, exports, module) {
389389
* changes
390390
*/
391391
function installListeners(editor) {
392-
editor.on("keydown", function (event, editor, domEvent) {
392+
editor.on("keydown.ParameterHints", function (event, editor, domEvent) {
393393
if (domEvent.keyCode === KeyEvent.DOM_VK_ESCAPE) {
394394
dismissHint();
395395
}
396-
}).on("scroll", function () {
396+
}).on("scroll.ParameterHints", function () {
397397
dismissHint();
398398
});
399399
}
400+
401+
/**
402+
* Clean up after installListeners()
403+
* @param {!Editor} editor
404+
*/
405+
function uninstallListeners(editor) {
406+
editor.off(".ParameterHints");
407+
}
400408

401409
/**
402410
* Add the function hint command at start up.
@@ -429,6 +437,7 @@ define(function (require, exports, module) {
429437
exports.addCommands = addCommands;
430438
exports.dismissHint = dismissHint;
431439
exports.installListeners = installListeners;
440+
exports.uninstallListeners = uninstallListeners;
432441
exports.isHintDisplayed = isHintDisplayed;
433442
exports.popUpHint = popUpHint;
434443
exports.popUpHintAtOpenParen = popUpHintAtOpenParen;

src/extensions/default/JavaScriptCodeHints/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ define(function (require, exports, module) {
640640
function uninstallEditorListeners(editor) {
641641
if (editor) {
642642
editor.off(HintUtils.eventName("change"));
643+
ParameterHintManager.uninstallListeners(editor);
643644
}
644645
}
645646

src/utils/EventDispatcher.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ define(function (require, exports, module) {
5858
"use strict";
5959

6060
var _ = require("thirdparty/lodash");
61+
62+
var LEAK_WARNING_THRESHOLD = 15;
6163

6264

6365
/**
@@ -88,6 +90,10 @@ define(function (require, exports, module) {
8890
var eventsList = events.split(/\s+/).map(splitNs),
8991
i;
9092

93+
if (!fn) {
94+
throw new Error("EventListener.on() called with no listener fn for event '" + events + "'");
95+
}
96+
9197
// Check for deprecation warnings
9298
if (this._deprecatedEvents) {
9399
for (i = 0; i < eventsList.length; i++) {
@@ -113,6 +119,11 @@ define(function (require, exports, module) {
113119
}
114120
eventsList[i].handler = fn;
115121
this._eventHandlers[eventName].push(eventsList[i]);
122+
123+
// Check for suspicious number of listeners being added to one object-event pair
124+
if (this._eventHandlers[eventName].length > LEAK_WARNING_THRESHOLD) {
125+
console.error("Possible memory leak: " + this._eventHandlers[eventName].length + " '" + eventName + "' listeners attached to", this);
126+
}
116127
}
117128

118129
return this; // for chaining

test/spec/EventDispatcher-test.js

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

2424

2525
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
26-
/*global jasmine, define, describe, beforeEach, it, expect */
26+
/*global jasmine, define, describe, beforeEach, it, expect, spyOn */
2727

2828
define(function (require, exports, module) {
2929
"use strict";
@@ -429,5 +429,27 @@ define(function (require, exports, module) {
429429
expect(fn1).toHaveBeenCalled();
430430
expect(fn2).toHaveBeenCalled();
431431
});
432+
433+
434+
it("on() should print warnings when too many listeners attached", function () {
435+
function makeStubListener() { // avoids JSLint "don't make functions in a loop" complaint
436+
return function () {};
437+
}
438+
439+
spyOn(console, "error");
440+
var i;
441+
for (i = 0; i < 15; i++) {
442+
dispatcher.on("foo", makeStubListener());
443+
}
444+
expect(console.error).not.toHaveBeenCalled();
445+
446+
// Prints warnings when number of listeners exceeds 15
447+
dispatcher.on("foo", fn1);
448+
expect(console.error).toHaveBeenCalled();
449+
450+
// ...but still attaches listener anyway
451+
dispatcher.trigger("foo");
452+
expect(fn1).toHaveBeenCalled();
453+
});
432454
});
433455
});

0 commit comments

Comments
 (0)