Skip to content

#11-tags-T2d-input-list: implement <input>.list IDREF → <datalist> resolution (HTML §4.10.5.1.16)#195

Merged
send merged 4 commits into
mainfrom
feat/tags-t2d-input-list
May 17, 2026
Merged

#11-tags-T2d-input-list: implement <input>.list IDREF → <datalist> resolution (HTML §4.10.5.1.16)#195
send merged 4 commits into
mainfrom
feat/tags-t2d-input-list

Conversation

@send
Copy link
Copy Markdown
Owner

@send send commented May 17, 2026

Why this PR

Closes the §H-7k T2d defer slot #11-tags-T2d-input-list (~50 LoC stub since PR #181 D-7 T2d). The original trigger ("T2d datalist surface lands → user / WPT report") has fired: PR #181 shipped <datalist> ctor + per-tag prototype + datalist.options HTMLCollection, so the IDREF back-ref from <input>.list is now implementable.

Per feedback_ship-first-over-close: ship-close rather than close-as-won't-fix.

Scope

Engine-indep (crates/dom/elidex-form/)

  • input.rs::resolve_input_list(dom, input_entity) — sibling to resolve_label_for (HTML §4.10.4), same shape:
    • Read list content attribute
    • Empty/absent → None
    • find_tree_root → root-inclusive pre-order DFS via traverse_descendants
    • Filter-during-walk: first <datalist> (ASCII-CI tag) with matching id (case-sensitive per §6.13.2)
  • matches_datalist_with_id helper — tag-first guard cascade
  • lib.rs re-export added
  • 12 unit tests locking spec edge cases (no-attr / empty / id-not-exist / target-not-datalist / matched / duplicate-id tree-order / case-sensitive / non-datalist-then-datalist / detached / self-ref / no-whitespace-trim / empty-id-skip-then-match)

VM thin binding (crates/script/elidex-js/src/vm/host/)

  • html_input_proto.rs::native_input_get_list — replaced Ok(JsValue::Null) stub with: require_input_receiver (brand check first) → elidex_form::resolve_input_listwrap_entity_or_null. Mirrors native_input_get_form (find_form_ancestor) call shape.
  • tests_html_input_proto.rs — deleted input_list_returns_null_stub, added 6 VM tests including identity-stable-across-reads regression

Boa parallel

  • No-op (boa has no input.list stub; deletion path per memory/project_boa_runtime_deletion.md)

Spec compliance

WHATWG HTML §4.10.5.1.16: "the first element in the tree of type HTMLDataListElement whose ID is equal to the value of the list attribute, if that element is in the same tree as the input element"

  • ✅ Tree-order first match (pre-order DFS root-inclusive)
  • ✅ Filter-during-walk (rejects <div id="x"> before <datalist id="x">)
  • ✅ Same-tree scope (find_tree_root; honors shadow boundaries)
  • ✅ Case sensitivity (id case-sensitive, tag ASCII-CI)
  • ✅ Whitespace not trimmed (browser parity)
  • ✅ Identity-stable across reads (create_element_wrapper per-entity cache; not [SameObject] in IDL but matches Chrome/Firefox)

Out of scope (documented)

  • Cross-tree resolution (shadow-piercing) — tracked at #11-form-elements-cross-tree
  • HTML-namespace filter — currently omitted because TagType implies element-hood in elidex's HTML-only model; tracked at #11-element-namespace-tracking (forward-reference comment in matches_datalist_with_id)

Defer ledger impact

Pre-PR: 36/35 over-cap-by-1.
Closes: #11-tags-T2d-input-list (§H-7k row).
Opens: 0 NEW.
Post-PR: 35/35 cap-at, headroom 0.

Test plan

  • cargo fmt --all
  • mise run ci — all green
  • cargo test -p elidex-form --all-features --lib input::tests::resolve_input_list12 pass
  • cargo test -p elidex-js --all-features --lib tests_html_input_proto::input_list6 pass
  • Pre-impl 3-agent self-review (arch / spec / r-traj) — 11 IMP + 7 MIN folded into plan v1.1
  • /simplify pre-push — 1 efficiency fix (drop tautological is_html_namespace) + 2 doc trims
  • /review pre-push — TERMINAL (0 CRIT, 0 IMP, 4 MIN optional, 0 FP)

🤖 Generated with Claude Code

…solution (HTML §4.10.5.1.16)

Closes the §H-7k T2d defer slot.  The previous stub returned `null`
unconditionally because the T2d datalist surface was not yet shipped;
PR #181 landed `<datalist>` infrastructure, so the IDREF back-ref
is now implementable.

Engine-indep resolver `resolve_input_list` lives in `elidex-form/src/input.rs`
sibling to `resolve_label_for` (HTML §4.10.4) — same shape: read attr,
walk input's tree root-inclusive, filter-during-walk for first matching
datalist.  VM-side getter is pure marshalling: brand check via
`require_input_receiver`, call resolver, wrap via `wrap_entity_or_null`.

- Tree scope honors shadow boundaries — nested shadow subtrees within
  the same root are excluded per spec "same tree" wording.  Cross-tree
  resolution tracked at `#11-form-elements-cross-tree`.
- Tag match ASCII-CI (HTML §3.2.6.5); id match case-sensitive (§6.13.2).
- Filter-during-walk: a `<div id="x">` appearing earlier in tree order
  before `<datalist id="x">` does NOT poison the lookup (spec: "first
  element of type HTMLDataListElement").
- Identity-stable across reads via `create_element_wrapper`'s per-entity
  cache — HTML §4.10.5.1.16 IDL is not `[SameObject]` but matches
  Chrome / Firefox behaviour.
- HTML-namespace filter currently omitted because `TagType` implies
  element-hood in elidex's HTML-only model; defer slot
  `#11-element-namespace-tracking` tracks the tightening.
- 12 engine-indep tests + 6 VM tests cover spec edge cases (no-attr /
  empty / id-not-exist / target-not-datalist / matched / duplicate-id
  tree-order / case-sensitive / non-datalist-then-datalist / detached /
  self-ref / no-whitespace-trim / empty-id-skip-then-match / identity).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 17, 2026 06:46
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements <input>.list datalist resolution by adding an engine-independent IDREF lookup helper and wiring the VM accessor to return the matched <datalist> wrapper instead of the previous null stub.

Changes:

  • Added elidex_form::resolve_input_list and re-exported it.
  • Updated HTMLInputElement.prototype.list to resolve and wrap matching datalist elements.
  • Expanded engine-independent and VM-level tests for list resolution behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
crates/dom/elidex-form/src/input.rs Adds the <input>.list resolution algorithm and unit tests.
crates/dom/elidex-form/src/lib.rs Re-exports resolve_input_list.
crates/script/elidex-js/src/vm/host/html_input_proto.rs Wires the list getter to the new form helper.
crates/script/elidex-js/src/vm/tests/tests_html_input_proto.rs Replaces the null-stub test with VM tests for datalist resolution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/dom/elidex-form/src/input.rs
… input type applicability (HTML §4.10.5.1.16)

Per WHATWG HTML §4.10.5.1.16: the `list` IDL attribute does not apply to
input types `hidden` / `checkbox` / `radio` / `file` / `submit` / `image` /
`reset` / `button` / `password`.  The getter must return null for these
types even when a matching `<datalist>` exists in the same tree.

- Add `FormControlKind::list_applies()` predicate (sibling to
  `readonly_applies` shape) listing the allow-listed kinds.
- Add `input_list_applies_to_type(dom, entity)` helper with the
  `FormControlState.kind` → raw `type` attribute fallback pattern from
  `label.rs::is_labelable_element`.
- Gate `resolve_input_list` on it (early null before any IDREF walk).
- 9 new engine-indep regression tests covering each excluded type plus
  the FormControlState-path and ASCII-CI attr-path coverage.
- 1 new VM test looping over all 8 excluded types.

Image type is documented as a pre-existing FormControlKind coverage gap
(falls back to TextInput via `from_type_str`) — out of scope here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

crates/dom/elidex-form/src/input.rs:755

  • This added test block pushes crates/dom/elidex-form/src/input.rs past the repository's documented ~1000-line convention for splitting large Rust modules/tests (for example, crates/dom/elidex-dom-api/src/element/mod.rs:35-36). Please split the new resolve_input_list tests into a sibling test module/file to keep this module within the established size limit.
    // -----------------------------------------------------------------
    // resolve_input_list — HTML §4.10.5.1.16 `<input>.list` IDREF
    // -----------------------------------------------------------------

Comment thread crates/dom/elidex-form/src/lib.rs Outdated
Comment thread crates/dom/elidex-form/src/input.rs Outdated
Comment thread crates/script/elidex-js/src/vm/tests/tests_html_input_proto.rs Outdated
…applicability check, includes image type (HTML §4.10.5.1.16)

Three R2 findings, single root cause: `input_list_applies_to_type`
preferred the cached `FormControlState.kind` over the live `type`
content attribute, and routed the exclusion check through
`FormControlKind::from_type_str` which collapses `"image"` onto
`TextInput`.  Two interlocking bugs:

1. `setAttribute('type', 'hidden')` (or `image` / `checkbox` / ...) only
   mutates `Attributes`; `state.kind` lags until a type-change sync
   pass.  Preferring the cached kind let stale state mask the mutation
   and incorrectly resolve a datalist.
2. `<input type=image>` falls back to `TextInput` via `from_type_str`,
   which was on the allow-list — wrongly admitting image.

Fix: rewrite `input_list_applies_to_type` to read the `type` content
attribute directly (spec source of truth per HTML §4.10.5.1.16) and
match against the literal exclusion set { hidden, checkbox, radio,
file, submit, image, reset, button, password }.  Dropped the orphan
`FormControlKind::list_applies` predicate (its sole caller was the
removed code path; the from_type_str/image gap made it
unmaintainable as a public predicate).

- Stale-state regression test
  (`resolve_input_list_ignores_stale_form_control_state_when_attr_excludes`)
  attaches a `TextInput`-kind state to an `<input type=hidden>` and
  asserts the attribute wins.
- Dedicated `resolve_input_list_returns_none_for_image_type` test.
- `image` added to the existing button-types loop + the VM
  inapplicable-types loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread crates/dom/elidex-form/src/input.rs Outdated
… to sibling input_tests.rs (1000-line convention)

R3 finding: `input.rs` reached 1075 LoC, violating the project's
~1000-line file convention documented in
`cleanup-1000-line-files-tranche-3-complete.md`.

Move the entire `#[cfg(test)] mod tests` body to a new sibling file
`input_tests.rs` and include it via the established
`#[cfg(test)] #[path = "input_tests.rs"] mod tests;` pattern (mirror
of `lib.rs:743` precedent).

Post-split: `input.rs` is 404 LoC (production-only), `input_tests.rs`
is 671 LoC (test-only).  Test count and contents unchanged — 55 tests
still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@send send merged commit 41cc905 into main May 17, 2026
10 checks passed
@send send deleted the feat/tags-t2d-input-list branch May 17, 2026 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants