events: Handle a range of this values for dispatchEvent

On the web, dispatchEvent is finicky about its `this` value. An exception is
thrown for `this` values which are not an EventTarget.

PR-URL: https://github.com/nodejs/node/pull/34015
Reviewed-By: Denys Otrishko <shishugi@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Zirak authored and James M Snell committed 2020-06-23 17:08:09 -07:00
1 parent 4b3654e923
commit a30babf672
2 files changed
+47 -1

No files matched your search

+22 -1
View File
@@ -9,6 +9,7 @@ const {
Object,
Set,
Symbol,
SymbolFor,
SymbolToStringTag,
} = primordials;
@@ -16,7 +17,8 @@ const {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_EVENT_RECURSION,
ERR_MISSING_ARGS
ERR_MISSING_ARGS,
ERR_INVALID_THIS,
}
} = require('internal/errors');
const { validateInteger, validateObject } = require('internal/validators');
@@ -24,6 +26,8 @@ const { validateInteger, validateObject } = require('internal/validators');
const { customInspectSymbol } = require('internal/util');
const { inspect } = require('util');
const kIsEventTarget = SymbolFor('nodejs.event_target');
const kEvents = Symbol('kEvents');
const kStop = Symbol('kStop');
const kTarget = Symbol('kTarget');
@@ -185,6 +189,10 @@ class Listener {
}
class EventTarget {
// Used in checking whether an object is an EventTarget. This is a well-known
// symbol as EventTarget may be used cross-realm. See discussion in #33661.
static [kIsEventTarget] = true;
[kEvents] = new Map();
#emitting = new Set();
@@ -257,6 +265,10 @@ class EventTarget {
throw new ERR_INVALID_ARG_TYPE('event', 'Event', event);
}
if (!isEventTarget(this)) {
throw new ERR_INVALID_THIS('EventTarget');
}
if (this.#emitting.has(event.type) ||
event[kTarget] !== null) {
throw new ERR_EVENT_RECURSION(event.type);
@@ -438,6 +450,15 @@ function validateEventListenerOptions(options) {
};
}
// Test whether the argument is an event object. This is far from a fool-proof
// test, for example this input will result in a false positive:
// > isEventTarget({ constructor: EventTarget })
// It stands in its current implementation as a compromise. For the relevant
// discussion, see #33661.
function isEventTarget(obj) {
return obj && obj.constructor && obj.constructor[kIsEventTarget];
}
function addCatch(that, promise, event) {
const then = promise.then;
if (typeof then === 'function') {
+25
View File
@@ -439,3 +439,28 @@ ok(EventTarget);
const event = new Event('');
strictEqual(event.toString(), '[object Event]');
}
{
// `this` value of dispatchEvent
const target = new EventTarget();
const target2 = new EventTarget();
const event = new Event('foo');
ok(target.dispatchEvent.call(target2, event));
[
'foo',
{},
[],
1,
null,
undefined,
false,
Symbol(),
/a/
].forEach((i) => {
throws(() => target.dispatchEvent.call(i, event), {
code: 'ERR_INVALID_THIS'
});
});
}