@@ -2143,29 +2143,18 @@ export function attach(
21432143 const { key} = fiber ;
21442144 const displayName = getDisplayNameForFiber ( fiber ) ;
21452145 const elementType = getElementTypeForFiber ( fiber ) ;
2146- const debugOwner = fiber . _debugOwner ;
2147-
2148- // Ideally we should call getFiberIDThrows() for _debugOwner,
2149- // since owners are almost always higher in the tree (and so have already been processed),
2150- // but in some (rare) instances reported in open source, a descendant mounts before an owner.
2151- // Since this is a DEV only field it's probably okay to also just lazily generate and ID here if needed.
2152- // See https://github.com/facebook/react/issues/21445
2153- let ownerID : number ;
2154- if ( debugOwner != null ) {
2155- if ( typeof debugOwner . tag === 'number' ) {
2156- const ownerFiberInstance = getFiberInstanceUnsafe ( ( debugOwner : any ) ) ;
2157- if ( ownerFiberInstance !== null ) {
2158- ownerID = ownerFiberInstance . id ;
2159- } else {
2160- ownerID = 0 ;
2161- }
2162- } else {
2163- // TODO: Track Server Component Owners.
2164- ownerID = 0 ;
2165- }
2166- } else {
2167- ownerID = 0 ;
2168- }
2146+
2147+ // Finding the owner instance might require traversing the whole parent path which
2148+ // doesn't have great big O notation. Ideally we'd lazily fetch the owner when we
2149+ // need it but we have some synchronous operations in the front end like Alt+Left
2150+ // which selects the owner immediately. Typically most owners are only a few parents
2151+ // away so maybe it's not so bad.
2152+ const debugOwner = getUnfilteredOwner ( fiber ) ;
2153+ const ownerInstance = findNearestOwnerInstance (
2154+ parentInstance ,
2155+ debugOwner ,
2156+ ) ;
2157+ const ownerID = ownerInstance === null ? 0 : ownerInstance . id ;
21692158 const parentID = parentInstance ? parentInstance . id : 0 ;
21702159
21712160 const displayNameStringID = getStringID ( displayName ) ;
@@ -2231,11 +2220,15 @@ export function attach(
22312220 displayName = env + '(' + displayName + ')' ;
22322221 }
22332222 const elementType = ElementTypeVirtual ;
2234- // TODO: Support Virtual Owners. To do this we need to find a matching
2235- // virtual instance which is not a super cheap parent traversal and so
2236- // we should ideally only do that lazily. We should maybe change the
2237- // frontend to get it lazily.
2238- const ownerID : number = 0 ;
2223+
2224+ // Finding the owner instance might require traversing the whole parent path which
2225+ // doesn't have great big O notation. Ideally we'd lazily fetch the owner when we
2226+ // need it but we have some synchronous operations in the front end like Alt+Left
2227+ // which selects the owner immediately. Typically most owners are only a few parents
2228+ // away so maybe it's not so bad.
2229+ const debugOwner = getUnfilteredOwner ( componentInfo ) ;
2230+ const ownerInstance = findNearestOwnerInstance ( parentInstance , debugOwner ) ;
2231+ const ownerID = ownerInstance === null ? 0 : ownerInstance . id ;
22392232 const parentID = parentInstance ? parentInstance . id : 0 ;
22402233
22412234 const displayNameStringID = getStringID ( displayName ) ;
@@ -3354,11 +3347,19 @@ export function attach(
33543347 }
33553348
33563349 function getUpdatersList ( root : any ) : Array < SerializedElement > | null {
3357- return root . memoizedUpdaters != null
3358- ? Array . from ( root . memoizedUpdaters )
3359- . filter ( fiber => getFiberIDUnsafe ( fiber ) !== null )
3360- . map ( fiberToSerializedElement )
3361- : null ;
3350+ const updaters = root . memoizedUpdaters ;
3351+ if ( updaters == null ) {
3352+ return null ;
3353+ }
3354+ const result = [ ] ;
3355+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
3356+ for ( const updater of updaters ) {
3357+ const inst = getFiberInstanceUnsafe ( updater ) ;
3358+ if ( inst !== null ) {
3359+ result . push ( instanceToSerializedElement ( inst ) ) ;
3360+ }
3361+ }
3362+ return result;
33623363 }
33633364
33643365 function handleCommitFiberUnmount ( fiber : any ) {
@@ -3923,13 +3924,26 @@ export function attach(
39233924 }
39243925 }
39253926
3926- function fiberToSerializedElement ( fiber : Fiber ) : SerializedElement {
3927- return {
3928- displayName : getDisplayNameForFiber ( fiber ) || 'Anonymous' ,
3929- id : getFiberIDThrows ( fiber ) ,
3930- key : fiber . key ,
3931- type : getElementTypeForFiber ( fiber ) ,
3932- } ;
3927+ function instanceToSerializedElement (
3928+ instance : DevToolsInstance ,
3929+ ) : SerializedElement {
3930+ if ( instance . kind === FIBER_INSTANCE ) {
3931+ const fiber = instance . data ;
3932+ return {
3933+ displayName : getDisplayNameForFiber ( fiber ) || 'Anonymous' ,
3934+ id : instance . id ,
3935+ key : fiber . key ,
3936+ type : getElementTypeForFiber ( fiber ) ,
3937+ } ;
3938+ } else {
3939+ const componentInfo = instance . data ;
3940+ return {
3941+ displayName : componentInfo . name || 'Anonymous' ,
3942+ id : instance . id ,
3943+ key : componentInfo . key == null ? null : componentInfo . key ,
3944+ type : ElementTypeVirtual ,
3945+ } ;
3946+ }
39333947 }
39343948
39353949 function getOwnersList ( id : number ) : Array < SerializedElement > | null {
@@ -3938,33 +3952,97 @@ export function attach(
39383952 console . warn ( `Could not find DevToolsInstance with id "${ id } "` ) ;
39393953 return null ;
39403954 }
3941- if (devtoolsInstance.kind !== FIBER_INSTANCE) {
3942- // TODO: Handle VirtualInstance.
3943- return null ;
3955+ const self = instanceToSerializedElement(devtoolsInstance);
3956+ const owners = getOwnersListFromInstance(devtoolsInstance);
3957+ // This is particular API is prefixed with the current instance too for some reason.
3958+ if (owners === null) {
3959+ return [ self ] ;
39443960 }
3945- const fiber =
3946- findCurrentFiberUsingSlowPathByFiberInstance(devtoolsInstance);
3947- if (fiber == null) {
3961+ owners.unshift(self);
3962+ owners.reverse();
3963+ return owners;
3964+ }
3965+
3966+ function getOwnersListFromInstance (
3967+ instance : DevToolsInstance ,
3968+ ) : Array < SerializedElement > | null {
3969+ let owner = getUnfilteredOwner ( instance . data ) ;
3970+ if ( owner === null ) {
39483971 return null ;
39493972 }
3973+ const owners : Array < SerializedElement > = [ ] ;
3974+ let parentInstance : null | DevToolsInstance = instance . parent ;
3975+ while ( parentInstance !== null && owner !== null ) {
3976+ const ownerInstance = findNearestOwnerInstance ( parentInstance , owner ) ;
3977+ if ( ownerInstance !== null ) {
3978+ owners . push ( instanceToSerializedElement ( ownerInstance ) ) ;
3979+ // Get the next owner and keep searching from the previous match.
3980+ owner = getUnfilteredOwner ( owner ) ;
3981+ parentInstance = ownerInstance . parent ;
3982+ } else {
3983+ break;
3984+ }
3985+ }
3986+ return owners;
3987+ }
39503988
3951- const owners: Array< SerializedElement > = [fiberToSerializedElement(fiber)];
3952-
3953- let owner = fiber._debugOwner;
3954- while (owner != null) {
3989+ function getUnfilteredOwner (
3990+ owner : ReactComponentInfo | Fiber | null | void ,
3991+ ) : ReactComponentInfo | Fiber | null {
3992+ if ( owner == null ) {
3993+ return null ;
3994+ }
3995+ if (typeof owner.tag === 'number') {
3996+ const ownerFiber : Fiber = ( owner : any ) ; // Refined
3997+ owner = ownerFiber . _debugOwner ;
3998+ } else {
3999+ const ownerInfo : ReactComponentInfo = ( owner : any ) ; // Refined
4000+ owner = ownerInfo . owner ;
4001+ }
4002+ while (owner) {
39554003 if ( typeof owner . tag === 'number' ) {
39564004 const ownerFiber : Fiber = ( owner : any ) ; // Refined
39574005 if ( ! shouldFilterFiber ( ownerFiber ) ) {
3958- owners . unshift ( fiberToSerializedElement ( ownerFiber ) ) ;
4006+ return ownerFiber ;
39594007 }
39604008 owner = ownerFiber . _debugOwner ;
39614009 } else {
3962- // TODO: Track Server Component Owners.
3963- break ;
4010+ const ownerInfo : ReactComponentInfo = ( owner : any ) ; // Refined
4011+ if ( ! shouldFilterVirtual ( ownerInfo ) ) {
4012+ return ownerInfo ;
4013+ }
4014+ owner = ownerInfo . owner ;
39644015 }
39654016 }
4017+ return null ;
4018+ }
39664019
3967- return owners ;
4020+ function findNearestOwnerInstance (
4021+ parentInstance : null | DevToolsInstance ,
4022+ owner : void | null | ReactComponentInfo | Fiber ,
4023+ ) : null | DevToolsInstance {
4024+ if ( owner == null ) {
4025+ return null ;
4026+ }
4027+ // Search the parent path for any instance that matches this kind of owner.
4028+ while (parentInstance !== null) {
4029+ if (
4030+ parentInstance . data === owner ||
4031+ // Typically both owner and instance.data would refer to the current version of a Fiber
4032+ // but it is possible for memoization to ignore the owner on the JSX. Then the new Fiber
4033+ // isn't propagated down as the new owner. In that case we might match the alternate
4034+ // instead. This is a bit hacky but the fastest check since type casting owner to a Fiber
4035+ // needs a duck type check anyway.
4036+ parentInstance . data === ( owner : any ) . alternate
4037+ ) {
4038+ return parentInstance ;
4039+ }
4040+ parentInstance = parentInstance.parent;
4041+ }
4042+ // It is technically possible to create an element and render it in a different parent
4043+ // but this is a weird edge case and it is worth not having to scan the tree or keep
4044+ // a register for every fiber/component info.
4045+ return null ;
39684046 }
39694047
39704048 // Fast path props lookup for React Native style editor.
@@ -4047,7 +4125,6 @@ export function attach(
40474125 }
40484126
40494127 const {
4050- _debugOwner : debugOwner ,
40514128 stateNode ,
40524129 key ,
40534130 memoizedProps ,
@@ -4174,21 +4251,8 @@ export function attach(
41744251 context = { value : context } ;
41754252 }
41764253
4177- let owners : null | Array < SerializedElement > = null ;
4178- let owner = debugOwner ;
4179- while ( owner != null ) {
4180- if ( typeof owner . tag === 'number' ) {
4181- const ownerFiber : Fiber = ( owner : any ) ; // Refined
4182- if ( owners === null ) {
4183- owners = [ ] ;
4184- }
4185- owners . push ( fiberToSerializedElement ( ownerFiber ) ) ;
4186- owner = ownerFiber . _debugOwner ;
4187- } else {
4188- // TODO: Track Server Component Owners.
4189- break ;
4190- }
4191- }
4254+ const owners : null | Array < SerializedElement > =
4255+ getOwnersListFromInstance ( fiberInstance ) ;
41924256
41934257 const isTimedOutSuspense =
41944258 tag === SuspenseComponent && memoizedState !== null ;
@@ -4352,8 +4416,8 @@ export function attach(
43524416 displayName = env + '(' + displayName + ')' ;
43534417 }
43544418
4355- // TODO: Support Virtual Owners.
4356- const owners : null | Array < SerializedElement > = null ;
4419+ const owners : null | Array < SerializedElement > =
4420+ getOwnersListFromInstance ( virtualInstance ) ;
43574421
43584422 let rootType = null ;
43594423 let targetErrorBoundaryID = null ;
0 commit comments