Skip to content

Commit 16525d4

Browse files
The stroke-join rung: the SVG2-only join values land as Chromium's own drop (#77)
The first checklist-loop rung, and the first measurement-only one: zero engine code (one comment is the only source edit). Chromium 149 parses stroke-linejoin's miter-clip and arcs as invalid declarations — byte- identical to miter, the same fate as garbage — and Stylo's three-keyword grammar drops them at the same parse stage, so both admissions agree by construction. The declaration-level cells prove where: CSS miter-clip over a round attribute paints round, because the invalid declaration ceases to exist and the presentation hint survives. Ten cells, all byte-exact with no tolerance: the two SVG2-only values as attributes, every implemented keyword in CSS spelling, author-beats-hint precedence, and the miter limit's CSS twin including the below-one carry SVG 1.1 forbade and SVG2 allows. Three checklist rows tick: attribute stroke-linejoin, CSS stroke-linejoin, CSS stroke-miterlimit. Precedent, stated for the next rung: a valid listed value the oracle itself treats as invalid does not gate a tick when the drop is celled — nothing refuses and no pixel diverges, which is stronger than the preserveAspectRatio precedent, not an extension of it. The grammar bar where cited references disagree is the standard-track index: fill-stroke-3's draft crop/fallback ship in no engine and sit outside the surface (now a named header rule). Collateral records accuracy, caught by this rung's audit: the reftest header's tolerance census ("six rows... the curved cells") predated the gradient ramp-quantization cells and is rewritten count-free in the two declared kinds.
1 parent 5fd7947 commit 16525d4

28 files changed

Lines changed: 240 additions & 14 deletions

crates/n0_cli/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ cargo run -p n0_cli --bin n0 -- \
9898
reference, authored element children, a `<symbol>`/nested-`<svg>`
9999
target, and reference chains beyond the expansion budget.
100100
A stroke is centred, its width is a cascaded length, and its cap, join and
101-
miter limit come from the one cascade; dashing does not. Paint is solid
101+
miter limit come from the one cascade; dashing does not. The SVG2-only
102+
join values `miter-clip` and `arcs` drop as invalid declarations exactly
103+
as Chromium drops them (measured, celled) — an agreement, not a hole.
104+
Paint is solid
102105
sRGB, opaque or translucent: `fill-opacity`, `stroke-opacity`, and a
103106
colour's own alpha multiply in float and quantize once (the translucency
104107
rung), Chromium-baked.

