commit 0b1b8ae99ebb40fd76e3e94ab3e6d7f3ae9b8d1d
parent c17df0353b5a9fb13589a0eead18b30185348094
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date: Wed, 8 Oct 2025 15:18:17 +0000
Bug 1991587 - Use consistent timestamp for FCP and LCP entries r=jlink,smaug
FCP and LCP were using different timestamps for the same paint. FCP used
`RefreshDriver()->MostRecentRefresh()` (tick start time) while LCP used
`TimeStamp::Now()` captured in the function, causing LCP to be reported
differently compared to FCP.
Both metrics now capture `TimeStamp::Now()` once and share it. This matches
the Paint Timing spec which says to capture "rendering update end time"
when mark paint timing runs, not reuse the tick start timestamp:
https://w3c.github.io/paint-timing/#mark-paint-timing
Differential Revision: https://phabricator.services.mozilla.com/D267757
Diffstat:
4 files changed, 1 insertion(+), 67 deletions(-)
diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
@@ -2760,7 +2760,7 @@ void nsPresContext::NotifyContentfulPaint() {
"We should only notify contentful paint during refresh "
"driver ticks");
if (!perf->HadFCPTimingEntry()) {
- TimeStamp nowTime = rootPresContext->RefreshDriver()->MostRecentRefresh();
+ TimeStamp nowTime = mMarkPaintTimingStart;
MOZ_ASSERT(!nowTime.IsNull(),
"Most recent refresh timestamp should exist since we are in "
"a refresh driver tick");
diff --git a/testing/web-platform/meta/largest-contentful-paint/first-paint-equals-lcp-text.html.ini b/testing/web-platform/meta/largest-contentful-paint/first-paint-equals-lcp-text.html.ini
@@ -1,5 +0,0 @@
-[first-paint-equals-lcp-text.html]
- expected:
- if (os == "android") and fission: [OK, TIMEOUT]
- [FCP and LCP are the same when there is a single text element in the page.]
- expected: FAIL
diff --git a/testing/web-platform/meta/paint-timing/fcp-only/fcp-ensure-update-the-rendering-step.html.ini b/testing/web-platform/meta/paint-timing/fcp-only/fcp-ensure-update-the-rendering-step.html.ini
@@ -1,6 +0,0 @@
-[fcp-ensure-update-the-rendering-step.html]
- expected:
- if (os == "android") and fission: [OK, TIMEOUT]
- [The first-contentful-paint timestamp should be same as the last RAF]
- expected:
- if (os == "android") and not debug: [PASS, FAIL]
diff --git a/testing/web-platform/tests/paint-timing/fcp-only/fcp-ensure-update-the-rendering-step.html b/testing/web-platform/tests/paint-timing/fcp-only/fcp-ensure-update-the-rendering-step.html
@@ -1,55 +0,0 @@
-<!DOCTYPE html>
-<head>
- <title>
- Ensure the timing is marked during the `update the rendering` step.
- </title>
-</head>
-<style>
- #main {
- width: 100px;
- height: 100px;
- left: 0px;
- position: relative;
- top: 0;
- background-image: url(../resources/circles.png);
- opacity: 0;
- }
-
- #main.contentful {
- opacity: 0.1;
- }
-</style>
-<body>
-<script src="../resources/utils.js"></script>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="main"></div>
-<script>
-setup({"hide_test_state": true});
-async_test(function (t) {
- assert_implements(window.PerformancePaintTiming, "Paint Timing isn't supported.");
- let fired = false;
- const main = document.getElementById('main');
- let animationFrameStamps = [];
- requestAnimationFrame(function frame(stamp) {
- animationFrameStamps.unshift(stamp);
- main.className = "contentful";
- while (performance.now() - stamp <= 5) {
- /* Busy-wait */
- }
- if(!fired)
- requestAnimationFrame(frame);
- });
- new PerformanceObserver(t.step_func(list=>{
- for (let entry of list.getEntries()) {
- if (entry.name == "first-contentful-paint") {
- fired = true;
- assert_any(assert_approx_equals, entry.startTime, animationFrameStamps, 1, "One of the past requestAnimationFrame should have the same timestamp as paint entry");
- t.done();
- }
- }
- })).observe({type: "paint"});
-}, 'The first-contentful-paint timestamp should be same as the last RAF');
-</script>
-</body>
-</html>