Skip to content

Fix #135: report one candidate per paint#154

Merged
mmocny merged 4 commits intow3c:mainfrom
mmocny:135_one_per_paint
Feb 6, 2026
Merged

Fix #135: report one candidate per paint#154
mmocny merged 4 commits intow3c:mainfrom
mmocny:135_one_per_paint

Conversation

@mmocny
Copy link
Copy Markdown
Contributor

@mmocny mmocny commented Nov 12, 2025

Copy link
Copy Markdown

@shaseley shaseley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet! Mostly LG.

@shaseley shaseley self-assigned this Nov 12, 2025
@mmocny mmocny marked this pull request as ready for review November 13, 2025 03:08
@mmocny
Copy link
Copy Markdown
Contributor Author

mmocny commented Nov 13, 2025

@smfr I cannot add you as an explicit reviewer, but fyi. (Let me know if you would like to be a reviewer for these in the future)

following criteria:

* |candidate|'s [=largest contentful paint candidate/element=]'s opacity is > 0
* |candidate|'s [=largest contentful paint candidate/element=] is a text node, or |candidate|'s [=largest contentful paint candidate/request=]'s
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewers: This wording stays but moves down into the "effective visual size" algo, because that algo calls this algo, and that was circular.

However, I removed the opacity>0 check because there are other similar checks (i.e. visibility true) that paint-timing enforces, and because paint-timing already checks that text nodes are paintable, which implies opacity... And also all these checks should apply consistently to element timing.

Entropy and effective visual size are unique to LCP, though.

1. Let |size| be the [=effective visual size=] of |imageElement| given |intersectionRect| and |record|'s [=pending image record/request=].
1. If |size| is less than or equal to |newCandidateSize|, continue.
1. Set |newCandidateSize| to |size|.
1. Set |newCandidate| to be a new [=largest contentful paint candidate=] with its [=largest contentful paint candidate/element=] set to |imageElement|, its [=largest contentful paint candidate/size=] set to |size|, its [=largest contentful paint candidate/request=] set to |record|'s [=pending image record/request=], and its [=largest contentful paint candidate/loadTime=] set to |record|'s [=pending image record/loadTime=].
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviews: An alternative is to skip the "candidate" struct and just create the LCP entry here, without emitting it until the end of this algorithm for the one largest entry created.

The upside of that would also be that you can more easily do custom image vs text related things.

However, that would require refactoring yet more of the spec and its already a large patch.

1. Intersect |intersectionRect| with the visual viewport.
1. <a>Potentially add a LargestContentfulPaint entry</a> with |candidate|, |intersectionRect|, |paintTimingInfo|, 0, and |document|.
1. Let |intersectionRect| be the union of the border boxes of all {{Text}} <a>nodes</a> in |textNode|'s <a>set of owned text nodes</a>, intersected with the visual viewport.
1. Let |size| be the [=effective visual size=] of |textNode| given |intersectionRect| and null.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing this does for text nodes is remove 100% width/height content, which is mostly only useful for images that act as full page backgrounds.

For 100% full viewport text the only example I can think of is "gaming" where ~invisible text is rendered, but I don't think this is meant to be a protection against that.

We might be able to skip this step, and that simplifies a few things in small ways.

@tunetheweb
Copy link
Copy Markdown
Member

Are we good to land this @mmocny ? Was trying to support the Firefox behaviour in web-vitals but gave up so landing this so they can change would be good!

@mmocny
Copy link
Copy Markdown
Contributor Author

mmocny commented Feb 2, 2026 via email

@tunetheweb
Copy link
Copy Markdown
Member

Ah see we tagged @smfr (for webkit) above but not @Bas-moz (for Mozilla).

Any comments @Bas-moz ?

@Bas-moz
Copy link
Copy Markdown

Bas-moz commented Feb 2, 2026

This change looks good to me.

@smfr
Copy link
Copy Markdown

smfr commented Feb 2, 2026

These changes remove the notion of the "content set" that ensures that a given element is reported only once. This is described by the Note in 4.2, but Notes are not normative, so what normative text describes this?

@mmocny
Copy link
Copy Markdown
Contributor Author

mmocny commented Feb 4, 2026

@smfr Scott has similar question in his review but we realized that the Paint Timing that tracks painted images and which calls into this algorithm already has a "content set" concept.

Specifically, images are only reported exactly once each, when finished loading and text has a set of elements with rendered text.


I do think there are a few more updates that need to be made to that spec (which I think you pointed out last year?) such as what happens if the same loaded Image is attached to multiple different Nodes, or when is the opacity check enforced. But AFAIK those issues equally affect the current wording so this change is a step forward.

WDYT?

@smfr
Copy link
Copy Markdown

smfr commented Feb 4, 2026

Having to refer to another spec for that is not great. Can we at least have a Note that refers to Paint Timing?

@mmocny
Copy link
Copy Markdown
Contributor Author

mmocny commented Feb 4, 2026

