Revert legacy plugin modules (#18638)

This commit is contained in:
Dominic Gannaway
2020-04-16 18:46:24 +01:00
committed by GitHub
parent 31977d1bb3
commit b1a083183c
6 changed files with 233 additions and 85 deletions
@@ -15,6 +15,7 @@ import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {PluginModule} from 'legacy-events/PluginModuleType';
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
import forEachAccumulated from 'legacy-events/forEachAccumulated';
import {
HostRoot,
@@ -45,6 +46,7 @@ import {
} from './DOMTopLevelEventTypes';
import {addTrappedEventListener} from './ReactDOMEventListener';
import {batchedEventUpdates} from './ReactDOMUpdateBatching';
import getListener from './getListener';
/**
* Summary of `DOMEventPluginSystem` event handling:
@@ -396,3 +398,203 @@ export function legacyTrapCapturedEvent(
);
listenerMap.set(topLevelType, {passive: undefined, listener});
}
function getParent(inst: Object | null): Object | null {
if (!inst) {
return null;
}
do {
inst = inst.return;
// TODO: If this is a HostRoot we might want to bail out.
// That is depending on if we want nested subtrees (layers) to bubble
// events to their parent. We could also go through parentNode on the
// host node but that wouldn't work for React Native and doesn't let us
// do the portal feature.
} while (inst && inst.tag !== HostComponent);
if (inst) {
return inst;
}
return null;
}
/**
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
*/
function traverseTwoPhase(
inst: Object,
fn: Function,
arg: ReactSyntheticEvent,
) {
const path = [];
while (inst) {
path.push(inst);
inst = getParent(inst);
}
let i;
for (i = path.length; i-- > 0; ) {
fn(path[i], 'captured', arg);
}
for (i = 0; i < path.length; i++) {
fn(path[i], 'bubbled', arg);
}
}
function listenerAtPhase(inst, event, propagationPhase) {
const registrationName =
event.dispatchConfig.phasedRegistrationNames[propagationPhase];
return getListener(inst, registrationName);
}
/**
* Return the lowest common ancestor of A and B, or null if they are in
* different trees.
*/
export function getLowestCommonAncestor(
instA: Object,
instB: Object,
): Object | null {
let depthA = 0;
for (let tempA = instA; tempA; tempA = getParent(tempA)) {
depthA++;
}
let depthB = 0;
for (let tempB = instB; tempB; tempB = getParent(tempB)) {
depthB++;
}
// If A is deeper, crawl up.
while (depthA - depthB > 0) {
instA = getParent(instA);
depthA--;
}
// If B is deeper, crawl up.
while (depthB - depthA > 0) {
instB = getParent(instB);
depthB--;
}
// Walk in lockstep until we find a match.
let depth = depthA;
while (depth--) {
if (instA === instB || instA === instB.alternate) {
return instA;
}
instA = getParent(instA);
instB = getParent(instB);
}
return null;
}
/**
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
* should would receive a `mouseEnter` or `mouseLeave` event.
*
* Does not invoke the callback on the nearest common ancestor because nothing
* "entered" or "left" that element.
*/
export function traverseEnterLeave(
from: Object,
to: Object,
fn: Function,
argFrom: ReactSyntheticEvent,
argTo: ReactSyntheticEvent,
) {
const common = from && to ? getLowestCommonAncestor(from, to) : null;
const pathFrom = [];
while (true) {
if (!from) {
break;
}
if (from === common) {
break;
}
const alternate = from.alternate;
if (alternate !== null && alternate === common) {
break;
}
pathFrom.push(from);
from = getParent(from);
}
const pathTo = [];
while (true) {
if (!to) {
break;
}
if (to === common) {
break;
}
const alternate = to.alternate;
if (alternate !== null && alternate === common) {
break;
}
pathTo.push(to);
to = getParent(to);
}
for (let i = 0; i < pathFrom.length; i++) {
fn(pathFrom[i], 'bubbled', argFrom);
}
for (let i = pathTo.length; i-- > 0; ) {
fn(pathTo[i], 'captured', argTo);
}
}
function accumulateDirectionalDispatches(inst, phase, event) {
if (__DEV__) {
if (!inst) {
console.error('Dispatching inst must not be null');
}
}
const listener = listenerAtPhase(inst, event, phase);
if (listener) {
event._dispatchListeners = accumulateInto(
event._dispatchListeners,
listener,
);
event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
}
}
function accumulateTwoPhaseDispatchesSingle(event) {
if (event && event.dispatchConfig.phasedRegistrationNames) {
traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
}
}
/**
* Accumulates without regard to direction, does not look for phased
* registration names. Same as `accumulateDirectDispatchesSingle` but without
* requiring that the `dispatchMarker` be the same as the dispatched ID.
*/
function accumulateDispatches(
inst: Object,
ignoredDirection: ?boolean,
event: Object,
): void {
if (inst && event && event.dispatchConfig.registrationName) {
const registrationName = event.dispatchConfig.registrationName;
const listener = getListener(inst, registrationName);
if (listener) {
event._dispatchListeners = accumulateInto(
event._dispatchListeners,
listener,
);
event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
}
}
}
export function accumulateTwoPhaseDispatches(
events: ReactSyntheticEvent | Array<ReactSyntheticEvent>,
): void {
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
}
export function accumulateEnterLeaveDispatches(
leave: ReactSyntheticEvent,
enter: ReactSyntheticEvent,
from: Fiber,
to: Fiber,
) {
traverseEnterLeave(from, to, accumulateDispatches, leave, enter);
}
@@ -28,7 +28,7 @@ import {
} from '../FallbackCompositionState';
import SyntheticCompositionEvent from '../SyntheticCompositionEvent';
import SyntheticInputEvent from '../SyntheticInputEvent';
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
const START_KEYCODE = 229;
@@ -276,7 +276,7 @@ function extractCompositionEvent(
}
}
accumulateTwoPhaseListeners(event);
accumulateTwoPhaseDispatches(event);
return event;
}
@@ -437,7 +437,7 @@ function extractBeforeInputEvent(
);
event.data = chars;
accumulateTwoPhaseListeners(event);
accumulateTwoPhaseDispatches(event);
return event;
}
@@ -27,13 +27,9 @@ import {updateValueIfChanged} from '../../client/inputValueTracking';
import {setDefaultValue} from '../../client/ReactDOMInput';
import {enqueueStateRestore} from '../ReactDOMControlledComponent';
import {
disableInputAttributeSyncing,
enableModernEventSystem,
} from 'shared/ReactFeatureFlags';
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
import {batchedUpdates} from '../ReactDOMUpdateBatching';
import {dispatchEventsInBatch} from '../DOMModernPluginEventSystem';
import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
const eventTypes = {
change: {
@@ -64,7 +60,7 @@ function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
event.type = 'change';
// Flag this event loop as needing state restore.
enqueueStateRestore(target);
accumulateTwoPhaseListeners(event);
accumulateTwoPhaseDispatches(event);
return event;
}
/**
@@ -105,11 +101,7 @@ function manualDispatchChangeEvent(nativeEvent) {
}
function runEventInBatch(event) {
if (enableModernEventSystem) {
dispatchEventsInBatch([event]);
} else {
runEventsInBatch(event);
}
runEventsInBatch(event);
}
function getInstIfValueChanged(targetInst) {
@@ -23,8 +23,7 @@ import {
} from '../../client/ReactDOMComponentTree';
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
import {getNearestMountedFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
import {enableModernEventSystem} from 'shared/ReactFeatureFlags';
import accumulateEnterLeaveListeners from '../accumulateEnterLeaveListeners';
import {accumulateEnterLeaveDispatches} from '../DOMLegacyEventPluginSystem';
const eventTypes = {
mouseEnter: {
@@ -67,26 +66,16 @@ const EnterLeaveEventPlugin = {
const isOutEvent =
topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_POINTER_OUT;
if (isOverEvent && (eventSystemFlags & IS_REPLAYED) === 0) {
const related = nativeEvent.relatedTarget || nativeEvent.fromElement;
if (related) {
if (enableModernEventSystem) {
// Due to the fact we don't add listeners to the document with the
// modern event system and instead attach listeners to roots, we
// need to handle the over event case. To ensure this, we just need to
// make sure the node that we're coming from is managed by React.
const inst = getClosestInstanceFromNode(related);
if (inst !== null) {
return null;
}
} else {
// If this is an over event with a target, then we've already dispatched
// the event in the out event of the other target. If this is replayed,
// then it's because we couldn't dispatch against this target previously
// so we have to do it now instead.
return null;
}
}
if (
isOverEvent &&
(eventSystemFlags & IS_REPLAYED) === 0 &&
(nativeEvent.relatedTarget || nativeEvent.fromElement)
) {
// If this is an over event with a target, then we've already dispatched
// the event in the out event of the other target. If this is replayed,
// then it's because we couldn't dispatch against this target previously
// so we have to do it now instead.
return null;
}
if (!isOutEvent && !isOverEvent) {
@@ -174,15 +163,13 @@ const EnterLeaveEventPlugin = {
enter.target = toNode;
enter.relatedTarget = fromNode;
accumulateEnterLeaveListeners(leave, enter, from, to);
accumulateEnterLeaveDispatches(leave, enter, from, to);
if (!enableModernEventSystem) {
// If we are not processing the first ancestor, then we
// should not process the same nativeEvent again, as we
// will have already processed it in the first ancestor.
if ((eventSystemFlags & IS_FIRST_ANCESTOR) === 0) {
return [leave];
}
// If we are not processing the first ancestor, then we
// should not process the same nativeEvent again, as we
// will have already processed it in the first ancestor.
if ((eventSystemFlags & IS_FIRST_ANCESTOR) === 0) {
return [leave];
}
return [leave, enter];
@@ -26,7 +26,7 @@ import {getNodeFromInstance} from '../../client/ReactDOMComponentTree';
import {hasSelectionCapabilities} from '../../client/ReactInputSelection';
import {DOCUMENT_NODE} from '../../shared/HTMLNodeType';
import {isListeningToAllDependencies} from '../DOMEventListenerMap';
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
const skipSelectionChangeEvent =
canUseDOM && 'documentMode' in document && document.documentMode <= 11;
@@ -135,7 +135,7 @@ function constructSelectEvent(nativeEvent, nativeEventTarget) {
syntheticEvent.type = 'select';
syntheticEvent.target = activeElement;
accumulateTwoPhaseListeners(syntheticEvent);
accumulateTwoPhaseDispatches(syntheticEvent);
return syntheticEvent;
}
@@ -166,16 +166,11 @@ const SelectEventPlugin = {
nativeEvent,
nativeEventTarget,
eventSystemFlags,
container,
) {
const containerOrDoc =
container || getEventTargetDocument(nativeEventTarget);
const doc = getEventTargetDocument(nativeEventTarget);
// Track whether all listeners exists for this plugin. If none exist, we do
// not extract events. See #3639.
if (
!containerOrDoc ||
!isListeningToAllDependencies('onSelect', containerOrDoc)
) {
if (!doc || !isListeningToAllDependencies('onSelect', doc)) {
return null;
}
@@ -14,10 +14,8 @@ import type {
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {PluginModule} from 'legacy-events/PluginModuleType';
import type {EventSystemFlags} from '../EventSystemFlags';
import SyntheticEvent from 'legacy-events/SyntheticEvent';
import {IS_TARGET_PHASE_ONLY} from '../EventSystemFlags';
import * as DOMTopLevelEventTypes from '../DOMTopLevelEventTypes';
import {
@@ -37,10 +35,7 @@ import SyntheticTransitionEvent from '../SyntheticTransitionEvent';
import SyntheticUIEvent from '../SyntheticUIEvent';
import SyntheticWheelEvent from '../SyntheticWheelEvent';
import getEventCharCode from '../getEventCharCode';
import accumulateTwoPhaseListeners from '../accumulateTwoPhaseListeners';
import accumulateEventTargetListeners from '../accumulateEventTargetListeners';
import {enableUseEventAPI} from 'shared/ReactFeatureFlags';
import {accumulateTwoPhaseDispatches} from '../DOMLegacyEventPluginSystem';
// Only used in DEV for exhaustiveness validation.
const knownHTMLTopLevelTypes: Array<DOMTopLevelEventType> = [
@@ -86,8 +81,6 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
targetInst: null | Fiber,
nativeEvent: MouseEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags?: EventSystemFlags,
targetContainer?: null | EventTarget,
): null | ReactSyntheticEvent {
const dispatchConfig = topLevelEventsToDispatchConfig.get(topLevelType);
if (!dispatchConfig) {
@@ -109,8 +102,6 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
break;
case DOMTopLevelEventTypes.TOP_BLUR:
case DOMTopLevelEventTypes.TOP_FOCUS:
case DOMTopLevelEventTypes.TOP_BEFORE_BLUR:
case DOMTopLevelEventTypes.TOP_AFTER_BLUR:
EventConstructor = SyntheticFocusEvent;
break;
case DOMTopLevelEventTypes.TOP_CLICK:
@@ -179,10 +170,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
break;
default:
if (__DEV__) {
if (
knownHTMLTopLevelTypes.indexOf(topLevelType) === -1 &&
dispatchConfig.customEvent !== true
) {
if (knownHTMLTopLevelTypes.indexOf(topLevelType) === -1) {
console.error(
'SimpleEventPlugin: Unhandled event type, `%s`. This warning ' +
'is likely caused by a bug in React. Please file an issue.',
@@ -201,23 +189,7 @@ const SimpleEventPlugin: PluginModule<MouseEvent> = {
nativeEvent,
nativeEventTarget,
);
// For TargetEvent only accumulation, we do not traverse through
// the React tree looking for managed React DOM elements that have
// events. Instead we only check the EventTarget Store Map to see
// if the container has listeners for the particular phase we're
// interested in. This is because we attach the native event listener
// only in the given phase.
if (
enableUseEventAPI &&
eventSystemFlags !== undefined &&
eventSystemFlags & IS_TARGET_PHASE_ONLY &&
targetContainer != null
) {
accumulateEventTargetListeners(event, targetContainer);
} else {
accumulateTwoPhaseListeners(event, true);
}
accumulateTwoPhaseDispatches(event);
return event;
},
};