Skip to content

Close a void element that set_tag_name renames to a non-void name (port of cloudflare/lol-html#337) - #4

Open
robobun wants to merge 1 commit into
bunfrom
robobun/void-rename-end-tag
Open

robobun wants to merge 1 commit into
bunfrom
robobun/void-rename-end-tag

Conversation

@robobun

@robobun robobun commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

The bun-branch port of cloudflare#337, for Bun's HTMLRewriter. It is the same patch with two differences for handler suspension. The start tag is reached through tag()/tag_mut(). StartTag::take_owned copies the new flag, so the end tag survives a suspension (new test void_element_renamed_across_suspension).

Element::set_tag_name on an HTML void element (<img>, <br>, <input>, <hr>, ...) rewrote only the start tag, so nothing closed the new name: <img src=a.png><p>next</p> renamed to picture came out as <picture src=a.png><p>next</p>, and both <p> re-parse as its children. With this change the serializer writes </picture> right after the start tag (before any after() content) unless the new name is itself void. Foreign-content /> elements, remove() and replace() are unchanged, and can_have_content() stays false.

Not to be merged ahead of upstream review: if cloudflare#337 changes there, this branch is replaced by a cherry-pick of the merged commit, as with #2.

Rebased on 2026-09-22 onto the v3.0.1-based bun (e1a306d). The earlier port for the v2.7.2 lineage is kept at robobun/void-rename-end-tag-v2.7.2 (5816ca4). oven-sh/bun#41968 (draft) pins that commit while Bun's main is on the v2.7.2 lineage, and moves to this branch after the v3.0.1 move lands in Bun.

cargo test --features _integration_test: 210 + 3 + 38 + 2 pass. cargo clippy --all-targets: no new warnings. cargo fmt --check: clean. The red checks on this PR fail at "Set up job" before any command runs, as they do on the bun branch itself.

robobun added a commit to oven-sh/bun that referenced this pull request Sep 15, 2026
… name

Pin oven-sh/lol-html at 5816ca4 (oven-sh/lol-html#4, the bun-branch
port of cloudflare/lol-html#337). `el.tagName = "picture"` on `<img>`
rewrote only the start tag, so the following siblings re-parsed as
children of the unclosed `<picture>`. lol-html now writes `</picture>`
right after the start tag unless the new name is void too.

The pin also moves past ea6125b (descendant-selector matching linear
in depth), which #41479 pins on its own.
`Element::set_tag_name` on an HTML void element (`<img>`, `<br>`,
`<input>`, `<hr>`, ...) rewrote only the start tag. A void element has
no end tag in the input, so nothing closed the new name: every sibling
after `<img>` renamed to `picture` re-parsed as a child of `<picture>`,
and after a rename to `textarea` or `style` the rest of the document
became its text.

The start tag now carries an `emit_end_tag` flag. `set_tag_name` sets
it when the element cannot have content, is in the HTML namespace, and
the new name is not itself a void element name. The serializer then
writes `</name>` straight after the start tag, before any `after()`
content, and drops a decorative `/>`. A rename to another void name,
a rename of a self-closing element in foreign content (closed by its
`/>` whatever the name), and `remove()`/`replace()` are unchanged. The
element still reports `can_have_content() == false`, so `prepend`,
`append` and `set_inner_content` stay no-ops and no end tag handlers
run for the written tag.

The void-name list moves from `selectors_vm::stack` to
`Tag::is_void_html_element` so both callers share it.

Nine existing tests used an `<img>` rename as a match marker and now
expect the end tag too.

Port of cloudflare#337 (8dba2f7) to the `bun` branch. The
differences: the start tag is reached through `tag()`/`tag_mut()`, and
`StartTag::take_owned` copies the flag so that it survives a handler
suspension (new test `void_element_renamed_across_suspension`).
@robobun
robobun force-pushed the robobun/void-rename-end-tag branch from 5816ca4 to 55edbfd Compare September 22, 2026 12:17
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.

1 participant