From 5355d64df18449b8c1468348105511a802feddfb Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Wed, 14 Feb 2024 14:41:30 -0800 Subject: [PATCH 1/4] wip types --- core/computed/trace-engine-result.js | 10 +- core/lib/trace-engine.js | 6 +- types/artifacts.d.ts | 12 +- types/trace-engine.d.ts | 1516 -------------------------- 4 files changed, 18 insertions(+), 1526 deletions(-) delete mode 100644 types/trace-engine.d.ts diff --git a/core/computed/trace-engine-result.js b/core/computed/trace-engine-result.js index 9073271051ff..49efeabc75ec 100644 --- a/core/computed/trace-engine-result.js +++ b/core/computed/trace-engine-result.js @@ -26,7 +26,9 @@ class TraceEngineResult { Samples: TraceEngine.TraceHandlers.Samples, Screenshots: TraceEngine.TraceHandlers.Screenshots, }); - await engine.parse(traceEvents); + await engine.parse(/** @type {import('/Users/cjamcl/src/devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/trace.js').Types.TraceEvents.TraceEventData[]} */ ( + traceEvents + )); return engine.data; } @@ -61,7 +63,11 @@ class TraceEngineResult { } } - return TraceEngineResult.runTraceEngine(traceEvents); + const result = await TraceEngineResult.runTraceEngine(traceEvents); + if (!result) { + throw new Error('null trace engine result'); + } + return result; } } diff --git a/core/lib/trace-engine.js b/core/lib/trace-engine.js index c2d30d551a11..4544ebe32962 100644 --- a/core/lib/trace-engine.js +++ b/core/lib/trace-engine.js @@ -1,15 +1,11 @@ -// @ts-expect-error missing types -import * as TraceEngine from '@paulirish/trace_engine'; +import * as TraceEngine from '/Users/cjamcl/src/devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/trace.js'; import {polyfillDOMRect} from './polyfill-dom-rect.js'; polyfillDOMRect(); -/** @type {import('../../types/trace-engine.js').TraceProcessor & typeof import('../../types/trace-engine.js').TraceProcessor} */ const TraceProcessor = TraceEngine.Processor.TraceProcessor; -/** @type {import('../../types/trace-engine.js').TraceHandlers} */ const TraceHandlers = TraceEngine.Handlers.ModelHandlers; -/** @type {import('../../types/trace-engine.js').RootCauses & typeof import('../../types/trace-engine.js').RootCauses} */ const RootCauses = TraceEngine.RootCauses.RootCauses.RootCauses; export { diff --git a/types/artifacts.d.ts b/types/artifacts.d.ts index 6d87731563ce..ba5bdf5e9238 100644 --- a/types/artifacts.d.ts +++ b/types/artifacts.d.ts @@ -5,6 +5,8 @@ */ import {Protocol as Crdp} from 'devtools-protocol/types/protocol.js'; +import * as TraceEngine from '../../devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/trace.js'; +import {LayoutShiftRootCausesData} from '../../devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/root-causes/LayoutShift.js'; import {parseManifest} from '../core/lib/manifest-parser.js'; import {Simulator} from '../core/lib/dependency-graph/simulator/simulator.js'; @@ -23,7 +25,6 @@ import LHResult from './lhr/lhr.js' import Protocol from './protocol.js'; import Util from './utility-types.js'; import Audit from './audit.js'; -import {TraceProcessor, LayoutShiftRootCauses} from './trace-engine.js'; export type Artifacts = BaseArtifacts & GathererArtifacts; @@ -569,10 +570,15 @@ declare module Artifacts { type?: string; } - type TraceEngineResult = TraceProcessor['data']; + type TraceEngineResult = Pick< + NonNullable< + ReturnType['data'] + >, + 'AuctionWorklets'|'Initiators'|'LayoutShifts'|'NetworkRequests'|'Renderer'|'Samples'|'Screenshots' + >; interface TraceEngineRootCauses { - layoutShifts: Record; + layoutShifts: Record; } interface ViewportDimensions { diff --git a/types/trace-engine.d.ts b/types/trace-engine.d.ts deleted file mode 100644 index 23c04c1a81b3..000000000000 --- a/types/trace-engine.d.ts +++ /dev/null @@ -1,1516 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import {Protocol} from 'devtools-protocol'; -import {TraceEvent} from './artifacts.js'; - -// This part is just the subset of types we need for the main API. - -type LayoutShiftTraceEvent = TraceEvent & { - args: {data: { - impacted_nodes: TraceImpactedNode[], - weighted_score_delta: number; - }}, -} - -export class TraceProcessor { - constructor(handlers: any); - createWithAllHandlers(): TraceProcessor; - parse(traceEvents: any[]): Promise; - data: { - LayoutShifts: { - clusters: Array<{ - events: LayoutShiftTraceEvent[]; - }>; - sessionMaxScore: number; - }; - }; -} - -export const TraceHandlers: Record; - -interface CSSDimensions { - width?: string; - height?: string; - aspectRatio?: string; -} - -type RootCauseRequest = { - request: TraceEventSyntheticNetworkRequest; - initiator?: Protocol.Network.Initiator; -} - -export type LayoutShiftRootCauses = { - fontChanges: Array; - iframes: Array<{ - iframe: Protocol.DOM.Node; - }>; - renderBlockingRequests: Array; - unsizedMedia: Array<{ - node: Protocol.DOM.Node; - authoredDimensions?: CSSDimensions; - computedDimensions: CSSDimensions; - }>; -}; - -export class RootCauses { - constructor(protocolInterface: any); - - layoutShifts: {rootCausesForEvent(data: any, event: any): Promise}; -} - -// The rest of this file is pulled from CDT frontend -// https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/front_end/models/trace/types/TraceEvents.ts;l=297?q=TraceEventSyntheticNetworkRequest&ss=chromium - -// Copyright 2022 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. - -export type MicroSeconds = number&{_tag: 'MicroSeconds'}; - -export type MilliSeconds = number&{_tag: 'MilliSeconds'}; -export type Seconds = number&{_tag: 'Seconds'}; - -export const enum TimeUnit { - MICROSECONDS = 0, - MILLISECONDS = 1, - SECONDS = 2, - MINUTES = 3, -} - -// Other types. - -export interface TraceWindow { - min: TimeFormat; - max: TimeFormat; - range: TimeFormat; -} - -export type TraceWindowMicroSeconds = TraceWindow; -export type TraceWindowMilliSeconds = TraceWindow; - -// Trace Events. -export const enum Phase { - // Standard - BEGIN = 'B', - END = 'E', - COMPLETE = 'X', - INSTANT = 'I', - COUNTER = 'C', - - // Async - ASYNC_NESTABLE_START = 'b', - ASYNC_NESTABLE_INSTANT = 'n', - ASYNC_NESTABLE_END = 'e', - ASYNC_STEP_INTO = 'T', - ASYNC_BEGIN = 'S', - ASYNC_END = 'F', - ASYNC_STEP_PAST = 'p', - - // Flow - FLOW_START = 's', - FLOW_STEP = 't', - FLOW_END = 'f', - - // Sample - SAMPLE = 'P', - - // Object - OBJECT_CREATED = 'N', - OBJECT_SNAPSHOT = 'O', - OBJECT_DESTROYED = 'D', - - // Metadata - METADATA = 'M', - - // Memory Dump - MEMORY_DUMP_GLOBAL = 'V', - MEMORY_DUMP_PROCESS = 'v', - - // Mark - MARK = 'R', - - // Clock sync - CLOCK_SYNC = 'c', -} - -export const enum TraceEventScope { - THREAD = 't', - PROCESS = 'p', - GLOBAL = 'g', -} - -export interface TraceEventData { - args?: TraceEventArgs; - cat: string; - name: string; - ph: Phase; - pid: ProcessID; - tid: ThreadID; - tts?: MicroSeconds; - ts: MicroSeconds; - tdur?: MicroSeconds; - dur?: MicroSeconds; -} - -export interface TraceEventArgs { - data?: TraceEventArgsData; -} - -export interface TraceEventArgsData { - stackTrace?: TraceEventCallFrame[]; - navigationId?: string; - frame?: string; -} - -export interface TraceEventCallFrame { - codeType?: string; - functionName: string; - scriptId: number; - columnNumber: number; - lineNumber: number; - url: string; -} - -export interface TraceFrame { - frame: string; - name: string; - processId: ProcessID; - url: string; - parent?: string; -} - -// Sample events. - -export interface TraceEventSample extends TraceEventData { - ph: Phase.SAMPLE; -} - -/** - * A fake trace event created to support CDP.Profiler.Profiles in the - * trace engine. - */ -export interface SyntheticTraceEventCpuProfile extends TraceEventInstant { - name: 'CpuProfile'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - cpuProfile: Protocol.Profiler.Profile, - }, - }; -} - -export interface TraceEventProfile extends TraceEventSample { - name: 'Profile'; - id: ProfileID; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - startTime: MicroSeconds, - }, - }; -} - -export interface TraceEventProfileChunk extends TraceEventSample { - name: 'ProfileChunk'; - id: ProfileID; - args: TraceEventArgs&{ - // `data` is only missing in "fake" traces - data?: TraceEventArgsData & { - cpuProfile?: TraceEventPartialProfile, - timeDeltas?: MicroSeconds[], - lines?: MicroSeconds[], - }, - }; -} - -export interface TraceEventPartialProfile { - nodes?: TraceEventPartialNode[]; - samples: CallFrameID[]; -} - -export interface TraceEventPartialNode { - callFrame: TraceEventCallFrame; - id: CallFrameID; - parent?: CallFrameID; -} - -// Complete events. - -export interface TraceEventComplete extends TraceEventData { - ph: Phase.COMPLETE; - dur: MicroSeconds; -} - -export interface TraceEventFireIdleCallback extends TraceEventComplete { - name: KnownEventName.FireIdleCallback; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - allottedMilliseconds: MilliSeconds, - frame: string, - id: number, - timedOut: boolean, - }, - }; -} - -export interface TraceEventDispatch extends TraceEventComplete { - name: 'EventDispatch'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - type: string, - }, - }; -} - -export interface TraceEventParseHTML extends TraceEventComplete { - name: 'ParseHTML'; - args: TraceEventArgs&{ - beginData: { - frame: string, - startLine: number, - url: string, - }, - endData?: { - endLine: number, - }, - }; -} - -export interface TraceEventBegin extends TraceEventData { - ph: Phase.BEGIN; -} - -export interface TraceEventEnd extends TraceEventData { - ph: Phase.END; -} - -/** - * This denotes a complete event created from a pair of begin and end - * events. For practicality, instead of always having to look for the - * end event corresponding to a begin event, we create a synthetic - * complete event that comprises the data of both from the beginning in - * the RendererHandler. - */ -export type TraceEventSyntheticCompleteEvent = TraceEventComplete; - -export interface TraceEventEventTiming extends TraceEventData { - ph: Phase.ASYNC_NESTABLE_START|Phase.ASYNC_NESTABLE_END; - name: KnownEventName.EventTiming; - id: string; - args: TraceEventArgs&{ - frame: string, - data?: TraceEventArgsData&{ - cancelable: boolean, - duration: MilliSeconds, - processingEnd: MilliSeconds, - processingStart: MilliSeconds, - timeStamp: MilliSeconds, - interactionId?: number, type: string, - }, - }; -} - -export interface TraceEventEventTimingBegin extends TraceEventEventTiming { - ph: Phase.ASYNC_NESTABLE_START; -} -export interface TraceEventEventTimingEnd extends TraceEventEventTiming { - ph: Phase.ASYNC_NESTABLE_END; -} - -export interface TraceEventGPUTask extends TraceEventComplete { - name: 'GPUTask'; - args: TraceEventArgs&{ - data?: TraceEventArgsData & { - /* eslint-disable @typescript-eslint/naming-convention */ - renderer_pid: ProcessID, - used_bytes: number, - /* eslint-enable @typescript-eslint/naming-convention */ - }, - }; -} - -export interface TraceEventSyntheticNetworkRedirect { - url: string; - priority: string; - requestMethod?: string; - ts: MicroSeconds; - dur: MicroSeconds; -} - -// TraceEventProcessedArgsData is used to store the processed data of a network -// request. Which is used to distinguish from the date we extract from the -// trace event directly. -interface TraceEventSyntheticArgsData { - dnsLookup: MicroSeconds; - download: MicroSeconds; - downloadStart: MicroSeconds; - finishTime: MicroSeconds; - initialConnection: MicroSeconds; - isDiskCached: boolean; - isHttps: boolean; - isMemoryCached: boolean; - isPushedResource: boolean; - networkDuration: MicroSeconds; - processingDuration: MicroSeconds; - proxyNegotiation: MicroSeconds; - queueing: MicroSeconds; - redirectionDuration: MicroSeconds; - requestSent: MicroSeconds; - sendStartTime: MicroSeconds; - ssl: MicroSeconds; - stalled: MicroSeconds; - totalTime: MicroSeconds; - waiting: MicroSeconds; -} - -export interface TraceEventSyntheticNetworkRequest extends TraceEventComplete { - args: TraceEventArgs&{ - data: TraceEventArgsData & { - syntheticData: TraceEventSyntheticArgsData, - // All fields below are from TraceEventsForNetworkRequest, - // Required fields - decodedBodyLength: number, - encodedDataLength: number, - frame: string, - fromServiceWorker: boolean, - host: string, - mimeType: string, - pathname: string, - search: string, - priority: Priority, - initialPriority: Priority, - protocol: string, - redirects: TraceEventSyntheticNetworkRedirect[], - renderBlocking: RenderBlocking, - requestId: string, - requestingFrameUrl: string, - statusCode: number, - url: string, - // Optional fields - requestMethod?: string, - timing?: TraceEventResourceReceiveResponseTimingData, - }, - }; - cat: 'loading'; - name: 'SyntheticNetworkRequest'; - ph: Phase.COMPLETE; - dur: MicroSeconds; - tdur: MicroSeconds; - ts: MicroSeconds; - tts: MicroSeconds; - pid: ProcessID; - tid: ThreadID; -} - -export const enum AuctionWorkletType { - BIDDER = 'bidder', - SELLER = 'seller', - // Not expected to be used, but here as a fallback in case new types get - // added and we have yet to update the trace engine. - UNKNOWN = 'unknown', -} - -export interface SyntheticAuctionWorkletEvent extends TraceEventInstant { - name: 'SyntheticAuctionWorkletEvent'; - // The PID that the AuctionWorklet is running in. - pid: ProcessID; - // URL - host: string; - // An ID used to pair up runningInProcessEvents with doneWithProcessEvents - target: string; - type: AuctionWorkletType; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - // There are two threads for a worklet that we care about, so we gather - // the thread_name events so we can know the PID and TID for them (and - // hence display the right events in the track for each thread) - utilityThread: TraceEventThreadName, - v8HelperThread: TraceEventThreadName, - } & - ( - // This type looks odd, but this is because these events could either have: - // 1. Just the DoneWithProcess event - // 2. Just the RunningInProcess event - // 3. Both events - // But crucially it cannot have both events missing, hence listing all the - // allowed cases. - // Clang is disabled as the combination of nested types and optional - // properties cause it to weirdly indent some of the properties and make it - // very unreadable. - // clang-format off - { - runningInProcessEvent: TraceEventAuctionWorkletRunningInProcess, - doneWithProcessEvent: TraceEventAuctionWorkletDoneWithProcess, - } | - { - runningInProcessEvent?: TraceEventAuctionWorkletRunningInProcess, - doneWithProcessEvent: TraceEventAuctionWorkletDoneWithProcess, - } | - { - doneWithProcessEvent?: TraceEventAuctionWorkletDoneWithProcess, - runningInProcessEvent: TraceEventAuctionWorkletRunningInProcess, - - }), - // clang-format on - }; -} -export interface TraceEventAuctionWorkletRunningInProcess extends TraceEventData { - name: 'AuctionWorkletRunningInProcess'; - ph: Phase.INSTANT; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - host: string, - pid: ProcessID, - target: string, - type: AuctionWorkletType, - }, - }; -} -export interface TraceEventAuctionWorkletDoneWithProcess extends TraceEventData { - name: 'AuctionWorkletDoneWithProcess'; - ph: Phase.INSTANT; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - host: string, - pid: ProcessID, - target: string, - type: AuctionWorkletType, - }, - }; -} - -// Snapshot events. - -export interface TraceEventSnapshot extends TraceEventData { - args: TraceEventArgs&{ - snapshot: string, - }; - name: 'Screenshot'; - cat: 'disabled-by-default-devtools.screenshot'; - ph: Phase.OBJECT_SNAPSHOT|Phase.INSTANT; // In Oct 2023, the phase was changed to Instant. crbug.com/798755 -} - -// Animation events. - -export interface TraceEventAnimation extends TraceEventData { - args: TraceEventArgs&{ - id?: string, - name?: string, - nodeId?: number, - nodeName?: string, - state?: string, - compositeFailed?: number, - unsupportedProperties?: string[], - }; - name: 'Animation'; - id2?: { - local?: string, - }; - ph: Phase.ASYNC_NESTABLE_START|Phase.ASYNC_NESTABLE_END; -} - -// Metadata events. - -export interface TraceEventMetadata extends TraceEventData { - ph: Phase.METADATA; - args: TraceEventArgs&{ - name?: string, - uptime?: string, - }; -} - -export interface TraceEventThreadName extends TraceEventMetadata { - name: KnownEventName.ThreadName; - args: TraceEventArgs&{ - name?: string, - }; -} - -export interface TraceEventProcessName extends TraceEventMetadata { - name: 'process_name'; -} - -// Mark events. - -export interface TraceEventMark extends TraceEventData { - ph: Phase.MARK; -} - -export interface TraceEventNavigationStart extends TraceEventMark { - name: 'navigationStart'; - args: TraceEventArgs&{ - data?: TraceEventArgsData & { - documentLoaderURL: string, - isLoadingMainFrame: boolean, - // isOutermostMainFrame was introduced in crrev.com/c/3625434 and exists - // because of Fenced Frames - // [github.com/WICG/fenced-frame/tree/master/explainer]. - // Fenced frames introduce a situation where isLoadingMainFrame could be - // true for a navigation, but that navigation be within an embedded "main - // frame", and therefore it wouldn't be on the top level main frame. - // In situations where we need to distinguish that, we can rely on - // isOutermostMainFrame, which will only be true for navigations on the - // top level main frame. - - // This flag is optional as it was introduced in May 2022; so users - // reasonably may import traces from before that date that do not have - // this field present. - isOutermostMainFrame?: boolean, navigationId: string, - }, - frame: string, - }; -} - -export interface TraceEventFirstContentfulPaint extends TraceEventMark { - name: 'firstContentfulPaint'; - args: TraceEventArgs&{ - frame: string, - data?: TraceEventArgsData&{ - navigationId: string, - }, - }; -} - -export interface TraceEventFirstPaint extends TraceEventMark { - name: 'firstPaint'; - args: TraceEventArgs&{ - frame: string, - data?: TraceEventArgsData&{ - navigationId: string, - }, - }; -} - -export type PageLoadEvent = TraceEventFirstContentfulPaint|TraceEventMarkDOMContent|TraceEventInteractiveTime| - TraceEventLargestContentfulPaintCandidate|TraceEventLayoutShift|TraceEventFirstPaint|TraceEventMarkLoad| - TraceEventNavigationStart; - -export interface TraceEventLargestContentfulPaintCandidate extends TraceEventMark { - name: 'largestContentfulPaint::Candidate'; - args: TraceEventArgs&{ - frame: string, - data?: TraceEventArgsData&{ - candidateIndex: number, - isOutermostMainFrame: boolean, - isMainFrame: boolean, - navigationId: string, - nodeId: Protocol.DOM.BackendNodeId, - type?: string, - }, - }; -} -export interface TraceEventLargestImagePaintCandidate extends TraceEventMark { - name: 'LargestImagePaint::Candidate'; - args: TraceEventArgs&{ - frame: string, - data?: TraceEventArgsData&{ - candidateIndex: number, - imageUrl: string, - // eslint-disable-next-line @typescript-eslint/naming-convention - DOMNodeId: Protocol.DOM.BackendNodeId, - }, - }; -} -export interface TraceEventLargestTextPaintCandidate extends TraceEventMark { - name: 'LargestTextPaint::Candidate'; - args: TraceEventArgs&{ - frame: string, - data?: TraceEventArgsData&{ - candidateIndex: number, - // eslint-disable-next-line @typescript-eslint/naming-convention - DOMNodeId: Protocol.DOM.BackendNodeId, - }, - }; -} - -export interface TraceEventInteractiveTime extends TraceEventMark { - name: 'InteractiveTime'; - args: TraceEventArgs&{ - args: { - // eslint-disable-next-line @typescript-eslint/naming-convention - total_blocking_time_ms: number, - }, - frame: string, - }; -} - -// Instant events. - -export interface TraceEventInstant extends TraceEventData { - ph: Phase.INSTANT; - s: TraceEventScope; -} - -export interface TraceEventUpdateCounters extends TraceEventInstant { - name: 'UpdateCounters'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - documents: number, - jsEventListeners: number, - jsHeapSizeUsed: number, - nodes: number, - gpuMemoryLimitKB?: number, - }, - }; -} - -export type TraceEventRendererEvent = TraceEventInstant|TraceEventComplete; - -export interface TraceEventTracingStartedInBrowser extends TraceEventInstant { - name: KnownEventName.TracingStartedInBrowser; - args: TraceEventArgs&{ - data?: TraceEventArgsData & { - frameTreeNodeId: number, - // Frames can only missing in "fake" traces - frames?: TraceFrame[], persistentIds: boolean, - }, - }; -} - -export interface TraceEventTracingSessionIdForWorker extends TraceEventInstant { - name: 'TracingSessionIdForWorker'; - args: TraceEventArgs&{ - data?: TraceEventArgsData & { - url: string, - workerId: WorkerId, - workerThreadId: ThreadID, - frame: string, - }, - }; -} - -export interface TraceEventFrameCommittedInBrowser extends TraceEventInstant { - name: 'FrameCommittedInBrowser'; - args: TraceEventArgs&{ - data?: TraceEventArgsData & TraceFrame, - }; -} - -export interface TraceEventMainFrameViewport extends TraceEventInstant { - name: 'PaintTimingVisualizer::Viewport'; - args: { - data: TraceEventArgsData&{ - // eslint-disable-next-line @typescript-eslint/naming-convention - viewport_rect: number[], - }, - }; -} - -export interface TraceEventCommitLoad extends TraceEventInstant { - name: 'CommitLoad'; - args: TraceEventArgs&{ - data?: TraceEventArgsData & { - frame: string, - isMainFrame: boolean, - name: string, - nodeId: number, - page: string, - parent: string, - url: string, - }, - }; -} - -export interface TraceEventMarkDOMContent extends TraceEventInstant { - name: 'MarkDOMContent'; - args: TraceEventArgs&{ - data?: TraceEventArgsData & { - frame: string, - isMainFrame: boolean, - page: string, - }, - }; -} - -export interface TraceEventMarkLoad extends TraceEventInstant { - name: 'MarkLoad'; - args: TraceEventArgs&{ - data?: TraceEventArgsData & { - frame: string, - isMainFrame: boolean, - page: string, - }, - }; -} - -export interface TraceEventAsync extends TraceEventData { - ph: Phase.ASYNC_NESTABLE_START|Phase.ASYNC_NESTABLE_INSTANT|Phase.ASYNC_NESTABLE_END|Phase.ASYNC_STEP_INTO| - Phase.ASYNC_BEGIN|Phase.ASYNC_END|Phase.ASYNC_STEP_PAST; -} - -export type TraceRect = [number, number, number, number]; -export type TraceImpactedNode = { - // These keys come from the trace data, so we have to use underscores. - /* eslint-disable @typescript-eslint/naming-convention */ - new_rect: TraceRect, - node_id: Protocol.DOM.BackendNodeId, - old_rect: TraceRect, - /* eslint-enable @typescript-eslint/naming-convention */ -}; - -type LayoutShiftData = TraceEventArgsData&{ - // These keys come from the trace data, so we have to use underscores. - /* eslint-disable @typescript-eslint/naming-convention */ - cumulative_score: number, - frame_max_distance: number, - had_recent_input: boolean, - impacted_nodes: TraceImpactedNode[] | undefined, - is_main_frame: boolean, - overall_max_distance: number, - region_rects: TraceRect[], - score: number, - weighted_score_delta: number, - /* eslint-enable @typescript-eslint/naming-convention */ -}; -// These keys come from the trace data, so we have to use underscores. -export interface TraceEventLayoutShift extends TraceEventInstant { - name: 'LayoutShift'; - normalized?: boolean; - args: TraceEventArgs&{ - frame: string, - data?: LayoutShiftData, - }; -} - -interface LayoutShiftSessionWindowData { - // The sum of the weighted score of all the shifts - // that belong to a session window. - cumulativeWindowScore: number; - // A consecutive generated in the frontend to - // to identify a session window. - id: number; -} -export interface LayoutShiftParsedData { - screenshotSource?: string; - timeFromNavigation?: MicroSeconds; - // The sum of the weighted scores of the shifts that - // belong to a session window up until this shift - // (inclusive). - cumulativeWeightedScoreInWindow: number; - sessionWindowData: LayoutShiftSessionWindowData; -} -export interface SyntheticLayoutShift extends TraceEventLayoutShift { - args: TraceEventArgs&{ - frame: string, - data?: LayoutShiftData&{ - rawEvent: TraceEventLayoutShift, - }, - }; - parsedData: LayoutShiftParsedData; -} - -export type Priority = 'Low'|'High'|'Medium'|'VeryHigh'|'Highest'; -export type RenderBlocking = 'blocking'|'non_blocking'|'in_body_parser_blocking'|'potentially_blocking'; -export interface TraceEventResourceSendRequest extends TraceEventInstant { - name: 'ResourceSendRequest'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - frame: string, - requestId: string, - url: string, - priority: Priority, - // TODO(crbug.com/1457985): change requestMethod to enum when confirm in the backend code. - requestMethod?: string, - renderBlocking?: RenderBlocking, - }, - }; -} - -export interface TraceEventResourceChangePriority extends TraceEventInstant { - name: 'ResourceChangePriority'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - requestId: string, - priority: Priority, - }, - }; -} - -export interface TraceEventResourceWillSendRequest extends TraceEventInstant { - name: 'ResourceWillSendRequest'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - requestId: string, - }, - }; -} - -export interface TraceEventResourceFinish extends TraceEventInstant { - name: 'ResourceFinish'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - decodedBodyLength: number, - didFail: boolean, - encodedDataLength: number, - finishTime: Seconds, - requestId: string, - }, - }; -} - -export interface TraceEventResourceReceivedData extends TraceEventInstant { - name: 'ResourceReceivedData'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - encodedDataLength: number, - frame: string, - requestId: string, - }, - }; -} - -interface TraceEventResourceReceiveResponseTimingData { - connectEnd: MilliSeconds; - connectStart: MilliSeconds; - dnsEnd: MilliSeconds; - dnsStart: MilliSeconds; - proxyEnd: MilliSeconds; - proxyStart: MilliSeconds; - pushEnd: MilliSeconds; - pushStart: MilliSeconds; - receiveHeadersEnd: MilliSeconds; - requestTime: Seconds; - sendEnd: MilliSeconds; - sendStart: MilliSeconds; - sslEnd: MilliSeconds; - sslStart: MilliSeconds; - workerReady: MilliSeconds; - workerStart: MilliSeconds; -} - -export interface TraceEventResourceReceiveResponse extends TraceEventInstant { - name: 'ResourceReceiveResponse'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - encodedDataLength: number, - frame: string, - fromCache: boolean, - fromServiceWorker: boolean, - mimeType: string, - requestId: string, - responseTime: MilliSeconds, - statusCode: number, - timing: TraceEventResourceReceiveResponseTimingData, - }, - }; -} - -export interface TraceEventResourceMarkAsCached extends TraceEventInstant { - name: 'ResourceMarkAsCached'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - requestId: string, - }, - }; -} - -export const enum LayoutInvalidationReason { - SIZE_CHANGED = 'Size changed', - ATTRIBUTE = 'Attribute', - ADDED_TO_LAYOUT = 'Added to layout', - SCROLLBAR_CHANGED = 'Scrollbar changed', - REMOVED_FROM_LAYOUT = 'Removed from layout', - STYLE_CHANGED = 'Style changed', - FONTS_CHANGED = 'Fonts changed', - UNKNOWN = 'Unknown', -} - -export interface TraceEventLayoutInvalidationTracking extends TraceEventInstant { - name: KnownEventName.LayoutInvalidationTracking; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - frame: string, - nodeId: Protocol.DOM.BackendNodeId, - reason: LayoutInvalidationReason, - nodeName?: string, - }, - }; -} - -export interface TraceEventScheduleStyleInvalidationTracking extends TraceEventInstant { - name: KnownEventName.ScheduleStyleInvalidationTracking; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - frame: string, - nodeId: Protocol.DOM.BackendNodeId, - invalidationSet?: string, - invalidatedSelectorId?: string, - reason?: LayoutInvalidationReason, - changedClass?: string, - nodeName?: string, - stackTrace?: TraceEventCallFrame[], - }, - }; -} - -export const enum StyleRecalcInvalidationReason { - ANIMATION = 'Animation', -} - -export interface TraceEventStyleRecalcInvalidation extends TraceEventInstant { - name: 'StyleRecalcInvalidationTracking'; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - frame: string, - nodeId: Protocol.DOM.BackendNodeId, - reason: StyleRecalcInvalidationReason, - subtree: boolean, - nodeName?: string, - extraData?: string, - }, - }; -} -export interface TraceEventScheduleStyleRecalculation extends TraceEventInstant { - name: KnownEventName.ScheduleStyleRecalculation; - args: TraceEventArgs&{ - data: { - frame: string, - }, - }; -} -export interface TraceEventPrePaint extends TraceEventComplete { - name: 'PrePaint'; -} - -export type TraceEventNestableAsync = TraceEventNestableAsyncBegin|TraceEventNestableAsyncEnd; -export interface TraceEventNestableAsyncBegin extends TraceEventData { - ph: Phase.ASYNC_NESTABLE_START; - // The id2 field gives flexibility to explicitly specify if an event - // id is global among processes or process local. However not all - // events use it, so both kind of ids need to be marked as optional. - id2?: {local?: string, global?: string}; - id?: string; -} - -export interface TraceEventNestableAsyncEnd extends TraceEventData { - ph: Phase.ASYNC_NESTABLE_END; - id2?: {local?: string, global?: string}; - id?: string; -} - -export type TraceEventAsyncPerformanceMeasure = TraceEventPerformanceMeasureBegin|TraceEventPerformanceMeasureEnd; - -export interface TraceEventPerformanceMeasureBegin extends TraceEventNestableAsyncBegin { - cat: 'blink.user_timing'; - id: string; -} - -export interface TraceEventPerformanceMeasureEnd extends TraceEventNestableAsyncEnd { - cat: 'blink.user_timing'; - id: string; -} - -export interface TraceEventConsoleTimeBegin extends TraceEventNestableAsyncBegin { - cat: 'blink.console'; - id2: { - local: string, - }; -} - -export interface TraceEventConsoleTimeEnd extends TraceEventNestableAsyncEnd { - cat: 'blink.console'; - id2: { - local: string, - }; -} - -export interface TraceEventTimeStamp extends TraceEventData { - cat: 'devtools.timeline'; - name: 'TimeStamp'; - ph: Phase.INSTANT; - id: string; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - frame: string, - message: string, - }, - }; -} - -export interface TraceEventPerformanceMark extends TraceEventData { - cat: 'blink.user_timing'; - ph: Phase.INSTANT|Phase.MARK; - id: string; -} - -// Nestable async events with a duration are made up of two distinct -// events: the begin, and the end. We need both of them to be able to -// display the right information, so we create these synthetic events. -export interface TraceEventSyntheticNestableAsyncEvent extends TraceEventData { - id?: string; - id2?: {local?: string, global?: string}; - dur: MicroSeconds; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - beginEvent: TraceEventNestableAsyncBegin, - endEvent: TraceEventNestableAsyncEnd, - }, - }; -} - -export interface TraceEventSyntheticUserTiming extends TraceEventSyntheticNestableAsyncEvent { - id: string; - dur: MicroSeconds; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - beginEvent: TraceEventPerformanceMeasureBegin, - endEvent: TraceEventPerformanceMeasureEnd, - }, - }; -} - -export interface TraceEventSyntheticConsoleTiming extends TraceEventSyntheticNestableAsyncEvent { - id2: {local: string}; - dur: MicroSeconds; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - beginEvent: TraceEventConsoleTimeBegin, - endEvent: TraceEventConsoleTimeEnd, - }, - }; -} - -export interface SyntheticInteractionEvent extends TraceEventSyntheticNestableAsyncEvent { - // InteractionID and type are available within the beginEvent's data, but we - // put them on the top level for ease of access. - interactionId: number; - type: string; - // This is equivalent to startEvent.ts; - ts: MicroSeconds; - // This duration can be calculated via endEvent.ts - startEvent.ts, but we do - // that and put it here to make it easier. This also makes these events - // consistent with real events that have a dur field. - dur: MicroSeconds; - // These values are provided in the startEvent's args.data field as - // millisecond values, but during the handler phase we parse these into - // microseconds and put them on the top level for easy access. - processingStart: MicroSeconds; - processingEnd: MicroSeconds; - // These 3 values represent the breakdown of the parts of an interaction: - // 1. inputDelay: time from the user clicking to the input being handled - inputDelay: MicroSeconds; - // 2. mainThreadHandling: time spent processing the event handler - mainThreadHandling: MicroSeconds; - // 3. presentationDelay: delay between the event being processed and the frame being rendered - presentationDelay: MicroSeconds; - args: TraceEventArgs&{ - data: TraceEventArgsData & { - beginEvent: TraceEventEventTimingBegin, - endEvent: TraceEventEventTimingEnd, - }, - }; -} - -/** - * An event created synthetically in the frontend that has a self time - * (the time spent running the task itself). - */ -export interface SyntheticEventWithSelfTime extends TraceEventData { - selfTime?: MicroSeconds; -} - -/** - * A profile call created in the frontend from samples disguised as a - * trace event. - */ -export interface TraceEventSyntheticProfileCall extends SyntheticEventWithSelfTime { - callFrame: Protocol.Runtime.CallFrame; - nodeId: Protocol.integer; -} - -/** - * A trace event augmented synthetically in the frontend to contain - * its self time. - */ -export type SyntheticRendererEvent = TraceEventRendererEvent&SyntheticEventWithSelfTime; - -export type TraceEntry = SyntheticRendererEvent|TraceEventSyntheticProfileCall; - -// Events relating to frames. - -export interface TraceEventDrawFrame extends TraceEventInstant { - name: KnownEventName.DrawFrame; - args: TraceEventArgs&{ - layerTreeId: number, - frameSeqId: number, - }; -} - -export interface TraceEventLegacyDrawFrameBegin extends TraceEventAsync { - name: KnownEventName.DrawFrame; - ph: Phase.ASYNC_NESTABLE_START; - args: TraceEventArgs&{ - layerTreeId: number, - frameSeqId: number, - }; -} - -export interface TraceEventBeginFrame extends TraceEventInstant { - name: KnownEventName.BeginFrame; - args: TraceEventArgs&{ - layerTreeId: number, - frameSeqId: number, - }; -} - -export interface TraceEventDroppedFrame extends TraceEventInstant { - name: KnownEventName.DroppedFrame; - args: TraceEventArgs&{ - layerTreeId: number, - frameSeqId: number, - hasPartialUpdate?: boolean, - }; -} - -export interface TraceEventRequestMainThreadFrame extends TraceEventInstant { - name: KnownEventName.RequestMainThreadFrame; - args: TraceEventArgs&{ - layerTreeId: number, - }; -} - -export interface TraceEventBeginMainThreadFrame extends TraceEventInstant { - name: KnownEventName.BeginMainThreadFrame; - args: TraceEventArgs&{ - layerTreeId: number, - data: TraceEventArgsData&{ - frameId?: number, - }, - }; -} - -export interface TraceEventNeedsBeginFrameChanged extends TraceEventInstant { - name: KnownEventName.NeedsBeginFrameChanged; - args: TraceEventArgs&{ - layerTreeId: number, - data: TraceEventArgsData&{ - needsBeginFrame: number, - }, - }; -} - -export interface TraceEventCommit extends TraceEventInstant { - name: KnownEventName.Commit; - args: TraceEventArgs&{ - layerTreeId: number, - frameSeqId: number, - }; -} - -export interface TraceEventRasterTask extends TraceEventComplete { - name: KnownEventName.RasterTask; - args: TraceEventArgs&{ - tileData?: { - layerId: number, - sourceFrameNumber: number, - tileId: { - // eslint-disable-next-line @typescript-eslint/naming-convention - id_ref: string, - }, - tileResolution: string, - }, - }; -} - -// CompositeLayers has been replaced by "Commit", but we support both to not break old traces being imported. -export interface TraceEventCompositeLayers extends TraceEventInstant { - name: KnownEventName.CompositeLayers; - args: TraceEventArgs&{ - layerTreeId: number, - }; -} - -export interface TraceEventActivateLayerTree extends TraceEventInstant { - name: KnownEventName.ActivateLayerTree; - args: TraceEventArgs&{ - layerTreeId: number, - frameId: number, - }; -} - - -export interface TraceEventUpdateLayoutTree extends TraceEventComplete { - name: KnownEventName.UpdateLayoutTree; - args: TraceEventArgs&{ - elementCount: number, - beginData?: { - frame: string, - }, - }; -} - -export interface TraceEventLayout extends TraceEventComplete { - name: KnownEventName.Layout; - args: TraceEventArgs&{ - beginData: { - frame: string, - dirtyObjects: number, - partialLayout: boolean, - totalObjects: number, - }, - endData: { - layoutRoots: Array<{ - depth: number, - nodeId: Protocol.DOM.BackendNodeId, - quads: number[][], - }>, - }, - }; -} - - -export class ProfileIdTag { - readonly #profileIdTag: (symbol|undefined); - } - export type ProfileID = string&ProfileIdTag; - - export class CallFrameIdTag { - readonly #callFrameIdTag: (symbol|undefined); - } - export type CallFrameID = number&CallFrameIdTag; - - - export class ProcessIdTag { - readonly #processIdTag: (symbol|undefined); - } - export type ProcessID = number&ProcessIdTag; - - - export class ThreadIdTag { - readonly #threadIdTag: (symbol|undefined); - } - export type ThreadID = number&ThreadIdTag; - - export class WorkerIdTag { - readonly #workerIdTag: (symbol|undefined); - } - export type WorkerId = string&WorkerIdTag; - -/** - * This is an exhaustive list of events we track in the Performance - * panel. Note not all of them are necessarliry shown in the flame - * chart, some of them we only use for parsing. - * TODO(crbug.com/1428024): Complete this enum. - */ -export const enum KnownEventName { - /* Metadata */ - ThreadName = 'thread_name', - - /* Task */ - Program = 'Program', - RunTask = 'RunTask', - AsyncTask = 'AsyncTask', - RunMicrotasks = 'RunMicrotasks', - - /* Load */ - XHRLoad = 'XHRLoad', - XHRReadyStateChange = 'XHRReadyStateChange', - /* Parse */ - ParseHTML = 'ParseHTML', - ParseCSS = 'ParseAuthorStyleSheet', - /* V8 */ - CompileCode = 'V8.CompileCode', - CompileModule = 'V8.CompileModule', - // Although V8 emits the V8.CompileScript event, the event that actually - // contains the useful information about the script (URL, etc), is contained - // in the v8.compile event. - // Yes, it is all lowercase compared to all the rest of the V8... events, - // that is not a typo :) - Compile = 'v8.compile', - CompileScript = 'V8.CompileScript', - Optimize = 'V8.OptimizeCode', - WasmStreamFromResponseCallback = 'v8.wasm.streamFromResponseCallback', - WasmCompiledModule = 'v8.wasm.compiledModule', - WasmCachedModule = 'v8.wasm.cachedModule', - WasmModuleCacheHit = 'v8.wasm.moduleCacheHit', - WasmModuleCacheInvalid = 'v8.wasm.moduleCacheInvalid', - /* Js */ - ProfileCall = 'ProfileCall', - EvaluateScript = 'EvaluateScript', - FunctionCall = 'FunctionCall', - EventDispatch = 'EventDispatch', - EvaluateModule = 'v8.evaluateModule', - RequestMainThreadFrame = 'RequestMainThreadFrame', - RequestAnimationFrame = 'RequestAnimationFrame', - CancelAnimationFrame = 'CancelAnimationFrame', - FireAnimationFrame = 'FireAnimationFrame', - RequestIdleCallback = 'RequestIdleCallback', - CancelIdleCallback = 'CancelIdleCallback', - FireIdleCallback = 'FireIdleCallback', - TimerInstall = 'TimerInstall', - TimerRemove = 'TimerRemove', - TimerFire = 'TimerFire', - WebSocketCreate = 'WebSocketCreate', - WebSocketSendHandshake = 'WebSocketSendHandshakeRequest', - WebSocketReceiveHandshake = 'WebSocketReceiveHandshakeResponse', - WebSocketDestroy = 'WebSocketDestroy', - CryptoDoEncrypt = 'DoEncrypt', - CryptoDoEncryptReply = 'DoEncryptReply', - CryptoDoDecrypt = 'DoDecrypt', - CryptoDoDecryptReply = 'DoDecryptReply', - CryptoDoDigest = 'DoDigest', - CryptoDoDigestReply = 'DoDigestReply', - CryptoDoSign = 'DoSign', - CryptoDoSignReply = 'DoSignReply', - CryptoDoVerify = 'DoVerify', - CryptoDoVerifyReply = 'DoVerifyReply', - V8Execute = 'V8.Execute', - - /* Gc */ - GC = 'GCEvent', - DOMGC = 'BlinkGC.AtomicPhase', - IncrementalGCMarking = 'V8.GCIncrementalMarking', - MajorGC = 'MajorGC', - MinorGC = 'MinorGC', - GCCollectGarbage = 'BlinkGC.AtomicPhase', - - /* Layout */ - ScheduleStyleRecalculation = 'ScheduleStyleRecalculation', - RecalculateStyles = 'RecalculateStyles', - Layout = 'Layout', - UpdateLayoutTree = 'UpdateLayoutTree', - InvalidateLayout = 'InvalidateLayout', - LayoutInvalidationTracking = 'LayoutInvalidationTracking', - ComputeIntersections = 'ComputeIntersections', - HitTest = 'HitTest', - PrePaint = 'PrePaint', - Layerize = 'Layerize', - LayoutShift = 'LayoutShift', - UpdateLayerTree = 'UpdateLayerTree', - ScheduleStyleInvalidationTracking = 'ScheduleStyleInvalidationTracking', - StyleRecalcInvalidationTracking = 'StyleRecalcInvalidationTracking', - StyleInvalidatorInvalidationTracking = 'StyleInvalidatorInvalidationTracking', - - /* Paint */ - ScrollLayer = 'ScrollLayer', - UpdateLayer = 'UpdateLayer', - PaintSetup = 'PaintSetup', - Paint = 'Paint', - PaintImage = 'PaintImage', - Commit = 'Commit', - CompositeLayers = 'CompositeLayers', - RasterTask = 'RasterTask', - ImageDecodeTask = 'ImageDecodeTask', - ImageUploadTask = 'ImageUploadTask', - DecodeImage = 'Decode Image', - ResizeImage = 'Resize Image', - DrawLazyPixelRef = 'Draw LazyPixelRef', - DecodeLazyPixelRef = 'Decode LazyPixelRef', - GPUTask = 'GPUTask', - Rasterize = 'Rasterize', - EventTiming = 'EventTiming', - - /* Compile */ - OptimizeCode = 'V8.OptimizeCode', - CacheScript = 'v8.produceCache', - CacheModule = 'v8.produceModuleCache', - // V8Sample events are coming from tracing and contain raw stacks with function addresses. - // After being processed with help of JitCodeAdded and JitCodeMoved events they - // get translated into function infos and stored as stacks in JSSample events. - V8Sample = 'V8Sample', - JitCodeAdded = 'JitCodeAdded', - JitCodeMoved = 'JitCodeMoved', - StreamingCompileScript = 'v8.parseOnBackground', - StreamingCompileScriptWaiting = 'v8.parseOnBackgroundWaiting', - StreamingCompileScriptParsing = 'v8.parseOnBackgroundParsing', - BackgroundDeserialize = 'v8.deserializeOnBackground', - FinalizeDeserialization = 'V8.FinalizeDeserialization', - - /* Markers */ - CommitLoad = 'CommitLoad', - MarkLoad = 'MarkLoad', - MarkDOMContent = 'MarkDOMContent', - MarkFirstPaint = 'firstPaint', - MarkFCP = 'firstContentfulPaint', - MarkLCPCandidate = 'largestContentfulPaint::Candidate', - MarkLCPInvalidate = 'largestContentfulPaint::Invalidate', - NavigationStart = 'navigationStart', - TimeStamp = 'TimeStamp', - ConsoleTime = 'ConsoleTime', - UserTiming = 'UserTiming', - InteractiveTime = 'InteractiveTime', - - /* Frames */ - BeginFrame = 'BeginFrame', - NeedsBeginFrameChanged = 'NeedsBeginFrameChanged', - BeginMainThreadFrame = 'BeginMainThreadFrame', - ActivateLayerTree = 'ActivateLayerTree', - DrawFrame = 'DrawFrame', - DroppedFrame = 'DroppedFrame', - FrameStartedLoading = 'FrameStartedLoading', - - /* Network request events */ - ResourceWillSendRequest = 'ResourceWillSendRequest', - ResourceSendRequest = 'ResourceSendRequest', - ResourceReceiveResponse = 'ResourceReceiveResponse', - ResourceReceivedData = 'ResourceReceivedData', - ResourceFinish = 'ResourceFinish', - ResourceMarkAsCached = 'ResourceMarkAsCached', - - /* Web sockets */ - WebSocketSendHandshakeRequest = 'WebSocketSendHandshakeRequest', - WebSocketReceiveHandshakeResponse = 'WebSocketReceiveHandshakeResponse', - - /* CPU Profiling */ - Profile = 'Profile', - StartProfiling = 'CpuProfiler::StartProfiling', - ProfileChunk = 'ProfileChunk', - UpdateCounters = 'UpdateCounters', - - /* Other */ - Animation = 'Animation', - ParseAuthorStyleSheet = 'ParseAuthorStyleSheet', - EmbedderCallback = 'EmbedderCallback', - SetLayerTreeId = 'SetLayerTreeId', - TracingStartedInPage = 'TracingStartedInPage', - TracingStartedInBrowser = 'TracingStartedInBrowser', - TracingSessionIdForWorker = 'TracingSessionIdForWorker', - LazyPixelRef = 'LazyPixelRef', - LayerTreeHostImplSnapshot = 'cc::LayerTreeHostImpl', - PictureSnapshot = 'cc::Picture', - DisplayItemListSnapshot = 'cc::DisplayItemList', - InputLatencyMouseMove = 'InputLatency::MouseMove', - InputLatencyMouseWheel = 'InputLatency::MouseWheel', - ImplSideFling = 'InputHandlerProxy::HandleGestureFling::started', -} - -export interface TraceEventSyntheticNetworkRequest extends TraceEventComplete { - args: TraceEventArgs&{ - data: TraceEventArgsData & { - syntheticData: TraceEventSyntheticArgsData, - // All fields below are from TraceEventsForNetworkRequest, - // Required fields - decodedBodyLength: number, - encodedDataLength: number, - frame: string, - fromServiceWorker: boolean, - host: string, - mimeType: string, - pathname: string, - search: string, - priority: Priority, - initialPriority: Priority, - protocol: string, - redirects: TraceEventSyntheticNetworkRedirect[], - renderBlocking: RenderBlocking, - requestId: string, - requestingFrameUrl: string, - statusCode: number, - url: string, - // Optional fields - requestMethod?: string, - timing?: TraceEventResourceReceiveResponseTimingData, - }, - }; - cat: 'loading'; - name: 'SyntheticNetworkRequest'; - ph: Phase.COMPLETE; - dur: MicroSeconds; - tdur: MicroSeconds; - ts: MicroSeconds; - tts: MicroSeconds; - pid: ProcessID; - tid: ThreadID; -} From 5fbb9b724dda800723123c0b7fbcb9132b7f1afa Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Wed, 14 Feb 2024 16:13:39 -0800 Subject: [PATCH 2/4] types --- core/computed/trace-engine-result.js | 2 +- core/gather/gatherers/root-causes.js | 25 ++++++++++++++++--------- core/lib/trace-engine.js | 2 +- package.json | 2 +- types/artifacts.d.ts | 4 ++-- yarn.lock | 8 ++++---- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/core/computed/trace-engine-result.js b/core/computed/trace-engine-result.js index 49efeabc75ec..332c142213de 100644 --- a/core/computed/trace-engine-result.js +++ b/core/computed/trace-engine-result.js @@ -26,7 +26,7 @@ class TraceEngineResult { Samples: TraceEngine.TraceHandlers.Samples, Screenshots: TraceEngine.TraceHandlers.Screenshots, }); - await engine.parse(/** @type {import('/Users/cjamcl/src/devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/trace.js').Types.TraceEvents.TraceEventData[]} */ ( + await engine.parse(/** @type {import('@paulirish/trace_engine').Types.TraceEvents.TraceEventData[]} */ ( traceEvents )); return engine.data; diff --git a/core/gather/gatherers/root-causes.js b/core/gather/gatherers/root-causes.js index 8e9c75ac2fa2..6c806580bd8a 100644 --- a/core/gather/gatherers/root-causes.js +++ b/core/gather/gatherers/root-causes.js @@ -32,26 +32,31 @@ class RootCauses extends BaseGatherer { // nodeIds if the DOM domain was enabled before this gatherer, invoke it to be safe. await driver.defaultSession.sendCommand('DOM.getDocument', {depth: -1, pierce: true}); + /** @type {import('@paulirish/trace_engine').RootCauses.RootCauses.RootCauseProtocolInterface} */ const protocolInterface = { /** @param {string} url */ // eslint-disable-next-line no-unused-vars getInitiatorForRequest(url) { return null; }, - /** @param {number[]} backendNodeIds */ + /** @param {import('@paulirish/trace_engine/generated/protocol.js').DOM.BackendNodeId[]} backendNodeIds */ async pushNodesByBackendIdsToFrontend(backendNodeIds) { const response = await driver.defaultSession.sendCommand( 'DOM.pushNodesByBackendIdsToFrontend', {backendNodeIds}); - return response.nodeIds; + const nodeIds = /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId[]} */( + response.nodeIds); + return nodeIds; }, - /** @param {number} nodeId */ + /** @param {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId} nodeId */ async getNode(nodeId) { try { const response = await driver.defaultSession.sendCommand('DOM.describeNode', {nodeId}); // This always zero, so let's fix it here. // https://bugs.chromium.org/p/chromium/issues/detail?id=1515175 response.node.nodeId = nodeId; - return response.node; + const node = /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.Node} */( + response.node); + return node; } catch (err) { if (err.message.includes('Could not find node with given id')) { // TODO: when injecting an iframe, the engine gets the node of that frame's document element. @@ -64,7 +69,8 @@ class RootCauses extends BaseGatherer { // When this is fixed, remove this try/catch. // Note: this could be buggy by giving the wrong node detail if a node id meant for a non-main frame // happens to match one from the main frame ... which is pretty likely... - return null; + // TODO: fix trace engine type to allow returning null. + return /** @type {any} */(null); } throw err; } @@ -79,17 +85,18 @@ class RootCauses extends BaseGatherer { return []; } }, - /** @param {number} nodeId */ + /** @param {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId} nodeId */ async getMatchedStylesForNode(nodeId) { try { const response = await driver.defaultSession.sendCommand( 'CSS.getMatchedStylesForNode', {nodeId}); - return response; - } catch { - return []; + return {...response, getError(){}}; + } catch (err) { + return /** @type {any} */({getError(){return err.toString()}}); } }, /** @param {string} url */ + // @ts-expect-error // eslint-disable-next-line no-unused-vars async fontFaceForSource(url) { return null; diff --git a/core/lib/trace-engine.js b/core/lib/trace-engine.js index 4544ebe32962..01231b941b5e 100644 --- a/core/lib/trace-engine.js +++ b/core/lib/trace-engine.js @@ -1,4 +1,4 @@ -import * as TraceEngine from '/Users/cjamcl/src/devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/trace.js'; +import * as TraceEngine from '@paulirish/trace_engine'; import {polyfillDOMRect} from './polyfill-dom-rect.js'; diff --git a/package.json b/package.json index 9b286b17cba2..45aa78f73e41 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,7 @@ "webtreemap-cdt": "^3.2.1" }, "dependencies": { - "@paulirish/trace_engine": "^0.0.7", + "@paulirish/trace_engine": "^0.0.11", "@sentry/node": "^6.17.4", "axe-core": "^4.8.4", "chrome-launcher": "^1.1.0", diff --git a/types/artifacts.d.ts b/types/artifacts.d.ts index ba5bdf5e9238..31066797c699 100644 --- a/types/artifacts.d.ts +++ b/types/artifacts.d.ts @@ -5,8 +5,8 @@ */ import {Protocol as Crdp} from 'devtools-protocol/types/protocol.js'; -import * as TraceEngine from '../../devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/trace.js'; -import {LayoutShiftRootCausesData} from '../../devtools/devtools-frontend/out/Default/gen/trace_engine/models/trace/root-causes/LayoutShift.js'; +import * as TraceEngine from '@paulirish/trace_engine'; +import {LayoutShiftRootCausesData} from '@paulirish/trace_engine/models/trace/root-causes/LayoutShift.js'; import {parseManifest} from '../core/lib/manifest-parser.js'; import {Simulator} from '../core/lib/dependency-graph/simulator/simulator.js'; diff --git a/yarn.lock b/yarn.lock index c4ff54ef423e..1ccffdfa3446 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1080,10 +1080,10 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@paulirish/trace_engine@^0.0.7": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@paulirish/trace_engine/-/trace_engine-0.0.7.tgz#c9494729f39a1c742325726474532aabfdee6f1b" - integrity sha512-TC639y9YlejyQcTG6LqJ3GJGuFfV9IJTBRsxyl+bGqxkfzyqJ+LG00fOht2FgnfwkCHImlIFBg4HDrX+2LHPHQ== +"@paulirish/trace_engine@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@paulirish/trace_engine/-/trace_engine-0.0.11.tgz#5751ca64dd0a75cb3d0ce07de5c4fc1e64e84ae0" + integrity sha512-QxTKFiGWCEDvmeBthcNkyWsA4y/upIp36u6DP+P9P6nFfzQWIY8blMVbnRazxY+nhLML4LhzBiZ3wpIKSFbMYw== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" From 1717308137548603598341c11ea22fc7f8e257a7 Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Wed, 14 Feb 2024 16:40:16 -0800 Subject: [PATCH 3/4] update --- core/audits/layout-shifts.js | 7 +++++-- .../metrics/cumulative-layout-shift.js | 8 +++++++- core/computed/trace-engine-result.js | 5 ++++- core/gather/gatherers/root-causes.js | 18 ++++++++++++------ core/gather/gatherers/trace-elements.js | 8 ++++++-- core/lib/trace-engine.js | 3 +++ package.json | 8 ++++---- types/artifacts.d.ts | 7 +------ yarn.lock | 16 ++++++++-------- 9 files changed, 50 insertions(+), 30 deletions(-) diff --git a/core/audits/layout-shifts.js b/core/audits/layout-shifts.js index dbd6d520e24d..d09da2722afb 100644 --- a/core/audits/layout-shifts.js +++ b/core/audits/layout-shifts.js @@ -69,13 +69,16 @@ class LayoutShifts extends Audit { /** @type {Item[]} */ const items = []; - const layoutShiftEvents = clusters.flatMap(c => c.events); + const layoutShiftEvents = + /** @type {import('../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */( + clusters.flatMap(c => c.events) + ); const topLayoutShiftEvents = layoutShiftEvents .sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta) .slice(0, MAX_LAYOUT_SHIFTS); for (const event of topLayoutShiftEvents) { const biggestImpactNodeId = TraceElements.getBiggestImpactNodeForShiftEvent( - event.args.data.impacted_nodes, impactByNodeId); + event.args.data.impacted_nodes || [], impactByNodeId); const biggestImpactElement = traceElements.find(t => t.nodeId === biggestImpactNodeId); // Turn root causes into sub-items. diff --git a/core/computed/metrics/cumulative-layout-shift.js b/core/computed/metrics/cumulative-layout-shift.js index fd58f2000ffc..fc02cee87ee7 100644 --- a/core/computed/metrics/cumulative-layout-shift.js +++ b/core/computed/metrics/cumulative-layout-shift.js @@ -159,7 +159,13 @@ class CumulativeLayoutShift { LayoutShifts: TraceEngine.TraceHandlers.LayoutShifts, Screenshots: TraceEngine.TraceHandlers.Screenshots, }); - await processor.parse(events); + // eslint-disable-next-line max-len + await processor.parse(/** @type {import('@paulirish/trace_engine').Types.TraceEvents.TraceEventData[]} */ ( + events + )); + if (!processor.data) { + throw new Error('null trace engine result'); + } return processor.data.LayoutShifts.sessionMaxScore; }; const cumulativeLayoutShift = await run(allFrameShiftEvents.map(e => e.event)); diff --git a/core/computed/trace-engine-result.js b/core/computed/trace-engine-result.js index 332c142213de..5d16eaff1aa8 100644 --- a/core/computed/trace-engine-result.js +++ b/core/computed/trace-engine-result.js @@ -26,10 +26,13 @@ class TraceEngineResult { Samples: TraceEngine.TraceHandlers.Samples, Screenshots: TraceEngine.TraceHandlers.Screenshots, }); + // eslint-disable-next-line max-len await engine.parse(/** @type {import('@paulirish/trace_engine').Types.TraceEvents.TraceEventData[]} */ ( traceEvents )); - return engine.data; + // TODO: use TraceEngine.TraceProcessor.createWithAllHandlers above. + return /** @type {import('@paulirish/trace_engine').Handlers.Types.TraceParseData} */( + engine.data); } /** diff --git a/core/gather/gatherers/root-causes.js b/core/gather/gatherers/root-causes.js index 6c806580bd8a..7a569dd80cb1 100644 --- a/core/gather/gatherers/root-causes.js +++ b/core/gather/gatherers/root-causes.js @@ -43,8 +43,9 @@ class RootCauses extends BaseGatherer { async pushNodesByBackendIdsToFrontend(backendNodeIds) { const response = await driver.defaultSession.sendCommand( 'DOM.pushNodesByBackendIdsToFrontend', {backendNodeIds}); - const nodeIds = /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId[]} */( - response.nodeIds); + const nodeIds = + /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId[]} */( + response.nodeIds); return nodeIds; }, /** @param {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId} nodeId */ @@ -54,8 +55,9 @@ class RootCauses extends BaseGatherer { // This always zero, so let's fix it here. // https://bugs.chromium.org/p/chromium/issues/detail?id=1515175 response.node.nodeId = nodeId; - const node = /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.Node} */( - response.node); + const node = + /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.Node} */( + response.node); return node; } catch (err) { if (err.message.includes('Could not find node with given id')) { @@ -90,9 +92,11 @@ class RootCauses extends BaseGatherer { try { const response = await driver.defaultSession.sendCommand( 'CSS.getMatchedStylesForNode', {nodeId}); - return {...response, getError(){}}; + return {...response, getError() {}}; } catch (err) { - return /** @type {any} */({getError(){return err.toString()}}); + return /** @type {any} */({getError() { + return err.toString(); + }}); } }, /** @param {string} url */ @@ -111,6 +115,8 @@ class RootCauses extends BaseGatherer { const layoutShiftEvents = traceEngineResult.LayoutShifts.clusters.flatMap(c => c.events); for (const event of layoutShiftEvents) { const r = await rootCausesEngine.layoutShifts.rootCausesForEvent(traceEngineResult, event); + if (!r) continue; + for (const cause of r.fontChanges) { // TODO: why isn't trace engine unwrapping this promise ... cause.fontFace = await cause.fontFace; diff --git a/core/gather/gatherers/trace-elements.js b/core/gather/gatherers/trace-elements.js index 8b2724e269a7..487d5ae85c90 100644 --- a/core/gather/gatherers/trace-elements.js +++ b/core/gather/gatherers/trace-elements.js @@ -117,15 +117,19 @@ class TraceElements extends BaseGatherer { static async getTopLayoutShifts(trace, traceEngineResult, rootCauses, context) { const {impactByNodeId} = await CumulativeLayoutShift.request(trace, context); const clusters = traceEngineResult.LayoutShifts.clusters ?? []; - const layoutShiftEvents = clusters.flatMap(c => c.events); + const layoutShiftEvents = + /** @type {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */( + clusters.flatMap(c => c.events) + ); return layoutShiftEvents .sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta) .slice(0, MAX_LAYOUT_SHIFTS) .flatMap(event => { const nodeIds = []; + const impactedNodes = event.args.data.impacted_nodes || []; const biggestImpactedNodeId = - this.getBiggestImpactNodeForShiftEvent(event.args.data.impacted_nodes, impactByNodeId); + this.getBiggestImpactNodeForShiftEvent(impactedNodes, impactByNodeId); if (biggestImpactedNodeId !== undefined) { nodeIds.push(biggestImpactedNodeId); } diff --git a/core/lib/trace-engine.js b/core/lib/trace-engine.js index 01231b941b5e..0914a9c14b52 100644 --- a/core/lib/trace-engine.js +++ b/core/lib/trace-engine.js @@ -2,6 +2,9 @@ import * as TraceEngine from '@paulirish/trace_engine'; import {polyfillDOMRect} from './polyfill-dom-rect.js'; +/** @typedef {import('@paulirish/trace_engine').Types.TraceEvents.SyntheticLayoutShift} SyntheticLayoutShift */ +/** @typedef {SyntheticLayoutShift & {args: {data: NonNullable}}} SaneSyntheticLayoutShift */ + polyfillDOMRect(); const TraceProcessor = TraceEngine.Processor.TraceProcessor; diff --git a/package.json b/package.json index 45aa78f73e41..0f6ed8e037fc 100644 --- a/package.json +++ b/package.json @@ -180,13 +180,13 @@ "webtreemap-cdt": "^3.2.1" }, "dependencies": { - "@paulirish/trace_engine": "^0.0.11", + "@paulirish/trace_engine": "^0.0.12", "@sentry/node": "^6.17.4", "axe-core": "^4.8.4", "chrome-launcher": "^1.1.0", "configstore": "^5.0.1", "csp_evaluator": "1.1.1", - "devtools-protocol": "0.0.1211954", + "devtools-protocol": "0.0.1232444", "enquirer": "^2.3.6", "http-link-header": "^1.1.1", "intl-messageformat": "^10.5.3", @@ -211,8 +211,8 @@ "yargs-parser": "^21.0.0" }, "resolutions": { - "puppeteer/**/devtools-protocol": "0.0.1211954", - "puppeteer-core/**/devtools-protocol": "0.0.1211954" + "puppeteer/**/devtools-protocol": "0.0.1232444", + "puppeteer-core/**/devtools-protocol": "0.0.1232444" }, "repository": "GoogleChrome/lighthouse", "keywords": [ diff --git a/types/artifacts.d.ts b/types/artifacts.d.ts index 31066797c699..c0207607f476 100644 --- a/types/artifacts.d.ts +++ b/types/artifacts.d.ts @@ -570,12 +570,7 @@ declare module Artifacts { type?: string; } - type TraceEngineResult = Pick< - NonNullable< - ReturnType['data'] - >, - 'AuctionWorklets'|'Initiators'|'LayoutShifts'|'NetworkRequests'|'Renderer'|'Samples'|'Screenshots' - >; + type TraceEngineResult = TraceEngine.Handlers.Types.TraceParseData; interface TraceEngineRootCauses { layoutShifts: Record; diff --git a/yarn.lock b/yarn.lock index 1ccffdfa3446..1b7e327b61fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1080,10 +1080,10 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@paulirish/trace_engine@^0.0.11": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@paulirish/trace_engine/-/trace_engine-0.0.11.tgz#5751ca64dd0a75cb3d0ce07de5c4fc1e64e84ae0" - integrity sha512-QxTKFiGWCEDvmeBthcNkyWsA4y/upIp36u6DP+P9P6nFfzQWIY8blMVbnRazxY+nhLML4LhzBiZ3wpIKSFbMYw== +"@paulirish/trace_engine@^0.0.12": + version "0.0.12" + resolved "https://registry.yarnpkg.com/@paulirish/trace_engine/-/trace_engine-0.0.12.tgz#6172c84f19d1e4254450d1927b1bb3eea5b5ba04" + integrity sha512-PglukOOYGvjqgMVKNSa3a4grWrWzL97L47bgDCzqAnDSTJ+vDOKxMPXR3rsnLECoZzpbb9LCzS3CEg/NwwhRHQ== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -2921,10 +2921,10 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -devtools-protocol@0.0.1211954, devtools-protocol@0.0.1232444: - version "0.0.1211954" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1211954.tgz#86ea77d011baab619fb096fc426fd8633ad2ef9b" - integrity sha512-f6BRhngr9wpHN8omZOoSaEJFscTL+tjNhmeBqHHC3CZ3K2N75sDeKXZeTkAEkTCcrusDatfwjRRBh0uz4ov/sA== +devtools-protocol@0.0.1232444: + version "0.0.1232444" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1232444.tgz#406345a90a871ba852c530d73482275234936eed" + integrity sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg== diff-sequences@^28.0.2: version "28.0.2" From f8b333c8214a01f72040ddb856d1a43cb165b52e Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Thu, 15 Feb 2024 13:00:54 -0800 Subject: [PATCH 4/4] Apply suggestions from code review --- core/gather/gatherers/root-causes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/gather/gatherers/root-causes.js b/core/gather/gatherers/root-causes.js index 7a569dd80cb1..3e4b41dc679c 100644 --- a/core/gather/gatherers/root-causes.js +++ b/core/gather/gatherers/root-causes.js @@ -100,7 +100,7 @@ class RootCauses extends BaseGatherer { } }, /** @param {string} url */ - // @ts-expect-error + // @ts-expect-error not using, dont care about type error. // eslint-disable-next-line no-unused-vars async fontFaceForSource(url) { return null;