diff --git a/FIDELITY.md b/FIDELITY.md index af68505..1e2213b 100644 --- a/FIDELITY.md +++ b/FIDELITY.md @@ -18,6 +18,16 @@ The committed PNGs under `testdata/renders/` back every claim here. Reproduce them with the commands at the bottom. The measured-vs-Chrome numbers live in [`bench/REPORT.md`](bench/REPORT.md). +## 2026-09-28 (round 139) — the Constraint Validation API (`setCustomValidity`/`.validity`/`.validationMessage`/`checkValidity`) was entirely missing — CONFIRMED extensive real usage on github.com's own client-side form-validation UI (username/label/2FA fields) (engine#239) + +Continued sweeping the same fresh github.com bundles round 138 found `.indeterminate` in, this time for method calls rather than property assignments. `.checkValidity(` (7 hits) and `.setCustomValidity(` (4 hits) stood out immediately, alongside `.validity.customError`/`.validationMessage` reads — GitHub's own form-validation UI relies on this API extensively for username/label/2FA-code field checks. + +- **Scoped precisely to what's evidenced, not the full spec surface**: this engine models NO native constraints at all — no `required`/`pattern`/`min`/`max`/`step` checking against a value. The corpus usage is exclusively the CUSTOM-validity pattern (`setCustomValidity(msg)` / `setCustomValidity("")` / reading `.validity.customError`/`.validationMessage` / gating on `.checkValidity()`), so that's what's implemented — but the `ValidityState` object's SHAPE is spec-complete (all nine other flags — `valueMissing`, `typeMismatch`, `patternMismatch`, `tooLong`, `tooShort`, `rangeUnderflow`, `rangeOverflow`, `stepMismatch`, `badInput` — are present and always `false`, not silently `undefined`), so a script that checks any of them gets a real, defined answer rather than a crash. +- **Fixed** (`dom.Node.CustomValidity string`, the same non-attribute-runtime-field shape as round 138's `Indeterminate`; `js/dom.go`): `setCustomValidity(msg)` sets it; `.validationMessage` and `.validity.customError`/`.valid` read it; `checkValidity()` returns `validity.valid` and, per spec, fires a cancelable `"invalid"` event at the element when invalid. +- **`.willValidate` deliberately left out**: zero corpus usage found for it anywhere in this session's fetched scripts, unlike the four members above. +- **One new test**, git-stash-confirmed: reverting makes the test's own script throw `TypeError: Cannot read property 'valid' of undefined` — the same class of failure rounds 124-126 documented for a missing method. +- **Bench**: flat/within already-documented noise across all ten pages, including github.com/golang/go itself (0.638, unchanged) — expected, pure JS-correctness fix with no paint-visible effect by design. All coverage floors held. + ## 2026-09-28 (round 138) — `HTMLInputElement.indeterminate` and the `:indeterminate` CSS pseudo-class were entirely missing — CONFIRMED real corpus usage on github.com's own "select all" bulk-action checkboxes (engine#238) Broadened the corpus sweep to a genuinely fresh page (github.com's own homepage and its `behaviors.js`/`element-registry.js`/`environment.js` bundles, not previously in this session's ten-page set), after the existing corpus stopped turning up new evidenced gaps. `.indeterminate=` was the one new real hit: `(0,v.l)("[data-indeterminate]",{constructor:HTMLInputElement,initialize(e){e.indeterminate=!0}})` — GitHub's own tri-state "select all" checkbox pattern. diff --git a/bench/REPORT.md b/bench/REPORT.md index 20b8b56..d32cc6b 100644 --- a/bench/REPORT.md +++ b/bench/REPORT.md @@ -2,7 +2,7 @@ -**Generated:** 2026-09-28 06:24 UTC +**Generated:** 2026-09-28 06:39 UTC **Viewport:** 1024×768, device-scale 1 **Timing:** median of N=5 runs after 1 warmup, wall-clock incl. network fetch **Chrome:** `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` @@ -11,16 +11,16 @@ | URL | SSIM | pixdiff % | webengine ms | chrome ms | speed× | region | status | |-----|-----:|----------:|-------------:|----------:|-------:|:------:|:------:| -| example.com/ | 0.954 | 1.5 | 52.3 | 1283.0 | 24.52 | 1024×768 | ok | -| en.wikipedia.org/wiki/Go_(programming_language) | 0.436 | 21.3 | 2699.3 | 3880.0 | 1.44 | 1024×2500 | ok | -| pkg.go.dev/net/http | 0.716 | 11.3 | 5461.5 | 3951.6 | 0.72 | 1024×2500 | ok | -| go.dev/blog/ | 0.690 | 16.7 | 4432.9 | 1995.8 | 0.45 | 1024×1439 | ok | -| react.dev/ | 0.725 | 33.0 | 3436.6 | 2266.9 | 0.66 | 1024×2500 | ok | -| news.ycombinator.com/ | 0.603 | 14.4 | 1233.1 | 2098.7 | 1.70 | 1024×1077 | ok | -| developer.mozilla.org/en-US/docs/Web/CSS | 0.606 | 18.1 | 540.9 | 1522.4 | 2.81 | 1009×2500 | ok | -| github.com/golang/go | 0.638 | 14.2 | 2607.3 | 1733.7 | 0.66 | 1024×2491 | ok | -| tailwindcss.com/ | 0.740 | 11.3 | 7475.3 | 1868.8 | 0.25 | 1024×2500 | ok | -| caniuse.com/ | 0.657 | 18.2 | 4899.5 | 3228.2 | 0.66 | 1024×1740 | ok | +| example.com/ | 0.954 | 1.5 | 52.5 | 1296.1 | 24.69 | 1024×768 | ok | +| en.wikipedia.org/wiki/Go_(programming_language) | 0.436 | 21.3 | 2701.7 | 3826.7 | 1.42 | 1024×2500 | ok | +| pkg.go.dev/net/http | 0.716 | 11.3 | 5235.0 | 3904.4 | 0.75 | 1024×2500 | ok | +| go.dev/blog/ | 0.690 | 16.7 | 4432.5 | 1879.3 | 0.42 | 1024×1439 | ok | +| react.dev/ | 0.725 | 33.0 | 3295.5 | 2173.4 | 0.66 | 1024×2500 | ok | +| news.ycombinator.com/ | 0.601 | 14.4 | 1238.8 | 2056.2 | 1.66 | 1024×1077 | ok | +| developer.mozilla.org/en-US/docs/Web/CSS | 0.606 | 18.1 | 524.6 | 1435.1 | 2.74 | 1009×2500 | ok | +| github.com/golang/go | 0.638 | 14.2 | 2404.3 | 1712.0 | 0.71 | 1024×2491 | ok | +| tailwindcss.com/ | 0.739 | 11.3 | 7522.3 | 1778.8 | 0.24 | 1024×2500 | ok | +| caniuse.com/ | 0.658 | 18.1 | 4805.9 | 3331.1 | 0.69 | 1024×1740 | ok | Speed× is `chrome_ms / webengine_ms`: >1 means webengine is faster. diff --git a/bench/out/caniuse.com.png b/bench/out/caniuse.com.png index f2cabfb..070dc78 100644 Binary files a/bench/out/caniuse.com.png and b/bench/out/caniuse.com.png differ diff --git a/bench/out/github.com_golang_go.png b/bench/out/github.com_golang_go.png index c317369..855e97a 100644 Binary files a/bench/out/github.com_golang_go.png and b/bench/out/github.com_golang_go.png differ diff --git a/bench/out/news.ycombinator.com.png b/bench/out/news.ycombinator.com.png index f3b2b97..35a2107 100644 Binary files a/bench/out/news.ycombinator.com.png and b/bench/out/news.ycombinator.com.png differ diff --git a/bench/out/tailwindcss.com.png b/bench/out/tailwindcss.com.png index b42f233..5ac837e 100644 Binary files a/bench/out/tailwindcss.com.png and b/bench/out/tailwindcss.com.png differ diff --git a/bench/results.json b/bench/results.json index 1d769d3..e18e0b5 100644 --- a/bench/results.json +++ b/bench/results.json @@ -1,9 +1,9 @@ [ { "url": "https://example.com/", - "webengine_ms": 52.324, - "chrome_ms": 1282.957, - "speed_ratio": 24.519474810794286, + "webengine_ms": 52.504, + "chrome_ms": 1296.099, + "speed_ratio": 24.68571918330032, "ssim": 0.9541451179596087, "pixdiff_pct": 1.53350830078125, "region_w": 1024, @@ -14,9 +14,9 @@ }, { "url": "https://en.wikipedia.org/wiki/Go_(programming_language)", - "webengine_ms": 2699.322, - "chrome_ms": 3879.953, - "speed_ratio": 1.4373805718621193, + "webengine_ms": 2701.66, + "chrome_ms": 3826.699, + "speed_ratio": 1.4164250867984869, "ssim": 0.4361758822140753, "pixdiff_pct": 21.279570312500002, "region_w": 1024, @@ -27,9 +27,9 @@ }, { "url": "https://pkg.go.dev/net/http", - "webengine_ms": 5461.531, - "chrome_ms": 3951.595, - "speed_ratio": 0.7235324673612582, + "webengine_ms": 5234.99, + "chrome_ms": 3904.379, + "speed_ratio": 0.745823583235116, "ssim": 0.7155686319990039, "pixdiff_pct": 11.3370703125, "region_w": 1024, @@ -40,9 +40,9 @@ }, { "url": "https://go.dev/blog/", - "webengine_ms": 4432.901, - "chrome_ms": 1995.772, - "speed_ratio": 0.4502180400599968, + "webengine_ms": 4432.459, + "chrome_ms": 1879.311, + "speed_ratio": 0.4239883549966283, "ssim": 0.6901862268272003, "pixdiff_pct": 16.726839384989574, "region_w": 1024, @@ -53,9 +53,9 @@ }, { "url": "https://react.dev/", - "webengine_ms": 3436.649, - "chrome_ms": 2266.886, - "speed_ratio": 0.6596210436387306, + "webengine_ms": 3295.483, + "chrome_ms": 2173.381, + "speed_ratio": 0.6595030227738998, "ssim": 0.72456508425769, "pixdiff_pct": 32.97015625, "region_w": 1024, @@ -66,11 +66,11 @@ }, { "url": "https://news.ycombinator.com/", - "webengine_ms": 1233.055, - "chrome_ms": 2098.676, - "speed_ratio": 1.7020132921889128, - "ssim": 0.6033293622898777, - "pixdiff_pct": 14.418306058495823, + "webengine_ms": 1238.799, + "chrome_ms": 2056.235, + "speed_ratio": 1.6598616886193807, + "ssim": 0.6013611994886636, + "pixdiff_pct": 14.430819115598887, "region_w": 1024, "region_h": 1077, "montage": "out/news.ycombinator.com.png", @@ -79,9 +79,9 @@ }, { "url": "https://developer.mozilla.org/en-US/docs/Web/CSS", - "webengine_ms": 540.939, - "chrome_ms": 1522.391, - "speed_ratio": 2.8143487528168616, + "webengine_ms": 524.552, + "chrome_ms": 1435.116, + "speed_ratio": 2.7358889109182694, "ssim": 0.6056768401997266, "pixdiff_pct": 18.121902874132807, "region_w": 1009, @@ -92,11 +92,11 @@ }, { "url": "https://github.com/golang/go", - "webengine_ms": 2607.312, - "chrome_ms": 1733.675, - "speed_ratio": 0.6649280945280043, - "ssim": 0.63813074167239, - "pixdiff_pct": 14.235309614612603, + "webengine_ms": 2404.346, + "chrome_ms": 1711.988, + "speed_ratio": 0.7120389494690033, + "ssim": 0.6381281554378134, + "pixdiff_pct": 14.235623243677237, "region_w": 1024, "region_h": 2491, "montage": "out/github.com_golang_go.png", @@ -105,11 +105,11 @@ }, { "url": "https://tailwindcss.com/", - "webengine_ms": 7475.3, - "chrome_ms": 1868.777, - "speed_ratio": 0.24999357885302262, - "ssim": 0.7396458938927728, - "pixdiff_pct": 11.30765625, + "webengine_ms": 7522.284, + "chrome_ms": 1778.841, + "speed_ratio": 0.23647618196813627, + "ssim": 0.7388802364988983, + "pixdiff_pct": 11.3133984375, "region_w": 1024, "region_h": 2500, "montage": "out/tailwindcss.com.png", @@ -118,11 +118,11 @@ }, { "url": "https://caniuse.com/", - "webengine_ms": 4899.46, - "chrome_ms": 3228.213, - "speed_ratio": 0.6588915921346434, - "ssim": 0.6569742927777596, - "pixdiff_pct": 18.19190014367816, + "webengine_ms": 4805.943, + "chrome_ms": 3331.103, + "speed_ratio": 0.6931216204603342, + "ssim": 0.6579995177089225, + "pixdiff_pct": 18.11444863505747, "region_w": 1024, "region_h": 1740, "montage": "out/caniuse.com.png", diff --git a/dom/dom.go b/dom/dom.go index 3e8fa5b..7e1c491 100644 --- a/dom/dom.go +++ b/dom/dom.go @@ -74,6 +74,18 @@ type Node struct { // `:indeterminate` CSS pseudo-class (css/selector.go). Indeterminate bool + // CustomValidity holds a form control's own "custom error message" set by + // script via `.setCustomValidity(msg)` (HTML Standard's Constraint + // Validation API) — empty means no custom error. This engine models NO + // native constraints at all (no required/pattern/min/max/step checking + // against a value), so this is the ONLY thing that can ever make + // `.validity.valid`/`.checkValidity()` false — a deliberate, disclosed + // scope boundary: this backs the real, evidenced usage (see js/dom.go's + // own doc comment), not a full constraint-validation engine. Runtime-only + // field, the same shape as Indeterminate above — there is no + // "customvalidity" content attribute. + CustomValidity string + // Shadow is the shadow root attached to this element (a declarative //