| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -59,6 +59,7 @@ const { | |||
| 59 | 59 | } = require('internal/util/inspect'); | |
| 60 | 60 | ||
| 61 | 61 | let spliceOne; | |
| 62 | + let kResistStopPropagation; | ||
| 62 | 63 | ||
| 63 | 64 | const { | |
| 64 | 65 | AbortError, | |
@@ -981,7 +982,10 @@ async function once(emitter, name, options = kEmptyObject) { | |||
| 981 | 982 | } | |
| 982 | 983 | resolve(args); | |
| 983 | 984 | }; | |
| 984 | - eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); | ||
| 985 | + | ||
| 986 | + kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation; | ||
| 987 | + const opts = { __proto__: null, once: true, [kResistStopPropagation]: true }; | ||
| 988 | + eventTargetAgnosticAddListener(emitter, name, resolver, opts); | ||
| 985 | 989 | if (name !== 'error' && typeof emitter.once === 'function') { | |
| 986 | 990 | // EventTarget does not have `error` event semantics like Node | |
| 987 | 991 | // EventEmitters, we listen to `error` events only on EventEmitters. | |
@@ -994,7 +998,7 @@ async function once(emitter, name, options = kEmptyObject) { | |||
| 994 | 998 | } | |
| 995 | 999 | if (signal != null) { | |
| 996 | 1000 | eventTargetAgnosticAddListener( | |
| 997 | - signal, 'abort', abortListener, { once: true }); | ||
| 1001 | + signal, 'abort', abortListener, { __proto__: null, once: true, [kResistStopPropagation]: true }); | ||
| 998 | 1002 | } | |
| 999 | 1003 | }); | |
| 1000 | 1004 | } | |
@@ -1119,11 +1123,12 @@ function on(emitter, event, options = kEmptyObject) { | |||
| 1119 | 1123 | } | |
| 1120 | 1124 | ||
| 1121 | 1125 | if (signal) { | |
| 1126 | + kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation; | ||
| 1122 | 1127 | eventTargetAgnosticAddListener( | |
| 1123 | 1128 | signal, | |
| 1124 | 1129 | 'abort', | |
| 1125 | 1130 | abortListener, | |
| 1126 | - { once: true }); | ||
| 1131 | + { __proto__: null, once: true, [kResistStopPropagation]: true }); | ||
| 1127 | 1132 | } | |
| 1128 | 1133 | ||
| 1129 | 1134 | function abortListener() { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -59,6 +59,7 @@ const kStop = Symbol('kStop'); | |||
| 59 | 59 | const kTarget = Symbol('kTarget'); | |
| 60 | 60 | const kHandlers = Symbol('kHandlers'); | |
| 61 | 61 | const kWeakHandler = Symbol('kWeak'); | |
| 62 | + const kResistStopPropagation = Symbol('kResistStopPropagation'); | ||
| 62 | 63 | ||
| 63 | 64 | const kHybridDispatch = SymbolFor('nodejs.internal.kHybridDispatch'); | |
| 64 | 65 | const kCreateEvent = Symbol('kCreateEvent'); | |
@@ -403,6 +404,7 @@ const kFlagPassive = 1 << 2; | |||
| 403 | 404 | const kFlagNodeStyle = 1 << 3; | |
| 404 | 405 | const kFlagWeak = 1 << 4; | |
| 405 | 406 | const kFlagRemoved = 1 << 5; | |
| 407 | + const kFlagResistStopPropagation = 1 << 6; | ||
| 406 | 408 | ||
| 407 | 409 | // The listeners for an EventTarget are maintained as a linked list. | |
| 408 | 410 | // Unfortunately, the way EventTarget is defined, listeners are accounted | |
@@ -413,7 +415,7 @@ const kFlagRemoved = 1 << 5; | |||
| 413 | 415 | // slower. | |
| 414 | 416 | class Listener { | |
| 415 | 417 | constructor(previous, listener, once, capture, passive, | |
| 416 | - isNodeStyleListener, weak) { | ||
| 418 | + isNodeStyleListener, weak, resistStopPropagation) { | ||
| 417 | 419 | this.next = undefined; | |
| 418 | 420 | if (previous !== undefined) | |
| 419 | 421 | previous.next = this; | |
@@ -431,6 +433,8 @@ class Listener { | |||
| 431 | 433 | flags |= kFlagNodeStyle; | |
| 432 | 434 | if (weak) | |
| 433 | 435 | flags |= kFlagWeak; | |
| 436 | + if (resistStopPropagation) | ||
| 437 | + flags |= kFlagResistStopPropagation; | ||
| 434 | 438 | this.flags = flags; | |
| 435 | 439 | ||
| 436 | 440 | this.removed = false; | |
@@ -468,6 +472,9 @@ class Listener { | |||
| 468 | 472 | get weak() { | |
| 469 | 473 | return Boolean(this.flags & kFlagWeak); | |
| 470 | 474 | } | |
| 475 | + get resistStopPropagation() { | ||
| 476 | + return Boolean(this.flags & kFlagResistStopPropagation); | ||
| 477 | + } | ||
| 471 | 478 | get removed() { | |
| 472 | 479 | return Boolean(this.flags & kFlagRemoved); | |
| 473 | 480 | } | |
@@ -564,6 +571,7 @@ class EventTarget { | |||
| 564 | 571 | signal, | |
| 565 | 572 | isNodeStyleListener, | |
| 566 | 573 | weak, | |
| 574 | + resistStopPropagation, | ||
| 567 | 575 | } = validateEventListenerOptions(options); | |
| 568 | 576 | ||
| 569 | 577 | if (!validateEventListener(listener)) { | |
@@ -588,16 +596,16 @@ class EventTarget { | |||
| 588 | 596 | // not prevent the event target from GC. | |
| 589 | 597 | signal.addEventListener('abort', () => { | |
| 590 | 598 | this.removeEventListener(type, listener, options); | |
| 591 | - }, { once: true, [kWeakHandler]: this }); | ||
| 599 | + }, { __proto__: null, once: true, [kWeakHandler]: this, [kResistStopPropagation]: true }); | ||
| 592 | 600 | } | |
| 593 | 601 | ||
| 594 | 602 | let root = this[kEvents].get(type); | |
| 595 | 603 | ||
| 596 | 604 | if (root === undefined) { | |
| 597 | - root = { size: 1, next: undefined }; | ||
| 605 | + root = { size: 1, next: undefined, resistStopPropagation: Boolean(resistStopPropagation) }; | ||
| 598 | 606 | // This is the first handler in our linked list. | |
| 599 | 607 | new Listener(root, listener, once, capture, passive, | |
| 600 | - isNodeStyleListener, weak); | ||
| 608 | + isNodeStyleListener, weak, resistStopPropagation); | ||
| 601 | 609 | this[kNewListener]( | |
| 602 | 610 | root.size, | |
| 603 | 611 | type, | |
@@ -624,8 +632,9 @@ class EventTarget { | |||
| 624 | 632 | } | |
| 625 | 633 | ||
| 626 | 634 | new Listener(previous, listener, once, capture, passive, | |
| 627 | - isNodeStyleListener, weak); | ||
| 635 | + isNodeStyleListener, weak, resistStopPropagation); | ||
| 628 | 636 | root.size++; | |
| 637 | + root.resistStopPropagation ||= Boolean(resistStopPropagation); | ||
| 629 | 638 | this[kNewListener](root.size, type, listener, once, capture, passive, weak); | |
| 630 | 639 | } | |
| 631 | 640 | ||
@@ -709,14 +718,21 @@ class EventTarget { | |||
| 709 | 718 | let handler = root.next; | |
| 710 | 719 | let next; | |
| 711 | 720 | ||
| 712 | - while (handler !== undefined && | ||
| 713 | - (handler.passive || event?.[kStop] !== true)) { | ||
| 721 | + const iterationCondition = () => { | ||
| 722 | + if (handler === undefined) { | ||
| 723 | + return false; | ||
| 724 | + } | ||
| 725 | + return root.resistStopPropagation || handler.passive || event?.[kStop] !== true; | ||
| 726 | + }; | ||
| 727 | + while (iterationCondition()) { | ||
| 714 | 728 | // Cache the next item in case this iteration removes the current one | |
| 715 | 729 | next = handler.next; | |
| 716 | 730 | ||
| 717 | - if (handler.removed) { | ||
| 731 | + if (handler.removed || (event?.[kStop] === true && !handler.resistStopPropagation)) { | ||
| 718 | 732 | // Deal with the case an event is removed while event handlers are | |
| 719 | 733 | // Being processed (removeEventListener called from a listener) | |
| 734 | + // And the case of event.stopImmediatePropagation() being called | ||
| 735 | + // For events not flagged as resistStopPropagation | ||
| 720 | 736 | handler = next; | |
| 721 | 737 | continue; | |
| 722 | 738 | } | |
@@ -984,6 +1000,7 @@ function validateEventListenerOptions(options) { | |||
| 984 | 1000 | passive: Boolean(options.passive), | |
| 985 | 1001 | signal: options.signal, | |
| 986 | 1002 | weak: options[kWeakHandler], | |
| 1003 | + resistStopPropagation: options[kResistStopPropagation] ?? false, | ||
| 987 | 1004 | isNodeStyleListener: Boolean(options[kIsNodeStyleListener]), | |
| 988 | 1005 | }; | |
| 989 | 1006 | } | |
@@ -1099,5 +1116,6 @@ module.exports = { | |||
| 1099 | 1116 | kRemoveListener, | |
| 1100 | 1117 | kEvents, | |
| 1101 | 1118 | kWeakHandler, | |
| 1119 | + kResistStopPropagation, | ||
| 1102 | 1120 | isEventTarget, | |
| 1103 | 1121 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -233,6 +233,18 @@ async function eventTargetAbortSignalBefore() { | |||
| 233 | 233 | }); | |
| 234 | 234 | } | |
| 235 | 235 | ||
| 236 | + async function eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped() { | ||
| 237 | + const et = new EventTarget(); | ||
| 238 | + const ac = new AbortController(); | ||
| 239 | + const { signal } = ac; | ||
| 240 | + signal.addEventListener('abort', (e) => e.stopImmediatePropagation(), { once: true }); | ||
| 241 | + | ||
| 242 | + process.nextTick(() => ac.abort()); | ||
| 243 | + return rejects(once(et, 'foo', { signal }), { | ||
| 244 | + name: 'AbortError', | ||
| 245 | + }); | ||
| 246 | + } | ||
| 247 | + | ||
| 236 | 248 | async function eventTargetAbortSignalAfter() { | |
| 237 | 249 | const et = new EventTarget(); | |
| 238 | 250 | const ac = new AbortController(); | |
@@ -270,6 +282,7 @@ Promise.all([ | |||
| 270 | 282 | abortSignalAfterEvent(), | |
| 271 | 283 | abortSignalRemoveListener(), | |
| 272 | 284 | eventTargetAbortSignalBefore(), | |
| 285 | + eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped(), | ||
| 273 | 286 | eventTargetAbortSignalAfter(), | |
| 274 | 287 | eventTargetAbortSignalAfterEvent(), | |
| 275 | 288 | ]).then(common.mustCall()); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -717,3 +717,15 @@ let asyncTest = Promise.resolve(); | |||
| 717 | 717 | et.removeEventListener(Symbol('symbol'), () => {}); | |
| 718 | 718 | }, TypeError); | |
| 719 | 719 | } | |
| 720 | + | ||
| 721 | + { | ||
| 722 | + // Test that event listeners are removed by signal even when | ||
| 723 | + // signal's abort event propagation stopped | ||
| 724 | + const controller = new AbortController(); | ||
| 725 | + const { signal } = controller; | ||
| 726 | + signal.addEventListener('abort', (e) => e.stopImmediatePropagation(), { once: true }); | ||
| 727 | + const et = new EventTarget(); | ||
| 728 | + et.addEventListener('foo', common.mustNotCall(), { signal }); | ||
| 729 | + controller.abort(); | ||
| 730 | + et.dispatchEvent(new Event('foo')); | ||
| 731 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments