Skip to content

forms: add enctype + 5 submitter form-* IDL accessors#2450

Merged
karlseguin merged 6 commits into
lightpanda-io:mainfrom
navidemad:fix-bug7-form-idl
May 14, 2026
Merged

forms: add enctype + 5 submitter form-* IDL accessors#2450
karlseguin merged 6 commits into
lightpanda-io:mainfrom
navidemad:fix-bug7-form-idl

Conversation

@navidemad
Copy link
Copy Markdown
Contributor

@navidemad navidemad commented May 13, 2026

What

Adds six missing form-submission IDL accessors so reads of form.enctype and submitter.{formAction, formEnctype, formMethod, formNoValidate, formTarget} return the spec-mandated string or boolean instead of undefined. Companion to #2279 (which wired up the content-attribute submission path for the same set of attributes) — same WHATWG sections, same canonicalization rules, just the JS-property side that downstream CDP clients read.

Also bundled: support for enctype="text/plain" form submissions per HTML §4.10.21.8. Before this PR the IDL accessor and the submission path silently agreed (both ignored text/plain). With the IDL accessor now returning "text/plain" per spec, the submission path was the next link in the chain — left untouched it would have re-encoded as urlencoded with a .not_implemented warning. Reviewers preferring to land just the read-only IDL accessors first can ask to drop commits 4b693db4..f0cce427; both are self-contained and revert cleanly.

Closes #2449.

Root cause

The accessors were absent from the JsApi blocks of Form.zig, Button.zig, and Input.zig. Reading form.enctype from JS resolved through V8 to "property not on the prototype" → undefined. Runtime.evaluate returned result.value === undefined. CDP clients that then call .toLowerCase() on the return crash with TypeError: Cannot read properties of undefined.

flowchart LR
    A[Runtime.evaluate form.enctype] --> B[no accessor on HTMLFormElement.prototype]
    B --> C[returns undefined, TypeError downstream]
    style B fill:#fdd
    style C fill:#fdd
Loading

Fix

Commit 2fdc82aa — IDL accessors:

  • src/browser/webapi/element/html/Form.zig — add getEnctype / setEnctype mirroring the existing getMethod / setMethod (limited-to-known-values pattern), register enctype in JsApi.
  • src/browser/webapi/element/html/Button.zig — add five getter/setter pairs (getFormAction, getFormEnctype, getFormMethod, getFormNoValidate, getFormTarget) and register them in JsApi. URL-resolving getFormAction mirrors Form.getAction.
  • src/browser/webapi/element/html/Input.zig — same five accessors registered alongside the existing form accessor.

Commit cedfdba0 — IDL DRY pass:

  • Extract Form.normalizeMethod / Form.normalizeEnctype helpers; the five IDL callsites (existing Form.getMethod + four new accessors) collapse to one-liners. Net -36 LOC.

Commit 4b693db4text/plain submission:

  • src/browser/webapi/net/FormData.zigEncType gains a .plaintext variant; plaintextEncode writes name=value\r\n per entry per HTML §4.10.21.8.
  • src/browser/Frame.zigsubmitForm recognizes text/plain before the urlencoded fallback, sets Content-Type: text/plain; charset=<form-charset> per spec step 21.4.

