chrome-devtools-frontend
Version:
Chrome DevTools UI
60 lines (52 loc) • 3.08 kB
text/typescript
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type * as Handlers from '../handlers/handlers.js';
import type * as Types from '../types/types.js';
import {get as getStackTrace} from './StackTraceForEvent.js';
/**
* There are bugs in the backend tracing that means that network requests are
* often incorrectly tied to an initiator. This function exists as a utility to
* look up an event's initiator regardless of the type of event, but also to
* provide a post-parsing fix for network initiators.
* The TL;DR is that images injected by a script will incorrectly have their
* initiator set to the root document. To fix this, we look at the stack trace
* when the request was sent, and use that.
*/
export function getNetworkInitiator(data: Handlers.Types.HandlerData, event: Types.Events.SyntheticNetworkRequest):
Types.Events.SyntheticNetworkRequest|undefined {
const networkHandlerInitiator = data.NetworkRequests.incompleteInitiator.get(event);
if (networkHandlerInitiator?.args.data.mimeType === 'text/css') {
// The bugs in tracing & initiators apply mostly to scripts; we have not
// seen a case where the trace events identify a CSS stylesheet as the
// initiator that is incorrect. Therefore, if a stylesheet is identified as
// the initiator, we trust that it is accurate and can exit early.
return networkHandlerInitiator;
}
// For network requests, it is more reliable to calculate the initiator via
// the stack trace if we have one.
// We have to use the raw source event (`ResourceSendRequest`) as that is
// the event with the `sampleStackId` property which is required to
// calculate this stacktrace correctly.
const stack = getStackTrace(event.rawSourceEvent, data);
// If the resource was injected by a script, it will have a parent call
// frame that points to the script. Otherwise, there is no parent and
// therefore we fallthrough to looking at the initiator directly on the
// network request.
const initiatorCallFrame = stack?.parent?.callFrames.at(0);
if (!initiatorCallFrame) {
return networkHandlerInitiator;
}
// Find all the requests for the URL we are searching for. Most of the time
// there is only 1, but there can be multiple requests for the same URL. The
// filtering by the timestamp ensures that we can never pick an initiator
// that happened after the initiated event.
const matchingRequestIds = data.NetworkRequests.requestIdsByURL.get(initiatorCallFrame.url) ?? [];
const matchingRequests = matchingRequestIds.map(id => data.NetworkRequests.byId.get(id))
.filter(req => req !== undefined)
.filter(req => req.ts < event.ts);
// Now we have filtered and have a list of requests that are before the
// event, we take the last one - the one closest to the initiated event.
// In the case that there are >1 requests, this is an educated guess.
return matchingRequests.at(-1);
}