Skip to content

Commit

Permalink
[feature] Add support for objets with a handleEvent() method
Browse files Browse the repository at this point in the history
Make `WebSocket.prototype.addEventListener()` support an event listener
specified as an object with a `handleEvent()` method.

Fixes #2092
  • Loading branch information
lpinca committed Nov 6, 2022
1 parent 38f7879 commit 9ab743a
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 18 deletions.
4 changes: 2 additions & 2 deletions doc/ws.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ handshake. This allows you to read headers from the server, for example
### websocket.addEventListener(type, listener[, options])

- `type` {String} A string representing the event type to listen for.
- `listener` {Function} The listener to add.
- `listener` {Function|Object} The listener to add.
- `options` {Object}
- `once` {Boolean} A `Boolean` indicating that the listener should be invoked
at most once after being added. If `true`, the listener would be
Expand Down Expand Up @@ -555,7 +555,7 @@ The current state of the connection. This is one of the ready state constants.
### websocket.removeEventListener(type, listener)

- `type` {String} A string representing the event type to remove.
- `listener` {Function} The listener to remove.
- `listener` {Function|Object} The listener to remove.

Removes an event listener emulating the `EventTarget` interface. This method
only removes listeners added with
Expand Down
28 changes: 22 additions & 6 deletions lib/event-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ const EventTarget = {
* Register an event listener.
*
* @param {String} type A string representing the event type to listen for
* @param {Function} listener The listener to add
* @param {(Function|Object)} listener 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
Expand All @@ -196,7 +196,7 @@ const EventTarget = {
});

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

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

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

event[kTarget] = this;
listener.call(this, event);
callListener(listener, this, event);
};
} else {
return;
Expand All @@ -244,7 +244,7 @@ const EventTarget = {
* Remove an event listener.
*
* @param {String} type A string representing the event type to remove
* @param {Function} handler The listener to remove
* @param {(Function|Object)} handler The listener to remove
* @public
*/
removeEventListener(type, handler) {
Expand All @@ -264,3 +264,19 @@ module.exports = {
EventTarget,
MessageEvent
};

/**
* Call an event listener
*
* @param {(Function|Object)} listener The listener to call
* @param {*} thisArg The value to use as `this`` when calling the listener
* @param {Event} event The event to pass to the listener
* @private
*/
function callListener(listener, thisArg, event) {
if (typeof listener === 'object' && listener.handleEvent) {
listener.handleEvent.call(listener, event);
} else {
listener.call(thisArg, event);
}
}
23 changes: 13 additions & 10 deletions test/websocket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3264,14 +3264,15 @@ describe('WebSocket', () => {

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

ws.addEventListener(
'message',
() => {
const listener = {
handleEvent() {
events.push('message');
assert.strictEqual(this, listener);
assert.strictEqual(ws.listenerCount('message'), 0);
},
{ once: true }
);
}
};

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

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

Expand Down Expand Up @@ -3318,17 +3319,19 @@ describe('WebSocket', () => {
it('supports the `removeEventListener` method', () => {
const ws = new WebSocket('ws://localhost', { agent: new CustomAgent() });

ws.addEventListener('message', NOOP);
const listener = { handleEvent() {} };

ws.addEventListener('message', listener);
ws.addEventListener('open', NOOP);

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

ws.removeEventListener('message', () => {});

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

ws.removeEventListener('message', NOOP);
ws.removeEventListener('message', listener);
ws.removeEventListener('open', NOOP);

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

0 comments on commit 9ab743a

Please sign in to comment.