UNPKG

chrome-devtools-frontend

Version:
136 lines (111 loc) 4.97 kB
// Copyright 2023 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Types from '../types/types.js'; const invalidationsForEvent = new Map<Types.Events.Event, Types.Events.InvalidationTrackingEvent[]>(); const invalidationCountForEvent = new Map<Types.Events.Event, number>(); let lastRecalcStyleEvent: Types.Events.UpdateLayoutTree|null = null; // Used to track paints so we track invalidations correctly per paint. let hasPainted = false; const allInvalidationTrackingEvents: Types.Events.InvalidationTrackingEvent[] = []; export function reset(): void { invalidationsForEvent.clear(); invalidationCountForEvent.clear(); lastRecalcStyleEvent = null; allInvalidationTrackingEvents.length = 0; hasPainted = false; maxInvalidationsPerEvent = null; } let maxInvalidationsPerEvent: number|null = null; export function handleUserConfig(userConfig: Types.Configuration.Configuration): void { maxInvalidationsPerEvent = userConfig.maxInvalidationEventsPerEvent; } function addInvalidationToEvent(event: Types.Events.Event, invalidation: Types.Events.InvalidationTrackingEvent): void { const existingInvalidations = invalidationsForEvent.get(event) || []; existingInvalidations.push(invalidation); if (maxInvalidationsPerEvent !== null && existingInvalidations.length > maxInvalidationsPerEvent) { existingInvalidations.shift(); } invalidationsForEvent.set(event, existingInvalidations); const count = invalidationCountForEvent.get(event) ?? 0; invalidationCountForEvent.set(event, count + 1); } export function handleEvent(event: Types.Events.Event): void { // Special case: if we have been configured to not store any invalidations, // we take that as a sign that we don't even want to gather any invalidations // data at all and early exit. if (maxInvalidationsPerEvent === 0) { return; } if (Types.Events.isUpdateLayoutTree(event)) { lastRecalcStyleEvent = event; // Associate any prior invalidations with this recalc event. for (const invalidation of allInvalidationTrackingEvents) { if (Types.Events.isLayoutInvalidationTracking(invalidation)) { // LayoutInvalidation events cannot be associated with a LayoutTree // event. continue; } const recalcFrameId = lastRecalcStyleEvent.args.beginData?.frame; if (recalcFrameId && invalidation.args.data.frame === recalcFrameId) { addInvalidationToEvent(event, invalidation); } } return; } if (Types.Events.isInvalidationTracking(event)) { if (hasPainted) { // If we have painted, then we can clear out the list of all existing // invalidations, as we cannot associate them across frames. allInvalidationTrackingEvents.length = 0; lastRecalcStyleEvent = null; hasPainted = false; } // Style invalidation events can occur before and during recalc styles. When we get a recalc style event (aka UpdateLayoutTree), we check and associate any prior invalidations with it. // But any invalidations that occur during a UpdateLayoutTree // event would be reported in trace events after. So each time we get an // invalidation that might be due to a style recalc, we check if the // timings overlap and if so associate them. if (lastRecalcStyleEvent && (Types.Events.isScheduleStyleInvalidationTracking(event) || Types.Events.isStyleRecalcInvalidationTracking(event) || Types.Events.isStyleInvalidatorInvalidationTracking(event))) { const recalcEndTime = lastRecalcStyleEvent.ts + (lastRecalcStyleEvent.dur || 0); if (event.ts >= lastRecalcStyleEvent.ts && event.ts <= recalcEndTime && lastRecalcStyleEvent.args.beginData?.frame === event.args.data.frame) { addInvalidationToEvent(lastRecalcStyleEvent, event); } } allInvalidationTrackingEvents.push(event); return; } if (Types.Events.isPaint(event)) { // Used to ensure that we do not create relationships across frames. hasPainted = true; return; } if (Types.Events.isLayout(event)) { const layoutFrame = event.args.beginData.frame; for (const invalidation of allInvalidationTrackingEvents) { // The only invalidations that cause a Layout are LayoutInvalidations :) if (!Types.Events.isLayoutInvalidationTracking(invalidation)) { continue; } if (invalidation.args.data.frame === layoutFrame) { addInvalidationToEvent(event, invalidation); } } } } export async function finalize(): Promise<void> { } interface InvalidationsData { invalidationsForEvent: Map<Types.Events.Event, Types.Events.InvalidationTrackingEvent[]>; invalidationCountForEvent: Map<Types.Events.Event, number>; } export function data(): InvalidationsData { return { invalidationsForEvent, invalidationCountForEvent, }; }