Skip to content

T130: non-integer order from calc() rounds half toward +infinity and clamps to int, as Chrome does - #61

Merged
thejackshelton merged 61 commits into
masterfrom
order-calc-round
Oct 3, 2026
Merged

thejackshelton merged 61 commits into
masterfrom
order-calc-round

Conversation

@thejackshelton

@thejackshelton thejackshelton commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

What changed

T130: a non-integer order from a math function now rounds as Chrome does. Blink's CSSMathFunctionValue::ComputeInteger (css_math_function_value.cc 89-97, range kInteger) uses RoundHalfTowardsPositiveInfinity: floor(v + 0.5) in double (math_extras.h 133-139), then ClampToWithNaNTo0. All sources are at 145.0.7632.6 (BSD). Chrome 145 getComputedStyle agrees on every probed value: calc(-1.5) gives -1, calc(0.5) gives 1, calc(0.49999999999999994) gives 1, and calc(3e9) and a 3000000000 literal both give 2147483647.

  • Engine:
    • units.ts adds orderInteger.
    • The environment pass (environment.ts) resolves order with it at every DPR.
    • validate.ts accepts any finite order, because the compiler sends a folded number calculation unrounded. NaN and infinities are still rejected.
    • Planted faults orderHalfEven and orderUnclamped (block.ts, translate harness.ts).
  • Compiler:
    • css/values.ts no longer refuses a non-integer order calculation.
    • emit/web-css.ts writes it as calc(<n>), so Chrome rounds it the same way the engine does.
    • An infinite or NaN order calculation is still refused, with its reason.
  • New fixture values-order-calc-round (ltr and rtl): 25 flex rows of [r, X, r] covering halves of both signs, values beyond the int range, nested min/max/clamp, and var(). Each plant fails it.
  • Regenerated outputs are in their own commits (871c1f5 and e6d86f8, which name the commands). Every existing capture and vector is byte-identical.

What passed

