mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
lib: implement passive listener behavior per spec
Implements the WHATWG DOM specification for passive event listeners, ensuring that calls to `preventDefault()` are correctly ignored within a passive listener context. An internal `kInPassiveListener` state is added to the Event object to track when a passive listener is executing. The `preventDefault()` method and the `returnValue` setter are modified to check this state, as well as the event's `cancelable` property. This state is reliably cleaned up within a `finally` block to prevent state pollution in case a listener throws an error. This resolves previously failing Web Platform Tests (WPT) in `AddEventListenerOptions-passive.any.js`. Refs: https://dom.spec.whatwg.org/#dom-event-preventdefault PR-URL: https://github.com/nodejs/node/pull/59995 Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com> Reviewed-By: Mattias Buelens <mattias@buelens.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jason Zhang <xzha4350@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
60f1a5d077
commit
6f941fcfba
|
|
@ -76,6 +76,7 @@ const { now } = require('internal/perf/utils');
|
|||
|
||||
const kType = Symbol('type');
|
||||
const kDetail = Symbol('detail');
|
||||
const kInPassiveListener = Symbol('kInPassiveListener');
|
||||
|
||||
const isTrustedSet = new SafeWeakSet();
|
||||
const isTrusted = ObjectGetOwnPropertyDescriptor({
|
||||
|
|
@ -127,6 +128,7 @@ class Event {
|
|||
|
||||
this[kTarget] = null;
|
||||
this[kIsBeingDispatched] = false;
|
||||
this[kInPassiveListener] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -178,6 +180,7 @@ class Event {
|
|||
preventDefault() {
|
||||
if (!isEvent(this))
|
||||
throw new ERR_INVALID_THIS('Event');
|
||||
if (!this.#cancelable || this[kInPassiveListener]) return;
|
||||
this.#defaultPrevented = true;
|
||||
}
|
||||
|
||||
|
|
@ -266,6 +269,19 @@ class Event {
|
|||
return !this.#cancelable || !this.#defaultPrevented;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
set returnValue(value) {
|
||||
if (!isEvent(this))
|
||||
throw new ERR_INVALID_THIS('Event');
|
||||
|
||||
if (!value) {
|
||||
if (!this.#cancelable || this[kInPassiveListener]) return;
|
||||
this.#defaultPrevented = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
|
|
@ -760,7 +776,6 @@ class EventTarget {
|
|||
throw new ERR_EVENT_RECURSION(event.type);
|
||||
|
||||
this[kHybridDispatch](event, event.type, event);
|
||||
|
||||
return event.defaultPrevented !== true;
|
||||
}
|
||||
|
||||
|
|
@ -813,8 +828,8 @@ class EventTarget {
|
|||
this[kRemoveListener](root.size, type, listener, capture);
|
||||
}
|
||||
|
||||
let arg;
|
||||
try {
|
||||
let arg;
|
||||
if (handler.isNodeStyleListener) {
|
||||
arg = nodeValue;
|
||||
} else {
|
||||
|
|
@ -824,6 +839,9 @@ class EventTarget {
|
|||
handler.callback.deref() : handler.callback;
|
||||
let result;
|
||||
if (callback) {
|
||||
if (handler.passive && !handler.isNodeStyleListener) {
|
||||
arg[kInPassiveListener] = true;
|
||||
}
|
||||
result = FunctionPrototypeCall(callback, this, arg);
|
||||
if (!handler.isNodeStyleListener) {
|
||||
arg[kIsBeingDispatched] = false;
|
||||
|
|
@ -833,6 +851,9 @@ class EventTarget {
|
|||
addCatch(result);
|
||||
} catch (err) {
|
||||
emitUncaughtException(err);
|
||||
} finally {
|
||||
if (arg?.[kInPassiveListener])
|
||||
arg[kInPassiveListener] = false;
|
||||
}
|
||||
|
||||
handler = next;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
require('../common');
|
||||
|
||||
// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/AddEventListenerOptions-passive.html
|
||||
// in order to define the `document` ourselves
|
||||
|
|
@ -58,7 +58,6 @@ const {
|
|||
testPassiveValue({}, true);
|
||||
testPassiveValue({ passive: false }, true);
|
||||
|
||||
common.skip('TODO: passive listeners is still broken');
|
||||
testPassiveValue({ passive: 1 }, false);
|
||||
testPassiveValue({ passive: true }, false);
|
||||
testPassiveValue({ passive: 0 }, true);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,4 @@
|
|||
{
|
||||
"AddEventListenerOptions-passive.any.js": {
|
||||
"fail": {
|
||||
"expected": [
|
||||
"preventDefault should be ignored if-and-only-if the passive option is true",
|
||||
"returnValue should be ignored if-and-only-if the passive option is true",
|
||||
"passive behavior of one listener should be unaffected by the presence of other listeners"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Event-dispatch-listener-order.window.js": {
|
||||
"skip": "document is not defined"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user