crates/websem/src/svg.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3565,6 +3565,11 @@ fn resolve_stroke(
35653565
StyloLinecap::Round => StrokeCap::Round,
35663566
StyloLinecap::Square => StrokeCap::Square,
35673567
};
3568+
// Three variants is the full supported grammar, not a subset: the
3569+
// SVG2-only `miter-clip` and `arcs` are invalid declarations in Chromium
3570+
// too (measured — byte-identical to `miter`, and an invalid style-attribute
3571+
// spelling drops so a valid attribute survives), so Stylo's three-keyword
3572+
// parse lands both admissions on the same fallback by construction.
35683573
let join = match style.clone_stroke_linejoin() {
35693574
StyloLinejoin::Miter => StrokeJoin::Miter,
35703575
StyloLinejoin::Round => StrokeJoin::Round,

crates/websem/tests/reftest_oracle.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
//! if any RGBA pixel differs from Chromium, or if CPU/PNG output changes across
66
//! two identical runs.
77
//!
8-
//! Six of the rows soften that last-but-one clause and only those six: a cell
9-
//! carrying a `tolerance` block is checked against its declared bound instead —
10-
//! at most N differing pixels, at most a D-per-channel delta, every one of them
11-
//! within a pixel of a named ideal boundary. They are the curved cells, where
12-
//! Chromium's Skia build and this one disagree along a rational conic; the
13-
//! bounds are measured values rather than headroom, and a shape in the wrong
14-
//! place still fails loudly because it leaves the boundary ring. Every other
8+
//! Rows carrying a `tolerance` block — and only those — soften that
9+
//! last-but-one clause, each checked against its declared bound instead.
10+
//! Two kinds exist: `aa-boundary-ring` for the curved cells, where Chromium's
11+
//! Skia build and this one disagree along a rational conic — at most N
12+
//! differing pixels, at most a D-per-channel delta, every one within a pixel
13+
//! of a named ideal boundary, so a shape in the wrong place still fails
14+
//! loudly because it leaves the ring — and `ramp-quantization` for the
15+
//! gradient cells, where the two builds round a colour ramp one code value
16+
//! apart. The bounds are measured values rather than headroom. Every other
1517
//! row is byte-exact with no tolerance at all.
1618
//!
1719
//! Every pixel renders through n0's one downstream — the proving painter

docs/wg/consolidation/web-checklist.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ surface ahead of it — HTML, CSS, and SVG in one document, because the three
1717
share one cascade and half a vocabulary. Items only, in the platform's
1818
own words, enumerated from the external spec indexes linked at each section
1919
head — never from this tree. A box ticks only when a Chromium-gated rung
20-
lands the named construct at its full listed grammar; a partial admission
20+
lands the named construct at its full listed grammar — where cited
21+
references disagree, the standard-track grammar is the bar, and an
22+
experimental draft grammar does not raise it; a partial admission
2123
stays unchecked — the remaining grammar is remaining work, and a construct's
2224
finer-grained gaps live in their own rows (`<rect>` is landed; `rx`/`ry` are
2325
not). A row ticks only for its own section's index entry: an attribute
@@ -967,8 +969,8 @@ excluded.
967969
- [ ] `stroke-dasharray`
968970
- [ ] `stroke-dashoffset`
969971
- [ ] `stroke-linecap`
970-
- [ ] `stroke-linejoin`
971-
- [ ] `stroke-miterlimit`
972+
- [x] `stroke-linejoin`
973+
- [x] `stroke-miterlimit`
972974
- [ ] `marker`
973975
- [ ] `marker-start`
974976
- [ ] `marker-mid`
@@ -1473,7 +1475,7 @@ for attributes the platform ships ahead of the SVG 2 indexes.
14731475
- [ ] `stroke-dasharray`
14741476
- [ ] `stroke-dashoffset`
14751477
- [ ] `stroke-linecap`
1476-
- [ ] `stroke-linejoin`
1478+
- [x] `stroke-linejoin`
14771479
- [x] `stroke-miterlimit`
14781480
- [x] `stroke-opacity`
14791481
- [ ] `stroke-width`

fixtures/web-first/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ is exactly what the engine renders pixel-for-pixel.
5151
| `svg-stroke-cap-butt.svg` · `svg-stroke-cap-round.svg` · `svg-stroke-cap-square.svg` · `svg-stroke-zero-length-dot.svg` | The caps on an **open** contour: butt stops at the endpoint, round and square extend by the radius, and a *zero-length* segment paints a cap-shaped dot (nothing at all under butt) — which is why the path normalization keeps one. |
5252
| `svg-stroke-cap-closed-{butt,round,square}.svg` · `svg-stroke-cap-{circle,ellipse}-{round,square}.svg` | The same caps on a **closed** contour, where SVG makes them inert, at a one-device-pixel width. Chromium's three captures of each are byte-identical to one another; ours were not until the cap was normalized away per closed geometry. Seven cells because the defect was per painter arm, not per element: a path and an oval diverged, a rect never did. |
5353
| `svg-stroke-join-miter.svg` · `svg-stroke-join-round.svg` · `svg-stroke-join-bevel.svg` · `svg-stroke-miter-limit.svg` | The joins, each with distinct ink at the same corner, plus a miter limit low enough to force the bevel. |
54+
| `svg-stroke-join-miter-clip.svg` · `svg-stroke-join-arcs.svg` | The SVG2-only join values, measured unimplemented: Chromium parses `miter-clip` and `arcs` as invalid declarations, so both drop to the initial miter — byte-identical to the `miter` cell, the same fate as garbage input (measured). Stylo's three-keyword grammar drops them in the same place, so both admissions agree without a special case. The grammar bar for both twin rows is SVG2's property index; the fill-stroke-3 draft's `crop`/`fallback` keywords ship in no engine and sit outside the standard-track surface. |
55+
| `svg-stroke-join-css-miter.svg` · `svg-stroke-join-css-round.svg` · `svg-stroke-join-css-bevel.svg` · `svg-stroke-join-css-over-attr.svg` | The CSS spelling of every implemented join keyword, plus the precedence cell: an author `stroke-linejoin: round` beats the `bevel` presentation attribute. |
56+
| `svg-stroke-join-css-miter-clip.svg` · `svg-stroke-join-css-arcs.svg` | The declaration-level proof the drop happens at parse: CSS `miter-clip` or `arcs` over a `round` **attribute** paints round — the invalid declaration ceases to exist and the hint survives, where an implemented value would have changed the corner. |
57+
| `svg-stroke-miter-limit-css.svg` · `svg-stroke-miter-limit-css-below-one.svg` | The miter limit's CSS twin at the same forced bevel, and a below-one limit — valid in SVG2 where SVG 1.1 forbade it — is not dropped: no miter can satisfy it, so it bevels identically. A negative limit instead drops as invalid and the initial 4 miters, and a CSS limit beats the attribute spelling (both measured, neither celled). |
5458
| `svg-stroke-scaled-group.svg` · `svg-stroke-nonuniform-scale.svg` | The width is a length in local space, so a group's `scale(2)` doubles it and `scale(2,1)` makes the pen elliptical — the stroke *outline* is transformed, not the width. |
5559
| `svg-stroke-zero-extent-rect.svg` | A zero-extent `<rect>` or `r="0"` `<circle>` renders nothing **including its stroke** (SVG2 §10.1) — baked as proof, since a naive stroke of a zero-extent box would draw a line. |
5660
| `svg-polygon-fill.svg` · `svg-points-trailing-comma.svg` | The points rung's fill cells: a triangle authored with mixed comma/whitespace separators, and the same shape with a trailing separator — which Blink accepts in `points` (measured), unlike the `viewBox` grammar. |

fixtures/web-first/STATUS.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Not a conformance claim: no score is computed or implied (FLIP is
1919
unratified), and the corpus enumerates constructs, not the SVG
2020
surface.
2121

22-
## Chromium-baked cells (192)
22+
## Chromium-baked cells (202)
2323

2424
Each renders byte-exact against its committed Chromium oracle
2525
(six curved cells and two gradient ramps carry a declared, bounded
@@ -169,13 +169,23 @@ to its fixture source. No new image is committed for this view.
169169
<a href="./svg-stroke-ellipse.svg" title="svg-stroke-ellipse (standalone-svg)"><img src="./chromium/svg-stroke-ellipse.png" width="56" alt="svg-stroke-ellipse"></a>
170170
<a href="./svg-stroke-inherited.svg" title="svg-stroke-inherited (standalone-svg)"><img src="./chromium/svg-stroke-inherited.png" width="56" alt="svg-stroke-inherited"></a>
171171
<a href="./svg-stroke-invalid-width.svg" title="svg-stroke-invalid-width (standalone-svg)"><img src="./chromium/svg-stroke-invalid-width.png" width="56" alt="svg-stroke-invalid-width"></a>
172+
<a href="./svg-stroke-join-arcs.svg" title="svg-stroke-join-arcs (standalone-svg)"><img src="./chromium/svg-stroke-join-arcs.png" width="56" alt="svg-stroke-join-arcs"></a>
172173
<a href="./svg-stroke-join-bevel.svg" title="svg-stroke-join-bevel (standalone-svg)"><img src="./chromium/svg-stroke-join-bevel.png" width="56" alt="svg-stroke-join-bevel"></a>
174+
<a href="./svg-stroke-join-css-arcs.svg" title="svg-stroke-join-css-arcs (standalone-svg)"><img src="./chromium/svg-stroke-join-css-arcs.png" width="56" alt="svg-stroke-join-css-arcs"></a>
175+
<a href="./svg-stroke-join-css-bevel.svg" title="svg-stroke-join-css-bevel (standalone-svg)"><img src="./chromium/svg-stroke-join-css-bevel.png" width="56" alt="svg-stroke-join-css-bevel"></a>
176+
<a href="./svg-stroke-join-css-miter.svg" title="svg-stroke-join-css-miter (standalone-svg)"><img src="./chromium/svg-stroke-join-css-miter.png" width="56" alt="svg-stroke-join-css-miter"></a>
177+
<a href="./svg-stroke-join-css-miter-clip.svg" title="svg-stroke-join-css-miter-clip (standalone-svg)"><img src="./chromium/svg-stroke-join-css-miter-clip.png" width="56" alt="svg-stroke-join-css-miter-clip"></a>
178+
<a href="./svg-stroke-join-css-over-attr.svg" title="svg-stroke-join-css-over-attr (standalone-svg)"><img src="./chromium/svg-stroke-join-css-over-attr.png" width="56" alt="svg-stroke-join-css-over-attr"></a>
179+
<a href="./svg-stroke-join-css-round.svg" title="svg-stroke-join-css-round (standalone-svg)"><img src="./chromium/svg-stroke-join-css-round.png" width="56" alt="svg-stroke-join-css-round"></a>
173180
<a href="./svg-stroke-join-miter.svg" title="svg-stroke-join-miter (standalone-svg)"><img src="./chromium/svg-stroke-join-miter.png" width="56" alt="svg-stroke-join-miter"></a>
181+
<a href="./svg-stroke-join-miter-clip.svg" title="svg-stroke-join-miter-clip (standalone-svg)"><img src="./chromium/svg-stroke-join-miter-clip.png" width="56" alt="svg-stroke-join-miter-clip"></a>
174182
<a href="./svg-stroke-join-round.svg" title="svg-stroke-join-round (standalone-svg)"><img src="./chromium/svg-stroke-join-round.png" width="56" alt="svg-stroke-join-round"></a>
175183
<a href="./svg-stroke-length-units.svg" title="svg-stroke-length-units (standalone-svg)"><img src="./chromium/svg-stroke-length-units.png" width="56" alt="svg-stroke-length-units"></a>
176184
<a href="./svg-stroke-line.svg" title="svg-stroke-line (standalone-svg)"><img src="./chromium/svg-stroke-line.png" width="56" alt="svg-stroke-line"></a>
177185
<a href="./svg-stroke-line-fill-never-paints.svg" title="svg-stroke-line-fill-never-paints (standalone-svg)"><img src="./chromium/svg-stroke-line-fill-never-paints.png" width="56" alt="svg-stroke-line-fill-never-paints"></a>
178186
<a href="./svg-stroke-miter-limit.svg" title="svg-stroke-miter-limit (standalone-svg)"><img src="./chromium/svg-stroke-miter-limit.png" width="56" alt="svg-stroke-miter-limit"></a>
187+
<a href="./svg-stroke-miter-limit-css.svg" title="svg-stroke-miter-limit-css (standalone-svg)"><img src="./chromium/svg-stroke-miter-limit-css.png" width="56" alt="svg-stroke-miter-limit-css"></a>
188+
<a href="./svg-stroke-miter-limit-css-below-one.svg" title="svg-stroke-miter-limit-css-below-one (standalone-svg)"><img src="./chromium/svg-stroke-miter-limit-css-below-one.png" width="56" alt="svg-stroke-miter-limit-css-below-one"></a>
179189
<a href="./svg-stroke-nonuniform-scale.svg" title="svg-stroke-nonuniform-scale (standalone-svg)"><img src="./chromium/svg-stroke-nonuniform-scale.png" width="56" alt="svg-stroke-nonuniform-scale"></a>
180190
<a href="./svg-stroke-opacity-join.svg" title="svg-stroke-opacity-join (standalone-svg)"><img src="./chromium/svg-stroke-opacity-join.png" width="56" alt="svg-stroke-opacity-join"></a>
181191
<a href="./svg-stroke-opacity-over-fill.svg" title="svg-stroke-opacity-over-fill (standalone-svg)"><img src="./chromium/svg-stroke-opacity-over-fill.png" width="56" alt="svg-stroke-opacity-over-fill"></a>
765 Bytes
Loading
747 Bytes
Loading
706 Bytes
Loading
747 Bytes
Loading

0 commit comments

Comments
 (0)