Caught up with master to the train 1 freeze base 1f17b6e (#64 SIZE-ar, #65, #66 INL1a breaks, #67 REPL-a engine, #68 T116):

  • block.ts, harness.ts and their fault lists keep master's four line-break faults and T130's two order faults; vectors/README.md takes master's 42-field line and aspectRatio entry with T130's order sentence.
  • environment.ts takes master's styleNeedsEnvironment and keeps T130's order check, now also for a replaced child: REPL-a's replaced leaves carry order, and without the check a replaced flex item's order 1.5 reached the engine unrounded at DPR 1. calc.test.ts pins it (it fails without the replaced check: [10, 0] instead of [0, 20]).

Stacked on CALC-2 (#60, calc2-contexts e6e6b7f, itself on T128) for the train, with the PM ruling's retarget: values-reject-order-fraction (CALC-2's pin of the refusal of order: calc(1.5), which T130 makes accepted) now pins order: calc(1 / 0), still refused with "it divides by zero or produces an infinite or NaN value" (the compiled diagnostic matches the spec's span and message prefix). The intent is kept: an order calculation Dragon still refuses. fixture-groups/values.ts and units.test.ts keep both sides.

docs/ports.json registers T130's citations, per the PM ruling: css_math_function_value.cc lists calc.test.ts as a reference, and a new entry math_extras.h (bsd-apple, header-bsd-2, lines 133-139) is referenced by calc.test.ts. /tmp/heavy-lease.sh pnpm regen rebuilt the generated outputs in their own commit (ebc17b9, fixed point after 3 passes).

On ebc17b9:

  • pnpm typecheck: pass.
  • Targeted: engine.test.ts, vectors.test.ts, calc.test.ts, units.test.ts, values.test.ts, chrome-ports.test.ts, regen.test.ts: 687 pass, 1 skipped (the DRAGON_REGEN_CHECK suite).
  • /tmp/heavy-lease.sh pnpm test on ebc17b9: queued behind the other members' regens; the result follows in a comment.
  • From the branch's own regen: engine and Chrome agree on 102/102 nodes at every DPR, in both directions; both plants are caught; the Swift and Kotlin translated engines agree; breaks: the engine equals Chrome on 1386/1386.
  • Pre-PR audit (AGENTS.md step 3) of every changed source and test file: no instance found.

Train 1 (freeze base f78f198 / 1f17b6e)

This PR is a member of merge train 1 (AGENTS.md step 8), in this order: #59 T128, #60 CALC-2, #61 T130, T129, seld-r1. The train merges each member onto the previous position, so members that touch the same lines are stacked, with their overlaps resolved and reviewed in the member branches rather than in the train: CALC-2 merges T128, T130 merges CALC-2, and T129 merges T130. Until an earlier member lands, GitHub shows its changes in this diff too. seld-r1 sits on master alone; its merges onto the earlier positions were simulated clean, and every position's reviewed-path patch id equals its member's clean head (git merge-tree chain plus patchIdOver), so each position should be vouched at landing.

The device lanes, and the regen of each position, run in the train itself: one device run per position. Master now has T116 (#68), so device-pixels is iOS 57 / Android 86 on master; each position's device run is compared against that.

Changed tests and checks

  • The validator's order rule changes from integer to any finite number. The reason: the engine now owns the rounding, as the PM ruling requires (the compiler may not round). calc.test.ts pins that NaN is still rejected.
  • New tests:
    • calc.test.ts: orderInteger against 21 Chrome rows, the plants, and document order for ties.
    • units.test.ts: the compiler sends the value unrounded, web CSS keeps calc(), a literal order: 1.5 is still invalid, and infinite or NaN stays refused.
    • values.test.ts: the plants and the getComputedStyle order of all 150 items.
  • values-reject-order-fraction (from CALC-2) is retargeted from calc(1.5) to calc(1 / 0), as above.
  • No tolerance is loosened and no check is removed.

🤖 Generated with Claude Code

Note

Add Chrome-compatible rounding and clamping for non-integer order from calc()

  • Non-integer order values from calc() are now passed through unrounded and rounded by the engine: half ties round toward positive infinity, then values clamp to the signed integer range, matching Blink's ComputeInteger (see units.orderInteger in units.ts).
  • Style validation no longer rejects fractional order values; the environment pass resolves them via the new environment.resolveOrder in environment.ts.
  • Two fault switches were added (orderHalfEven, orderUnclamped) in block.ts, wired into the translate harness and generated Swift/Kotlin output.
  • Also fixes border shorthand parsing: calc()/min()/max()/clamp() widths in physical and logical border shorthands are now parsed as border-width values instead of being rejected (values.ts, border.ts, logical.ts).
  • Adds parity fixtures and vectors for these cases, and emits non-integer order values to web CSS as calc(...) declarations in web-css.ts.
  • Behavioral Change: EngineFaults in the generated Swift/Kotlin Block files gained two new fields; out-of-tree consumers of these generated types must be updated.

Macroscope summarized ebc17b9.

…xt px is proven in, and in the border shorthands

css/values.ts: a math function in border, border-<side>, border-block(-start/-end) and border-inline(-start/-end) is the <line-width>, typed and refused as the border-*-width longhands are (css-backgrounds-3 §3.1, css-logical-1 §6.3); shorthands/border.ts and logical.ts assign it to the width.

Fixtures (values group, ltr and rtl): values-calc-contexts-block, -flex, -positioned, -display-none, -root, -root-min-max put each of calc(), min(), max() and clamp() in every box property in the block, flex-row/column, relative, absolute (cb-ltr and cb-rtl), display-none and root contexts, and gaps in every flex line mode and on a non-flex container, plus px, rem and % insets, flex-basis and gaps on a static block; values-calc-border-shorthand; values-calc-length-max, the CSS_LENGTH_MAX golden (T118J): Chrome collapses float(33554429) with -33554430 to -2 device px at every DPR. values-reject-order-fraction pins the order refusal.

Retargets: values-reject-border-shorthand moves from calc(1px + 0.5vw), now accepted, to calc(1px * 2px / 1px) solid #000, still refused (typed arithmetic), refused on border-left-width. values.test.ts blinkNumber writes printf %g (3.35544e+07), which the new computed values need.
…ture, then pnpm run profile:rows and pnpm run parity:capture three times to the fixed point, then pnpm run profile:rows; pnpm run parity:dpr-capture; pnpm run layout:vectors; pnpm run layout:dpr-vectors; pnpm run layout:break-vectors; pnpm run parity:break-capture; pnpm run parity:pixel-capture; pnpm run native:gen; node --conditions=dragon-internal examples/music-player/tools/check.ts (pnpm run north-star:check lacks the condition, T124); pnpm run parity:glyph-b3 -- --write-bottom-pins; node --conditions=dragon-internal packages/parity/src/cli/media-sweep.ts (then --check: 18/18, 286 samples); pnpm run wpt:run -- --target web and pnpm run wpt:update-expectations -- --target web (no change); pnpm run tw:sweep. Profiles: 1922 rows added on each target (web and ios exact, android unsupported), none removed or changed. Tailwind 4.3.3: web and ios 2214 -> 4121 supported, none the other way, 0 mismatch
…nes -- --run-host) for the CALC-2 cases; device lanes not run (device step pending, landing-queue rule)
…h its own height treated as auto (T128)

css-flexbox-1 §4.5 content size suggestion; ported from Blink third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc at 145.0.7632.6 (BSD, The Chromium Authors; sha256 b0ea68654c62d2ca1d73d801ffca395901aa37e51569281266fc9ef87a9e892c) lines 914-923 and 1117-1120 (LayoutResult::IntrinsicBlockSize). A percentage height child of such an item is refused as percent-height-flex, since Blink resolves it against the item's set height.
…ontainer shorter than its items, px and calc() heights, empty and non-empty content, ltr and rtl; Chrome captures from pnpm run parity:capture and pnpm run parity:dpr-capture
…ize (flex_layout_algorithm.cc lines 1078-1084 at 145.0.7632.6): an item that cannot shrink takes its specified size without measuring its content, which gives the same used size and avoids 4 percent-height-flex refusals
…ure values-calc-flex-column-auto-min): pnpm run grammar:gen; pnpm run parity:capture and pnpm run profile:rows twice (fixed point); pnpm run parity:dpr-capture; pnpm run layout:vectors; pnpm run layout:dpr-vectors; pnpm run layout:break-vectors; pnpm run parity:break-capture; pnpm run parity:pixel-capture; pnpm run native:gen; node --conditions=dragon-internal examples/music-player/tools/check.ts; pnpm run tw:sweep; pnpm run wpt:run -- --target web; pnpm run wpt:update-expectations -- --target web; pnpm run parity:glyph-b3 -- --write-bottom-pins; node --conditions=dragon-internal packages/parity/src/cli/media-sweep.ts (then --check); pnpm run parity:lanes -- --run-host. Every existing vector and capture is byte-identical; emitted CSS changes only in the compilation-digest header; the new fixture adds its vectors, captures, pixel and break records; corpus.json and corpus-dpr.json carry the new engine digests
…ne as Chrome does

Blink CSSMathFunctionValue::ComputeInteger (css_math_function_value.cc 89-97, range kInteger) is RoundHalfTowardsPositiveInfinity, floor(v + 0.5) in double (math_extras.h 133-139), then ClampToWithNaNTo0<int>; Chrome 145 getComputedStyle agrees on every probed value (calc(-1.5) is -1, calc(0.5) 1, calc(0.49999999999999994) 1, calc(3e9) and a 3000000000 literal 2147483647).

Engine: units.ts orderInteger; the environment pass (environment.ts) resolves order with it, at every DPR; validate.ts takes any finite order (the compiler sends a folded number calculation unrounded); planted faults orderHalfEven and orderUnclamped (block.ts, harness.ts).
Compiler: css/values.ts no longer refuses a non-integer order calculation; emit/web-css.ts writes it as calc(<n>) so Chrome rounds it as the engine does.
Fixture values-order-calc-round (ltr and rtl): 25 flex rows of [r, X, r], halves of both signs, values beyond the int range, nested min/max/clamp and var(); each plant fails it. Tests: calc.test.ts (engine), units.test.ts (compiler), values.test.ts (plants and getComputedStyle order of all 150 items).
…the translator requires it); an infinite or NaN order calculation stays refused with its reason (units.test.ts)
…e, identical), parity:dpr-capture, layout:vectors, layout:dpr-vectors, native:gen, north-star:check (unchanged), parity:lanes -- --run-host

