From 61ae9190bca1a7b7065a92c2b432bb38e567d514 Mon Sep 17 00:00:00 2001 From: acreskeyMoz Date: Thu, 6 Aug 2026 09:20:04 -0400 Subject: [PATCH] Show which network requests were prefetched Consumes the secPurpose and deliveryType Network marker fields added in bug 2060983, as tooltip details plus hidden schema fields so searching the network chart for "prefetch" narrows to those requests. Fixes #6248 --- src/components/tooltip/NetworkMarker.tsx | 16 +++++++ src/profile-logic/marker-schema.ts | 12 +++++ src/test/components/NetworkChart.test.tsx | 56 ++++++++++++++++++++++ src/test/components/TooltipMarker.test.tsx | 24 ++++++++++ src/types/markers.ts | 9 ++++ 5 files changed, 117 insertions(+) diff --git a/src/components/tooltip/NetworkMarker.tsx b/src/components/tooltip/NetworkMarker.tsx index c7f231c6c3..91c9a4b329 100644 --- a/src/components/tooltip/NetworkMarker.tsx +++ b/src/components/tooltip/NetworkMarker.tsx @@ -338,6 +338,22 @@ export function getNetworkMarkerDetails( ); } + if (payload.secPurpose) { + details.push( + + {payload.secPurpose} + + ); + } + + if (payload.deliveryType) { + details.push( + + {payload.deliveryType} + + ); + } + if (typeof payload.count === 'number') { details.push( diff --git a/src/profile-logic/marker-schema.ts b/src/profile-logic/marker-schema.ts index 59454a280d..5383bdee19 100644 --- a/src/profile-logic/marker-schema.ts +++ b/src/profile-logic/marker-schema.ts @@ -83,6 +83,18 @@ export const markerSchemaFrontEndOnly: MarkerSchema[] = [ label: 'Response Status', hidden: true, }, + { + format: 'string', + key: 'secPurpose', + label: 'Sec-Purpose', + hidden: true, + }, + { + format: 'string', + key: 'deliveryType', + label: 'Delivery Type', + hidden: true, + }, ], }, ]; diff --git a/src/test/components/NetworkChart.test.tsx b/src/test/components/NetworkChart.test.tsx index df129b0c15..ba71ab9d91 100644 --- a/src/test/components/NetworkChart.test.tsx +++ b/src/test/components/NetworkChart.test.tsx @@ -622,6 +622,62 @@ describe('NetworkChartRowBar MIME-type filter', function () { }); }); +describe('Network chart prefetch search', function () { + // The fields are hidden in the schema, so they never render in the chart; + // they exist so a search for "prefetch" narrows to just these requests. + function setup() { + return setupWithPayload([ + ...getNetworkMarkers({ uri: 'https://mozilla.org/plain', id: 1 }), + ...getNetworkMarkers({ + uri: 'https://mozilla.org/speculative', + id: 2, + payload: { secPurpose: 'prefetch' }, + }), + ...getNetworkMarkers({ + uri: 'https://mozilla.org/activated', + id: 3, + payload: { deliveryType: 'navigational-prefetch' }, + }), + ]); + } + + function visibleUrls(container: HTMLElement) { + return Array.from( + container.querySelectorAll('.networkChartRowItemLabel') + ).map((node) => node.textContent); + } + + it('shows every request when not searching', function () { + const { container } = setup(); + expect(visibleUrls(container)).toHaveLength(3); + }); + + it('narrows to prefetches when searching for "prefetch"', function () { + const { container, dispatch } = setup(); + + act(() => { + dispatch(changeNetworkSearchString('prefetch')); + }); + + const urls = visibleUrls(container); + expect(urls).toHaveLength(2); + expect(urls.join(' ')).toContain('/speculative'); + expect(urls.join(' ')).toContain('/activated'); + }); + + it('narrows to the activated navigation alone', function () { + const { container, dispatch } = setup(); + + act(() => { + dispatch(changeNetworkSearchString('navigational-prefetch')); + }); + + const urls = visibleUrls(container); + expect(urls).toHaveLength(1); + expect(urls[0]).toContain('/activated'); + }); +}); + describe('EmptyReasons', () => { it("shows a reason when a profile's network markers have been filtered out", () => { const { dispatch, container } = setupWithPayload([...NETWORK_MARKERS]); diff --git a/src/test/components/TooltipMarker.test.tsx b/src/test/components/TooltipMarker.test.tsx index 938a5bc361..c80c87ae87 100644 --- a/src/test/components/TooltipMarker.test.tsx +++ b/src/test/components/TooltipMarker.test.tsx @@ -686,6 +686,30 @@ describe('TooltipMarker', function () { expect(container.firstChild).toMatchSnapshot(); }); + it('renders the Sec-Purpose of a prefetched request', () => { + setupWithPayload( + getNetworkMarkers({ + uri: 'https://example.org/next-page.html', + payload: { secPurpose: 'prefetch;anonymous-client-ip' }, + }) + ); + + expect(getValueForProperty('Sec-Purpose')).toBe( + 'prefetch;anonymous-client-ip' + ); + }); + + it('renders the delivery type of a navigation served from a prefetch', () => { + setupWithPayload( + getNetworkMarkers({ + uri: 'https://example.org/next-page.html', + payload: { deliveryType: 'navigational-prefetch' }, + }) + ); + + expect(getValueForProperty('Delivery Type')).toBe('navigational-prefetch'); + }); + it('renders the page information for network markers', () => { setupWithPayload( getNetworkMarkers({ diff --git a/src/types/markers.ts b/src/types/markers.ts index e3eb9217b5..f60d98dfdd 100644 --- a/src/types/markers.ts +++ b/src/types/markers.ts @@ -556,6 +556,15 @@ export type NetworkPayload = { // Used to show the HTTP response status code responseStatus?: number; + // The `Sec-Purpose` request header, only present for prefetches. + // It's always absent in Firefox < 155. + secPurpose?: string; + + // Matches PerformanceNavigationTiming's deliveryType. Only present when the + // navigation was served from a prefetch, as "navigational-prefetch". + // It's always absent in Firefox < 155. + deliveryType?: string; + // NOTE: the following comments are valid for the merged markers. For the raw // markers, startTime and endTime have different meanings. Please look // `src/profile-logic/marker-data.js` for more information.