v0.18.13 - Empty non-void HTML elements inside <foreignObject> are serialized as XML self-closing (<div/>)
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 68/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 静か
- 技術スタック
- html, node.js
- 領域
- web-dev
調査の方向性
提供された parseHTML の再現コードから始め、foreignObject における namespace の処理に焦点を当てながら、document.body.innerHTML がシリアライズされる過程を追跡します。非 void HTML 要素が明示的な終了タグを使用し、列挙された void HTML 要素は自己終了できることを確認し、その後、シリアライズされた結果をラウンドトリップしても兄弟要素の内容が飲み込まれないことを検証します。
索引モデルが issue の本文から書いたものです。
説明
Empty non-void HTML elements inside <foreignObject> are serialized as XML self-closing (<div/>), producing invalid HTML that swallows following content
Summary
When an empty HTML element (e.g. <div>, <p>, <span>) appears inside an SVG <foreignObject>, parseHTML(...).document.toString() / .innerHTML serializes it using XML self-closing syntax (<div/>) instead of an explicit start/end tag pair (<div></div>).
Per the HTML standard, the self-closing slash is ignored on non-void HTML elements, so <div/> is parsed as an open <div>. Because <foreignObject> is an HTML integration point (its subtree is parsed as HTML, not SVG), this output is invalid: when a spec-compliant HTML parser (i.e. any browser) re-parses it, the unclosed <div> swallows every following sibling — the rest of the <foreignObject>, the rest of the <svg>, and all content after it.
The result is that a serialized document round-tripped through linkedom renders blank / severely mis-nested in a browser.
Environment
- linkedom: 0.18.13
- Node: v22.16.0
Minimal reproduction
const { parseHTML } = require('linkedom');
const input = '<svg><foreignObject><div></div></foreignObject></svg>';
const { document } = parseHTML('<!DOCTYPE html><html><body>' + input + '</body></html>');
console.log(document.body.innerHTML);
Actual output
<svg><foreignobject><div /></foreignobject></svg>
Expected output
<svg><foreignObject><div></div></foreignObject></svg>
<div> is a non-void HTML element and must be serialized as <div></div>. (foreignObject should also retain its camelCase — see note below.)
Scope / characterization
Only empty non-void elements in an SVG/foreignObject context are affected:
| Input | Output | Correct? |
|---|---|---|
<svg><foreignObject><div></div></foreignObject></svg> |
<div /> |
❌ self-closed |
<svg><foreignObject><p></p></foreignObject></svg> |
<p /> |
❌ self-closed |
<svg><foreignObject><div>hi</div></foreignObject></svg> |
<div>hi</div> |
✅ (non-empty) |
<div></div> (plain HTML) |
<div></div> |
✅ (outside SVG) |
So the trigger is: an empty element serialized while it is (incorrectly) treated as being in the XML/SVG namespace, even though <foreignObject> switches its subtree back to the HTML namespace.
Why it matters
<div/> is not equivalent to <div></div> in HTML. Given sibling content:
parseHTML('<!DOCTYPE html><body><svg><foreignObject><div></div><span>X</span></foreignObject></svg>')
.document.body.innerHTML
// => <svg><foreignobject><div /><span>X</span></foreignobject></svg>
linkedom keeps <span> as a sibling internally, but the emitted string is not round-trippable: a browser parsing <div /><span>X</span> inside a foreignObject nests <span> (and everything after) inside the <div>. Real-world impact: serializing a captured page that contains an inline SVG icon built with <foreignObject><div .../></foreignObject> (common on e.g. google.com) makes the whole page render blank.
Suggested fix
Elements inside a <foreignObject> are in the HTML namespace and should follow HTML serialization rules: only the HTML void elements (area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr) may be self-closed; all other elements must be serialized with an explicit end tag.
- 主要言語
- HTML
- スター
- 2.1k
- フォーク
- 108
- PR マージ指標
- 30日以内にマージされた PR はありません
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
WebReflection/linkedom のほかの issue
-
難易度 3/5 1〜2日 初心者へのやさしさ 52/100
WebReflection/linkedom#327 · コメント 1 件 ·
-
難易度 3/5 1〜2日 初心者へのやさしさ 48/100
WebReflection/linkedom#326 · コメント 5 件 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 15/100
WebReflection/linkedom#321 · コメント 1 件 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
WebReflection/linkedom#318 · コメント 6 件 ·
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
WebReflection/linkedom#317 · リアクション 2 件 ·
WebReflection/linkedom の issue をすべて見る
似ている issue
-
needs:triage
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
txn2/mcp-data-platform#1984 ·
メンテナーはふだん 1 日以内に返信
-
ai-discovered
難易度 2/5 1〜3時間 初心者へのやさしさ 83/100
メンテナーはふだん 1 日以内に返信
-
agent-canvas bug llm priority:low ready-for-dev
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
OpenHands/OpenHands#17806 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
todo
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
RocketChat/Rocket.Chat#42442 ·
メンテナーはふだん 1 日以内に返信