New files only for values-order-calc-round(-rtl): Chrome captures at DPR 1, 2, 3 and 2.625, emitted CSS, vectors and snap vectors; every existing capture and vector is byte-identical. Profile rows gain the case as a proof (no status changes). The translated engines and harnesses carry orderInteger and the two order faults; corpus.json and corpus-dpr.json move with the new fault keys and vectors; lanes.json is host-only (device lanes not run).
…s carry the new compilation digest; bodies and captures unchanged), layout:break-vectors, parity:break-capture (engine equals Chrome breaks 1386/1386), parity:pixel-capture, parity:lanes -- --run-host (lanes.json unchanged)

New files only for values-order-calc-round(-rtl) in break-vectors, expected-breaks and expected-pixels (manifest appended).
thejackshelton added a commit that referenced this pull request Oct 1, 2026
…in amendment 1 (catch up before review: GitHub can't use the merge driver)
…catch up before review): no source conflicts; generated outputs keep this side (master's .gitattributes merge=dragon-generated) and pnpm regen rebuilds them in the next commit
…1: catch up before review): no source conflicts; generated outputs keep this side (master's .gitattributes merge=dragon-generated) and pnpm regen rebuilds them in the next commit
… catch up before review): no source conflicts; generated outputs keep this side (master's .gitattributes merge=dragon-generated) and pnpm regen rebuilds them in the next commit
@macroscopeapp

macroscopeapp Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — Although many changed files are generated parity artifacts, the source changes modify CSS acceptance/emission and existing flex and order layout behavior across production paths. The new intrinsic measurement logic and cross-target generated engine changes make this broader than a small, self-contained compatibility fix.

You can add or adjust custom eligibility rules. Learn more.

 now in the base): css_math_function_value.cc lists packages/layout/test/calc.test.ts as a reference; new entry platform/wtf/math_extras.h (bsd-apple, header-bsd-2, tag 145.0.7632.6 sha256 c71bc62c..., lines 133-139 RoundHalfTowardsPositiveInfinity) referenced by calc.test.ts. THIRD_PARTY_NOTICES.md is rebuilt by pnpm regen next
