Open Bug 1977827 Opened 21 days ago Updated 2 days ago

Multiple LCP entries emitted for no apparent reason

Categories

(Core :: DOM: Performance APIs, defect)

Firefox 140
defect

Tracking

()

People

(Reporter: barry, Unassigned)

Details

Attachments

(1 file)

Attached image firefox-lcp.png

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36

Steps to reproduce:

Load this page: https://www.tunetheweb.com/experiments/firefox-lcp/
Open the console and not LCP candidates
Reload it until you get two LCP candidates logged for the page load. For me (on MacOS) it happens more often than not (see attached screenshot).

Actual results:

You often see two LCP candidates:

LCP candidate: 208 2292 <p>
LCP candidate: 208 4183 <h1>

I don't understand why. You should see one LCP candidate (the H1 element). It is the first element on the page, and the biggest.

In this example both elements had the exact same LCP time (208 milliseconds) but the h1 was larger (4183 pixels, as opposed to 2292). I would expect both to be rendered at the exact same time on such a simple page, but if anything the H1 should be rendered first. So, given that, there is never a time that the <p> element should be the LCP candidate. Yet it frequently reports both. Why?

It's also not consistent since frequently ONLY the H1 is reported as LCP.

Expected results:

You should see one LCP candidate (the H1 element). It is the first element on the page, and the biggest.

Component: Untriaged → DOM: Performance APIs
Product: Firefox → Core

The severity field is not set for this bug.
:bas.schouten, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(bas)

I can confirm this is happening for me on Windows as well. I'll have a look.

Severity: -- → S3
Status: UNCONFIRMED → NEW
Ever confirmed: true
Flags: needinfo?(bas)

As far as I can tell, this is simply how the spec is written.

https://www.w3.org/TR/largest-contentful-paint/#sec-report-largest-contentful-paint

As far as I can tell, this indicates the entries should be processed in order, and there's nothing in there about there only being one entry per paint. The order in which we paint these two elements seems to be non-deterministic. They do occur consistently in the same paint for me.

As far as I can tell, this means we're behaving as specified, which I suppose leaves two questions:

  1. Did I perhaps miss something in the spec?
  2. Are we unhappy with the spec'ed behavior and should we always only report the largest entry in a single paint?

I think you are right about the spec. Here is my read:

  • Paint Timing Spec defines "mark paint timing" called from HTML "update the rendering"
  • Per spec, we collect a set of ALL painted image/text nodes that paint.
  • We create a callback in step 10 to flushPaintTimings. (This is optionally called synchronously, or in parallel after presentation time is available)
  • flushPaintTimings calls "report largest contentful paint" with the list of image/text candidates.
  • "report largest contentful paint", will iterate the list (in arbitrary order) and create entries.

I would support changing "report largest contentful paint" to find the single largest candidate in the set and report that one.

In Chromium, although we do maintain the full list of image/text paint candidates in each rendering update, we actually also have the LCP implementation maintain a singleton reference to the single largest image/text candidate, and it will get overwritten with each new candidate paint, and only that one value is collected during mark paint timing.

We currently do within each paint, and thus before "mark paint timing", and when the LCP algorithm is invoked we already only have the single largest reference. However, I think it would make sense to align with the spec even in our own implementation, since right now e.g. two paints before next presentation is available might overwrite the largest candidate and we would skip reporting an entry. (arguably that doesn't matter as the new largest candidate will get reported soon-- but per spec it should be reported.)


If course, a PerformanceObserver might always be called with many paint timing's worth of entries, anyway. So another option is just to leave it ambiguous and recommend that library authors simple call: list.getEntries().at(-1) if they only care about the last largest.

If you are sending fetch beacons with each new candidate, even reporting a single candidate per animation frame will not prevent network races. You might want to use fetchLater or unload reporting or just sort candidates server-side.

Personally I’d prefer a “less chatty” observation of LCP candidates and I find the Firefox implementation a bit overly chatty with no real extra benefits. While later LCP candidates are useful (to not have to wait until document unload to get an LCP candidate, and also to see the progress of a page load) multiple candidates within a single paint (or even a single presentation time) is not that useful IMHO and just “noise”.

Let’s raise a spec issue and discuss in next Webperf WG? Out at the moment but happy to raise it on my return as the original reporter for this issue, unless one do you gets there first.

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: