Add native <form> submission fallback - #93
Merged
Merged
Conversation
F4 of the interactive-form-input plan (#89-#91): the path for a plain server-rendered login form with no JS submit handler at all. LiveDocument.Submit(ctx, form) dispatches "submit" through the live session first, exactly like a real browser. If a listener calls preventDefault() (the common SPA case, already reachable via Focus/Type/Click plus the engine's existing fetch()/XHR support), nothing further happens and the current page stays open. Otherwise it gathers named form-control values (input/textarea/select, skipping disabled controls and unchecked checkboxes/radios, matching what a real browser submits) and performs the actual GET-with-querystring or POST-urlencoded request per the form's action/method, then opens the response as a new LiveDocument and closes the old one — a real navigation ending the previous page's JS, same as a browser. Caught two real gaps while writing this against the actual DOM model rather than assuming: <select>'s submitted value needs its own selected-option walk (no shared helper existed at the engine-package level); <textarea> has no dedicated JS value accessor (js/dom.go's generic "value" property just reads the "value" attribute, which a textarea doesn't carry until something explicitly sets it) — Submit's own value-gathering falls back to the element's text content for an untouched textarea, matching what a real browser's textarea.value defaults to, while documenting the JS-binding gap for anyone who hits it from script instead. 100% coverage on every function except postForm (83.3% — the remaining gaps are low-level I/O failure branches http.NewRequest/ ReadAll/decodeCharset/dom.Parse can each return, consistent with this codebase's own existing tolerance for that class of near-unreachable path: Fetch and decodeCharset sit at 82.4%/75% on main today for the same reason). Full existing suite + -race unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tannevaled
added a commit
that referenced
this pull request
Sep 3, 2026
…d as #93 while this branch was in flight)
tannevaled
added a commit
that referenced
this pull request
Sep 3, 2026
* fix(css): hide <option> content instead of leaking it into layout
A <select> falls into the generic display:inline UA bucket alongside
<button>/<input>, but unlike those its <option> children carry real
visible text with no UA rule to say a native control never lays it
out. Observed live on pkg.go.dev/net/http: a version/tab-switcher
<select> holding the page's entire symbol index as option text
rendered as a garbled, concatenated block near the top of the page.
Fixed with the same precedent already used for <template>'s inert
content: option { display: none }. Matches the existing, accepted
simplification that an <input>'s value is never shown either.
pkg.go.dev/net/http: SSIM 0.530->0.616, pixdiff 45.8%->40.0%. Full
10-page bench corpus re-run confirms nothing else regressed.
* fix PR number citation: engine#93 -> engine#94 (a concurrent PR landed as #93 while this branch was in flight)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
F4 of the interactive-form-input plan (#89–#91): the path for a plain server-rendered login form with no JS submit handler at all.
LiveDocument.Submit(ctx, form)dispatchessubmitthrough the live session first, exactly like a real browser. If a listener callspreventDefault()(the common SPA case, already reachable viaFocus/Type/Clickplus the engine's existingfetch()/XHR support), nothing further happens and the current page stays open. Otherwise it gathers named form-control values and performs the real GET-with-querystring or POST-urlencoded request per the form'saction/method, opens the response as a newLiveDocument, and closes the old one — a real navigation ending the previous page's JS, same as a browser.Two real gaps surfaced while writing this against the actual DOM model:
<select>needed its own selected-option walk (nothing at the engine-package level did this yet), and<textarea>has no dedicated JS value accessor at all (js/dom.go's genericvalueproperty just reads thevalueattribute, which a textarea never carries until something sets it) —Submit's gathering falls back to the element's text content for an untouched textarea, matching realtextarea.valuesemantics, with the JS-binding gap documented for whoever hits it from script.Test plan
-racepass,go vet/gofmtcleanpostForm(83.3% — remaining gaps are low-level I/O failure branches, matching this codebase's own existing tolerance:Fetch/decodeCharsetsit at 82.4%/75% onmainfor the same class of near-unreachable path)TestSubmitPOST/TestSubmitGETAppendsQuery— real HTTP round-trip against anhttptestserver, checking the server actually received the typed valuesTestSubmitSkippedWhenJSPreventsDefault— no request made when a script intercepts submitTestFormDataGathering— checkbox/radio/disabled/select/textarea/button-exclusion all in one fixture🤖 Generated with Claude Code