Having to refer to another spec for that is not great. Can we at least have a Note that refers to Paint Timing?

The note that I added links to paint-timing/#mark-paint-timing, but I can update however you recommend.

(Honest question, I don't know how to type that in a way that cannot be interpreted sarcastically :P I might not know of a better way to cross reference another spec that you are expecting!)


FWIW, LCP spec already depends on paint timing in a deep way already for its image/text paint notifications, anyway. If it was some weird spaghetti back and forth I would agree with you that it is not great, but I think we're getting to the point of being a fairly clean separation:

  • Paint Timing defines which specific element paints are considered "contentful" (i.e. the first paint after content is loaded & visible to user)
  • Other specs observe this stream of element paints (LCP, but also element and container timing) and filter/map/reduce them.

We did discuss at TPAC if it's more convenient to just merge everything into a single spec (simplify things, and its weird that paint timing defines but doesn't use element-paints itself), but decided it was best to leave most of the complicated algorithms in a single spec (Paint Timing) and the other specs sitting above were narrowly scoped to defining the PerformanceEntry interfaces and the specific value they bring relative to others (i.e. LCP adds "largest area" semantics and element attribution).

We thought this had some minor advantage in terms of documentation, and also better for historical/documentation reasons not to drop having a dedicated LCP spec that devs occasionally expect to find.

Copy link
Copy Markdown

@shaseley shaseley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took another look, LGTM.

@shaseley
Copy link
Copy Markdown

shaseley commented Feb 6, 2026

The note that I added links to paint-timing/#mark-paint-timing, but I can update however you recommend.

(Honest question, I don't know how to type that in a way that cannot be interpreted sarcastically :P I might not know of a better way to cross reference another spec that you are expecting!)

Sounds reasonable and not sarcastic to me :).

@mmocny
Copy link
Copy Markdown
Contributor Author

mmocny commented Feb 6, 2026

Thanks for all the feedback, I'll land this now because Scott is working on a (tentative) WPT update to check this (Bas-- Mozilla will fail initially until fixed).

We can iterate on remaining wording if needed. Cheers!

@mmocny mmocny merged commit ba0fb1c into w3c:main Feb 6, 2026
3 checks passed
beckysiegel pushed a commit to chromium/chromium that referenced this pull request Feb 26, 2026
The spec changes have landed, so removing the tentative qualifier.
w3c/largest-contentful-paint#154.

Bug: 482261053
Change-Id: Ic294a3b32de8952a1f9f56e366232261696522ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7614144
Commit-Queue: Michal Mocny <mmocny@chromium.org>
Reviewed-by: Michal Mocny <mmocny@chromium.org>
Auto-Submit: Scott Haseley <shaseley@chromium.org>
Commit-Queue: Scott Haseley <shaseley@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1591153}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this pull request Feb 27, 2026
The spec changes have landed, so removing the tentative qualifier.
w3c/largest-contentful-paint#154.

Bug: 482261053
Change-Id: Ic294a3b32de8952a1f9f56e366232261696522ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7614144
Commit-Queue: Michal Mocny <mmocny@chromium.org>
Reviewed-by: Michal Mocny <mmocny@chromium.org>
Auto-Submit: Scott Haseley <shaseley@chromium.org>
Commit-Queue: Scott Haseley <shaseley@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1591153}
DanielRyanSmith pushed a commit to web-platform-tests/wpt that referenced this pull request Feb 27, 2026
)

The spec changes have landed, so removing the tentative qualifier.
w3c/largest-contentful-paint#154.

Bug: 482261053
Change-Id: Ic294a3b32de8952a1f9f56e366232261696522ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7614144
Commit-Queue: Michal Mocny <mmocny@chromium.org>
Reviewed-by: Michal Mocny <mmocny@chromium.org>
Auto-Submit: Scott Haseley <shaseley@chromium.org>
Commit-Queue: Scott Haseley <shaseley@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1591153}

Co-authored-by: Scott Haseley <shaseley@chromium.org>
lando-worker bot pushed a commit to mozilla-firefox/firefox that referenced this pull request Mar 10, 2026
…entry-sequence.html test, a=testonly

Automatic update from web-platform-tests
LCP: remove .tentative from performance-entry-sequence.html test (#58114)

The spec changes have landed, so removing the tentative qualifier.
w3c/largest-contentful-paint#154.

Bug: 482261053
Change-Id: Ic294a3b32de8952a1f9f56e366232261696522ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7614144
Commit-Queue: Michal Mocny <mmocny@chromium.org>
Reviewed-by: Michal Mocny <mmocny@chromium.org>
Auto-Submit: Scott Haseley <shaseley@chromium.org>
Commit-Queue: Scott Haseley <shaseley@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1591153}

Co-authored-by: Scott Haseley <shaseley@chromium.org>
--

wpt-commits: c054f5b6a1373de116f87eb1b1f7f844909cb40d
wpt-pr: 58114
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants