Revert legacy plugin modules (#18638)
Dominic Gannaway committed
Apr 16, 2020 at 18:46 UTC
b1a083183c2f834e8550b4d7437bb0db1a51fdfb
6 files changed
+233
-85
packages/react-dom/src/events/DOMLegacyEventPluginSystem.js
+202
@@ -15,6 +15,7 @@ import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
15
import type {PluginModule} from 'legacy-events/PluginModuleType';
16
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
17
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
18
+import forEachAccumulated from 'legacy-events/forEachAccumulated';
19
20
import {
21
HostRoot,
@@ -45,6 +46,7 @@ import {
46
} from './DOMTopLevelEventTypes';
47
import {addTrappedEventListener} from './ReactDOMEventListener';
48
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
49
+import getListener from './getListener';
50
51
/**
52
* Summary of `DOMEventPluginSystem` event handling:
@@ -396,3 +398,203 @@ export function legacyTrapCapturedEvent(
398
);
399
listenerMap.set(topLevelType, {passive: undefined, listener});
400
}
401
+
402
+function getParent(inst: Object | null): Object | null {
403
+ if (!inst) {
404
+ return null;
405
+ }
406
+ do {
407
+ inst = inst.return;
408
+ // TODO: If this is a HostRoot we might want to bail out.
409
+ // That is depending on if we want nested subtrees (layers) to bubble
410
+ // events to their parent. We could also go through parentNode on the
411
+ // host node but that wouldn't work for React Native and doesn't let us
412
+ // do the portal feature.
413
+ } while (inst && inst.tag !== HostComponent);
414
+ if (inst) {
415
+ return inst;
416
+ }
417
+ return null;
418
+}
419
+
420
+/**
421
+ * Simulates the traversal of a two-phase, capture/bubble event dispatch.
422
+ */
423
+function traverseTwoPhase(
424
+ inst: Object,
425
+ fn: Function,
426
+ arg: ReactSyntheticEvent,
427
+) {
428
+ const path = [];
429
+ while (inst) {
430
+ path.push(inst);
431
+ inst = getParent(inst);
432
+ }
433
+ let i;
434
+ for (i = path.length; i-- > 0; ) {
435
+ fn(path[i], 'captured', arg);
436
+ }
437
+ for (i = 0; i < path.length; i++) {
438
+ fn(path[i], 'bubbled', arg);
439
+ }
440
+}
441
+
442
+function listenerAtPhase(inst, event, propagationPhase) {
443
+ const registrationName =
444
+ event.dispatchConfig.phasedRegistrationNames[propagationPhase];
445
+ return getListener(inst, registrationName);
446
+}
447
+
448
+/**
449
+ * Return the lowest common ancestor of A and B, or null if they are in
450
+ * different trees.
451
+ */
452
+export function getLowestCommonAncestor(
453
+ instA: Object,
454
+ instB: Object,
455
+): Object | null {
456
+ let depthA = 0;
457
+ for (let tempA = instA; tempA; tempA = getParent(tempA)) {
458
+ depthA++;
459
+ }
460
+ let depthB = 0;
461
+ for (let tempB = instB; tempB; tempB = getParent(tempB)) {
462
+ depthB++;
463
+ }
464
+
465
+ // If A is deeper, crawl up.
466
+ while (depthA - depthB > 0) {
467
+ instA = getParent(instA);
468
+ depthA--;
469
+ }
470
+
471
+ // If B is deeper, crawl up.
472
+ while (depthB - depthA > 0) {
473
+ instB = getParent(instB);
474
+ depthB--;
475
+ }
476
+
477
+ // Walk in lockstep until we find a match.
478
+ let depth = depthA;
479
+ while (depth--) {
480
+ if (instA === instB || instA === instB.alternate) {
481
+ return instA;
482
+ }
483
+ instA = getParent(instA);
484
+ instB = getParent(instB);
485
+ }
486
+ return null;
487
+}
488
+
489
+/**
490
+ * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
491
+ * should would receive a `mouseEnter` or `mouseLeave` event.
492
+ *
493
+ * Does not invoke the callback on the nearest common ancestor because nothing
494
+ * "entered" or "left" that element.
495
+ */
496
+export function traverseEnterLeave(
497
+ from: Object,
498
+ to: Object,
499
+ fn: Function,
500
+ argFrom: ReactSyntheticEvent,
501
+ argTo: ReactSyntheticEvent,
502
+) {
503
+ const common = from && to ? getLowestCommonAncestor(from, to) : null;
504
+ const pathFrom = [];
505
+ while (true) {
506
+ if (!from) {
507
+ break;
508
+ }
509
+ if (from === common) {
510
+ break;
511
+ }
512
+ const alternate = from.alternate;
513
+ if (alternate !== null && alternate === common) {
514
+ break;
515
+ }
516
+ pathFrom.push(from);
517
+ from = getParent(from);
518
+ }
519
+ const pathTo = [];
520
+ while (true) {
521
+ if (!to) {
522
+ break;
523
+ }
524
+ if (to === common) {
525
+ break;
526
+ }
527
+ const alternate = to.alternate;
528
+ if (alternate !== null && alternate === common) {
529
+ break;
530
+ }
531
+ pathTo.push(to);
532
+ to = getParent(to);
533
+ }
534
+ for (let i = 0; i < pathFrom.length; i++) {
535
+ fn(pathFrom[i], 'bubbled', argFrom);
536
+ }
537
+ for (let i = pathTo.length; i-- > 0; ) {
538
+ fn(pathTo[i], 'captured', argTo);
539
+ }
540
+}
541
+
542
+function accumulateDirectionalDispatches(inst, phase, event) {
543
+ if (__DEV__) {
544
+ if (!inst) {
545
+ console.error('Dispatching inst must not be null');
546
+ }
547
+ }
548
+ const listener = listenerAtPhase(inst, event, phase);
549
+ if (listener) {
550
+ event._dispatchListeners = accumulateInto(
551
+ event._dispatchListeners,
552
+ listener,
553
+ );
554
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
555
+ }
556
+}
557
+
558
+function accumulateTwoPhaseDispatchesSingle(event) {
559
+ if (event && event.dispatchConfig.phasedRegistrationNames) {
560
+ traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
561
+ }
562
+}
563
+
564
+/**
565
+ * Accumulates without regard to direction, does not look for phased
566
+ * registration names. Same as `accumulateDirectDispatchesSingle` but without
567
+ * requiring that the `dispatchMarker` be the same as the dispatched ID.
568
+ */
569
+function accumulateDispatches(
570
+ inst: Object,
571
+ ignoredDirection: ?boolean,
572
+ event: Object,
573
+): void {
574
+ if (inst && event && event.dispatchConfig.registrationName) {
575
+ const registrationName = event.dispatchConfig.registrationName;
576
+ const listener = getListener(inst, registrationName);
577
+ if (listener) {
578
+ event._dispatchListeners = accumulateInto(
579
+ event._dispatchListeners,
580
+ listener,
581
+ );
582
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
583
+ }
584
+ }
585
+}
586
+
587
+export function accumulateTwoPhaseDispatches(
588
+ events: ReactSyntheticEvent | Array<ReactSyntheticEvent>,
589
+): void {
590
+ forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
591
+}
592
+
593
+export function accumulateEnterLeaveDispatches(
594
+ leave: ReactSyntheticEvent,
595
+ enter: ReactSyntheticEvent,
596
+ from: Fiber,
597
+ to: Fiber,
598
+) {
599
+ traverseEnterLeave(from, to, accumulateDispatches, leave, enter);
600
+}
packages/react-dom/src/events/plugins/LegacyBeforeInputEventPlugin.js
+3
-3
@@ -28,7 +28,7 @@ import {
28
} from '../FallbackCompositionState';
29
import SyntheticCompositionEvent from '../SyntheticCompositionEvent';
30
import SyntheticInputEvent from '../SyntheticInputEvent';
31
-import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
31
+import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
32
33
const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
34
const START_KEYCODE = 229;
@@ -276,7 +276,7 @@ function extractCompositionEvent(
276
}
277
}
278
279
- accumulateTwoPhaseListeners(event);
279
+ accumulateTwoPhaseDispatches(event);
280
return event;
281
}
282
@@ -437,7 +437,7 @@ function extractBeforeInputEvent(
437
);
438
439
event.data = chars;
440
- accumulateTwoPhaseListeners(event);
440
+ accumulateTwoPhaseDispatches(event);
441
return event;
442
}
443
packages/react-dom/src/events/plugins/LegacyChangeEventPlugin.js
+4
-12
@@ -27,13 +27,9 @@ import {updateValueIfChanged} from '../../client/inputValueTracking';
27
import {setDefaultValue} from '../../client/ReactDOMInput';
28
import {enqueueStateRestore} from '../ReactDOMControlledComponent';
29
30
-import {
31
- disableInputAttributeSyncing,
32
- enableModernEventSystem,
33
-} from 'shared/ReactFeatureFlags';
34
-import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
30
+import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
31
import {batchedUpdates} from '../ReactDOMUpdateBatching';
36
-import {dispatchEventsInBatch} from '../DOMModernPluginEventSystem';
32
+import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
33
34
const eventTypes = {
35
change: {
@@ -64,7 +60,7 @@ function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
60
event.type = 'change';
61
// Flag this event loop as needing state restore.
62
enqueueStateRestore(target);
67
- accumulateTwoPhaseListeners(event);
63
+ accumulateTwoPhaseDispatches(event);
64
return event;
65
}
66
/**
@@ -105,11 +101,7 @@ function manualDispatchChangeEvent(nativeEvent) {
101
}
102
103
function runEventInBatch(event) {
108
- if (enableModernEventSystem) {
109
- dispatchEventsInBatch([event]);
110
- } else {
111
- runEventsInBatch(event);
112
- }
104
+ runEventsInBatch(event);
105
}
106
107
function getInstIfValueChanged(targetInst) {
packages/react-dom/src/events/plugins/LegacyEnterLeaveEventPlugin.js
+17
-30
@@ -23,8 +23,7 @@ import {
23
} from '../../client/ReactDOMComponentTree';
24
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
25
import {getNearestMountedFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
26
-import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
27
-import accumulateEnterLeaveListeners from '../accumulateEnterLeaveListeners';
26
+import {accumulateEnterLeaveDispatches} from '../DOMLegacyEventPluginSystem';
27
28
const eventTypes = {
29
mouseEnter: {
@@ -67,26 +66,16 @@ const EnterLeaveEventPlugin = {
66
const isOutEvent =
67
topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_POINTER_OUT;
68
70
- if (isOverEvent && (eventSystemFlags & IS_REPLAYED) === 0) {
71
- const related = nativeEvent.relatedTarget || nativeEvent.fromElement;
72
- if (related) {
73
- if (enableModernEventSystem) {
74
- // Due to the fact we don't add listeners to the document with the
75
- // modern event system and instead attach listeners to roots, we
76
- // need to handle the over event case. To ensure this, we just need to
77
- // make sure the node that we're coming from is managed by React.
78
- const inst = getClosestInstanceFromNode(related);
79
- if (inst !== null) {
80
- return null;
81
- }
82
- } else {
83
- // If this is an over event with a target, then we've already dispatched
84
- // the event in the out event of the other target. If this is replayed,
85
- // then it's because we couldn't dispatch against this target previously
86
- // so we have to do it now instead.
87
- return null;
88
- }
89
- }
69
+ if (
70
+ isOverEvent &&
71
+ (eventSystemFlags & IS_REPLAYED) === 0 &&
72
+ (nativeEvent.relatedTarget || nativeEvent.fromElement)
73
+ ) {
74
+ // If this is an over event with a target, then we've already dispatched
75
+ // the event in the out event of the other target. If this is replayed,
76
+ // then it's because we couldn't dispatch against this target previously
77
+ // so we have to do it now instead.
78
+ return null;
79
}
80
81
if (!isOutEvent && !isOverEvent) {
@@ -174,15 +163,13 @@ const EnterLeaveEventPlugin = {
163
enter.target = toNode;
164
enter.relatedTarget = fromNode;
165
177
- accumulateEnterLeaveListeners(leave, enter, from, to);
166
+ accumulateEnterLeaveDispatches(leave, enter, from, to);
167
179
- if (!enableModernEventSystem) {
180
- // If we are not processing the first ancestor, then we
181
- // should not process the same nativeEvent again, as we
182
- // will have already processed it in the first ancestor.
183
- if ((eventSystemFlags & IS_FIRST_ANCESTOR) === 0) {
184
- return [leave];
185
- }
168
+ // If we are not processing the first ancestor, then we
169
+ // should not process the same nativeEvent again, as we
170
+ // will have already processed it in the first ancestor.
171
+ if ((eventSystemFlags & IS_FIRST_ANCESTOR) === 0) {
172
+ return [leave];
173
}
174
175
return [leave, enter];
packages/react-dom/src/events/plugins/LegacySelectEventPlugin.js
+4
-9
@@ -26,7 +26,7 @@ import {getNodeFromInstance} from '../../client/ReactDOMComponentTree';
26
import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
27
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
28
import {isListeningToAllDependencies} from '../DOMEventListenerMap';
29
-import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
29
+import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
30
31
const skipSelectionChangeEvent =
32
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
@@ -135,7 +135,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
135
syntheticEvent.type = 'select';
136
syntheticEvent.target = activeElement;
137
138
- accumulateTwoPhaseListeners(syntheticEvent);
138
+ accumulateTwoPhaseDispatches(syntheticEvent);
139
140
return syntheticEvent;
141
}
@@ -166,16 +166,11 @@ const SelectEventPlugin = {
166
nativeEvent,
167
nativeEventTarget,
168
eventSystemFlags,
169
- container,
169
) {
171
- const containerOrDoc =
172
- container || getEventTargetDocument(nativeEventTarget);
170
+ const doc = getEventTargetDocument(nativeEventTarget);
171
// Track whether all listeners exists for this plugin. If none exist, we do
172
// not extract events. See #3639.
175
- if (
176
- !containerOrDoc ||
177
- !isListeningToAllDependencies('onSelect', containerOrDoc)
178
- ) {
173
+ if (!doc || !isListeningToAllDependencies('onSelect', doc)) {
174
return null;
175
}
176
packages/react-dom/src/events/plugins/LegacySimpleEventPlugin.js
+3
-31
@@ -14,10 +14,8 @@ import type {
14
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
15
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
16
import type {PluginModule} from 'legacy-events/PluginModuleType';
17
-import type {EventSystemFlags} from '../EventSystemFlags';
17
18
import SyntheticEvent from 'legacy-events/SyntheticEvent';
20
-import {IS_TARGET_PHASE_ONLY} from '../EventSystemFlags';
19
20
import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
21
import {
@@ -37,10 +35,7 @@ import SyntheticTransitionEvent from '../SyntheticTransitionEvent';
35
import SyntheticUIEvent from '../SyntheticUIEvent';
36
import SyntheticWheelEvent from '../SyntheticWheelEvent';
37
import getEventCharCode from '../getEventCharCode';
40
-import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
41
-import accumulateEventTargetListeners from '../accumulateEventTargetListeners';
42
-
43
-import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
38
+import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
39
40
// Only used in DEV for exhaustiveness validation.
41
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
@@ -86,8 +81,6 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
81
targetInst: null | Fiber,
82
nativeEvent: MouseEvent,
83
nativeEventTarget: null | EventTarget,
89
- eventSystemFlags?: EventSystemFlags,
90
- targetContainer?: null | EventTarget,
84
): null | ReactSyntheticEvent {
85
const dispatchConfig = topLevelEventsToDispatchConfig.get(topLevelType);
86
if (!dispatchConfig) {
@@ -109,8 +102,6 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
102
break;
103
case DOMTopLevelEventTypes.TOP_BLUR:
104
case DOMTopLevelEventTypes.TOP_FOCUS:
112
- case DOMTopLevelEventTypes.TOP_BEFORE_BLUR:
113
- case DOMTopLevelEventTypes.TOP_AFTER_BLUR:
105
EventConstructor = SyntheticFocusEvent;
106
break;
107
case DOMTopLevelEventTypes.TOP_CLICK:
@@ -179,10 +170,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
170
break;
171
default:
172
if (__DEV__) {
182
- if (
183
- knownHTMLTopLevelTypes.indexOf(topLevelType) === -1 &&
184
- dispatchConfig.customEvent !== true
185
- ) {
173
+ if (knownHTMLTopLevelTypes.indexOf(topLevelType) === -1) {
174
console.error(
175
'SimpleEventPlugin: Unhandled event type, `%s`. This warning ' +
176
'is likely caused by a bug in React. Please file an issue.',
@@ -201,23 +189,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
189
nativeEvent,
190
nativeEventTarget,
191
);
204
-
205
- // For TargetEvent only accumulation, we do not traverse through
206
- // the React tree looking for managed React DOM elements that have
207
- // events. Instead we only check the EventTarget Store Map to see
208
- // if the container has listeners for the particular phase we're
209
- // interested in. This is because we attach the native event listener
210
- // only in the given phase.
211
- if (
212
- enableUseEventAPI &&
213
- eventSystemFlags !== undefined &&
214
- eventSystemFlags & IS_TARGET_PHASE_ONLY &&
215
- targetContainer != null
216
- ) {
217
- accumulateEventTargetListeners(event, targetContainer);
218
- } else {
219
- accumulateTwoPhaseListeners(event, true);
220
- }
192
+ accumulateTwoPhaseDispatches(event);
193
return event;
194
},
195
};