@@ -732,35 +732,85 @@ const fiberToFiberInstanceMap: Map<Fiber, FiberInstance> = new Map();
732
732
// operations that should be the same whether the current and work-in-progress Fiber is used.
733
733
const idToDevToolsInstanceMap : Map < number , DevToolsInstance > = new Map ( ) ;
734
734
735
- // Map of resource DOM nodes to all the Fibers that depend on it.
736
- const hostResourceToFiberMap : Map < HostInstance , Set < Fiber >> = new Map ( ) ;
735
+ // Map of canonical HostInstances to the nearest parent DevToolsInstance.
736
+ const publicInstanceToDevToolsInstanceMap : Map < HostInstance , DevToolsInstance > =
737
+ new Map ( ) ;
738
+ // Map of resource DOM nodes to all the nearest DevToolsInstances that depend on it.
739
+ const hostResourceToDevToolsInstanceMap : Map <
740
+ HostInstance ,
741
+ Set < DevToolsInstance > ,
742
+ > = new Map ( ) ;
743
+
744
+ function getPublicInstance ( instance : HostInstance ) : HostInstance {
745
+ // Typically the PublicInstance and HostInstance is the same thing but not in Fabric.
746
+ // So we need to detect this and use that as the public instance.
747
+ return typeof instance === 'object' &&
748
+ instance !== null &&
749
+ typeof instance . canonical === 'object'
750
+ ? ( instance . canonical : any )
751
+ : instance ;
752
+ }
753
+
754
+ function aquireHostInstance (
755
+ nearestInstance : DevToolsInstance ,
756
+ hostInstance : HostInstance ,
757
+ ) : void {
758
+ const publicInstance = getPublicInstance ( hostInstance ) ;
759
+ publicInstanceToDevToolsInstanceMap . set ( publicInstance , nearestInstance ) ;
760
+ }
761
+
762
+ function releaseHostInstance (
763
+ nearestInstance : DevToolsInstance ,
764
+ hostInstance : HostInstance ,
765
+ ) : void {
766
+ const publicInstance = getPublicInstance ( hostInstance ) ;
767
+ if (
768
+ publicInstanceToDevToolsInstanceMap . get ( publicInstance ) === nearestInstance
769
+ ) {
770
+ publicInstanceToDevToolsInstanceMap . delete ( publicInstance ) ;
771
+ }
772
+ }
737
773
738
774
function aquireHostResource (
739
- fiber : Fiber ,
775
+ nearestInstance : DevToolsInstance ,
740
776
resource : ?{ instance ?: HostInstance } ,
741
777
) : void {
742
778
const hostInstance = resource && resource . instance ;
743
779
if ( hostInstance ) {
744
- let resourceFibers = hostResourceToFiberMap . get ( hostInstance ) ;
745
- if ( resourceFibers === undefined ) {
746
- resourceFibers = new Set ( ) ;
747
- hostResourceToFiberMap . set ( hostInstance , resourceFibers ) ;
780
+ const publicInstance = getPublicInstance ( hostInstance ) ;
781
+ let resourceInstances =
782
+ hostResourceToDevToolsInstanceMap . get ( publicInstance ) ;
783
+ if ( resourceInstances === undefined ) {
784
+ resourceInstances = new Set ( ) ;
785
+ hostResourceToDevToolsInstanceMap . set ( publicInstance , resourceInstances ) ;
786
+ // Store the first match in the main map for quick access when selecting DOM node.
787
+ publicInstanceToDevToolsInstanceMap . set ( publicInstance , nearestInstance ) ;
748
788
}
749
- resourceFibers . add ( fiber ) ;
789
+ resourceInstances . add ( nearestInstance ) ;
750
790
}
751
791
}
752
792
753
793
function releaseHostResource (
754
- fiber : Fiber ,
794
+ nearestInstance : DevToolsInstance ,
755
795
resource : ?{ instance ?: HostInstance } ,
756
796
) : void {
757
797
const hostInstance = resource && resource . instance ;
758
798
if ( hostInstance ) {
759
- const resourceFibers = hostResourceToFiberMap . get ( hostInstance ) ;
760
- if ( resourceFibers !== undefined ) {
761
- resourceFibers . delete ( fiber ) ;
762
- if ( resourceFibers . size === 0 ) {
763
- hostResourceToFiberMap . delete ( hostInstance ) ;
799
+ const publicInstance = getPublicInstance ( hostInstance ) ;
800
+ const resourceInstances =
801
+ hostResourceToDevToolsInstanceMap . get ( publicInstance ) ;
802
+ if ( resourceInstances !== undefined ) {
803
+ resourceInstances . delete ( nearestInstance ) ;
804
+ if ( resourceInstances . size === 0 ) {
805
+ hostResourceToDevToolsInstanceMap . delete ( publicInstance ) ;
806
+ publicInstanceToDevToolsInstanceMap . delete ( publicInstance ) ;
807
+ } else if ( publicInstanceToDevToolsInstanceMap . get ( publicInstance ) === nearestInstance ) {
808
+ // This was the first one. Store the next first one in the main map for easy access.
809
+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
810
+ for ( const firstInstance of resourceInstances ) {
811
+ publicInstanceToDevToolsInstanceMap . set ( firstInstance , nearestInstance ) ;
812
+ break ;
813
+ }
764
814
}
765
815
}
766
816
}
@@ -1448,6 +1498,18 @@ export function attach(
1448
1498
fiberToFiberInstanceMap . delete ( alternate ) ;
1449
1499
}
1450
1500
}
1501
+
1502
+ // TODO: This is not enough if this Fiber was filtered since we don't end up
1503
+ // untracking it. We could use a WeakMap but that doesn't work for Paper tags.
1504
+ if ( fiber . tag === HostHoistable ) {
1505
+ releaseHostResource ( fiberInstance , fiber . memoizedState ) ;
1506
+ } else if (
1507
+ fiber . tag === HostComponent ||
1508
+ fiber . tag === HostText ||
1509
+ fiber . tag === HostSingleton
1510
+ ) {
1511
+ releaseHostInstance ( fiberInstance , fiber . stateNode ) ;
1512
+ }
1451
1513
}
1452
1514
1453
1515
function getChangeDescription (
@@ -2557,7 +2619,21 @@ export function attach(
2557
2619
}
2558
2620
2559
2621
if ( fiber . tag === HostHoistable ) {
2560
- aquireHostResource ( fiber , fiber . memoizedState ) ;
2622
+ const nearestInstance = reconcilingParent ;
2623
+ if ( nearestInstance === null ) {
2624
+ throw new Error ( 'Did not expect a host hoistable to be the root' ) ;
2625
+ }
2626
+ aquireHostResource ( nearestInstance , fiber . memoizedState ) ;
2627
+ } else if (
2628
+ fiber . tag === HostComponent ||
2629
+ fiber . tag === HostText ||
2630
+ fiber . tag === HostSingleton
2631
+ ) {
2632
+ const nearestInstance = reconcilingParent ;
2633
+ if ( nearestInstance === null ) {
2634
+ throw new Error ( 'Did not expect a host hoistable to be the root' ) ;
2635
+ }
2636
+ aquireHostInstance ( nearestInstance , fiber . stateNode ) ;
2561
2637
}
2562
2638
2563
2639
if ( fiber . tag === SuspenseComponent ) {
@@ -3159,8 +3235,12 @@ export function attach(
3159
3235
}
3160
3236
try {
3161
3237
if ( nextFiber . tag === HostHoistable ) {
3162
- releaseHostResource ( prevFiber , prevFiber . memoizedState ) ;
3163
- aquireHostResource ( nextFiber , nextFiber . memoizedState ) ;
3238
+ const nearestInstance = reconcilingParent ;
3239
+ if ( nearestInstance === null ) {
3240
+ throw new Error ( 'Did not expect a host hoistable to be the root' ) ;
3241
+ }
3242
+ releaseHostResource ( nearestInstance , prevFiber . memoizedState ) ;
3243
+ aquireHostResource ( nearestInstance , nextFiber . memoizedState ) ;
3164
3244
}
3165
3245
3166
3246
const isSuspense = nextFiber . tag === SuspenseComponent ;
@@ -3639,84 +3719,33 @@ export function attach(
3639
3719
}
3640
3720
3641
3721
function getNearestMountedHostInstance (
3642
- hostInstance : HostInstance,
3722
+ publicInstance : HostInstance ,
3643
3723
) : null | HostInstance {
3644
- const mountedFiber = renderer . findFiberByHostInstance ( hostInstance ) ;
3724
+ // TODO: Remove dependency on findFiberByHostInstance.
3725
+ const mountedFiber = renderer . findFiberByHostInstance ( publicInstance ) ;
3645
3726
if ( mountedFiber != null ) {
3646
- if ( mountedFiber . stateNode !== hostInstance ) {
3727
+ if ( mountedFiber . stateNode !== publicInstance ) {
3647
3728
// If it's not a perfect match the specific one might be a resource.
3648
3729
// We don't need to look at any parents because host resources don't have
3649
3730
// children so it won't be in any parent if it's not this one.
3650
- if ( hostResourceToFiberMap . has ( hostInstance ) ) {
3651
- return hostInstance ;
3731
+ if ( publicInstanceToDevToolsInstanceMap . has ( publicInstance ) ) {
3732
+ return publicInstance ;
3652
3733
}
3653
3734
}
3654
3735
return mountedFiber . stateNode ;
3655
3736
}
3656
- if ( hostResourceToFiberMap . has ( hostInstance ) ) {
3657
- return hostInstance ;
3658
- }
3659
- return null;
3660
- }
3661
-
3662
- function findNearestUnfilteredElementID ( searchFiber : Fiber ) {
3663
- let fiber : null | Fiber = searchFiber ;
3664
- while ( fiber !== null ) {
3665
- const fiberInstance = getFiberInstanceUnsafe ( fiber ) ;
3666
- if ( fiberInstance !== null ) {
3667
- // TODO: Ideally we would not have any filtered FiberInstances which
3668
- // would make this logic much simpler. Unfortunately, we sometimes
3669
- // eagerly add to the map and some times don't eagerly clean it up.
3670
- // TODO: If the fiber is filtered, the FiberInstance wouldn't really
3671
- // exist which would mean that we also don't have a way to get to the
3672
- // VirtualInstances.
3673
- if ( ! shouldFilterFiber ( fiberInstance . data ) ) {
3674
- return fiberInstance . id ;
3675
- }
3676
- // We couldn't use this Fiber but we might have a VirtualInstance
3677
- // that is the nearest unfiltered instance.
3678
- let parentInstance = fiberInstance . parent ;
3679
- while ( parentInstance !== null ) {
3680
- if ( parentInstance . kind === FIBER_INSTANCE ) {
3681
- // If we find a parent Fiber, it might not be the nearest parent
3682
- // so we break out and continue walking the Fiber tree instead.
3683
- break ;
3684
- } else {
3685
- if ( ! shouldFilterVirtual ( parentInstance . data ) ) {
3686
- return parentInstance . id ;
3687
- }
3688
- }
3689
- parentInstance = parentInstance . parent ;
3690
- }
3691
- }
3692
- fiber = fiber . return ;
3737
+ if ( publicInstanceToDevToolsInstanceMap . has ( publicInstance ) ) {
3738
+ return publicInstance ;
3693
3739
}
3694
3740
return null ;
3695
3741
}
3696
3742
3697
3743
function getElementIDForHostInstance (
3698
- hostInstance : HostInstance ,
3699
- findNearestUnfilteredAncestor : boolean = false ,
3744
+ publicInstance : HostInstance ,
3700
3745
) : number | null {
3701
- const resourceFibers = hostResourceToFiberMap . get ( hostInstance ) ;
3702
- if ( resourceFibers !== undefined ) {
3703
- // This is a resource. Find the first unfiltered instance.
3704
- // eslint-disable-next-line no-for-of-loops/no-for-of-loops
3705
- for ( const resourceFiber of resourceFibers ) {
3706
- const elementID = findNearestUnfilteredElementID ( resourceFiber ) ;
3707
- if ( elementID !== null ) {
3708
- return elementID ;
3709
- }
3710
- }
3711
- // If we don't find one, fallthrough to select the parent instead.
3712
- }
3713
- const fiber = renderer . findFiberByHostInstance ( hostInstance ) ;
3714
- if ( fiber != null ) {
3715
- if ( ! findNearestUnfilteredAncestor ) {
3716
- // TODO: Remove this option. It's not used.
3717
- return getFiberIDThrows ( fiber ) ;
3718
- }
3719
- return findNearestUnfilteredElementID ( fiber ) ;
3746
+ const instance = publicInstanceToDevToolsInstanceMap . get ( publicInstance ) ;
3747
+ if ( instance !== undefined ) {
3748
+ return instance . id ;
3720
3749
}
3721
3750
return null ;
3722
3751
}
0 commit comments