diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2fe0ee..b6e5734 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ permissions: env: CGO_ENABLED: "0" - GO_VERSION: "1.26.4" + GO_VERSION: "1.27.1" jobs: test: diff --git a/FIDELITY.md b/FIDELITY.md index 23062d0..4aa0721 100644 --- a/FIDELITY.md +++ b/FIDELITY.md @@ -47,6 +47,23 @@ A survey of how other renderers accelerate loading recommended honouring Retry-A - **Test**: `TestFetchModuleSourceHonoursRetryAfter` serves 429 with `Retry-After: 1` for the first second. With the fix it succeeds after about 1.0s. Stash-verified: the old code drops the module at 0.46s. - **Corpus effect**: none measured. The saved Tailwind, Wikipedia and Hacker News renders are pixel-identical before and after, because no module on those pages was rate-limited. The change corrects a case the corpus does not exercise. +## 2026-10-04 (round 162) — Go 1.27.1 becomes the floor; the upgrade surfaced one real test-harness assumption (golden PNGs compared by encoded bytes), now corrected by pixels; govulncheck clean for the code (engine#262, v0.5.0) + +Upgrade: `go 1.27.1` in `go.mod`, the CI `GO_VERSION`, the README badge and floor, and the bench module. Go 1.27.1 is the final release (1.27.0 and 1.27.1 are published; 1.27 release candidates are not used). + +**Breakages found and what they were** +- **Golden PNGs failed (`TestSVGGolden`, `TestRenderDocumentGolden`)**. The goldens compared the encoded PNG bytes. Go 1.27's DEFLATE chooses different block types, so the same pixels encode to different bytes (2503 vs 2394 bytes). Measured: the SVG golden has 0 differing pixels out of 28000. The golden comparison now accepts the same pixels, decoded; regeneration with `UPDATE_GOLDEN=1` is unchanged. This is a test-harness correction, not a rendering change. +- **gofmt reindents** (the fleet note's first mechanism): a formatting-only change across 23 files, one of which removes a trailing blank line. +- **Coverage**: unchanged floors met (css 99.5, layout 100, paint 100, dom 98.5, paginate 100). + +**Security (govulncheck, Go 1.27.1)** +- Code-affected vulnerabilities: 0. The six standard-library findings that the go1.26.4 pin carried are fixed in the new toolchain. +- Module dependency: `github.com/klauspost/compress` v1.17.4 → v1.18.7 (fix for GO-2026-5841). It is an indirect dependency through the browser HTTP transport. +- `golang.org/x/crypto` v0.57.0 has GO-2026-5932, with no fixed release; the code does not call the affected path. It is recorded here, not worked around. +- The SSRF finding from round 161 stands: the library does not filter destinations, and the README and docs say so. + +**Release**: minor under 0.x, because the Go floor rises to 1.27.1 for every consumer. + ## 2026-10-04 (round 161) — security audit: a 62KB image file could allocate 128MB; raster decode now refuses declared canvases above 25 megapixels; SSRF and toolchain findings recorded (engine#261, patch) Scope: outbound fetches the engine makes for a page (images, sheets, fonts, modules, fetch/XHR, form posts), the decode path for untrusted bytes, the link hit-map, and the Go toolchain (govulncheck). diff --git a/README.md b/README.md index 0d4c796..7f8ad79 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/go-webengine/engine.svg)](https://pkg.go.dev/github.com/go-webengine/engine) [![Docs](https://img.shields.io/badge/docs-mkdocs--material-0079A8)](https://go-webengine.github.io/docs/) [![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](LICENSE) -[![Go 1.26.4+](https://img.shields.io/badge/Go-1.26.4%2B-00ADD8?logo=go)](https://go.dev/dl/) +[![Go 1.27.1+](https://img.shields.io/badge/Go-1.27.1%2B-00ADD8?logo=go)](https://go.dev/dl/) A pure-Go, **`CGO_ENABLED=0`** headless web engine: it fetches a URL, parses the HTML into a DOM, applies a real CSS subset (cascade + inheritance + `var()` + @@ -226,7 +226,7 @@ paint and paginate 100%, dom 97.4%), which CI fails below and which is raised paths (root `engine` package, `cmd/render`) are excluded from the gate because their coverage is not reproducible in CI. The `bench/` fidelity harness is a separate nested module (it pulls chromedp) and is not in the CGO=0 six-arch CI. -`go.mod` floor is `go 1.26.4`; cross-built for all six 64-bit Go targets. +`go.mod` floor is `go 1.27.1`; cross-built for all six 64-bit Go targets. ## Links diff --git a/bench/README.md b/bench/README.md index e6d3e55..e044493 100644 --- a/bench/README.md +++ b/bench/README.md @@ -17,7 +17,7 @@ engine's `go build ./...` / `go test ./...` from the repo root do **not** see it ## Requirements -- Go 1.26.4+ +- Go 1.27.1+ - A Chrome / Chromium binary. Point `CHROME_BIN` at it; default is `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`. diff --git a/bench/go.mod b/bench/go.mod index 5a79abd..304ff31 100644 --- a/bench/go.mod +++ b/bench/go.mod @@ -1,6 +1,6 @@ module github.com/go-webengine/engine/bench -go 1.26.4 +go 1.27.1 // Local parent engine module. Keeps chromedp out of the engine's CGO=0, // 6-arch build and coverage gate. @@ -20,7 +20,7 @@ require ( github.com/breml/rootcerts v0.3.7 // indirect github.com/chromedp/sysutil v1.1.0 // indirect github.com/dlclark/regexp2/v2 v2.5.2 // indirect - github.com/dop251/goja v0.0.0-20260917113740-793a2a65c13b // indirect + github.com/dop251/goja v0.0.0-20260926152631-39ec2650adc9 // indirect github.com/evanw/esbuild v0.28.2 // indirect github.com/go-browserhttp/browserhttp v0.2.0 // indirect github.com/go-gfx/gfx v0.34.0 // indirect @@ -29,7 +29,7 @@ require ( github.com/go-images/jpeg2000 v0.1.0 // indirect github.com/go-images/png v0.1.0 // indirect github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 // indirect - github.com/go-opentype/fonts v0.9.1-0.20260907071658-dd9656234a3d // indirect + github.com/go-opentype/fonts v0.10.0 // indirect github.com/go-opentype/opentype v0.13.1-0.20260927180318-ae6327b14eac // indirect github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect github.com/go-webengine/esbuildsandbox v0.1.0 // indirect @@ -38,7 +38,7 @@ require ( github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.4.0 // indirect github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.18.7 // indirect github.com/refraction-networking/utls v1.8.2 // indirect github.com/sergeymakinen/go-bmp v1.0.0 // indirect github.com/sergeymakinen/go-ico v1.0.0 // indirect diff --git a/bench/go.sum b/bench/go.sum index bf5762b..20e4bcd 100644 --- a/bench/go.sum +++ b/bench/go.sum @@ -14,8 +14,8 @@ github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipw github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8= github.com/dlclark/regexp2/v2 v2.5.2 h1:HAsucWRhsqcDzl6Ua9aR8JwYOTzrZyPrF0/FNxJVAI0= github.com/dlclark/regexp2/v2 v2.5.2/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU= -github.com/dop251/goja v0.0.0-20260917113740-793a2a65c13b h1:UMDLDHFR1Chu3qnsPNCrVxq0lZgG6JqHpLL5+iqfSkw= -github.com/dop251/goja v0.0.0-20260917113740-793a2a65c13b/go.mod h1:u8yZRUavu+N4EnFFy6J5fVtjE7lEcZ2YyV2GcBXY9c8= +github.com/dop251/goja v0.0.0-20260926152631-39ec2650adc9 h1:Ksd3Vil12b/ykT5NjGzd3qNc688cJB9OUXJZZChEktI= +github.com/dop251/goja v0.0.0-20260926152631-39ec2650adc9/go.mod h1:u8yZRUavu+N4EnFFy6J5fVtjE7lEcZ2YyV2GcBXY9c8= github.com/evanw/esbuild v0.28.2 h1:A2uETn4jrQTcXaT/shwTDTYBxDjl7fV7nXmUrJxfA2w= github.com/evanw/esbuild v0.28.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/go-browserhttp/browserhttp v0.2.0 h1:3RcjOQVHgqhSOmI5cfsURpexw/vetoDUoLXKzgTCSac= @@ -34,8 +34,8 @@ github.com/go-images/png v0.1.0 h1:OI4yHotS0lA75sLnG2FTL2Vooh4PWpVWEidre1msSe0= github.com/go-images/png v0.1.0/go.mod h1:V9vaEbr2nrZGorUWn4xv9F24Yuu1/qLVtmailM9GBVo= github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3 h1:UADEEmDKgfXbtnGJZ97beY5XLo9ZechG1nlU4KnRrkE= github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg= -github.com/go-opentype/fonts v0.9.1-0.20260907071658-dd9656234a3d h1:9WvCVDUtnMBVmviCyC65IGN1Sun3k/goiB7wsp/sbjM= -github.com/go-opentype/fonts v0.9.1-0.20260907071658-dd9656234a3d/go.mod h1:b+o8v1zDZ6B3GixA398jGILAh09+WwfyoYz52zKe7DA= +github.com/go-opentype/fonts v0.10.0 h1:9f7ik795SlBEJjz6iqBE/ZV2PaTauAj537MMWw9t4OQ= +github.com/go-opentype/fonts v0.10.0/go.mod h1:b+o8v1zDZ6B3GixA398jGILAh09+WwfyoYz52zKe7DA= github.com/go-opentype/opentype v0.13.1-0.20260927180318-ae6327b14eac h1:mpzfJQe1RlF2zawKqvL5poyNdaV6pMbHkK1h+GVLMuM= github.com/go-opentype/opentype v0.13.1-0.20260927180318-ae6327b14eac/go.mod h1:UjZOTCMtkHheDPzfP0ZghDoQ7wIr6L72T/kuC9Ph1hw= github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= @@ -54,8 +54,8 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= +github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= diff --git a/css/external_test.go b/css/external_test.go index b46f08d..4ed5448 100644 --- a/css/external_test.go +++ b/css/external_test.go @@ -85,11 +85,11 @@ func TestImportURLs(t *testing.T) { []string{"x.css"}, []string{""}}, {` @import url( "spaced.css" ) ;`, []string{"spaced.css"}, []string{""}}, {`body{color:red}`, nil, nil}, - {`@import ;`, nil, nil}, // no clause + {`@import ;`, nil, nil}, // no clause {`@import url(unterminated`, nil, nil}, // no semicolon - {`@import url(;`, nil, nil}, // no closing paren before ; - {`@import badtoken.css;`, nil, nil}, // neither url() nor quoted - {`@import "";`, nil, nil}, // empty url + {`@import url(;`, nil, nil}, // no closing paren before ; + {`@import badtoken.css;`, nil, nil}, // neither url() nor quoted + {`@import "";`, nil, nil}, // empty url {`/* unterminated comment`, nil, nil}, {`@charset "utf-8"`, nil, nil}, // charset without semicolon } diff --git a/css/grid.go b/css/grid.go index db2e1d0..3e6923f 100644 --- a/css/grid.go +++ b/css/grid.go @@ -375,4 +375,3 @@ func applyGridArea(s *Style, v string) { s.GridRowEnd = get(2) s.GridColumnEnd = get(3) } - diff --git a/css/gridflex_test.go b/css/gridflex_test.go index b70b48d..d28c2f4 100644 --- a/css/gridflex_test.go +++ b/css/gridflex_test.go @@ -396,10 +396,10 @@ func TestParseGridTemplateAreas(t *testing.T) { func TestParseGridTemplateAreasRejects(t *testing.T) { for _, v := range []string{ - ``, // no rows - `"a b" "c"`, // ragged - `"unterminated`, // missing close quote - `"" ""`, // zero columns + ``, // no rows + `"a b" "c"`, // ragged + `"unterminated`, // missing close quote + `"" ""`, // zero columns } { if got, ok := parseGridTemplateAreas(v); ok { t.Errorf("areas(%q) unexpectedly ok = %v", v, got) diff --git a/css/selector_test.go b/css/selector_test.go index af23b6a..7dd4395 100644 --- a/css/selector_test.go +++ b/css/selector_test.go @@ -1084,8 +1084,8 @@ func TestAttributeSelectorGitHubDarkModeBug(t *testing.T) { // toggle is :checked. With no user interaction the toggle is unchecked, so the // reveal rule must NOT apply and the menu stays hidden — Chrome's static state. func TestCheckboxHack(t *testing.T) { - hide, _ := parseComplex(".menu") // display:none base rule - reveal, _ := parseComplex("#toggle:checked ~ .menu") // reveal when checked + hide, _ := parseComplex(".menu") // display:none base rule + reveal, _ := parseComplex("#toggle:checked ~ .menu") // reveal when checked toggle := checkbox("toggle", "checkbox", false) menu := el("div", "", "menu") diff --git a/css/value_test.go b/css/value_test.go index 6865174..433691f 100644 --- a/css/value_test.go +++ b/css/value_test.go @@ -121,11 +121,11 @@ func TestParseFontFamily(t *testing.T) { func TestParseFontFamilyKeepsTheNames(t *testing.T) { cases := map[string]string{ `'IBM Plex Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif`: "ibm plex sans,helvetica neue,helvetica,arial", - `Spectral, 'Iowan Old Style', Palatino, Georgia, serif`: "spectral,iowan old style,palatino,georgia", - `"IBM Plex Mono", Menlo, monospace`: "ibm plex mono,menlo", - "sans-serif": "", - "serif, sans-serif": "", - ` Spectral `: "spectral", + `Spectral, 'Iowan Old Style', Palatino, Georgia, serif`: "spectral,iowan old style,palatino,georgia", + `"IBM Plex Mono", Menlo, monospace`: "ibm plex mono,menlo", + "sans-serif": "", + "serif, sans-serif": "", + ` Spectral `: "spectral", } for in, want := range cases { if got := parseFontFamily(in); got.Names != want { diff --git a/dynamic_test.go b/dynamic_test.go index c4288cc..7e457ac 100644 --- a/dynamic_test.go +++ b/dynamic_test.go @@ -79,16 +79,16 @@ func TestLayoutMetricsComputed(t *testing.T) { n := &dom.Node{Type: dom.Element, Tag: "div"} st := &css.Style{ Display: css.DisplayBlock, Position: css.PositionRelative, - Margin: css.Edges{Top: 1, Right: 2, Bottom: 3, Left: 4}, - Padding: css.Edges{Top: 5, Right: 6, Bottom: 7, Left: 8}, - FontSize: 16, FontWeight: 700, Italic: true, + Margin: css.Edges{Top: 1, Right: 2, Bottom: 3, Left: 4}, + Padding: css.Edges{Top: 5, Right: 6, Bottom: 7, Left: 8}, + FontSize: 16, FontWeight: 700, Italic: true, LineHeight: css.LineHeight{Px: 24}, Color: css.Color{R: 1, G: 2, B: 3, A: 255}, Background: css.Color{R: 4, G: 5, B: 6, A: 255}, TextAlign: css.AlignCenter, BoxSizing: css.BorderBox, HasOpacity: true, Opacity: 0.5, ZIndexAuto: false, ZIndex: 7, - Top: css.Length{Px: 10}, Right: css.Length{Auto: true}, + Top: css.Length{Px: 10}, Right: css.Length{Auto: true}, Bottom: css.Length{IsPercent: true, Percent: 0.25}, Left: css.Length{Px: 0}, } box := &layout.Box{Node: n, X: 0, Y: 0, W: 100, H: 50} diff --git a/engine_test.go b/engine_test.go index e41361d..0d7acfc 100644 --- a/engine_test.go +++ b/engine_test.go @@ -164,8 +164,8 @@ func TestRenderDocumentGolden(t *testing.T) { if err != nil { t.Fatalf("read golden (run with UPDATE_GOLDEN=1 to create): %v", err) } - if !bytes.Equal(png, want) { - t.Errorf("render does not match golden fixture.png (%d vs %d bytes)", len(png), len(want)) + if !bytes.Equal(png, want) && !samePixels(t, png, want) { + t.Errorf("render does not match golden fixture.png (pixels differ)") } } diff --git a/fixtures_checkbox_test.go b/fixtures_checkbox_test.go index 6aa56b7..bb25792 100644 --- a/fixtures_checkbox_test.go +++ b/fixtures_checkbox_test.go @@ -13,9 +13,10 @@ import "testing" // Vector-2022 dropdowns. Solid-colour blocks only → font-independent bytes. // // Row layout (body margin 0, blocks stack): -// A hdr y 0..20 green (0,150,0) ; A menu HIDDEN (unchecked) -// B hdr y 20..40 olive (150,150,0) ; B menu SHOWN y 40..70 red (220,40,40) -// C hdr y 70..90 teal (0,150,150) ; C inv HIDDEN (:not(:checked) matches) +// +// A hdr y 0..20 green (0,150,0) ; A menu HIDDEN (unchecked) +// B hdr y 20..40 olive (150,150,0) ; B menu SHOWN y 40..70 red (220,40,40) +// C hdr y 70..90 teal (0,150,150) ; C inv HIDDEN (:not(:checked) matches) func TestCheckboxHackGolden(t *testing.T) { img := renderFixture(t, "checkbox_hack.html", 200, 130) diff --git a/fixtures_position_test.go b/fixtures_position_test.go index 6dd95a8..93b3cc8 100644 --- a/fixtures_position_test.go +++ b/fixtures_position_test.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "image" + stdpng "image/png" "os" "path/filepath" "testing" @@ -58,11 +59,40 @@ func checkGolden(t *testing.T, img *image.RGBA, name string) { if err != nil { t.Fatalf("read golden (run with UPDATE_GOLDEN=1 to create): %v", err) } - if !bytes.Equal(png, want) { - t.Errorf("render does not match golden %s (%d vs %d bytes)", name, len(png), len(want)) + // The contract is the pixels, not the encoder's output: the same pixels + // encode to different bytes when the compressor changes (Go 1.27 picks + // different DEFLATE block types), so compare the decoded images. + if !bytes.Equal(png, want) && !samePixels(t, png, want) { + t.Errorf("render does not match golden %s (pixels differ)", name) } } +func samePixels(t *testing.T, a, b []byte) bool { + t.Helper() + ia, err := stdpng.Decode(bytes.NewReader(a)) + if err != nil { + t.Fatalf("decode render: %v", err) + } + ib, err := stdpng.Decode(bytes.NewReader(b)) + if err != nil { + t.Fatalf("decode golden: %v", err) + } + if ia.Bounds() != ib.Bounds() { + return false + } + bounds := ia.Bounds() + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + r1, g1, b1, a1 := ia.At(x, y).RGBA() + r2, g2, b2, a2 := ib.At(x, y).RGBA() + if r1 != r2 || g1 != g2 || b1 != b2 || a1 != a2 { + return false + } + } + } + return true +} + // TestPositionDemoGolden verifies CSS position relative/absolute/fixed and // z-index stacking with exact painted-pixel invariants (the fixture uses only // solid colour blocks, so its bytes are font-independent and byte-golden-safe). diff --git a/go.mod b/go.mod index 41d2cb3..c727927 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-webengine/engine -go 1.26.4 +go 1.27.1 require ( github.com/dop251/goja v0.0.0-20260926152631-39ec2650adc9 @@ -29,7 +29,7 @@ require ( github.com/go-images/png v0.1.0 // indirect github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.18.7 // indirect github.com/refraction-networking/utls v1.8.2 // indirect github.com/sergeymakinen/go-bmp v1.0.0 // indirect github.com/sergeymakinen/go-ico v1.0.0 // indirect diff --git a/go.sum b/go.sum index 18a18c1..51ae9ae 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw= +github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo= github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M= diff --git a/images_test.go b/images_test.go index 990c427..1dfae9f 100644 --- a/images_test.go +++ b/images_test.go @@ -17,12 +17,12 @@ import ( func TestCSSImageSize(t *testing.T) { // Intrinsic 900x298 (the go.dev Google wordmark shape). cases := []struct { - name string - st *css.Style - iw, ih int - wantW int - wantH int - wantOK bool + name string + st *css.Style + iw, ih int + wantW int + wantH int + wantOK bool }{ {"nil style keeps intrinsic", nil, 900, 298, 0, 0, false}, {"no CSS dims keeps intrinsic", diff --git a/js/dynamic_test.go b/js/dynamic_test.go index 2165ed6..2ddf280 100644 --- a/js/dynamic_test.go +++ b/js/dynamic_test.go @@ -88,15 +88,15 @@ func TestSessionGeometryReadback(t *testing.T) { mustHaveJS(t, logs, "rect=10,20,200,40,210,60,10,20", "off=200,40,200,40,200,40", - "soff=5,2", // span relative to its offsetParent (the div) - "xoff=0,0,ow=0", // unlaid-out node: zeros, no ancestor rect - "xrect=0", // zero DOMRect - "rects=1,x=0", // d has a rect; x has none + "soff=5,2", // span relative to its offsetParent (the div) + "xoff=0,0,ow=0", // unlaid-out node: zeros, no ancestor rect + "xrect=0", // zero DOMRect + "rects=1,x=0", // d has a rect; x has none "disp=block,w=186px", - "unknown=[]", // unknown computed property → "" - "pri=[],txt=[]", // priority + cssText inert - "has=true,keys=0", // Has true, Keys empty (read-only view) - "dispAfter=block", // setProperty/removeProperty were no-ops + "unknown=[]", // unknown computed property → "" + "pri=[],txt=[]", // priority + cssText inert + "has=true,keys=0", // Has true, Keys empty (read-only view) + "dispAfter=block", // setProperty/removeProperty were no-ops ) } diff --git a/js/js_test.go b/js/js_test.go index eb13b8a..81edd84 100644 --- a/js/js_test.go +++ b/js/js_test.go @@ -566,8 +566,8 @@ func TestElementLangDirTabIndex(t *testing.T) { `) mustHave(t, logs, "lang=fr dir=rtl", - "anchorTab=0", // natively interactive: default 0 - "divTab=-1", // not interactive, no attribute: default -1 + "anchorTab=0", // natively interactive: default 0 + "divTab=-1", // not interactive, no attribute: default -1 "explicitTab=5", "badTab=-1", // unparseable tabindex falls back to the element's default "setTab=3") diff --git a/layout/edge17_test.go b/layout/edge17_test.go index 7de2d44..9c24a0c 100644 --- a/layout/edge17_test.go +++ b/layout/edge17_test.go @@ -264,8 +264,8 @@ func TestGridJustifyStartBorderBoxWidth(t *testing.T) { `
A
` g := findBox(layoutHTML(t, src, 400), "div") a := g.Children[0] - assertF(t, "jbb.A.W", a.W, 60) // border-box width preserved - assertF(t, "jbb.A.X", a.X, 40) // right-aligned: 100-60 + assertF(t, "jbb.A.W", a.W, 60) // border-box width preserved + assertF(t, "jbb.A.X", a.X, 40) // right-aligned: 100-60 } func TestFlexAlignContentFlexStart(t *testing.T) { @@ -327,13 +327,13 @@ func TestGridAreaNameNotFound(t *testing.T) { func TestGridSpanStartVariants(t *testing.T) { cases := []struct { - decl string + decl string wantX, wantW float64 }{ - {"grid-column:span 2", 0, 100}, // span-only start, auto-placed - {"grid-column:span 1 / 3", 50, 50}, // span 1 + end line - {"grid-column:span 3 / 2", 0, 150}, // span clamped to start 0 - {"grid-column-end:span 1", 0, 50}, // end span of 1 + {"grid-column:span 2", 0, 100}, // span-only start, auto-placed + {"grid-column:span 1 / 3", 50, 50}, // span 1 + end line + {"grid-column:span 3 / 2", 0, 150}, // span clamped to start 0 + {"grid-column-end:span 1", 0, 50}, // end span of 1 } for _, c := range cases { src := `
` + diff --git a/layout/features_test.go b/layout/features_test.go index c9ff561..894fb59 100644 --- a/layout/features_test.go +++ b/layout/features_test.go @@ -34,7 +34,7 @@ func TestMarginPercentShorthandNarrowsBoxSymmetrically(t *testing.T) { // with body margin 0 so cw is exactly 1024. src := `
x
` div := findBox(layoutHTML(t, src, 1024), "div") - assertF(t, "percent-margin.X", div.X, 102.4) // 1024 * 0.10 + assertF(t, "percent-margin.X", div.X, 102.4) // 1024 * 0.10 assertF(t, "percent-margin.ContentW", div.ContentW, 819.2) // 1024 - 2*102.4 } @@ -44,7 +44,7 @@ func TestMarginPercentLonghandsIndependently(t *testing.T) { // the shorthand (applyMarginShorthand). src := `
x
` div := findBox(layoutHTML(t, src, 1000), "div") - assertF(t, "percent-longhand.X", div.X, 250) // 1000 * 0.25 + assertF(t, "percent-longhand.X", div.X, 250) // 1000 * 0.25 assertF(t, "percent-longhand.ContentW", div.ContentW, 700) // 1000 - 250 - 50 } @@ -56,7 +56,7 @@ func TestMarginPercentLeftAutoRightMixed(t *testing.T) { // pre-existing precedence. src := `
x
` div := findBox(layoutHTML(t, src, 1000), "div") - assertF(t, "mixed.X", div.X, 100) // 1000 * 0.10 + assertF(t, "mixed.X", div.X, 100) // 1000 * 0.10 assertF(t, "mixed.ContentW", div.ContentW, 200) // explicit width, unaffected } diff --git a/layout/flex17_test.go b/layout/flex17_test.go index bebb665..2a5865a 100644 --- a/layout/flex17_test.go +++ b/layout/flex17_test.go @@ -99,7 +99,7 @@ func TestFlexRowGapWithWrap(t *testing.T) { `
C
` outer := findBox(layoutHTML(t, src, 400), "div") assertF(t, "rgap.C.Y", outer.Children[2].Y, 30) // 20 line + 10 gap - assertF(t, "rgap.container.H", outer.H, 50) // 20 + 10 + 20 + assertF(t, "rgap.container.H", outer.H, 50) // 20 + 10 + 20 } func TestFlexGapShorthandTwoValues(t *testing.T) { diff --git a/modules_test.go b/modules_test.go index f8c778d..5ca9e97 100644 --- a/modules_test.go +++ b/modules_test.go @@ -272,12 +272,12 @@ func TestResolveModuleSpecifier(t *testing.T) { importer, spec, want string ok bool }{ - {"", "https://cdn.ex/x.js", "https://cdn.ex/x.js", true}, // absolute - {"", "/packs/a.js", "https://ex.com/packs/a.js", true}, // root-relative vs page + {"", "https://cdn.ex/x.js", "https://cdn.ex/x.js", true}, // absolute + {"", "/packs/a.js", "https://ex.com/packs/a.js", true}, // root-relative vs page {"https://ex.com/packs/a.js", "./b.js", "https://ex.com/packs/b.js", true}, // relative vs importer {"https://ex.com/packs/sub/a.js", "../c.js", "https://ex.com/packs/c.js", true}, - {"", "react", "", false}, // bare specifier - {"", "", "", false}, // empty + {"", "react", "", false}, // bare specifier + {"", "", "", false}, // empty } for _, c := range cases { got, ok := resolveModuleSpecifier(page, c.importer, c.spec) diff --git a/paint/list_test.go b/paint/list_test.go index 4791385..6d10f1f 100644 --- a/paint/list_test.go +++ b/paint/list_test.go @@ -77,8 +77,8 @@ func TestPaintCircleMarker(t *testing.T) { func TestPaintDecimalMarker(t *testing.T) { img := paintMarkerOnly(48, 24, &layout.Marker{ Type: css.ListDecimal, Text: "1.", - Style: &css.Style{FontFamily: css.Sans, FontSize: 16, FontWeight: 400, Color: markerBlack}, - X: 12, Y: 2, Ascent: 12, + Style: &css.Style{FontFamily: css.Sans, FontSize: 16, FontWeight: 400, Color: markerBlack}, + X: 12, Y: 2, Ascent: 12, }) // Some ink lands in the glyph region. ink := 0