{"version":3,"file":"ScreenshotsHandler.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/handlers/ScreenshotsHandler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport type {HandlerName} from './types.js';\n\n// Each thread contains events. Events indicate the thread and process IDs, which are\n// used to store the event in the correct process thread entry below.\nconst unpairedAsyncEvents: Types.Events.PipelineReporter[] = [];\n\nconst legacyScreenshotEvents: Types.Events.LegacyScreenshot[] = [];\nconst modernScreenshotEvents: Types.Events.Screenshot[] = [];\nconst syntheticScreenshots: Types.Events.LegacySyntheticScreenshot[] = [];\nlet frameSequenceToTs: Record = {};\n\nexport function reset(): void {\n unpairedAsyncEvents.length = 0;\n legacyScreenshotEvents.length = 0;\n syntheticScreenshots.length = 0;\n modernScreenshotEvents.length = 0;\n frameSequenceToTs = {};\n}\n\nexport function handleEvent(event: Types.Events.Event): void {\n if (Types.Events.isLegacyScreenshot(event)) {\n legacyScreenshotEvents.push(event);\n } else if (Types.Events.isScreenshot(event)) {\n modernScreenshotEvents.push(event);\n } else if (Types.Events.isPipelineReporter(event)) {\n unpairedAsyncEvents.push(event);\n }\n}\n\nexport async function finalize(): Promise {\n const pipelineReporterEvents = Helpers.Trace.createMatchedSortedSyntheticEvents(unpairedAsyncEvents);\n\n frameSequenceToTs = Object.fromEntries(pipelineReporterEvents.map(evt => {\n const args = evt.args.data.beginEvent.args;\n const frameReporter = 'frame_reporter' in args ? args.frame_reporter : args.chrome_frame_reporter;\n const frameSequenceId = frameReporter.frame_sequence;\n const presentationTs = Types.Timing.Micro(evt.ts + evt.dur);\n return [frameSequenceId, presentationTs];\n }));\n\n for (const snapshotEvent of legacyScreenshotEvents) {\n const {cat, name, ph, pid, tid} = snapshotEvent;\n const syntheticEvent = Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent<\n Types.Events.LegacySyntheticScreenshot>({\n rawSourceEvent: snapshotEvent,\n cat,\n name,\n ph,\n pid,\n tid,\n // TODO(paulirish, crbug.com/41363012): investigate why getPresentationTimestamp(snapshotEvent) seems less accurate. Resolve screenshot timing inaccuracy.\n // `getPresentationTimestamp(snapshotEvent) - snapshotEvent.ts` is how many microsec the screenshot should be adjusted to the right/later\n ts: snapshotEvent.ts,\n args: {\n dataUri: `data:image/jpg;base64,${snapshotEvent.args.snapshot}`,\n },\n });\n syntheticScreenshots.push(syntheticEvent);\n }\n}\n\nexport function screenshotImageDataUri(event: Types.Events.LegacySyntheticScreenshot|Types.Events.Screenshot): string {\n if (Types.Events.isLegacySyntheticScreenshot(event)) {\n return event.args.dataUri;\n }\n return `data:image/jpg;base64,${event.args.snapshot}`;\n}\n\n/**\n * Correct the screenshot timestamps\n * The screenshot 'snapshot object' trace event has the \"frame sequence number\" attached as an ID.\n * We match that up with the \"PipelineReporter\" trace events as they terminate at presentation.\n * Presentation == when the pixels hit the screen. AKA Swap on the GPU\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction getPresentationTimestamp(screenshotEvent: Types.Events.LegacyScreenshot): Types.Timing.Micro {\n const frameSequence = parseInt(screenshotEvent.id, 16);\n // If it's 1, then it's an old trace (before https://crrev.com/c/4957973) and cannot be corrected.\n if (frameSequence === 1) {\n return screenshotEvent.ts;\n }\n // The screenshot trace event's `ts` reflects the \"expected display time\" which is ESTIMATE.\n // It is set by the compositor frame sink from the `expected_display_time`, which is based on a previously known\n // frame start PLUS the vsync interval (eg 16.6ms)\n const updatedTs = frameSequenceToTs[frameSequence];\n // Do we always find a match? No...\n // We generally don't match the very first screenshot and, sometimes, the last\n // The very first screenshot is requested immediately (even if nothing is painting). As a result there's no compositor\n // instrumentation running alongside.\n // The last one is sometimes missing as because the trace terminates right before the associated PipelineReporter is emitted.\n return updatedTs ?? screenshotEvent.ts;\n}\n\nexport interface Data {\n // These are nullable because in January 2025 a CL in Chromium\n // crrev.com/c/6197645 landed which changed the format of screenshots. For a\n // given trace, it can have either \"legacy\" screenshot events, or \"modern\"\n // screenshot events, but no trace can ever contain both.\n // So, if either of these arrays are empty, we instead return `null`. This forces consumers to check the presence of the array.\n // Traces can have no screenshots if the trace category is not enabled, so it\n // is possible for a trace to return null for both of these arrays.\n legacySyntheticScreenshots: Types.Events.LegacySyntheticScreenshot[]|null;\n screenshots: Types.Events.Screenshot[]|null;\n}\n// TODO(crbug/41484172): should be readonly\nexport function data(): Data {\n return {\n legacySyntheticScreenshots: syntheticScreenshots.length ? syntheticScreenshots : null,\n screenshots: modernScreenshotEvents.length ? modernScreenshotEvents : null,\n };\n}\n\nexport function deps(): HandlerName[] {\n return ['Meta'];\n}\n"]}