blob: 5f2991c6f14d51d9f75c3415885ac21668f10a16 [file]
# 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 asyncio
import os
import tempfile
from contextlib import ExitStack
from .. import trace
__FCP_HISTOGRAM_NAME = "PageLoad.PaintTiming.NavigationToFirstContentfulPaint"
async def measure_fcp(app, url, trace_file=None, trace_config=None):
"""Measures FCP to navigate to `url` by checking the value recorded into
`PageLoad.PaintTiming.NavigationToFirstContentfulPaint`
See `tools/metrics/histograms/metadata/page/histograms.xml` for more
details about when the metric is recorded.
This function performs a navigation to `url` while recording a trace and
then queries the trace for the histogram value. The navigation starts from
the currently open page.
Args:
app: The chrome.App object with which to start and stop chrome
url: The URL to navigate to.
trace_file: An optional `trace.TraceFile` object to store the trace.
If not provided, a temporary file will be created.
trace_config: An optional path to a Perfetto trace config file.
If not provided, a default config is used.
Returns:
The FCP time in milliseconds.
"""
with ExitStack() as stack:
if trace_config is None:
trace_config = stack.enter_context(
trace.histograms_trace_config(__FCP_HISTOGRAM_NAME))
if not os.path.exists(trace_config):
raise FileNotFoundError(
f"Trace config not found at {trace_config}")
# If no trace file is provided, create a temporary one that will be
# cleaned up upon exiting the context.
if trace_file is None:
temporary_recorded_trace = stack.enter_context(
tempfile.NamedTemporaryFile(mode='w'))
trace_file = trace.TraceFile(temporary_recorded_trace.name)
return await _measure_fcp(app, url, trace_file, trace_config)
async def _measure_fcp(app, url, trace_file, trace_config):
async with trace_file.record(trace_config):
# Allow Perfetto to start recording the trace
await asyncio.sleep(2)
await app.start(url=url)
await asyncio.sleep(10)
df = await trace_file.query(
trace.histogram_values_query(__FCP_HISTOGRAM_NAME))
try:
return int(df.iloc[0, 0])
except IndexError:
raise LookupError