Skip to content

Grid: skip contribution measurement when no spanned track can receive it - #1177

Merged
nicoburns merged 2 commits into
mainfrom
devin/1788439473-grid-skip-unaffected-contributions
Sep 3, 2026
Merged

Grid: skip contribution measurement when no spanned track can receive it#1177
nicoburns merged 2 commits into
mainfrom
devin/1788439473-grid-skip-unaffected-contributions

Conversation

@nicoburns

@nicoburns nicoburns commented Sep 3, 2026

Copy link
Copy Markdown
Member

Objective

In resolve_intrinsic_track_sizes, the loops for "2. content-based minimums", "3. max-content minimums" (both the max-content-constraint variant and the unconditional one), "5. intrinsic maximums" and "6. max-content maximums" call item_sizer.min_content_contribution / max_content_contribution for every item in the batch, and only afterwards filter tracks via the track_is_affected predicate inside distribute_item_space_to_*. Only step 1 (minimum_contribution) is gated on the spanned tracks.

So an item spanning only tracks the step cannot apply to (e.g. 12.25rem minmax(0, 1fr)) still gets measured under a min-content constraint. That measurement is a full subtree measure and is then discarded. On Wikipedia's Vector skin (.mw-page-container-inner { grid-template-columns: 12.25rem minmax(0, 1fr) }) this measures the entire article under MinContent, inside which .mw-body's minmax(0, 59.25rem) column legitimately collapses to 0, so every inline layout in the article is line-broken at width 0 (one word per line) for nothing.

This PR moves the same predicate in front of the measurement:

for item in batch.iter_mut() {
    if !item.spans_track_matching(axis, axis_tracks, has_min_or_max_content_min_track_sizing_function) {
        continue;
    }
    let space = item_sizer.min_content_contribution(item, axis_tracks);
    ...
}

Track sizing results are unchanged (the distribution step would have been a no-op for these items). All generated fixtures pass. New test grid_item_spanning_only_unaffected_tracks_is_not_measured: 3 measurements on main → 1 (the final layout) with this change.

Measured on a Blitz headless load of the Barack Obama Wikipedia article: zero-width break_lines passes 916 → 3, first layout 845–862 ms → 592–626 ms, relayout 737–758 ms → 473–502 ms, live heap after layout 88.6 MB → 63.0 MB, layout geometry unchanged.

Second commit: expand_flexible_tracks max-content measurement

Skipping the step-2/3 measurements exposed a latent bug: expand_flexible_tracks (11.7, indefinite free space) measured each item's max-content contribution with Size::NONE for both grid area and available space (there was a TODO about it). max_content_contribution_cached ignores the sizes passed to it, so previously the cache was always pre-filled by steps 2/3 (which measure with the real grid-area size) and the Size::NONE never took effect. With the skip, the flex step became the first measurer for items spanning e.g. 50px / minmax(10px, 1fr), text was measured at an unconstrained width, and 1fr rows came out one line tall — WPT css/css-grid/grid-definition/flex-content-resolution-rows-002.html went 21/21 → 18/21 in Blitz.

Fix: route the flex step through IntrinsicSizeMeasurer::max_content_contribution like every other contribution step (this also includes the item's margins, as the other steps do). New Chrome-generated fixture grid_fr_row_max_content_uses_column_width (fails on main + first commit alone, passes now). WPT css-grid/css-flexbox/CSS2 in Blitz: no regressions vs Blitz main, +2 newly passing.

Context

Blink does the same: GridTrackSizingAlgorithm::IncreaseTrackSizesToAccommodateGridItems collects sets_to_grow via IsContributionAppliedToSet and continues if empty before calling contribution_size(...); additionally ResolveIntrinsicTrackSizes only considers items with IsSpanningIntrinsicTrack. Spec: css-grid-2 §12.5 scopes each step to "items spanning tracks with a min/max track sizing function of …".

Per-step predicates used:

  • step 2: min sizing fn is min-content/max-content
  • step 3 (max-content constraint): min sizing fn is auto (and max not min-content) or max-content
  • max-content minimums (all cases): min sizing fn is max-content
  • step 5: max sizing fn has no definite value
  • step 6: max sizing fn is max-content-alike (or percentage with indefinite container)

Related: DioxusLabs/blitz#835 repins Taffy to this branch and clamps negative inline available widths (a downstream symptom of the same discarded pass).

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/3a0b8cc210c041d8955c841fb2a76a8e
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/3a0b8cc210c041d8955c841fb2a76a8e?variant=devin-insiders
Requested by: @nicoburns

In the intrinsic track sizing steps for content-based minimums, max-content
minimums, intrinsic maximums and max-content maximums, an item's min-/max-
content contribution was measured for every item in the batch, and only then
filtered by the track predicate inside distribute_item_space_*. Items spanning
no track that the step applies to (e.g. only minmax(0, 1fr) or fixed tracks)
therefore got measured under a min-content constraint for nothing. Blink checks
the affected sets before computing the contribution; do the same.
@staging-devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

… grid area size

expand_flexible_tracks measured max-content with Size::NONE. This was masked
because the contribution cache ignores the passed sizes and steps 2/3 had
already measured every item with the real grid-area size. Now that those
measurements are skipped for items spanning no affected track, the
flexible-track step became the first measurer and text wrapped at an
infinite width (WPT flex-content-resolution-rows-002). Route it through
IntrinsicSizeMeasurer like the other contribution steps.
@nicoburns
nicoburns merged commit 117195c into main Sep 3, 2026
28 checks passed
@nicoburns
nicoburns deleted the devin/1788439473-grid-skip-unaffected-contributions branch September 3, 2026 14:22
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.

1 participant