exportfunctionlistenToAllSupportedEvents(rootContainerElement: EventTarget) { if (!(rootContainerElement: any)[listeningMarker]) { (rootContainerElement: any)[listeningMarker] = true; allNativeEvents.forEach(domEventName => { // We handle selectionchange separately because it // doesn't bubble and needs to be on the document. if (domEventName !== 'selectionchange') { if (!nonDelegatedEvents.has(domEventName)) { listenToNativeEvent(domEventName, false, rootContainerElement); } listenToNativeEvent(domEventName, true, rootContainerElement); } }); const ownerDocument = (rootContainerElement: any).nodeType === DOCUMENT_NODE ? rootContainerElement : (rootContainerElement: any).ownerDocument; if (ownerDocument !== null) { // The selectionchange event also needs deduplication // but it is attached to the document. if (!(ownerDocument: any)[listeningMarker]) { (ownerDocument: any)[listeningMarker] = true; listenToNativeEvent('selectionchange', false, ownerDocument); } } } }
functionaddTrappedEventListener( targetContainer: EventTarget, domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, isCapturePhaseListener: boolean, isDeferredListenerForLegacyFBSupport?: boolean, ) { let listener = createEventListenerWrapperWithPriority( targetContainer, domEventName, eventSystemFlags, ); // If passive option is not supported, then the event will be // active and not passive. letisPassiveListener: void | boolean = undefined; if (passiveBrowserEventsSupported) { // Browsers introduced an intervention, making these events // passive by default on document. React doesn't bind them // to document anymore, but changing this now would undo // the performance wins from the change. So we emulate // the existing behavior manually on the roots now. // https://github.com/facebook/react/issues/19651 if ( domEventName === 'touchstart' || domEventName === 'touchmove' || domEventName === 'wheel' ) { isPassiveListener = true; } }
let unsubscribeListener; // TODO: There are too many combinations here. Consolidate them. if (isCapturePhaseListener) { if (isPassiveListener !== undefined) { unsubscribeListener = addEventCaptureListenerWithPassiveFlag( targetContainer, domEventName, listener, isPassiveListener, ); } else { unsubscribeListener = addEventCaptureListener( targetContainer, domEventName, listener, ); } } else { if (isPassiveListener !== undefined) { unsubscribeListener = addEventBubbleListenerWithPassiveFlag( targetContainer, domEventName, listener, isPassiveListener, ); } else { unsubscribeListener = addEventBubbleListener( targetContainer, domEventName, listener, ); } } }
exportfunctionfindInstanceBlockingTarget( targetNode: Node, ): null | Container | SuspenseInstance { // TODO: Warn if _enabled is false.
return_targetInst = null;
let targetInst = getClosestInstanceFromNode(targetNode);
if (targetInst !== null) { const nearestMounted = getNearestMountedFiber(targetInst); if (nearestMounted === null) { // This tree has been unmounted already. Dispatch without a target. targetInst = null; } else { const tag = nearestMounted.tag; if (tag === SuspenseComponent) { const instance = getSuspenseInstanceFromFiber(nearestMounted); if (instance !== null) { return instance; } // This shouldn't happen targetInst = null; } elseif (tag === HostRoot) { constroot: FiberRoot = nearestMounted.stateNode; if (isRootDehydrated(root)) { // If this happens during a replay something went wrong and it might block // the whole system. returngetContainerFromFiber(nearestMounted); } targetInst = null; } elseif (nearestMounted !== targetInst) { // If we get an event (ex: img onload) before committing that // component's mount, ignore it for now (that is, treat it as if it was an // event on a non-React tree). We might also consider queueing events and // dispatching them after the mount. targetInst = null; } } } return_targetInst = targetInst; // We're not blocked on anything. returnnull; }
functionextractEvents( dispatchQueue: DispatchQueue, domEventName: DOMEventName, targetInst: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: null | EventTarget, eventSystemFlags: EventSystemFlags, targetContainer: EventTarget, ) { // TODO: we should remove the concept of a "SimpleEventPlugin". // This is the basic functionality of the event system. All // the other plugins are essentially polyfills. So the plugin // should probably be inlined somewhere and have its logic // be core the to event system. This would potentially allow // us to ship builds of React without the polyfilled plugins below. SimpleEventPlugin.extractEvents( dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer, ); const shouldProcessPolyfillPlugins = (eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0; // We don't process these events unless we are in the // event's native "bubble" phase, which means that we're // not in the capture phase. That's because we emulate // the capture phase here still. This is a trade-off, // because in an ideal world we would not emulate and use // the phases properly, like we do with the SimpleEvent // plugin. However, the plugins below either expect // emulation (EnterLeave) or use state localized to that // plugin (BeforeInput, Change, Select). The state in // these modules complicates things, as you'll essentially // get the case where the capture phase event might change // state, only for the following bubble event to come in // later and not trigger anything as the state now // invalidates the heuristics of the event plugin. We // could alter all these plugins to work in such ways, but // that might cause other unknown side-effects that we // can't foresee right now. if (shouldProcessPolyfillPlugins) { EnterLeaveEventPlugin.extractEvents( dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer, ); ChangeEventPlugin.extractEvents( dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer, ); SelectEventPlugin.extractEvents( dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer, ); BeforeInputEventPlugin.extractEvents( dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer, ); FormActionEventPlugin.extractEvents( dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer, ); } if (enableScrollEndPolyfill) { ScrollEndEventPlugin.extractEvents( dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer, ); } }
// Some events don't bubble in the browser. // In the past, React has always bubbled them, but this can be surprising. // We're going to try aligning closer to the browser behavior by not bubbling // them in React either. We'll start by not bubbling onScroll, and then expand. const accumulateTargetOnly = !inCapturePhase && // TODO: ideally, we'd eventually add all events from // nonDelegatedEvents list in DOMPluginEventSystem. // Then we can remove this special list. // This is a breaking change that can wait until React 18. (domEventName === 'scroll' || domEventName === 'scrollend');
// This is intentionally a factory so that we have different returned constructors. // If we had a single constructor, it would be megamorphic and engines would deopt. functioncreateSyntheticEvent(Interface: EventInterfaceType) { /** * Synthetic events are dispatched by event plugins, typically in response to a * top-level event delegation handler. * * These systems should generally use pooling to reduce the frequency of garbage * collection. The system should check `isPersistent` to determine whether the * event should be released into the pool after being dispatched. Users that * need a persisted event should invoke `persist`. * * Synthetic events (and subclasses) implement the DOM Level 3 Events API by * normalizing browser quirks. Subclasses do not necessarily have to implement a * DOM interface; custom application-specific events can also subclass this. */ // $FlowFixMe[missing-this-annot] functionSyntheticBaseEvent( reactName: string | null, reactEventType: string, targetInst: Fiber | null, nativeEvent: {[propName: string]: mixed, ...}, nativeEventTarget: null | EventTarget, ) { this._reactName = reactName; this._targetInst = targetInst; this.type = reactEventType; this.nativeEvent = nativeEvent; this.target = nativeEventTarget; this.currentTarget = null;
for (const propName inInterface) { if (!Interface.hasOwnProperty(propName)) { continue; } const normalize = Interface[propName]; if (normalize) { this[propName] = normalize(nativeEvent); } else { this[propName] = nativeEvent[propName]; } }
// $FlowFixMe[prop-missing] found when upgrading Flow assign(SyntheticBaseEvent.prototype, { // $FlowFixMe[missing-this-annot] preventDefault: function () { this.defaultPrevented = true; const event = this.nativeEvent; if (!event) { return; }
if (event.preventDefault) { event.preventDefault(); // $FlowFixMe[illegal-typeof] - flow is not aware of `unknown` in IE } elseif (typeof event.returnValue !== 'unknown') { event.returnValue = false; } this.isDefaultPrevented = functionThatReturnsTrue; },
// $FlowFixMe[missing-this-annot] stopPropagation: function () { const event = this.nativeEvent; if (!event) { return; }
if (event.stopPropagation) { event.stopPropagation(); // $FlowFixMe[illegal-typeof] - flow is not aware of `unknown` in IE } elseif (typeof event.cancelBubble !== 'unknown') { // The ChangeEventPlugin registers a "propertychange" event for // IE. This event does not support bubbling or cancelling, and // any references to cancelBubble throw "Member not found". A // typeof check of "unknown" circumvents this issue (and is also // IE specific). event.cancelBubble = true; }
let instance = targetFiber; let lastHostComponent = null;
// Accumulate all instances and listeners via the target -> root path. while (instance !== null) { const {stateNode, tag} = instance; // Handle listeners that are on HostComponents (i.e. <div>) if ( (tag === HostComponent || tag === HostHoistable || tag === HostSingleton) && stateNode !== null ) { lastHostComponent = stateNode;
// Standard React on* listeners, i.e. onClick or onClickCapture if (reactEventName !== null) { const listener = getListener(instance, reactEventName); if (listener != null) { listeners.push( createDispatchListener(instance, listener, lastHostComponent), ); } } } // If we are only accumulating events for the target, then we don't // continue to propagate through the React fiber tree to find other // listeners. if (accumulateTargetOnly) { break; }