| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -61,6 +61,7 @@ const { | |||
| 61 | 61 | let spliceOne; | |
| 62 | 62 | let FixedQueue; | |
| 63 | 63 | let kFirstEventParam; | |
| 64 | + let kResistStopPropagation; | ||
| 64 | 65 | ||
| 65 | 66 | const { | |
| 66 | 67 | AbortError, | |
@@ -978,7 +979,10 @@ async function once(emitter, name, options = kEmptyObject) { | |||
| 978 | 979 | } | |
| 979 | 980 | resolve(args); | |
| 980 | 981 | }; | |
| 981 | - eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); | ||
| 982 | + | ||
| 983 | + kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation; | ||
| 984 | + const opts = { __proto__: null, once: true, [kResistStopPropagation]: true }; | ||
| 985 | + eventTargetAgnosticAddListener(emitter, name, resolver, opts); | ||
| 982 | 986 | if (name !== 'error' && typeof emitter.once === 'function') { | |
| 983 | 987 | // EventTarget does not have `error` event semantics like Node | |
| 984 | 988 | // EventEmitters, we listen to `error` events only on EventEmitters. | |
@@ -991,7 +995,7 @@ async function once(emitter, name, options = kEmptyObject) { | |||
| 991 | 995 | } | |
| 992 | 996 | if (signal != null) { | |
| 993 | 997 | eventTargetAgnosticAddListener( | |
| 994 | - signal, 'abort', abortListener, { once: true }); | ||
| 998 | + signal, 'abort', abortListener, { __proto__: null, once: true, [kResistStopPropagation]: true }); | ||
| 995 | 999 | } | |
| 996 | 1000 | }); | |
| 997 | 1001 | } | |
@@ -1149,11 +1153,12 @@ function on(emitter, event, options = kEmptyObject) { | |||
| 1149 | 1153 | } | |
| 1150 | 1154 | } | |
| 1151 | 1155 | if (signal) { | |
| 1156 | + kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation; | ||
| 1152 | 1157 | eventTargetAgnosticAddListener( | |
| 1153 | 1158 | signal, | |
| 1154 | 1159 | 'abort', | |
| 1155 | 1160 | abortListener, | |
| 1156 | - { once: true }); | ||
| 1161 | + { __proto__: null, once: true, [kResistStopPropagation]: true }); | ||
| 1157 | 1162 | } | |
| 1158 | 1163 | ||
| 1159 | 1164 | return iterator; | |
| 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'); | |
@@ -421,6 +422,7 @@ const kFlagPassive = 1 << 2; | |||
| 421 | 422 | const kFlagNodeStyle = 1 << 3; | |
| 422 | 423 | const kFlagWeak = 1 << 4; | |
| 423 | 424 | const kFlagRemoved = 1 << 5; | |
| 425 | + const kFlagResistStopPropagation = 1 << 6; | ||
| 424 | 426 | ||
| 425 | 427 | // The listeners for an EventTarget are maintained as a linked list. | |
| 426 | 428 | // Unfortunately, the way EventTarget is defined, listeners are accounted | |
@@ -431,7 +433,7 @@ const kFlagRemoved = 1 << 5; | |||
| 431 | 433 | // slower. | |
| 432 | 434 | class Listener { | |
| 433 | 435 | constructor(previous, listener, once, capture, passive, | |
| 434 | - isNodeStyleListener, weak) { | ||
| 436 | + isNodeStyleListener, weak, resistStopPropagation) { | ||
| 435 | 437 | this.next = undefined; | |
| 436 | 438 | if (previous !== undefined) | |
| 437 | 439 | previous.next = this; | |
@@ -449,6 +451,8 @@ class Listener { | |||
| 449 | 451 | flags |= kFlagNodeStyle; | |
| 450 | 452 | if (weak) | |
| 451 | 453 | flags |= kFlagWeak; | |
| 454 | + if (resistStopPropagation) | ||
| 455 | + flags |= kFlagResistStopPropagation; | ||
| 452 | 456 | this.flags = flags; | |
| 453 | 457 | ||
| 454 | 458 | this.removed = false; | |
@@ -486,6 +490,9 @@ class Listener { | |||
| 486 | 490 | get weak() { | |
| 487 | 491 | return Boolean(this.flags & kFlagWeak); | |
| 488 | 492 | } | |
| 493 | + get resistStopPropagation() { | ||
| 494 | + return Boolean(this.flags & kFlagResistStopPropagation); | ||
| 495 | + } | ||
| 489 | 496 | get removed() { | |
| 490 | 497 | return Boolean(this.flags & kFlagRemoved); | |
| 491 | 498 | } | |
@@ -583,6 +590,7 @@ class EventTarget { | |||
| 583 | 590 | signal, | |
| 584 | 591 | isNodeStyleListener, | |
| 585 | 592 | weak, | |
| 593 | + resistStopPropagation, | ||
| 586 | 594 | } = validateEventListenerOptions(options); | |
| 587 | 595 | ||
| 588 | 596 | validateAbortSignal(signal, 'options.signal'); | |
@@ -609,16 +617,16 @@ class EventTarget { | |||
| 609 | 617 | // not prevent the event target from GC. | |
| 610 | 618 | signal.addEventListener('abort', () => { | |
| 611 | 619 | this.removeEventListener(type, listener, options); | |
| 612 | - }, { once: true, [kWeakHandler]: this }); | ||
| 620 | + }, { __proto__: null, once: true, [kWeakHandler]: this, [kResistStopPropagation]: true }); | ||
| 613 | 621 | } | |
| 614 | 622 | ||
| 615 | 623 | let root = this[kEvents].get(type); | |
| 616 | 624 | ||
| 617 | 625 | if (root === undefined) { | |
| 618 | - root = { size: 1, next: undefined }; | ||
| 626 | + root = { size: 1, next: undefined, resistStopPropagation: Boolean(resistStopPropagation) }; | ||
| 619 | 627 | // This is the first handler in our linked list. | |
| 620 | 628 | new Listener(root, listener, once, capture, passive, | |
| 621 | - isNodeStyleListener, weak); | ||
| 629 | + isNodeStyleListener, weak, resistStopPropagation); | ||
| 622 | 630 | this[kNewListener]( | |
| 623 | 631 | root.size, | |
| 624 | 632 | type, | |
@@ -645,8 +653,9 @@ class EventTarget { | |||
| 645 | 653 | } | |
| 646 | 654 | ||
| 647 | 655 | new Listener(previous, listener, once, capture, passive, | |
| 648 | - isNodeStyleListener, weak); | ||
| 656 | + isNodeStyleListener, weak, resistStopPropagation); | ||
| 649 | 657 | root.size++; | |
| 658 | + root.resistStopPropagation ||= Boolean(resistStopPropagation); | ||
| 650 | 659 | this[kNewListener](root.size, type, listener, once, capture, passive, weak); | |
| 651 | 660 | } | |
| 652 | 661 | ||
@@ -730,14 +739,21 @@ class EventTarget { | |||
| 730 | 739 | let handler = root.next; | |
| 731 | 740 | let next; | |
| 732 | 741 | ||
| 733 | - while (handler !== undefined && | ||
| 734 | - (handler.passive || event?.[kStop] !== true)) { | ||
| 742 | + const iterationCondition = () => { | ||
| 743 | + if (handler === undefined) { | ||
| 744 | + return false; | ||
| 745 | + } | ||
| 746 | + return root.resistStopPropagation || handler.passive || event?.[kStop] !== true; | ||
| 747 | + }; | ||
| 748 | + while (iterationCondition()) { | ||
| 735 | 749 | // Cache the next item in case this iteration removes the current one | |
| 736 | 750 | next = handler.next; | |
| 737 | 751 | ||
| 738 | - if (handler.removed) { | ||
| 752 | + if (handler.removed || (event?.[kStop] === true && !handler.resistStopPropagation)) { | ||
| 739 | 753 | // Deal with the case an event is removed while event handlers are | |
| 740 | 754 | // Being processed (removeEventListener called from a listener) | |
| 755 | + // And the case of event.stopImmediatePropagation() being called | ||
| 756 | + // For events not flagged as resistStopPropagation | ||
| 741 | 757 | handler = next; | |
| 742 | 758 | continue; | |
| 743 | 759 | } | |
@@ -1005,6 +1021,7 @@ function validateEventListenerOptions(options) { | |||
| 1005 | 1021 | passive: Boolean(options.passive), | |
| 1006 | 1022 | signal: options.signal, | |
| 1007 | 1023 | weak: options[kWeakHandler], | |
| 1024 | + resistStopPropagation: options[kResistStopPropagation] ?? false, | ||
| 1008 | 1025 | isNodeStyleListener: Boolean(options[kIsNodeStyleListener]), | |
| 1009 | 1026 | }; | |
| 1010 | 1027 | } | |
@@ -1132,5 +1149,6 @@ module.exports = { | |||
| 1132 | 1149 | kRemoveListener, | |
| 1133 | 1150 | kEvents, | |
| 1134 | 1151 | kWeakHandler, | |
| 1152 | + kResistStopPropagation, | ||
| 1135 | 1153 | isEventTarget, | |
| 1136 | 1154 | }; | |
| 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 | |
|---|---|---|---|
@@ -726,3 +726,15 @@ let asyncTest = Promise.resolve(); | |||
| 726 | 726 | et.removeEventListener(Symbol('symbol'), () => {}); | |
| 727 | 727 | }, TypeError); | |
| 728 | 728 | } | |
| 729 | + | ||
| 730 | + { | ||
| 731 | + // Test that event listeners are removed by signal even when | ||
| 732 | + // signal's abort event propagation stopped | ||
| 733 | + const controller = new AbortController(); | ||
| 734 | + const { signal } = controller; | ||
| 735 | + signal.addEventListener('abort', (e) => e.stopImmediatePropagation(), { once: true }); | ||
| 736 | + const et = new EventTarget(); | ||
| 737 | + et.addEventListener('foo', common.mustNotCall(), { signal }); | ||
| 738 | + controller.abort(); | ||
| 739 | + et.dispatchEvent(new Event('foo')); | ||
| 740 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments