@@ -84,10 +84,13 @@ const eventListeners:
8484 ($Shape< PartialEventObject > ) => void ,
8585 > = new PossiblyWeakMap ( ) ;
8686
87- let alreadyDispatching = false ;
87+ const responderOwners : Map <
88+ ReactEventResponder ,
89+ ReactEventComponentInstance ,
90+ > = new Map ( ) ;
91+ let globalOwner = null ;
8892
8993let currentTimers = new Map ( ) ;
90- let currentOwner = null ;
9194let currentInstance : null | ReactEventComponentInstance = null ;
9295let currentEventQueue : null | EventQueue = null ;
9396
@@ -131,8 +134,9 @@ const eventResponderContext: ReactResponderContext = {
131134 eventListeners . set ( eventObject , listener ) ;
132135 eventQueue . events . push ( eventObject ) ;
133136 } ,
134- isPositionWithinTouchHitTarget ( doc : Document , x : number , y : number ) : boolean {
137+ isPositionWithinTouchHitTarget ( x : number , y : number ) : boolean {
135138 validateResponderContext ( ) ;
139+ const doc = getActiveDocument ( ) ;
136140 // This isn't available in some environments (JSDOM)
137141 if ( typeof doc . elementFromPoint !== 'function' ) {
138142 return false ;
@@ -188,6 +192,27 @@ const eventResponderContext: ReactResponderContext = {
188192 }
189193 return false ;
190194 } ,
195+ isTargetWithinEventResponderScope ( target : Element | Document ) : boolean {
196+ validateResponderContext ( ) ;
197+ const responder = ( ( currentInstance : any ) : ReactEventComponentInstance )
198+ . responder ;
199+ if ( target != null ) {
200+ let fiber = getClosestInstanceFromNode ( target ) ;
201+ while ( fiber !== null ) {
202+ if ( fiber . stateNode === currentInstance ) {
203+ return true ;
204+ }
205+ if (
206+ fiber . tag === EventComponent &&
207+ fiber . stateNode . responder === responder
208+ ) {
209+ return false ;
210+ }
211+ fiber = fiber . return ;
212+ }
213+ }
214+ return false ;
215+ } ,
191216 isTargetWithinElement (
192217 childTarget : Element | Document ,
193218 parentTarget : Element | Document ,
@@ -204,12 +229,10 @@ const eventResponderContext: ReactResponderContext = {
204229 }
205230 return false ;
206231 } ,
207- addRootEventTypes (
208- doc : Document ,
209- rootEventTypes : Array < ReactEventResponderEventType > ,
210- ) : void {
232+ addRootEventTypes ( rootEventTypes : Array < ReactEventResponderEventType > ) : void {
211233 validateResponderContext ( ) ;
212- listenToResponderEventTypesImpl ( rootEventTypes , doc ) ;
234+ const activeDocument = getActiveDocument ( ) ;
235+ listenToResponderEventTypesImpl ( rootEventTypes , activeDocument ) ;
213236 for ( let i = 0 ; i < rootEventTypes . length ; i ++ ) {
214237 const rootEventType = rootEventTypes [ i ] ;
215238 const topLevelEventType =
@@ -265,25 +288,38 @@ const eventResponderContext: ReactResponderContext = {
265288 } ,
266289 hasOwnership ( ) : boolean {
267290 validateResponderContext ( ) ;
268- return currentOwner === currentInstance ;
291+ const responder = ( ( currentInstance : any ) : ReactEventComponentInstance )
292+ . responder ;
293+ return (
294+ globalOwner === currentInstance ||
295+ responderOwners . get ( responder ) === currentInstance
296+ ) ;
269297 } ,
270- requestOwnership ( ) : boolean {
298+ requestGlobalOwnership ( ) : boolean {
271299 validateResponderContext ( ) ;
272- if ( currentOwner !== null ) {
300+ if ( globalOwner !== null ) {
273301 return false ;
274302 }
275- currentOwner = currentInstance ;
276- triggerOwnershipListeners ( ) ;
303+ globalOwner = currentInstance ;
304+ triggerOwnershipListeners ( null ) ;
277305 return true ;
278306 } ,
279- releaseOwnership ( ) : boolean {
307+ requestResponderOwnership ( ) : boolean {
280308 validateResponderContext ( ) ;
281- if ( currentOwner !== currentInstance ) {
309+ const eventComponentInstance = ( ( currentInstance : any ) : ReactEventComponentInstance ) ;
310+ const responder = eventComponentInstance . responder ;
311+ if ( responderOwners . has ( responder ) ) {
282312 return false ;
283313 }
284- currentOwner = null ;
285- triggerOwnershipListeners ( ) ;
286- return false ;
314+ responderOwners . set ( responder , eventComponentInstance ) ;
315+ triggerOwnershipListeners ( responder ) ;
316+ return true ;
317+ } ,
318+ releaseOwnership ( ) : boolean {
319+ validateResponderContext ( ) ;
320+ return releaseOwnershipForEventComponentInstance (
321+ ( ( currentInstance : any ) : ReactEventComponentInstance ) ,
322+ ) ;
287323 } ,
288324 setTimeout ( func : ( ) = > void , delay ) : Symbol {
289325 validateResponderContext ( ) ;
@@ -330,9 +366,6 @@ const eventResponderContext: ReactResponderContext = {
330366 let node = ( ( eventComponentInstance . currentFiber : any ) : Fiber ) . child ;
331367
332368 while ( node !== null ) {
333- if ( node . stateNode === currentInstance ) {
334- break ;
335- }
336369 if ( isFiberHostComponentFocusable ( node ) ) {
337370 focusableElements . push ( node . stateNode ) ;
338371 } else {
@@ -353,13 +386,44 @@ const eventResponderContext: ReactResponderContext = {
353386 if ( parent === null ) {
354387 break ;
355388 }
389+ if ( parent . stateNode === currentInstance ) {
390+ break ;
391+ }
356392 node = parent . sibling ;
357393 }
358394
359395 return focusableElements ;
360396 } ,
397+ getActiveDocument ,
361398} ;
362399
400+ function getActiveDocument ( ) : Document {
401+ const eventComponentInstance = ( ( currentInstance : any ) : ReactEventComponentInstance ) ;
402+ const rootElement = ( ( eventComponentInstance . rootInstance : any ) : Element ) ;
403+ return rootElement . ownerDocument ;
404+ }
405+
406+ function releaseOwnershipForEventComponentInstance (
407+ eventComponentInstance : ReactEventComponentInstance ,
408+ ) : boolean {
409+ const responder = eventComponentInstance . responder ;
410+ let triggerOwnershipListenersWith ;
411+ if ( responderOwners . get ( responder ) === eventComponentInstance ) {
412+ responderOwners . delete ( responder ) ;
413+ triggerOwnershipListenersWith = responder ;
414+ }
415+ if ( globalOwner === eventComponentInstance ) {
416+ globalOwner = null ;
417+ triggerOwnershipListenersWith = null ;
418+ }
419+ if ( triggerOwnershipListenersWith !== undefined ) {
420+ triggerOwnershipListeners ( triggerOwnershipListenersWith ) ;
421+ return true ;
422+ } else {
423+ return false ;
424+ }
425+ }
426+
363427function isFiberHostComponentFocusable ( fiber : Fiber ) : boolean {
364428 if ( fiber . tag !== HostComponent ) {
365429 return false ;
@@ -368,18 +432,22 @@ function isFiberHostComponentFocusable(fiber: Fiber): boolean {
368432 if ( memoizedProps . tabIndex === - 1 || memoizedProps . disabled ) {
369433 return false ;
370434 }
371- if ( memoizedProps . tabIndex === 0 ) {
435+ if ( memoizedProps . tabIndex === 0 || memoizedProps . contentEditable === true ) {
372436 return true ;
373437 }
374438 if ( type === 'a' || type === 'area' ) {
375- return ! ! memoizedProps . href ;
439+ return ! ! memoizedProps . href && memoizedProps . rel !== 'ignore' ;
440+ }
441+ if ( type === 'input' ) {
442+ return memoizedProps . type !== 'hidden' && memoizedProps . type !== 'file' ;
376443 }
377444 return (
378445 type === 'button' ||
379446 type === 'textarea' ||
380- type === 'input' ||
381447 type === 'object' ||
382- type === 'select'
448+ type === 'select' ||
449+ type === 'iframe' ||
450+ type === 'embed'
383451 ) ;
384452}
385453
@@ -487,15 +555,13 @@ function getTargetEventResponderInstances(
487555 // Traverse up the fiber tree till we find event component fibers.
488556 if ( node . tag === EventComponent ) {
489557 const eventComponentInstance = node . stateNode ;
490- if ( currentOwner === null || currentOwner === eventComponentInstance ) {
491- const responder = eventComponentInstance . responder ;
492- const targetEventTypes = responder . targetEventTypes ;
493- // Validate the target event type exists on the responder
494- if ( targetEventTypes !== undefined ) {
495- const targetEventTypesSet = getTargetEventTypesSet ( targetEventTypes ) ;
496- if ( targetEventTypesSet . has ( topLevelType ) ) {
497- eventResponderInstances . push ( eventComponentInstance ) ;
498- }
558+ const responder = eventComponentInstance . responder ;
559+ const targetEventTypes = responder . targetEventTypes ;
560+ // Validate the target event type exists on the responder
561+ if ( targetEventTypes !== undefined ) {
562+ const targetEventTypesSet = getTargetEventTypesSet ( targetEventTypes ) ;
563+ if ( targetEventTypesSet . has ( topLevelType ) ) {
564+ eventResponderInstances . push ( eventComponentInstance ) ;
499565 }
500566 }
501567 }
@@ -516,18 +582,35 @@ function getRootEventResponderInstances(
516582
517583 for ( let i = 0 ; i < rootEventComponentInstances . length ; i ++ ) {
518584 const rootEventComponentInstance = rootEventComponentInstances [ i ] ;
519-
520- if (
521- currentOwner === null ||
522- currentOwner === rootEventComponentInstance
523- ) {
524- eventResponderInstances . push ( rootEventComponentInstance ) ;
525- }
585+ eventResponderInstances . push ( rootEventComponentInstance ) ;
526586 }
527587 }
528588 return eventResponderInstances ;
529589}
530590
591+ function shouldSkipEventComponent (
592+ eventResponderInstance : ReactEventComponentInstance ,
593+ propagatedEventResponders : null | Set < ReactEventResponder > ,
594+ ) : boolean {
595+ const responder = eventResponderInstance . responder ;
596+ if ( propagatedEventResponders !== null && responder . stopLocalPropagation ) {
597+ if ( propagatedEventResponders . has ( responder ) ) {
598+ return true ;
599+ }
600+ propagatedEventResponders . add ( responder ) ;
601+ }
602+ if ( globalOwner && globalOwner !== eventResponderInstance ) {
603+ return true ;
604+ }
605+ if (
606+ responderOwners . has ( responder ) &&
607+ responderOwners . get ( responder ) !== eventResponderInstance
608+ ) {
609+ return true ;
610+ }
611+ return false ;
612+ }
613+
531614function traverseAndHandleEventResponderInstances (
532615 topLevelType : DOMTopLevelEventType ,
533616 targetFiber : null | Fiber ,
@@ -564,14 +647,16 @@ function traverseAndHandleEventResponderInstances(
564647 for ( i = length ; i -- > 0 ; ) {
565648 const targetEventResponderInstance = targetEventResponderInstances [ i ] ;
566649 const { responder , props , state } = targetEventResponderInstance ;
567- if ( responder . stopLocalPropagation ) {
568- if ( propagatedEventResponders . has ( responder ) ) {
569- continue ;
570- }
571- propagatedEventResponders . add ( responder ) ;
572- }
573650 const eventListener = responder . onEventCapture ;
574651 if ( eventListener !== undefined ) {
652+ if (
653+ shouldSkipEventComponent (
654+ targetEventResponderInstance ,
655+ propagatedEventResponders ,
656+ )
657+ ) {
658+ continue ;
659+ }
575660 currentInstance = targetEventResponderInstance ;
576661 eventListener ( responderEvent , eventResponderContext, props, state) ;
577662 }
@@ -582,14 +667,16 @@ function traverseAndHandleEventResponderInstances(
582667 for ( i = 0 ; i < length ; i ++ ) {
583668 const targetEventResponderInstance = targetEventResponderInstances [ i ] ;
584669 const { responder, props, state} = targetEventResponderInstance ;
585- if ( responder . stopLocalPropagation ) {
586- if ( propagatedEventResponders . has ( responder ) ) {
587- continue ;
588- }
589- propagatedEventResponders . add ( responder ) ;
590- }
591670 const eventListener = responder . onEvent ;
592671 if ( eventListener !== undefined ) {
672+ if (
673+ shouldSkipEventComponent (
674+ targetEventResponderInstance ,
675+ propagatedEventResponders ,
676+ )
677+ ) {
678+ continue ;
679+ }
593680 currentInstance = targetEventResponderInstance ;
594681 eventListener ( responderEvent , eventResponderContext , props , state ) ;
595682 }
@@ -606,20 +693,28 @@ function traverseAndHandleEventResponderInstances(
606693 const { responder , props , state } = rootEventResponderInstance ;
607694 const eventListener = responder . onRootEvent ;
608695 if ( eventListener !== undefined ) {
696+ if ( shouldSkipEventComponent ( rootEventResponderInstance , null ) ) {
697+ continue ;
698+ }
609699 currentInstance = rootEventResponderInstance ;
610700 eventListener ( responderEvent , eventResponderContext , props , state ) ;
611701 }
612702 }
613703 }
614704}
615705
616- function triggerOwnershipListeners ( ) : void {
706+ function triggerOwnershipListeners (
707+ limitByResponder : null | ReactEventResponder ,
708+ ) : void {
617709 const listeningInstances = Array . from ( ownershipChangeListeners ) ;
618710 const previousInstance = currentInstance ;
619711 try {
620712 for ( let i = 0 ; i < listeningInstances . length ; i ++ ) {
621713 const instance = listeningInstances [ i ] ;
622714 const { props , responder , state } = instance ;
715+ if ( limitByResponder !== null && limitByResponder !== responder ) {
716+ continue ;
717+ }
623718 currentInstance = instance ;
624719 const onOwnershipChange = responder . onOwnershipChange ;
625720 if ( onOwnershipChange !== undefined ) {
@@ -670,9 +765,12 @@ export function unmountEventResponder(
670765 currentTimers = null ;
671766 }
672767 }
673- if ( currentOwner === eventComponentInstance ) {
674- currentOwner = null ;
675- triggerOwnershipListeners ( ) ;
768+ try {
769+ currentEventQueue = createEventQueue ( ) ;
770+ releaseOwnershipForEventComponentInstance ( eventComponentInstance ) ;
771+ processEventQueue ( ) ;
772+ } finally {
773+ currentEventQueue = null ;
676774 }
677775 if ( responder . onOwnershipChange !== undefined ) {
678776 ownershipChangeListeners . delete ( eventComponentInstance ) ;
@@ -709,10 +807,10 @@ export function dispatchEventForResponderEventSystem(
709807 eventSystemFlags : EventSystemFlags ,
710808) : void {
711809 if ( enableEventAPI ) {
712- if ( alreadyDispatching ) {
713- return ;
714- }
715- alreadyDispatching = true ;
810+ const previousEventQueue = currentEventQueue ;
811+ const previousInstance = currentInstance ;
812+ const previousTimers = currentTimers ;
813+ currentTimers = null ;
716814 currentEventQueue = createEventQueue ( ) ;
717815 try {
718816 traverseAndHandleEventResponderInstances (
@@ -724,10 +822,9 @@ export function dispatchEventForResponderEventSystem(
724822 ) ;
725823 processEventQueue ( ) ;
726824 } finally {
727- currentTimers = null ;
728- currentInstance = null ;
729- currentEventQueue = null ;
730- alreadyDispatching = false ;
825+ currentTimers = previousTimers ;
826+ currentInstance = previousInstance ;
827+ currentEventQueue = previousEventQueue ;
731828 }
732829 }
733830}
0 commit comments