fix: align HTML parsing with the spec

Merged
FB
7mo ago
+3246-978

Lots of changes to align us better with the HTML spec:

  • Raw-text tags: <iframe>, <noembed>, <noframes>, and <plaintext> now behave like <script> and <style> — their content is treated as raw text, not parsed as HTML
  • RCDATA entities: <textarea> now decodes entities like <title> already did
  • Bogus comments: <?…>, <!…> (non-DOCTYPE), and </ …> in HTML mode now emit comments instead of being silently dropped or emitted as text
  • Comment edge cases: <!-->, <!--->, <!->, and <!> are now parsed as valid (empty) comments per the spec
  • EOF handling: Unclosed comments, partial <!DOCTYPE, <?…, and <![CDATA[… at end-of-input now emit the correct token type instead of raw text
  • CDATA in HTML: Partial CDATA matches like <![CD> emit a bogus comment; unclosed <![CDATA[… at EOF emits [CDATA[… as a comment (without a spurious ]] suffix)
  • </>: Empty close tags are now silently ignored in HTML mode instead of emitting </> as text
  • DOCTYPE without space: <!DOCTYPEhtml> is now recognized as a DOCTYPE, not a bogus comment
  • SVG/MathML support: Tag names inside <svg> are case-adjusted per the spec (e.g. foreignObject, clipPath); CDATA sections are treated as text; special-tag detection is disabled inside foreign content
  • <image> alias: <image> is rewritten to <img> outside foreign content, matching browser behavior
  • Implicit closes: <h1>–<h6> now close an open heading; <a> closes a previous <a>; nested <form> is ignored when one is already open
  • Self-closing in HTML: <script/>, <style/>, etc. now correctly enter their raw-text state in HTML mode (the / is ignored per spec) unless recognizeSelfClosing is enabled
  • Foreign context correctness: Stray </svg> or </math> no longer corrupt the parser's context tracking; implicitly closed foreign elements are properly cleaned up

Helped by a number of AI Agents (Opus 4.6, GPT-5.4, Devin Reviews).