Skip to content

T116: same-colour border corners painted solid via Blink's side painter (device-pixels iOS 65→57, Android 94→86) - #68

Merged
thejackshelton merged 22 commits into
masterfrom
t116-border-join-v2
Oct 2, 2026
Merged

thejackshelton merged 22 commits into
masterfrom
t116-border-join-v2

Conversation

@thejackshelton

@thejackshelton thejackshelton commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

T116: the border join. Where two same-colour sides meet, native now paints the shared corner pixel solid, as Chrome does, instead of two anti-aliased diagonal halves (75% coverage). It also closes T144, which was the same bug.

Device-pixels delta (pnpm run parity:devices on master 7c5419f plus T116)

Case iOS Android
position-absolute-out-of-flow (T144) 2 → 0 2 → 0
position-absolute-out-of-flow-rtl (T144) 2 → 0 2 → 0
sizing-ratio-abspos (SIZE-ar's recorded corner) 2 → 0 2 → 0
sizing-ratio-abspos-rtl 2 → 0 2 → 0
Total device-pixels failures 65 → 57 94 → 86
  • The new border-join fixture passes on both targets.
  • Every lane is in master's state, and lane parity passes.
  • The T075J cycle proof holds: after the device run, profile:rows leaves the profiles and lanes.json unchanged, with nothing stale.

What changed

  • Solid-only boxes use the side painter. A box whose visible sides are all solid now goes through the translated Blink side painter (paint-dash.ts borderNeedsSidePainter). Same-colour sides meet with no miter (ComputeMiter). A box with a visible double side and no dashed or dotted side keeps the band painter.
  • The border-join fixture covers same-colour corners, mixed colours, uneven and fractional widths, and translucent borders.
  • p6a-dash-oracle.test is widened. It now compares every crisp border pixel of every case with a solid side against Chrome, at DPR 2, 3 and 2.625.
    • It skips pixels outside Chrome's raster and under glyphs.
    • It proves those skips drop no pixel that the pre-T116 comparison checked.
  • The oracle's occluder rule follows CSS2 Appendix E paint order. Positioned boxes and their descendants paint over in-flow boxes, in tree order within each group. This corrects the tree-order rule from P6a-dash: dashed and dotted borders as Blink 145 paints them (device-pixels iOS 135→61, Android 182→90) #63 round 1, and it is pinned on position-relative-percent.
  • The dash vector inputs gain solid-only style sets.

What passed

  • pnpm typecheck.
  • pnpm test: 160 files; 3514 pass and 1 is skipped (the opt-in pnpm regen --check).
  • pnpm regen at a fixed point.

No profile row is added or removed, and no status changes. No tolerance, check or test is loosened or removed. The reviewed diff is 25.5 KB in 6 files.

🤖 Generated with Claude Code

Note

Route same-colour solid border corners through the side painter (iOS 65→57, Android 94→86 device-pixel failures)

  • borderNeedsSidePainter in paint-dash.ts now selects the side painter when at least one border side is visible and all visible sides are solid, in addition to the existing dashed/dotted route. Visible double borders without dashed/dotted sides stay on the band painter.
  • Adds the border-join parity fixture in border-join.html with eight border cases, plus generated expected pixels, DPR snapshots, and expected-breaks at DPR 1, 2, 2.625, and 3. The border-join case is registered in the BORDER_PAINT fixture group.
  • Reworks p6a-dash-oracle.test.ts with paint-order and positioned-layer occlusion rules, text-overlap predicates, and overflow-clip handling; the oracle now compares solid border cases and keeps a pre-T116 mode. A subset test ensures no previously checked pixel is dropped.
  • Updates the webProfile, iosProfile, and androidProfile support records to associate border-join with border-color features, and lowers the recorded iOS device-pixel failures from 65 to 57 and Android from 94 to 86 in native-lanes.ts.
  • Behavioral Change: solid borders now paint via the side painter (four plain side fills, no-miter join for same colour, anti-aliased trapezoid fill for differing colours) instead of the band painter; mixed non-solid visible styles remain unchanged. Check paintDash_borderNeedsSidePainter in PaintDash.swift and PaintDash.kt.

Macroscope summarized 16234f6.

…our corners are solid as in Chrome (rebuilt on p6a-promote-v2)

T116: solid-only borders go through Blink's side painter (paint-dash.ts), so same-colour sides meet with no miter (ComputeMiter) and the corner pixel is solid as in Chrome, not two anti-aliased diagonal halves; boxes with a visible double side and no dashed or dotted one keep the band painter. border-join fixture (solid corners: same colour, mixed colours, uneven and fractional widths, translucent); p6a-dash-oracle.test.ts compares every crisp border pixel of every case with a solid side against Chrome (pixels past the raster and under glyphs skipped) and pins border-join's crisp corners; dash vector inputs gain solid-only style sets

p6a-dash-oracle.test.ts: prove the T116 raster-bounds and glyph skips drop no pixel the pre-T116 comparison checked (dashed and dotted boxes only, no skips): for every dashed or dotted case at every DPR the pre-T116 checked pixels are a subset of the current ones

Rebuilt from t116-border-join 2e300022 (8e9d0a71, 17dc8b28) on the split p6a stack: the same source changes, outputs regenerated in the next commit.
…committed sources, then pnpm run profile:rows: env DRAGON_WRITE_DASH_INPUTS=1 npx vitest run packages/layout/test/paint-dash.test.ts; grammar:gen, ua:capture, parity:capture, profile:rows, parity:capture, profile:rows, layout:vectors, parity:dpr-capture, layout:dpr-vectors, layout:break-vectors, parity:break-capture, parity:pixel-capture, layout:paint-vectors, native:gen, node --conditions=dragon-internal examples/music-player/tools/check.ts, wpt:run --target web, wpt:update-expectations --target web, tw:sweep, parity:glyph-b3 --write-bottom-pins, media-sweep.ts and --check, parity:lanes --run-host): the border-join captures, DPR and break captures, vectors and Chrome PNGs; the dash paint vectors gain the solid-only style sets; the translated PaintDash; border-join joins the proofs of the rows it uses (no row added or removed and no status changed on ios, android or web), so emitted CSS digests move; corpus-dpr.json; host-only lanes.json. Pass 2 changed only native-lanes.ts (pass 1's profile:rows read the pre-run lanes.json), and the final profile:rows left it unchanged
… into t116-border-join-v2: no source conflict; generated files kept from this side for pnpm regen to rebuild
…der-join-v2: p6a-dash-oracle.test keeps T116's glyph skips and #63's paint-order occluders (the ancestors helper goes with the old occluder rule); paint-dash.ts keeps T116's side painter routing and #63's innerBorderRect; the merged dash inputs equal dashVectorInputs(); generated files kept from this side for pnpm regen to rebuild
…se.sh pnpm regen: fixed point after 3 passes): the translated engine (PaintDash with T116's side painter routing and innerBorderRect) and the dash paint vectors over the merged inputs; lanes.json and native-lanes.ts follow from the device run
… from the device run on T116 (/tmp/device-lease.sh pnpm run parity:devices): device-pixels ios 61 -> 57, android 90 -> 86 (master after #63): position-absolute-out-of-flow and -rtl (the T144 solid-corner joins, 2 + 2 per target) go to 0; the border-join cases pass on both targets; lane states equal master's, lane parity passes
…ease.sh pnpm regen: fixed point after 2 passes) over T116's device record: ios notPassing device-pixels 57, android 86, nothing stale; profiles and lanes.json unchanged (the T075J cycle proof)
…r (paintsOver: a box in a positioned layer, a non-static box or a descendant of one, paints over every in-flow box; within a phase, tree order), not tree order alone. #63's tree-order rule compared position-relative-percent's in-flow c3 border under the earlier relatively positioned b5, which Chrome paints over it (730 mismatches once T116 compares solid borders); the earlier-sibling case #63 fixed stays compared. paintsOver and positionedLayer are pinned on that fixture
…64 SIZE-ar, #65 P6a-promote) (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes): the border-join captures and vectors gain SIZE-ar's aspect-ratio; the pixel manifest regains SIZE-ar's sizing-ratio sets (the merge kept this side's manifest); profile proofs list both branches' cases (no row added or removed and no status changed on ios, android or web), so the emitted CSS digests move; the translated engine; corpus-dpr.json; lanes.json and native-lanes.ts follow from the device run
… from the device run on T116 after the merge of origin/master 7c5419f (/tmp/device-lease.sh pnpm run parity:devices): device-pixels ios 65 -> 57, android 94 -> 86; position-absolute-out-of-flow(-rtl) (T144) and sizing-ratio-abspos(-rtl) (SIZE-ar's corner pixels) go from 2 + 2 + 2 + 2 to 0 per target; border-join passes on both targets; lane states equal master's, lane parity passes
…ease.sh pnpm regen: fixed point after 2 passes) over T116's device record: device-pixels the only lane not passing (ios 57, android 86), nothing stale; profiles and lanes.json unchanged (the T075J cycle proof)
thejackshelton added a commit that referenced this pull request Oct 2, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Approvability

Verdict: Approved at 16234f6

Macroscope's review found this PR approvable — This is a narrowly scoped border-rendering correction that routes solid-only borders through the existing Blink-compatible side painter while preserving the double-border fallback. Unit, Chrome pixel-parity, multi-DPR, and device-lane coverage account for the changed behavior.

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

…aster 6295700 (#67 REPL-a engine) (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes); lanes.json and native-lanes.ts follow from the device run
thejackshelton added a commit that referenced this pull request Oct 2, 2026
…igin/master 6295700 (/tmp/device-lease.sh pnpm run parity:devices): failure lists unchanged (ios 57, android 86)
thejackshelton added a commit that referenced this pull request Oct 2, 2026
…66 INL1a part 1) (/tmp/heavy-lease.sh pnpm regen: fixed point after 3 passes): the translated engine; profile proofs list both branches' cases (no row added or removed and no status changed on ios, android or web), so the emitted CSS digests move; the pixel manifest, font captures and corpus-dpr.json; lanes.json and native-lanes.ts follow from the device run
…igin/master 30f03f4 (/tmp/device-lease.sh pnpm run parity:devices): device-pixels ios 65 -> 57, android 94 -> 86 against master (position-absolute-out-of-flow(-rtl) and sizing-ratio-abspos(-rtl) to 0); failure lists unchanged from the previous run; lane states equal master's, lane parity passes
Comment thread packages/parity/test/p6a-dash-oracle.test.ts Outdated
…glyph only when its text paints over the border (textPaintsOver, CSS2 Appendix E): over an in-flow border every text does, over a positioned border only its own text and that of boxes painting over it; a positioned border under in-flow text is compared now. Pinned with paintsOver on the position-relative-percent ids; every case still matches Chrome.
Comment thread packages/parity/test/p6a-dash-oracle.test.ts Outdated
…a-dash-oracle.test keeps master's tree-order occluder rule, so the subset proof (every pixel the pre-T116 comparison checked is still checked) measures the paint-order change as well as the raster-bounds and glyph skips; it holds on every dashed and dotted case at every DPR
Comment thread packages/parity/test/p6a-dash-oracle.test.ts
Comment thread packages/parity/test/p6a-dash-oracle.test.ts
Comment thread packages/parity/test/p6a-dash-oracle.test.ts
…the whole occluder model of p6a-dash-oracle.test re-audited (the file's third round)

An occluder now hides only what it paints, where it paints, when it paints:
- paint order within a phase is order-modified document order (paintOrder: flex items by order, stably), not program order;
- a box hides its border box only when its background is not transparent, else only its visible border bands;
- every occluder and glyph is clipped by the overflow-clipping boxes on its containing-block chain (an absolutely positioned box escapes the clips below its containing block, a fixed one all of them), glyphs also by their own box;
- a transparent text run hides nothing; a translucent one still changes the pixel, so it still hides it.
The pre-T116 comparison keeps master's rule for the subset proof. paintOrder and intersect are pinned; position-absolute-scroll-container proves the containing-block escape (a naive tree clip compared a border pixel the escaping box paints over). Every case still matches Chrome at every DPR.
Comment thread packages/parity/test/p6a-dash-oracle.test.ts Outdated
…ex container's in-flow items by order; an absolutely positioned child is not a flex item and keeps its tree-order slot. Pinned with an out-of-flow child carrying an order and a relatively positioned item that is reordered. The engine has no fixed positioning, so the dead fixed branches go.
@thejackshelton
thejackshelton merged commit f78f198 into master Oct 2, 2026
4 checks passed
thejackshelton added a commit that referenced this pull request Oct 2, 2026
…-a: device-failures-*.json take master's (device-run outputs; train 2's device run rewrites them); generated outputs rebuilt by pnpm regen next
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