3219
}
3220
}
3221
3222
+ const hostAsyncInfoCache: WeakMap<{...}, ReactAsyncInfo> = new WeakMap();
3223
+
3224
+ function trackDebugInfoFromHostResource(
3225
+ devtoolsInstance: DevToolsInstance,
3226
+ fiber: Fiber,
3227
+ ): void {
3228
+ const resource: ?{
3229
+ type: 'stylesheet' | 'style' | 'script' | 'void',
3230
+ instance?: null | HostInstance,
3231
+ ...
3232
+ } = fiber.memoizedState;
3233
+ if (resource == null) {
3234
+ return;
3235
+ }
3236
+
3237
+ // Use a cached entry based on the resource. This ensures that if we use the same
3238
+ // resource in multiple places, it gets deduped and inner boundaries don't consider it
3239
+ // as contributing to those boundaries.
3240
+ const existingEntry = hostAsyncInfoCache.get(resource);
3241
+ if (existingEntry !== undefined) {
3242
+ insertSuspendedBy(existingEntry);
3243
+ return;
3244
+ }
3245
+
3246
+ const props: {
3247
+ href?: string,
3248
+ media?: string,
3249
+ ...
3250
+ } = fiber.memoizedProps;
3251
+
3252
+ // Stylesheet resources may suspend. We need to track that.
3253
+ const mayResourceSuspendCommit =
3254
+ resource.type === 'stylesheet' &&
3255
+ // If it doesn't match the currently debugged media, then it doesn't count.
3256
+ (typeof props.media !== 'string' ||
3257
+ typeof matchMedia !== 'function' ||
3258
+ matchMedia(props.media));
3259
+ if (!mayResourceSuspendCommit) {
3260
+ return;
3261
+ }
3262
+
3263
+ const instance = resource.instance;
3264
+ if (instance == null) {
3265
+ return;
3266
+ }
3267
+
3268
+ // Unlike props.href, this href will be fully qualified which we need for comparison below.
3269
+ const href = instance.href;
3270
+ if (typeof href !== 'string') {
3271
+ return;
3272
+ }
3273
+ let start = -1;
3274
+ let end = -1;
3275
+ // $FlowFixMe[method-unbinding]
3276
+ if (typeof performance.getEntriesByType === 'function') {
3277
+ // We may be able to collect the start and end time of this resource from Performance Observer.
3278
+ const resourceEntries = performance.getEntriesByType('resource');
3279
+ for (let i = 0; i < resourceEntries.length; i++) {
3280
+ const resourceEntry = resourceEntries[i];
3281
+ if (resourceEntry.name === href) {
3282
+ start = resourceEntry.startTime;
3283
+ end = start + resourceEntry.duration;
3284
+ }
3285
+ }
3286
+ }
3287
+ const value = instance.sheet;
3288
+ const promise = Promise.resolve(value);
3289
+ (promise: any).status = 'fulfilled';
3290
+ (promise: any).value = value;
3291
+ const ioInfo: ReactIOInfo = {
3292
+ name: 'stylesheet',
3293
+ start,
3294
+ end,
3295
+ value: promise,
3296
+ // $FlowFixMe: This field doesn't usually take a Fiber but we're only using inside this file.
3297
+ owner: fiber, // Allow linking to the <link> if it's not filtered.
3298
+ };
3299
+ const asyncInfo: ReactAsyncInfo = {
3300
+ awaited: ioInfo,
3301
+ // $FlowFixMe: This field doesn't usually take a Fiber but we're only using inside this file.
3302
+ owner: fiber._debugOwner == null ? null : fiber._debugOwner,
3303
+ debugStack: fiber._debugStack == null ? null : fiber._debugStack,
3304
+ debugTask: fiber._debugTask == null ? null : fiber._debugTask,
3305
+ };
3306
+ hostAsyncInfoCache.set(resource, asyncInfo);
3307
+ insertSuspendedBy(asyncInfo);
3308
+ }
3309
+
3310
function mountVirtualChildrenRecursively(
3311
firstChild: Fiber,
3312
lastChild: null | Fiber, // non-inclusive
3534
throw new Error('Did not expect a host hoistable to be the root');
3535
}
3536
aquireHostResource(nearestInstance, fiber.memoizedState);
3537
+ trackDebugInfoFromHostResource(nearestInstance, fiber);
3538
} else if (
3539
fiber.tag === HostComponent ||
3540
fiber.tag === HostText ||
4371
}
4372
releaseHostResource(nearestInstance, prevFiber.memoizedState);
4373
aquireHostResource(nearestInstance, nextFiber.memoizedState);
4374
+ trackDebugInfoFromHostResource(nearestInstance, nextFiber);
4375
} else if (
4376
(nextFiber.tag === HostComponent ||
4377
nextFiber.tag === HostText ||