Skip to content

fix: do not line-end-normalize a CR character reference in content#41

Merged
rgrove merged 1 commit into
rgrove:mainfrom
spokodev:fix/cr-char-ref-in-content
Jun 27, 2026
Merged

fix: do not line-end-normalize a CR character reference in content#41
rgrove merged 1 commit into
rgrove:mainfrom
spokodev:fix/cr-char-ref-in-content

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

Problem

A character reference resolving to a carriage return (
 or 
) in element text content is wrongly converted to a line feed.

parseXml('<a>&#xD;</a>').root.children[0].text // '\n'  (wrong)
// expected: '\r'

XML 1.0 section 2.11 end-of-line normalization applies only to literal line breaks in the input, not to characters produced by a character or entity reference (see sections 4.6 / 4.1). The reference must be passed through to the application as the literal character it denotes.

The cause: consumeContentReference routes the resolved reference text through addText, which unconditionally calls normalizeLineBreaks. So a resolved &#xD; is collapsed to \n just like a literal CR in the source would be.

Evidence this is the intended behavior

  • The vendored W3C conformance test xmltest/valid/sa/067.xml (<doc>&#13;</doc>) has canonical output <doc>&#13;</doc>, i.e. the CR must survive.
  • The existing attribute-side test ("doesn't normalize a character reference for a whitespace character", issue &#xA; is interpreted as space instead of line feed #6) already asserts this for attributes. The content path was simply never tested, and the attribute path uses a different code route, so the bug stayed hidden.

Fix

Add a normalize parameter to addText (default true) gating the normalizeLineBreaks call, and pass false from consumeContentReference. The CDATA and CharData callers keep the default true, since literal content is subject to section 2.11.

Verification

  • New test fails on main ('\n' !== '\r') and passes with the fix.
  • Full suite green (1378 passing, up from 1377), 100% coverage maintained.
  • W3C conformance suite still green.

References

XML 1.0 section 2.11 end-of-line normalization applies only to literal
line breaks in the input, not to characters produced by a character or
entity reference (sections 4.6/4.1). A character reference resolving to a
carriage return (`&#xD;` or `&rgrove#13;`) in element content was being
converted to a line feed because consumeContentReference routed the
resolved text through addText's unconditional normalizeLineBreaks call.

Add a `normalize` parameter to addText (default true) and pass false from
consumeContentReference so resolved references preserve U+000D. The CDATA
and CharData callers keep the default, since literal content is subject to
section 2.11. The attribute path was already correct (issue rgrove#6).

@rgrove rgrove left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks!

@rgrove
rgrove merged commit 5454796 into rgrove:main Jun 27, 2026
4 checks passed
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