…npm regen: fixed point after 3 passes, 34 steps run): emitted CSS (compilation digests), the translated Swift and Kotlin engines and harnesses, corpus.json and corpus-dpr.json, the values-calc-flex-column-auto-min vectors, profiles and expected-fonts, and the host-only lanes.json (device lanes not run; the train's device run supplies them)
… buildItem (the flex-shrink 0 shortcut, lines 1078-1084) and columnIntrinsicBlockSize (IntrinsicBlockSize, 914-923 and 1117-1120), now that PORT-0 (#52) is in the base; chrome-ports.test.ts 13/13
…tHub cannot apply the dragon-generated merge driver, so the PR was conflicting on generated files; no source conflicts; generated outputs keep this side and pnpm regen rebuilds them next
…s again, before the first regen: no source conflicts; generated outputs keep this side (merge=dragon-generated) and pnpm regen rebuilds them next
…und again, before the first regen: no source conflicts; generated outputs keep this side (merge=dragon-generated) and pnpm regen rebuilds them next
…npm regen: fixed point after 2 passes, 19 steps run): the translated engines and harnesses with master's EMS-b paint roots, corpus files and the host-only lanes.json (device lanes not run; the train's device run supplies them)
…y-lease.sh pnpm regen: fixed point after 3 passes, 36 steps run): emitted CSS digests, the translated engines and harnesses, corpus files, profiles, tailwind snapshot and the host-only lanes.json (device lanes not run; the train's device run supplies them)
…y-lease.sh pnpm regen: fixed point after 2 passes, 19 steps run): THIRD_PARTY_NOTICES.md with math_extras.h, emitted CSS digests, the translated engines and harnesses, corpus files and the host-only lanes.json (device lanes not run; the train's device run supplies them)
…-round: no source conflicts; generated outputs keep this side (merge=dragon-generated) and pnpm regen rebuilds them next
…npm regen: fixed point after 3 passes, 36 steps run): emitted CSS digests, profiles, the translated engines and harnesses, corpus files and the host-only lanes.json (device lanes not run; the train's device run supplies them)
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 2, 2026
…REPL-a engine and board) into flex-auto-min for train 1: flex.ts buildItem keeps master's ratio and replaced-item branches and T128's column suggestion for a sized box item (columnIntrinsicBlockSize; a replaced item keeps contentMain); percentMainHeight takes master's LayoutNode; docs/ports.json flex_layout_algorithm.cc lists master's ratio.ts port and T128's two flex.ts ports; generated outputs keep this side and pnpm regen rebuilds them next
…icts; generated outputs keep this side and pnpm regen rebuilds them
…pm regen: fixed point after 3 passes, 38 steps run, 3466.8 s): host-only lanes.json (device lanes run in the train)
…pm regen: fixed point after 3 passes, 38 steps run, 3287.8 s): host-only lanes.json (device lanes run in the train)
thejackshelton and others added 20 commits October 2, 2026 17:56
Commands: pnpm regen; pnpm run parity:devices; pnpm regen
…ng stop (LAND-3 ruling); PNT1 split; FORM-a restack
- train build judges each position's device run against the device evidence committed on the train's base (lanes.json and device-failures-<target>.json), not by the lanes command's exit code: every lane must pass or fail as on the base, a failing lane may list no failure the base does not, and lane parity must pass with no stale lane (deviceRunProblems). It also checks the run rewrote lanes.json and the lanes commands exited 0 or 1.
- train build runs pnpm regen again after the device run (POSITION_STEPS), so native-lanes.ts follows the run's lanes.json in the position's regen commit.
- pr:review vouches for Macroscope's 'No code objects were reviewed.' skip under the 'already reviewed' guard: the head's patch id over reviewed paths must equal an earlier reviewed commit's.
- A member whose PR head is a position from an earlier build is merged at that head (memberTip walks its train-made commits down to the clean head), so land's push fast-forwards it; check still predicts against the clean head's patch id.
- land stops on a review/* base with the exact 'gh pr edit <n> --base master'.
…review passes a commit whose every Macroscope check was skipped with exactly 'Monthly spending limit reached (workspace setting).', prints UNREVIEWED, and still requires CI to pass and every finding to be answered (reviewExit); a near-miss title or a mixed result fails. AGENTS.md step 4 records the rule. pr-review.test.ts pins each case; the outcome pins gain unreviewed: false
LAND-3: merge-train fixes from train 1 (device run judged on evidence, regen after devices, no-code skip vouch, rebuild on an earlier position)
…ication time against the run's start, not by a content change: train 1's rebuilt position 1 merged #59's earlier position, whose lanes.json already held this tree's device run, so the new run wrote identical bytes and the build stopped with 'the device run did not rewrite'. deviceRunWrote is pinned in merge-train.test.ts
LAND-3b: train build tells the device run wrote lanes.json by its file time, not a content change
Commands: pnpm regen; pnpm run parity:devices; pnpm regen
T128: column flex automatic minimum size measures content with the item height as auto (Blink IntrinsicBlockSize)
Commands: pnpm regen; pnpm run parity:devices; pnpm regen
Commands: pnpm regen; pnpm run parity:devices; pnpm regen
@thejackshelton
thejackshelton merged commit ac2e13e into master Oct 3, 2026
4 checks passed
@thejackshelton
thejackshelton deleted the order-calc-round branch October 3, 2026 16:36
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