@@ -84,10 +84,13 @@ const eventListeners:
84
84
($Shape< PartialEventObject > ) => void ,
85
85
> = new PossiblyWeakMap ( ) ;
86
86
87
- let alreadyDispatching = false ;
87
+ const responderOwners : Map <
88
+ ReactEventResponder ,
89
+ ReactEventComponentInstance ,
90
+ > = new Map ( ) ;
91
+ let globalOwner = null ;
88
92
89
93
let currentTimers = new Map ( ) ;
90
- let currentOwner = null ;
91
94
let currentInstance : null | ReactEventComponentInstance = null ;
92
95
let currentEventQueue : null | EventQueue = null ;
93
96
@@ -131,8 +134,9 @@ const eventResponderContext: ReactResponderContext = {
131
134
eventListeners . set ( eventObject , listener ) ;
132
135
eventQueue . events . push ( eventObject ) ;
133
136
} ,
134
- isPositionWithinTouchHitTarget ( doc : Document , x : number , y : number ) : boolean {
137
+ isPositionWithinTouchHitTarget ( x : number , y : number ) : boolean {
135
138
validateResponderContext ( ) ;
139
+ const doc = getActiveDocument ( ) ;
136
140
// This isn't available in some environments (JSDOM)
137
141
if ( typeof doc . elementFromPoint !== 'function' ) {
138
142
return false ;
@@ -188,6 +192,27 @@ const eventResponderContext: ReactResponderContext = {
188
192
}
189
193
return false ;
190
194
} ,
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
+ } ,
191
216
isTargetWithinElement (
192
217
childTarget : Element | Document ,
193
218
parentTarget : Element | Document ,
@@ -204,12 +229,10 @@ const eventResponderContext: ReactResponderContext = {
204
229
}
205
230
return false ;
206
231
} ,
207
- addRootEventTypes (
208
- doc : Document ,
209
- rootEventTypes : Array < ReactEventResponderEventType > ,
210
- ) : void {
232
+ addRootEventTypes ( rootEventTypes : Array < ReactEventResponderEventType > ) : void {
211
233
validateResponderContext ( ) ;
212
- listenToResponderEventTypesImpl ( rootEventTypes , doc ) ;
234
+ const activeDocument = getActiveDocument ( ) ;
235
+ listenToResponderEventTypesImpl ( rootEventTypes , activeDocument ) ;
213
236
for ( let i = 0 ; i < rootEventTypes . length ; i ++ ) {
214
237
const rootEventType = rootEventTypes [ i ] ;
215
238
const topLevelEventType =
@@ -265,25 +288,38 @@ const eventResponderContext: ReactResponderContext = {
265
288
} ,
266
289
hasOwnership ( ) : boolean {
267
290
validateResponderContext ( ) ;
268
- return currentOwner === currentInstance ;
291
+ const responder = ( ( currentInstance : any ) : ReactEventComponentInstance )
292
+ . responder ;
293
+ return (
294
+ globalOwner === currentInstance ||
295
+ responderOwners . get ( responder ) === currentInstance
296
+ ) ;
269
297
} ,
270
- requestOwnership ( ) : boolean {
298
+ requestGlobalOwnership ( ) : boolean {
271
299
validateResponderContext ( ) ;
272
- if ( currentOwner !== null ) {
300
+ if ( globalOwner !== null ) {
273
301
return false ;
274
302
}
275
- currentOwner = currentInstance ;
276
- triggerOwnershipListeners ( ) ;
303
+ globalOwner = currentInstance ;
304
+ triggerOwnershipListeners ( null ) ;
277
305
return true ;
278
306
} ,
279
- releaseOwnership ( ) : boolean {
307
+ requestResponderOwnership ( ) : boolean {
280
308
validateResponderContext ( ) ;
281
- if ( currentOwner !== currentInstance ) {
309
+ const eventComponentInstance = ( ( currentInstance : any ) : ReactEventComponentInstance ) ;
310
+ const responder = eventComponentInstance . responder ;
311
+ if ( responderOwners . has ( responder ) ) {
282
312
return false ;
283
313
}
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
+ ) ;
287
323
} ,
288
324
setTimeout ( func : ( ) = > void , delay ) : Symbol {
289
325
validateResponderContext ( ) ;
@@ -330,9 +366,6 @@ const eventResponderContext: ReactResponderContext = {
330
366
let node = ( ( eventComponentInstance . currentFiber : any ) : Fiber ) . child ;
331
367
332
368
while ( node !== null ) {
333
- if ( node . stateNode === currentInstance ) {
334
- break ;
335
- }
336
369
if ( isFiberHostComponentFocusable ( node ) ) {
337
370
focusableElements . push ( node . stateNode ) ;
338
371
} else {
@@ -353,13 +386,44 @@ const eventResponderContext: ReactResponderContext = {
353
386
if ( parent === null ) {
354
387
break ;
355
388
}
389
+ if ( parent . stateNode === currentInstance ) {
390
+ break ;
391
+ }
356
392
node = parent . sibling ;
357
393
}
358
394
359
395
return focusableElements ;
360
396
} ,
397
+ getActiveDocument ,
361
398
} ;
362
399
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
+
363
427
function isFiberHostComponentFocusable ( fiber : Fiber ) : boolean {
364
428
if ( fiber . tag !== HostComponent ) {
365
429
return false ;
@@ -368,18 +432,22 @@ function isFiberHostComponentFocusable(fiber: Fiber): boolean {
368
432
if ( memoizedProps . tabIndex === - 1 || memoizedProps . disabled ) {
369
433
return false ;
370
434
}
371
- if ( memoizedProps . tabIndex === 0 ) {
435
+ if ( memoizedProps . tabIndex === 0 || memoizedProps . contentEditable === true ) {
372
436
return true ;
373
437
}
374
438
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' ;
376
443
}
377
444
return (
378
445
type === 'button' ||
379
446
type === 'textarea' ||
380
- type === 'input' ||
381
447
type === 'object' ||
382
- type === 'select'
448
+ type === 'select' ||
449
+ type === 'iframe' ||
450
+ type === 'embed'
383
451
) ;
384
452
}
385
453
@@ -487,15 +555,13 @@ function getTargetEventResponderInstances(
487
555
// Traverse up the fiber tree till we find event component fibers.
488
556
if ( node . tag === EventComponent ) {
489
557
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 ) ;
499
565
}
500
566
}
501
567
}
@@ -516,18 +582,35 @@ function getRootEventResponderInstances(
516
582
517
583
for ( let i = 0 ; i < rootEventComponentInstances . length ; i ++ ) {
518
584
const rootEventComponentInstance = rootEventComponentInstances [ i ] ;
519
-
520
- if (
521
- currentOwner === null ||
522
- currentOwner === rootEventComponentInstance
523
- ) {
524
- eventResponderInstances . push ( rootEventComponentInstance ) ;
525
- }
585
+ eventResponderInstances . push ( rootEventComponentInstance ) ;
526
586
}
527
587
}
528
588
return eventResponderInstances ;
529
589
}
530
590
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
+
531
614
function traverseAndHandleEventResponderInstances (
532
615
topLevelType : DOMTopLevelEventType ,
533
616
targetFiber : null | Fiber ,
@@ -564,14 +647,16 @@ function traverseAndHandleEventResponderInstances(
564
647
for ( i = length ; i -- > 0 ; ) {
565
648
const targetEventResponderInstance = targetEventResponderInstances [ i ] ;
566
649
const { responder , props , state } = targetEventResponderInstance ;
567
- if ( responder . stopLocalPropagation ) {
568
- if ( propagatedEventResponders . has ( responder ) ) {
569
- continue ;
570
- }
571
- propagatedEventResponders . add ( responder ) ;
572
- }
573
650
const eventListener = responder . onEventCapture ;
574
651
if ( eventListener !== undefined ) {
652
+ if (
653
+ shouldSkipEventComponent (
654
+ targetEventResponderInstance ,
655
+ propagatedEventResponders ,
656
+ )
657
+ ) {
658
+ continue ;
659
+ }
575
660
currentInstance = targetEventResponderInstance ;
576
661
eventListener ( responderEvent , eventResponderContext, props, state) ;
577
662
}
@@ -582,14 +667,16 @@ function traverseAndHandleEventResponderInstances(
582
667
for ( i = 0 ; i < length ; i ++ ) {
583
668
const targetEventResponderInstance = targetEventResponderInstances [ i ] ;
584
669
const { responder, props, state} = targetEventResponderInstance ;
585
- if ( responder . stopLocalPropagation ) {
586
- if ( propagatedEventResponders . has ( responder ) ) {
587
- continue ;
588
- }
589
- propagatedEventResponders . add ( responder ) ;
590
- }
591
670
const eventListener = responder . onEvent ;
592
671
if ( eventListener !== undefined ) {
672
+ if (
673
+ shouldSkipEventComponent (
674
+ targetEventResponderInstance ,
675
+ propagatedEventResponders ,
676
+ )
677
+ ) {
678
+ continue ;
679
+ }
593
680
currentInstance = targetEventResponderInstance ;
594
681
eventListener ( responderEvent , eventResponderContext , props , state ) ;
595
682
}
@@ -606,20 +693,28 @@ function traverseAndHandleEventResponderInstances(
606
693
const { responder , props , state } = rootEventResponderInstance ;
607
694
const eventListener = responder . onRootEvent ;
608
695
if ( eventListener !== undefined ) {
696
+ if ( shouldSkipEventComponent ( rootEventResponderInstance , null ) ) {
697
+ continue ;
698
+ }
609
699
currentInstance = rootEventResponderInstance ;
610
700
eventListener ( responderEvent , eventResponderContext , props , state ) ;
611
701
}
612
702
}
613
703
}
614
704
}
615
705
616
- function triggerOwnershipListeners ( ) : void {
706
+ function triggerOwnershipListeners (
707
+ limitByResponder : null | ReactEventResponder ,
708
+ ) : void {
617
709
const listeningInstances = Array . from ( ownershipChangeListeners ) ;
618
710
const previousInstance = currentInstance ;
619
711
try {
620
712
for ( let i = 0 ; i < listeningInstances . length ; i ++ ) {
621
713
const instance = listeningInstances [ i ] ;
622
714
const { props , responder , state } = instance ;
715
+ if ( limitByResponder !== null && limitByResponder !== responder ) {
716
+ continue ;
717
+ }
623
718
currentInstance = instance ;
624
719
const onOwnershipChange = responder . onOwnershipChange ;
625
720
if ( onOwnershipChange !== undefined ) {
@@ -670,9 +765,12 @@ export function unmountEventResponder(
670
765
currentTimers = null ;
671
766
}
672
767
}
673
- if ( currentOwner === eventComponentInstance ) {
674
- currentOwner = null ;
675
- triggerOwnershipListeners ( ) ;
768
+ try {
769
+ currentEventQueue = createEventQueue ( ) ;
770
+ releaseOwnershipForEventComponentInstance ( eventComponentInstance ) ;
771
+ processEventQueue ( ) ;
772
+ } finally {
773
+ currentEventQueue = null ;
676
774
}
677
775
if ( responder . onOwnershipChange !== undefined ) {
678
776
ownershipChangeListeners . delete ( eventComponentInstance ) ;
@@ -709,10 +807,10 @@ export function dispatchEventForResponderEventSystem(
709
807
eventSystemFlags : EventSystemFlags ,
710
808
) : void {
711
809
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 ;
716
814
currentEventQueue = createEventQueue ( ) ;
717
815
try {
718
816
traverseAndHandleEventResponderInstances (
@@ -724,10 +822,9 @@ export function dispatchEventForResponderEventSystem(
724
822
) ;
725
823
processEventQueue ( ) ;
726
824
} finally {
727
- currentTimers = null ;
728
- currentInstance = null ;
729
- currentEventQueue = null ;
730
- alreadyDispatching = false ;
825
+ currentTimers = previousTimers ;
826
+ currentInstance = previousInstance ;
827
+ currentEventQueue = previousEventQueue ;
731
828
}
732
829
}
733
830
}
0 commit comments