Skip to content

Commit c34f474

Browse files
jasnellcodebytere
authored andcommitted
events: improve argument handling, start passive
Co-authored-by: Benjamin Gruenbaum <[email protected]> PR-URL: #34015 Reviewed-By: Denys Otrishko <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent ea1a2d7 commit c34f474

File tree

4 files changed

+319
-157
lines changed

4 files changed

+319
-157
lines changed

lib/internal/event_target.js

+32-6
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,31 @@ class EventTarget {
203203
[kRemoveListener](size, type, listener, capture) {}
204204

205205
addEventListener(type, listener, options = {}) {
206-
validateListener(listener);
207-
type = String(type);
206+
if (arguments.length < 2)
207+
throw new ERR_MISSING_ARGS('type', 'listener');
208208

209+
// We validateOptions before the shouldAddListeners check because the spec
210+
// requires us to hit getters.
209211
const {
210212
once,
211213
capture,
212214
passive
213215
} = validateEventListenerOptions(options);
214216

217+
if (!shouldAddListener(listener)) {
218+
// The DOM silently allows passing undefined as a second argument
219+
// No error code for this since it is a Warning
220+
// eslint-disable-next-line no-restricted-syntax
221+
const w = new Error(`addEventListener called with ${listener}` +
222+
' which has no effect.');
223+
w.name = 'AddEventListenerArgumentTypeWarning';
224+
w.target = this;
225+
w.type = type;
226+
process.emitWarning(w);
227+
return;
228+
}
229+
type = String(type);
230+
215231
let root = this[kEvents].get(type);
216232

217233
if (root === undefined) {
@@ -242,9 +258,15 @@ class EventTarget {
242258
}
243259

244260
removeEventListener(type, listener, options = {}) {
245-
validateListener(listener);
261+
if (!shouldAddListener(listener))
262+
return;
263+
246264
type = String(type);
247-
const { capture } = validateEventListenerOptions(options);
265+
// TODO(@jasnell): If it's determined this cannot be backported
266+
// to 12.x, then this can be simplified to:
267+
// const capture = Boolean(options?.capture);
268+
const capture = options != null && options.capture === true;
269+
248270
const root = this[kEvents].get(type);
249271
if (root === undefined || root.next === undefined)
250272
return;
@@ -426,13 +448,17 @@ Object.defineProperties(NodeEventTarget.prototype, {
426448

427449
// EventTarget API
428450

429-
function validateListener(listener) {
451+
function shouldAddListener(listener) {
430452
if (typeof listener === 'function' ||
431453
(listener != null &&
432454
typeof listener === 'object' &&
433455
typeof listener.handleEvent === 'function')) {
434-
return;
456+
return true;
435457
}
458+
459+
if (listener == null)
460+
return false;
461+
436462
throw new ERR_INVALID_ARG_TYPE('listener', 'EventListener', listener);
437463
}
438464

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
4+
const common = require('../common');
5+
6+
const {
7+
Event,
8+
EventTarget,
9+
} = require('internal/event_target');
10+
11+
const {
12+
fail,
13+
ok,
14+
strictEqual
15+
} = require('assert');
16+
17+
// Manually ported from WPT AddEventListenerOptions-passive.html
18+
{
19+
const document = new EventTarget();
20+
let supportsPassive = false;
21+
const query_options = {
22+
get passive() {
23+
supportsPassive = true;
24+
return false;
25+
},
26+
get dummy() {
27+
fail('dummy value getter invoked');
28+
return false;
29+
}
30+
};
31+
32+
document.addEventListener('test_event', null, query_options);
33+
ok(supportsPassive);
34+
35+
supportsPassive = false;
36+
document.removeEventListener('test_event', null, query_options);
37+
strictEqual(supportsPassive, false);
38+
}
39+
{
40+
function testPassiveValue(optionsValue, expectedDefaultPrevented) {
41+
const document = new EventTarget();
42+
let defaultPrevented;
43+
function handler(e) {
44+
if (e.defaultPrevented) {
45+
fail('Event prematurely marked defaultPrevented');
46+
}
47+
e.preventDefault();
48+
defaultPrevented = e.defaultPrevented;
49+
}
50+
document.addEventListener('test', handler, optionsValue);
51+
// TODO the WHATWG test is more extensive here and tests dispatching on
52+
// document.body, if we ever support getParent we should amend this
53+
const ev = new Event('test', { bubbles: true, cancelable: true });
54+
const uncanceled = document.dispatchEvent(ev);
55+
56+
strictEqual(defaultPrevented, expectedDefaultPrevented);
57+
strictEqual(uncanceled, !expectedDefaultPrevented);
58+
59+
document.removeEventListener('test', handler, optionsValue);
60+
}
61+
testPassiveValue(undefined, true);
62+
testPassiveValue({}, true);
63+
testPassiveValue({ passive: false }, true);
64+
65+
common.skip('TODO: passive listeners is still broken');
66+
testPassiveValue({ passive: 1 }, false);
67+
testPassiveValue({ passive: true }, false);
68+
testPassiveValue({ passive: 0 }, true);
69+
}

0 commit comments

Comments
 (0)