Skip to content

Commit

Permalink
Modern Event System: refactor legacy FB support logic (#18336)
Browse files Browse the repository at this point in the history
  • Loading branch information
trueadm committed Mar 19, 2020
1 parent 1cf4a17 commit 8311cb5
Show file tree
Hide file tree
Showing 13 changed files with 1,721 additions and 1,686 deletions.
1 change: 1 addition & 0 deletions packages/legacy-events/EventSystemFlags.js
Expand Up @@ -16,3 +16,4 @@ export const IS_ACTIVE = 1 << 3;
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
export const IS_REPLAYED = 1 << 5;
export const IS_FIRST_ANCESTOR = 1 << 6;
export const LEGACY_FB_SUPPORT = 1 << 7;
1 change: 0 additions & 1 deletion packages/react-dom/src/client/ReactDOMComponent.js
Expand Up @@ -1365,7 +1365,6 @@ export function listenToEventResponderEventTypes(
(targetEventType: any),
true,
passiveItem.listener,
true,
);
listenerMap.delete(passiveKey);
}
Expand Down
68 changes: 29 additions & 39 deletions packages/react-dom/src/events/DOMModernPluginEventSystem.js
Expand Up @@ -24,6 +24,7 @@ import {registrationNameDependencies} from 'legacy-events/EventPluginRegistry';
import {batchedEventUpdates} from 'legacy-events/ReactGenericBatching';
import {executeDispatchesInOrder} from 'legacy-events/EventPluginUtils';
import {plugins} from 'legacy-events/EventPluginRegistry';
import {LEGACY_FB_SUPPORT, IS_REPLAYED} from 'legacy-events/EventSystemFlags';

import {HostRoot, HostPortal} from 'shared/ReactWorkTags';

