Skip to content

Don't reject a document that starts with a "<?xml…" processing instruction - #47

Merged
rgrove merged 1 commit into
rgrove:mainfrom
spokodev:fix/pi-target-starting-with-xml
Jul 26, 2026
Merged

Don't reject a document that starts with a "<?xml…" processing instruction#47
rgrove merged 1 commit into
rgrove:mainfrom
spokodev:fix/pi-target-starting-with-xml

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

Problem

consumeXmlDeclaration matches the 5-character literal <?xml and then requires
whitespace, so a document that begins with a processing instruction whose
target merely starts with xml is forced down the XML-declaration path and
rejected:

parseXml('<?xml-stylesheet type="text/xsl" href="a.xsl"?><a/>');
// throws: Invalid XML declaration (line 1, column 6)
parseXml('<?xml-model href="a.rng"?><a/>');
// throws: Invalid XML declaration (line 1, column 6)

These are well-formed documents, and both <?xml-stylesheet?> and <?xml-model?>
are extremely common in the wild (RSS/Atom, SVG, any XML+XSLT/CSS document). The
same PI parses fine when it isn't first:

parseXml('<?xml version="1.0"?><?xml-stylesheet href="a.xsl"?><a/>'); // OK

Per XML 1.0:

  • An XML declaration is '<?xml' VersionInfo … where VersionInfo ::= S 'version' …
    — the <?xml must be followed by whitespace.
  • PITarget ::= Name - (('X'|'x')('M'|'m')('L'|'l')) subtracts only the exact
    3-letter name xml; xml-stylesheet / xml-model are distinct, valid Names.
  • prolog ::= XMLDecl? Misc* and Misc ::= Comment | PI | S (§2.8) allow a
    processing instruction as the first item when no declaration is present.

The names-beginning-with-xml-are-reserved note (§2.3) is a standardization
reservation, not a well-formedness constraint — processors must accept such names
(the same reason xmlns is legal).

Fix

After consuming <?xml, if the next character is a name character this is a PI
whose target begins with xml: rewind and let it be parsed as a processing
instruction (the caller's Misc* loop handles it). <?xml followed by
whitespace (a real declaration) or by a non-name character (e.g. <?xml?>, whose
bare xml target is reserved and has no valid interpretation) is unchanged.

Test

Added a case asserting <?xml-stylesheet?> at document start parses as a PI with
the root element following. Repointed the existing "invalid XML declaration" test
to <?xml?> (still correctly rejected). Full suite green (1380 passing) at 100%
coverage.

…ction

`consumeXmlDeclaration` matched the 5-character literal `<?xml` and then
required whitespace, so a document beginning with a processing instruction
whose target merely starts with `xml` — `<?xml-stylesheet ?>`,
`<?xml-model ?>` — was forced down the XML-declaration path and rejected
with "Invalid XML declaration":

  parseXml('<?xml-stylesheet type="text/xsl" href="a.xsl"?><a/>') // threw

Those are valid documents: an XML declaration is `'<?xml' S 'version' …`
(the `<?xml` must be followed by whitespace), whereas `xml-stylesheet` and
`xml-model` are distinct, well-formed PITargets, and `prolog ::= XMLDecl?
Misc*` (XML 1.0 §2.8) allows a processing instruction as the first item
when no declaration is present.

After consuming `<?xml`, if the next character is a name character this is
such a PI: rewind and let it be parsed as a processing instruction. `<?xml`
followed by whitespace (a real declaration) or by a non-name character
(e.g. `<?xml?>`, whose bare `xml` target is reserved) is unchanged.

@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 779dc27 into rgrove:main Jul 26, 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