Skip to content

Commit 22cbd4c

Browse files
committed
Track virtual debug info from suspensey images
1 parent ac7820a commit 22cbd4c

File tree

2 files changed

+129
-9
lines changed

2 files changed

+129
-9
lines changed

packages/react-devtools-shared/src/backend/fiber/renderer.js

Lines changed: 125 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ export function getInternalReactConstants(version: string): {
366366
ReactPriorityLevels: ReactPriorityLevelsType,
367367
ReactTypeOfWork: WorkTagMap,
368368
StrictModeBits: number,
369+
SuspenseyImagesMode: number,
369370
} {
370371
// **********************************************************
371372
// The section below is copied from files in React repo.
@@ -406,6 +407,8 @@ export function getInternalReactConstants(version: string): {
406407
StrictModeBits = 0b10;
407408
}
408409

410+
const SuspenseyImagesMode = 0b0100000;
411+
409412
let ReactTypeOfWork: WorkTagMap = ((null: any): WorkTagMap);
410413

411414
// **********************************************************
@@ -819,6 +822,7 @@ export function getInternalReactConstants(version: string): {
819822
ReactPriorityLevels,
820823
ReactTypeOfWork,
821824
StrictModeBits,
825+
SuspenseyImagesMode,
822826
};
823827
}
824828
@@ -987,6 +991,7 @@ export function attach(
987991
ReactPriorityLevels,
988992
ReactTypeOfWork,
989993
StrictModeBits,
994+
SuspenseyImagesMode,
990995
} = getInternalReactConstants(version);
991996
const {
992997
ActivityComponent,
@@ -3307,6 +3312,114 @@ export function attach(
33073312
insertSuspendedBy(asyncInfo);
33083313
}
33093314
3315+
function trackDebugInfoFromHostComponent(
3316+
devtoolsInstance: DevToolsInstance,
3317+
fiber: Fiber,
3318+
): void {
3319+
if (fiber.tag !== HostComponent) {
3320+
return;
3321+
}
3322+
if ((fiber.mode & SuspenseyImagesMode) === 0) {
3323+
// In any released version, Suspensey Images are only enabled inside a ViewTransition
3324+
// subtree, which is enabled by the SuspenseyImagesMode.
3325+
// TODO: If we ever enable the enableSuspenseyImages flag then it would be enabled for
3326+
// all images and we'd need some other check for if the version of React has that enabled.
3327+
return;
3328+
}
3329+
3330+
const type = fiber.type;
3331+
const props: {
3332+
src?: string,
3333+
onLoad?: (event: any) => void,
3334+
loading?: 'eager' | 'lazy',
3335+
...
3336+
} = fiber.memoizedProps;
3337+
3338+
const maySuspendCommit =
3339+
type === 'img' &&
3340+
props.src != null &&
3341+
props.src !== '' &&
3342+
props.onLoad == null &&
3343+
props.loading !== 'lazy';
3344+
3345+
// Note: We don't track "maySuspendCommitOnUpdate" separately because it doesn't matter if
3346+
// it didn't suspend this particular update if it would've suspended if it mounted in this
3347+
// state, since we're tracking the dependencies inside the current state.
3348+
3349+
if (!maySuspendCommit) {
3350+
return;
3351+
}
3352+
3353+
const instance = fiber.stateNode;
3354+
if (instance == null) {
3355+
// Should never happen.
3356+
return;
3357+
}
3358+
3359+
// Unlike props.src, currentSrc will be fully qualified which we need for comparison below.
3360+
// Unlike instance.src it will be resolved into the media queries currently matching which is
3361+
// the state we're inspecting.
3362+
const src = instance.currentSrc;
3363+
if (typeof src !== 'string' || src === '') {
3364+
return;
3365+
}
3366+
let start = -1;
3367+
let end = -1;
3368+
let fileSize = 0;
3369+
// $FlowFixMe[method-unbinding]
3370+
if (typeof performance.getEntriesByType === 'function') {
3371+
// We may be able to collect the start and end time of this resource from Performance Observer.
3372+
const resourceEntries = performance.getEntriesByType('resource');
3373+
for (let i = 0; i < resourceEntries.length; i++) {
3374+
const resourceEntry = resourceEntries[i];
3375+
if (resourceEntry.name === src) {
3376+
start = resourceEntry.startTime;
3377+
end = start + resourceEntry.duration;
3378+
// $FlowFixMe[prop-missing]
3379+
fileSize = (resourceEntry.encodedBodySize: any) || 0;
3380+
}
3381+
}
3382+
}
3383+
// A representation of the image data itself.
3384+
// TODO: We could render a little preview in the front end from the resource API.
3385+
const value: {
3386+
currentSrc: string,
3387+
naturalWidth?: number,
3388+
naturalHeight?: number,
3389+
fileSize?: number,
3390+
} = {
3391+
currentSrc: src,
3392+
};
3393+
if (instance.naturalWidth > 0 && instance.naturalHeight > 0) {
3394+
// The intrinsic size of the file value itself, if it's loaded
3395+
value.naturalWidth = instance.naturalWidth;
3396+
value.naturalHeight = instance.naturalHeight;
3397+
}
3398+
if (fileSize > 0) {
3399+
// Cross-origin images won't have a file size that we can access.
3400+
value.fileSize = fileSize;
3401+
}
3402+
const promise = Promise.resolve(value);
3403+
(promise: any).status = 'fulfilled';
3404+
(promise: any).value = value;
3405+
const ioInfo: ReactIOInfo = {
3406+
name: 'img',
3407+
start,
3408+
end,
3409+
value: promise,
3410+
// $FlowFixMe: This field doesn't usually take a Fiber but we're only using inside this file.
3411+
owner: fiber, // Allow linking to the <link> if it's not filtered.
3412+
};
3413+
const asyncInfo: ReactAsyncInfo = {
3414+
awaited: ioInfo,
3415+
// $FlowFixMe: This field doesn't usually take a Fiber but we're only using inside this file.
3416+
owner: fiber._debugOwner == null ? null : fiber._debugOwner,
3417+
debugStack: fiber._debugStack == null ? null : fiber._debugStack,
3418+
debugTask: fiber._debugTask == null ? null : fiber._debugTask,
3419+
};
3420+
insertSuspendedBy(asyncInfo);
3421+
}
3422+
33103423
function mountVirtualChildrenRecursively(
33113424
firstChild: Fiber,
33123425
lastChild: null | Fiber, // non-inclusive
@@ -3545,6 +3658,7 @@ export function attach(
35453658
throw new Error('Did not expect a host hoistable to be the root');
35463659
}
35473660
aquireHostInstance(nearestInstance, fiber.stateNode);
3661+
trackDebugInfoFromHostComponent(nearestInstance, fiber);
35483662
}
35493663
35503664
if (fiber.tag === OffscreenComponent && fiber.memoizedState !== null) {
@@ -4373,20 +4487,22 @@ export function attach(
43734487
aquireHostResource(nearestInstance, nextFiber.memoizedState);
43744488
trackDebugInfoFromHostResource(nearestInstance, nextFiber);
43754489
} else if (
4376-
(nextFiber.tag === HostComponent ||
4377-
nextFiber.tag === HostText ||
4378-
nextFiber.tag === HostSingleton) &&
4379-
prevFiber.stateNode !== nextFiber.stateNode
4490+
nextFiber.tag === HostComponent ||
4491+
nextFiber.tag === HostText ||
4492+
nextFiber.tag === HostSingleton
43804493
) {
4381-
// In persistent mode, it's possible for the stateNode to update with
4382-
// a new clone. In that case we need to release the old one and aquire
4383-
// new one instead.
43844494
const nearestInstance = reconcilingParent;
43854495
if (nearestInstance === null) {
43864496
throw new Error('Did not expect a host hoistable to be the root');
43874497
}
4388-
releaseHostInstance(nearestInstance, prevFiber.stateNode);
4389-
aquireHostInstance(nearestInstance, nextFiber.stateNode);
4498+
if (prevFiber.stateNode !== nextFiber.stateNode) {
4499+
// In persistent mode, it's possible for the stateNode to update with
4500+
// a new clone. In that case we need to release the old one and aquire
4501+
// new one instead.
4502+
releaseHostInstance(nearestInstance, prevFiber.stateNode);
4503+
aquireHostInstance(nearestInstance, nextFiber.stateNode);
4504+
}
4505+
trackDebugInfoFromHostComponent(nearestInstance, nextFiber);
43904506
}
43914507
43924508
let updateFlags = NoUpdate;

packages/shared/ReactIODescription.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ export function getIODescription(value: any): string {
2626
return value.url;
2727
} else if (typeof value.href === 'string') {
2828
return value.href;
29+
} else if (typeof value.src === 'string') {
30+
return value.src;
31+
} else if (typeof value.currentSrc === 'string') {
32+
return value.currentSrc;
2933
} else if (typeof value.command === 'string') {
3034
return value.command;
3135
} else if (

0 commit comments

Comments
 (0)