Expand Down Expand Up @@ -66,6 +67,7 @@ import {
TOP_RATE_CHANGE,
TOP_PROGRESS,
TOP_PLAYING,
TOP_CLICK,
} from './DOMTopLevelEventTypes';
import {
getClosestInstanceFromNode,
Expand All @@ -78,7 +80,7 @@ import {
ELEMENT_NODE,
} from '../shared/HTMLNodeType';

import {enableLegacyFBPrimerSupport} from 'shared/ReactFeatureFlags';
import {enableLegacyFBSupport} from 'shared/ReactFeatureFlags';

const capturePhaseEvents = new Set([
TOP_FOCUS,
Expand Down Expand Up @@ -197,7 +199,6 @@ export function listenToTopLevelEvent(
topLevelType,
isCapturePhase,
((listenerEntry: any): ElementListenerMapEntry).listener,
((listenerEntry: any): ElementListenerMapEntry).passive,
);
}
const listener = addTrappedEventListener(
Expand Down Expand Up @@ -225,42 +226,24 @@ export function listenToEvent(
}
}

const validFBLegacyPrimerRels = new Set([
'dialog',
'dialog-post',
'async',
'async-post',
'theater',
'toggle',
]);

function willDeferLaterForFBLegacyPrimer(nativeEvent: any): boolean {
let node = nativeEvent.target;
const type = nativeEvent.type;
if (type !== 'click') {
function willDeferLaterForLegacyFBSupport(
topLevelType: DOMTopLevelEventType,
targetContainer: EventTarget,
): boolean {
if (topLevelType !== TOP_CLICK) {
return false;
}
while (node !== null) {
// Primer works by intercepting a click event on an <a> element
// that has a "rel" attribute that matches one of the valid ones
// in the Set above. If we intercept this before Primer does, we
// will need to defer the current event till later and discontinue
// execution of the current event. To do this we can add a document
// event listener and continue again later after propagation.
if (node.tagName === 'A' && validFBLegacyPrimerRels.has(node.rel)) {
const legacyFBSupport = true;
const isCapture = nativeEvent.eventPhase === 1;
addTrappedEventListener(
null,
((type: any): DOMTopLevelEventType),
isCapture,
legacyFBSupport,
);
return true;
}
node = node.parentNode;
}
return false;
// We defer all click events with legacy FB support mode on.
// This means we add a one time event listener to trigger
// after the FB delegated listeners fire.
const isDeferredListenerForLegacyFBSupport = true;
addTrappedEventListener(
targetContainer,
topLevelType,
false,
isDeferredListenerForLegacyFBSupport,
);
return true;
}

function isMatchingRootContainer(
Expand Down Expand Up @@ -303,12 +286,19 @@ export function dispatchEventForPluginEventSystem(
// TODO: useEvent for document and window
return;
}
// If we detect the FB legacy primer system, we
// If we are using the legacy FB support flag, we
// defer the event to the null with a one
// time event listener so we can defer the event.
if (
enableLegacyFBPrimerSupport &&
willDeferLaterForFBLegacyPrimer(nativeEvent)
enableLegacyFBSupport &&
// We do not want to defer if the event system has already been
// set to LEGACY_FB_SUPPORT. LEGACY_FB_SUPPORT only gets set when
// we call willDeferLaterForLegacyFBSupport, thus not bailing out
// will result in endless cycles like an infinite loop.
(eventSystemFlags & LEGACY_FB_SUPPORT) === 0 &&
// We also don't want to defer during event replaying.
(eventSystemFlags & IS_REPLAYED) === 0 &&
willDeferLaterForLegacyFBSupport(topLevelType, targetContainer)
) {
return;
}
Expand Down
33 changes: 20 additions & 13 deletions packages/react-dom/src/events/ReactDOMEventListener.js
Expand Up @@ -42,6 +42,7 @@ import {
IS_PASSIVE,
IS_ACTIVE,
PASSIVE_NOT_SUPPORTED,
LEGACY_FB_SUPPORT,
} from 'legacy-events/EventSystemFlags';

import {
Expand All @@ -59,7 +60,7 @@ import {passiveBrowserEventsSupported} from './checkPassiveEvents';
import {
enableDeprecatedFlareAPI,
enableModernEventSystem,
enableLegacyFBPrimerSupport,
enableLegacyFBSupport,
enableUseEventAPI,
} from 'shared/ReactFeatureFlags';
import {
Expand Down Expand Up @@ -129,10 +130,10 @@ export function addResponderEventSystemEvent(
}

export function addTrappedEventListener(
targetContainer: null | EventTarget,
targetContainer: EventTarget,
topLevelType: DOMTopLevelEventType,
capture: boolean,
legacyFBSupport?: boolean,
isDeferredListenerForLegacyFBSupport?: boolean,
passive?: boolean,
priority?: EventPriority,
): any => void {
Expand All @@ -159,11 +160,15 @@ export function addTrappedEventListener(
if (passive === true && !passiveBrowserEventsSupported) {
passive = false;
}
const eventSystemFlags =
enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
? PLUGIN_EVENT_SYSTEM | LEGACY_FB_SUPPORT
: PLUGIN_EVENT_SYSTEM;

listener = listenerWrapper.bind(
null,
topLevelType,
PLUGIN_EVENT_SYSTEM,
eventSystemFlags,
targetContainer,
);

Expand All @@ -177,14 +182,17 @@ export function addTrappedEventListener(
targetContainer = document;
}

const validTargetContainer = ((targetContainer: any): EventTarget);
targetContainer =
enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
? (targetContainer: any).ownerDocument
: targetContainer;

const rawEventName = getRawEventName(topLevelType);

let unsubscribeListener;
// When legacyFBSupport is enabled, it's for when we
// want to add a one time event listener to a container.
// This should only be used with enableLegacyFBPrimerSupport
// This should only be used with enableLegacyFBSupport
// due to requirement to provide compatibility with
// internal FB www event tooling. This works by removing
// the event listener as soon as it is invoked. We could
Expand All @@ -193,14 +201,14 @@ export function addTrappedEventListener(
// browsers do not support this today, and given this is
// to support legacy code patterns, it's likely they'll
// need support for such browsers.
if (enableLegacyFBPrimerSupport && legacyFBSupport) {
if (enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport) {
const originalListener = listener;
listener = function(...p) {
try {
return originalListener.apply(this, p);
} finally {
removeEventListener(
validTargetContainer,
targetContainer,
rawEventName,
unsubscribeListener,
capture,
Expand All @@ -212,14 +220,14 @@ export function addTrappedEventListener(
if (enableUseEventAPI && passive !== undefined) {
// This is only used with passive is either true or false.
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
validTargetContainer,
targetContainer,
rawEventName,
listener,
passive,
);
} else {
unsubscribeListener = addEventCaptureListener(
validTargetContainer,
targetContainer,
rawEventName,
listener,
);
Expand All @@ -228,14 +236,14 @@ export function addTrappedEventListener(
if (enableUseEventAPI && passive !== undefined) {
// This is only used with passive is either true or false.
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
validTargetContainer,
targetContainer,
rawEventName,
listener,
passive,
);
} else {
unsubscribeListener = addEventBubbleListener(
validTargetContainer,
targetContainer,
rawEventName,
listener,
);
Expand All @@ -249,7 +257,6 @@ export function removeTrappedEventListener(
topLevelType: DOMTopLevelEventType,
capture: boolean,
listener: any => void,
passive: void | boolean,
): void {
const rawEventName = getRawEventName(topLevelType);
removeEventListener(targetContainer, rawEventName, listener, capture);
Expand Down

0 comments on commit 8311cb5

Please sign in to comment.