Skip to content

Commit

Permalink
[fix] Add the same event listener only once
Browse files Browse the repository at this point in the history
Prevent `WebSocket.prototype.addEventListener()` from adding the same
event listener multiple times.
  • Loading branch information
lpinca committed Nov 6, 2022
1 parent 9ab743a commit 1cec17d
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 24 deletions.
24 changes: 17 additions & 7 deletions lib/event-target.js
Expand Up @@ -178,15 +178,25 @@ const EventTarget = {
* Register an event listener.
*
* @param {String} type A string representing the event type to listen for
* @param {(Function|Object)} listener The listener to add
* @param {(Function|Object)} handler The listener to add
* @param {Object} [options] An options object specifies characteristics about
* the event listener
* @param {Boolean} [options.once=false] A `Boolean` indicating that the
* listener should be invoked at most once after being added. If `true`,
* the listener would be automatically removed when invoked.
* @public
*/
addEventListener(type, listener, options = {}) {
addEventListener(type, handler, options = {}) {
for (const listener of this.listeners(type)) {
if (
!options[kForOnEventAttribute] &&
listener[kListener] === handler &&
!listener[kForOnEventAttribute]
) {
return;
}
}

let wrapper;

if (type === 'message') {
Expand All @@ -196,7 +206,7 @@ const EventTarget = {
});

event[kTarget] = this;
callListener(listener, this, event);
callListener(handler, this, event);
};
} else if (type === 'close') {
wrapper = function onClose(code, message) {
Expand All @@ -207,7 +217,7 @@ const EventTarget = {
});

event[kTarget] = this;
callListener(listener, this, event);
callListener(handler, this, event);
};
} else if (type === 'error') {
wrapper = function onError(error) {
Expand All @@ -217,21 +227,21 @@ const EventTarget = {
});

event[kTarget] = this;
callListener(listener, this, event);
callListener(handler, this, event);
};
} else if (type === 'open') {
wrapper = function onOpen() {
const event = new Event('open');

event[kTarget] = this;
callListener(listener, this, event);
callListener(handler, this, event);
};
} else {
return;
}

wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
wrapper[kListener] = listener;
wrapper[kListener] = handler;

if (options.once) {
this.once(type, wrapper);
Expand Down
41 changes: 24 additions & 17 deletions test/websocket.test.js
Expand Up @@ -3257,10 +3257,13 @@ describe('WebSocket', () => {
ws.addEventListener('foo', () => {});
assert.strictEqual(ws.listenerCount('foo'), 0);

ws.addEventListener('open', () => {
function onOpen() {
events.push('open');
assert.strictEqual(ws.listenerCount('open'), 1);
});
}

ws.addEventListener('open', onOpen);
ws.addEventListener('open', onOpen);

assert.strictEqual(ws.listenerCount('open'), 1);

Expand All @@ -3273,9 +3276,28 @@ describe('WebSocket', () => {
};

ws.addEventListener('message', listener, { once: true });
ws.addEventListener('message', listener);

assert.strictEqual(ws.listenerCount('message'), 1);

ws.addEventListener('close', NOOP);
ws.onclose = NOOP;

let listeners = ws.listeners('close');

assert.strictEqual(listeners.length, 2);
assert.strictEqual(listeners[0][kListener], NOOP);
assert.strictEqual(listeners[1][kListener], NOOP);

ws.onerror = NOOP;
ws.addEventListener('error', NOOP);

listeners = ws.listeners('error');

assert.strictEqual(listeners.length, 2);
assert.strictEqual(listeners[0][kListener], NOOP);
assert.strictEqual(listeners[1][kListener], NOOP);

ws.emit('open');
ws.emit('message', EMPTY_BUFFER, false);

Expand Down Expand Up @@ -3353,21 +3375,6 @@ describe('WebSocket', () => {
assert.strictEqual(ws.listenerCount('message'), 0);
assert.strictEqual(ws.listenerCount('open'), 0);

// Multiple listeners.
ws.addEventListener('message', NOOP);
ws.addEventListener('message', NOOP);

assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);
assert.strictEqual(ws.listeners('message')[1][kListener], NOOP);

ws.removeEventListener('message', NOOP);

assert.strictEqual(ws.listeners('message')[0][kListener], NOOP);

ws.removeEventListener('message', NOOP);

assert.strictEqual(ws.listenerCount('message'), 0);

// Listeners not added with `websocket.addEventListener()`.
ws.on('message', NOOP);

Expand Down

0 comments on commit 1cec17d

Please sign in to comment.