Commit f0cce427 — submission-path DRY:

  • src/browser/Frame.zigsubmitForm now consumes Form.normalizeMethod / Form.normalizeEnctype instead of inlining the eqlIgnoreCase chain a sixth time. Single function owns the canonical mapping for both IDL reads and the submission path. Drops the log.warn(.not_implemented, "FormData.encoding", ...) branch — after text/plain lands, the only values still reaching the urlencoded fallback are spec-invalid ones, which per §4.10.21.5 silently canonicalize (Chrome doesn't log either).

Defaults follow WHATWG HTML strictly:

Accessor Missing Invalid
form.enctype application/x-www-form-urlencoded application/x-www-form-urlencoded
submitter.formEnctype "" application/x-www-form-urlencoded
submitter.formMethod "" get
submitter.formAction document URL (frame.url) resolved URL
submitter.formTarget "" (no enumeration — passes through)
submitter.formNoValidate false true (boolean attribute reflection)
flowchart LR
    A[Runtime.evaluate form.enctype] --> B[Form.getEnctype reads attribute, normalizes]
    B --> C[returns application/x-www-form-urlencoded]
    style B fill:#dfd
    style C fill:#dfd
Loading

Test

  • HTML fixtures under src/browser/tests/element/html/:
    • form.html — three new <script> blocks covering enctype initial / set / normalization.
    • button.html — eleven new <script> blocks covering all five submitter overrides (initial / set / invalid-value / normalization).
    • input-attrs.html — eight new <script> blocks mirroring the button coverage on <input type=submit>.
  • Zig unit tests under src/browser/webapi/net/FormData.zig:
    • FormData: plaintext write — three entries including one with embedded = and CRLF; asserts the spec's verbatim-write semantics.
    • FormData: plaintext empty body — empty entry list → empty output.
  • All runners pass: TEST_FILTER='WebApi: HTML.Form', 'WebApi: HTML.Button', 'WebApi: HTML.Input', 'FormData: plaintext'. Full zig build test clean (639/639).
  • Toggle-off check: reverting FormData.zig while keeping Frame.zig causes a compile failure on the new .plaintext variant (proves the production code routes through it); reverting all three webapi/element/html/*.zig production files while keeping the HTML fixtures causes the three runners to fail (proves the fixtures exercise the new accessors).
  • Reproducer (Closes-linked issue forms: enctype + 5 submitter form-* IDL accessors return undefined #2449): a self-contained repro.sh exits 1 against installed nightly (1.0.0-nightly.6198+2e159aaf — all 11 IDL reads return undefined) and exits 0 against a local debug build off this branch (all 11 reads return spec-typed values, including formAction resolving to the document URL when missing).

Notes

  • The submitter formAction getter intentionally diverges from the submitter.X || form.X short-circuit some downstream JS uses: per HTML §4.10.21.6 it MUST return the document URL when the attribute is missing, not "". Callers that want fall-through to the form's value should check submitter.hasAttribute("formaction") first (matches Chrome behavior).
  • No CDP surface changes — this is purely a JS API addition. Existing CDP Network.requestWillBeSent payloads for submitter-triggered submissions are unaffected (those were addressed by forms: honor formaction / formmethod / formenctype on submit button #2279).

navidemad and others added 6 commits May 13, 2026 17:49
Six form-submission IDL accessors were missing from the JsApi blocks of
HTMLFormElement, HTMLButtonElement, and HTMLInputElement, so reads
produced undefined instead of the spec-mandated string/boolean. The
content-attribute path (clicking a submit button honoring formaction /
formmethod / formenctype) was wired up in lightpanda-io#2279; this commit adds the
matching IDL-property accessors per WHATWG HTML §4.10.18.6 and §4.10.21.5.

- Form.enctype: limited to known values, missing+invalid both default to
  application/x-www-form-urlencoded (mirrors getMethod's shape).
- Button/Input formAction: returns frame.url when missing/empty, else the
  resolved URL (mirrors Form.getAction).
- Button/Input formEnctype, formMethod: limited to known values with no
  missing-value default ("" when missing, canonical invalid-value default
  application/x-www-form-urlencoded / get when invalid).
- Button/Input formTarget: plain reflection, defaults to "".
- Button/Input formNoValidate: boolean reflection of formnovalidate.

Closes lightpanda-io#2449
The "limited to only known values" canonicalization (per WHATWG HTML
§2.2.2) was duplicated five times: Form.getMethod + Form.getEnctype +
{Button,Input}.{getFormMethod,getFormEnctype}. Each callsite differed
only in the missing-value default ("" for submitter overrides, "get" /
"application/x-www-form-urlencoded" for the form-side).

Extract into two pub helpers on Form.zig taking the attribute slice +
the missing-value default. The five callers collapse to one-liners.

Behavior-preserving: existing form.html / button.html / input-attrs.html
fixtures all pass unchanged; full suite 637/637 green.

Net: -36 LOC.
Closing the divergence introduced by the new IDL accessors: `submitter.formEnctype`
(and `form.enctype`) now return "text/plain" for that attribute value per WHATWG
HTML §4.10.21.5, but `Frame.submitForm` previously fell back to urlencoded with
a `.not_implemented` log when it saw the same value on the submission path.

Implement the spec's text/plain encoding algorithm (HTML §4.10.21.8):

  - FormData.EncType gains a `.plaintext` variant.
  - FormData.plaintextEncode writes "name=value CRLF" per entry, no URL-encoding,
    no escaping — the spec accepts that text/plain is a lossy, human-readable
    encoding (values containing "=" or CRLF produce an ambiguous wire format
    by design).
  - Frame.submitForm recognizes "text/plain" before the urlencoded fallback and
    sets the Content-Type header to "text/plain; charset=<form-charset>", per
    spec step 21.4.

Two new Zig unit tests cover encoding output (`FormData: plaintext write`,
`FormData: plaintext empty body`). Full suite 639/639 green.

This is bundled with the IDL accessor commits because returning "text/plain"
from the IDL while the submission silently re-encodes as urlencoded is a
spec-internal inconsistency the IDL change itself introduces. Reviewers who'd
prefer to land just the read-only accessors first should feel free to ask for
a split — this commit is self-contained and reverts cleanly.
…nctype

The submitForm encoding path was the last duplicate of the "limited to
only known values" canonicalization the previous commit consolidated for
the IDL getters. Now it consumes the same Form.normalizeMethod /
Form.normalizeEnctype helpers, so a single function owns the canonical
mapping (`""` / unknown -> spec default, recognized values pass through
unchanged).

Side effect of routing through the helper: the
`log.warn(.not_implemented, "FormData.encoding", ...)` branch falls out.
After commit 4b693db added `text/plain`, the only attribute values that
still reach the urlencoded fallback are spec-invalid ones, which per
HTML §4.10.21.5 silently canonicalize to
`application/x-www-form-urlencoded`. The warning was firing for valid
spec behavior — Chrome doesn't log either.

Behavior-preserving on all observable surfaces: full suite 639/639 green;
existing form-submission integration tests (multipart, urlencoded,
text/plain, GET-ignores-enctype) all pass unchanged.
@karlseguin karlseguin merged commit 143bffd into lightpanda-io:main May 14, 2026
21 of 23 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators May 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

forms: enctype + 5 submitter form-* IDL accessors return undefined

2 participants