fix(xslt): name the html output method doctype HTML, not the element name (closes output40/48/60) (#259)
* fix(xslt): name the html output method doctype HTML, not the element name XSLT 1.0 16.2: for the html output method the name following <!DOCTYPE should be HTML or html, not the document element name (which is the xml method's rule, 16.1). The serializer used the document element name for both methods, so method=html with a non-html root element (e.g. <root>) emitted <!DOCTYPE root PUBLIC ...> instead of <!DOCTYPE HTML PUBLIC ...>. Closes Apache Xalan conformance cases output40, output48, output60. * fix(xslt): emit no html doctype when the result has no element Addresses the doctype critic finding: a doctype precedes the first element (16.2), so a text-only result must emit none even for the html method. The fixed HTML name made the existing no-element guard unreachable; restore it by guarding on the document element before choosing the name.
This commit is contained in:
parent
f3d9de2c86
commit
445412b105
@ -52,6 +52,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- The `html` output method now names the document type declaration `HTML` rather than the document element name (XSLT 1.0 16.2: "The name following the `<!DOCTYPE` should be HTML or html"). With `method="html"` and a `doctype-public` or `doctype-system`, the emitted declaration is `<!DOCTYPE HTML PUBLIC "...">` regardless of the result's root element; the `xml` method still uses the document element name (16.1). Closes Apache Xalan conformance cases `output40`, `output48`, and `output60`.
|
||||
|
||||
- The `html` output method no longer escapes a `<` character in an attribute value (XSLT 1.0 16.2). The HTML output method escapes `&` and `"` in attribute values but leaves `<` and `>` literal, unlike the `xml` method; the serializer was escaping `<` to `<` there. The `xml` output method is unchanged (it still escapes both). Closes Apache Xalan conformance cases `output49` and `output74`.
|
||||
|
||||
- A string-form `document()` reference now resolves against the base URI of the stylesheet element holding it (XSLT 1.0 12.1), which for a declaration in an `xsl:include`d or `xsl:import`ed file is that file's URI rather than the top stylesheet's. The parser already tracked each top-level declaration's source-file base; that base is now carried onto the compiled templates and global variables and threaded into expression evaluation, so a relative `document('data.xml')` (or `document('')`, the self-reference) in an included file resolves against the included file, not the importing one. Closes Apache Xalan conformance cases `mdocs12` and `mdocs13`.
|
||||
|
||||
@ -186,7 +186,12 @@ public extension PureXML.XSLT {
|
||||
/// The `<!DOCTYPE …>` prologue for the result's root element when the output
|
||||
/// declares `doctype-system` (optionally with `doctype-public`), else empty.
|
||||
private static func doctype(for result: PureXML.Model.Node, _ output: Output, method: String) -> String {
|
||||
guard let name = rootName(of: result) else { return "" }
|
||||
// A doctype precedes the first element, so emit none without one. The
|
||||
// html output method then names it HTML (16.2: "The name following
|
||||
// <!DOCTYPE should be HTML or html"); the xml method uses the document
|
||||
// element name (16.1).
|
||||
guard let elementName = rootName(of: result) else { return "" }
|
||||
let name = method == "html" ? "HTML" : elementName
|
||||
if let system = output.doctypeSystem {
|
||||
let external = output.doctypePublic.map { "PUBLIC \"\($0)\" \"\(system)\"" } ?? "SYSTEM \"\(system)\""
|
||||
return "<!DOCTYPE \(name) \(external)>\n"
|
||||
|
||||
@ -29,9 +29,6 @@ numbering/numbering63
|
||||
output/output22
|
||||
output/output26
|
||||
output/output32
|
||||
output/output40
|
||||
output/output48
|
||||
output/output60
|
||||
output/output63
|
||||
output/output70
|
||||
output/output72
|
||||
|
||||
@ -20,17 +20,44 @@ struct XSLTDoctypeOutputTests {
|
||||
#expect(try transform(style, "<x/>") == "<!DOCTYPE note SYSTEM \"note.dtd\">\n<note/>")
|
||||
}
|
||||
|
||||
@Test("doctype-public and doctype-system emit a PUBLIC doctype")
|
||||
@Test("the html method names the doctype HTML regardless of the document element")
|
||||
func test_publicAndSystem() throws {
|
||||
// XSLT 1.0 16.2: the html output method's doctype name is HTML, not the
|
||||
// document element name (here `root`), unlike the xml method (Apache
|
||||
// Xalan output40, output48, output60).
|
||||
let style = """
|
||||
<xsl:stylesheet version="1.0" \(xsl)>
|
||||
<xsl:output method="html" doctype-public="-//W3C//DTD HTML 4.01//EN" doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>
|
||||
<xsl:template match="/"><html/></xsl:template>
|
||||
<xsl:template match="/"><root/></xsl:template>
|
||||
</xsl:stylesheet>
|
||||
"""
|
||||
let out = try transform(style, "<x/>")
|
||||
#expect(out.hasPrefix("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"))
|
||||
#expect(out.contains("<html></html>"))
|
||||
#expect(out.hasPrefix("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"))
|
||||
#expect(out.contains("<root></root>"))
|
||||
}
|
||||
|
||||
@Test("the xml method still uses the document element name for the doctype")
|
||||
func test_xmlMethodUsesElementName() throws {
|
||||
let style = """
|
||||
<xsl:stylesheet version="1.0" \(xsl)>
|
||||
<xsl:output method="xml" omit-xml-declaration="yes" doctype-system="note.dtd"/>
|
||||
<xsl:template match="/"><note/></xsl:template>
|
||||
</xsl:stylesheet>
|
||||
"""
|
||||
#expect(try transform(style, "<x/>") == "<!DOCTYPE note SYSTEM \"note.dtd\">\n<note/>")
|
||||
}
|
||||
|
||||
@Test("the html method emits no doctype when the result has no element")
|
||||
func test_htmlNoElementNoDoctype() throws {
|
||||
// A doctype precedes the first element (16.2); a text-only result has
|
||||
// none, so even the html method with doctype-public emits nothing.
|
||||
let style = """
|
||||
<xsl:stylesheet version="1.0" \(xsl)>
|
||||
<xsl:output method="html" doctype-public="-//W3C//DTD HTML 4.01//EN"/>
|
||||
<xsl:template match="/">just text</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
"""
|
||||
#expect(try transform(style, "<x/>") == "just text")
|
||||
}
|
||||
|
||||
@Test("Without doctype-system no doctype is emitted")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user