From e2b36e6665e94b884b7e4ef19196883cf32d5b6b Mon Sep 17 00:00:00 2001 From: Axel Spoerl Date: Tue, 18 Apr 2023 13:44:00 +0200 Subject: [PATCH] QDomDocument: no longer drop a provided 'standalone' attribute if 'no' - Definition of 'standalone' attribute: An XML declaration containing the attribute 'standalone' with its value set to 'yes', tells the parser to ignore markup declarations in the DTD and to use them only for validation. The declaration attribute is optional and defaults to 'no'. - Behavior Qt5 In qt5, DOM documents contained the standalone attribute, regardless of whether or not it was explicitly specified. - Behavior Qt6 In Qt6, the standalone attribute was only contained in a DOM document, if its value was 'yes'. If it was explicitly declared with the value being 'no', it was dropped in the DOM document. - Expected behavior If the source specified it overtly, then the generated XML should contain the attribute, even when it takes its default value. - Code base QXmlStreamReader provides a public bool getter isStandaloneDocument(). This says whether the document is standalone or not. The information whether the attribute was actually specified gets lost. In consequence, the attribute was always dropped on non-standalone documents. - Fix This patch makes hasStandalone a member of QXmlStreamReaderPrivate, to record whether the attribute has been explicitly specified. QDomParser is modified to retain the standalone attribute, if QXmlStreamReaderPrivate::hasStandalone is true. - Test The patch adds a test function in tst_QDom. Fixes: QTBUG-111200 Change-Id: I06a0f230a2d69597dd6453f8fd3b036943d08735 Reviewed-by: Volker Hilsheimer Reviewed-by: Giuseppe D'Angelo (cherry picked from commit 11f14c3b0dee239644097b3eddbbf464e4d69600) Reviewed-by: Shawn Rutledge --- src/corelib/serialization/qxmlstream.cpp | 2 +- src/corelib/serialization/qxmlstream_p.h | 3 +++ src/xml/dom/qdomhelpers.cpp | 8 +++++--- tests/auto/xml/dom/qdom/tst_qdom.cpp | 26 ++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index cdb8aca63c6..c7226969cc1 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -823,6 +823,7 @@ void QXmlStreamReaderPrivate::init() isWhitespace = true; isCDATA = false; standalone = false; + hasStandalone = false; tos = 0; resumeReduction = 0; state_stack[tos++] = 0; @@ -1775,7 +1776,6 @@ void QXmlStreamReaderPrivate::startDocument() * proper order: * * [23] XMLDecl ::= '' */ - bool hasStandalone = false; for (qsizetype i = 0; err.isNull() && i < n; ++i) { Attribute &attrib = attributeStack[i]; diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h index 563dab4d608..543fe3ca436 100644 --- a/src/corelib/serialization/qxmlstream_p.h +++ b/src/corelib/serialization/qxmlstream_p.h @@ -389,6 +389,7 @@ public: uint hasExternalDtdSubset : 1; uint lockEncoding : 1; uint namespaceProcessing : 1; + uint hasStandalone : 1; // TODO: expose in public API int resumeReduction; void resume(int rule); @@ -515,6 +516,8 @@ public: QXmlStreamEntityResolver *entityResolver; + static QXmlStreamReaderPrivate *get(QXmlStreamReader *q) { return q->d_func(); } + private: /*! \internal Never assign to variable type directly. Instead use this function. diff --git a/src/xml/dom/qdomhelpers.cpp b/src/xml/dom/qdomhelpers.cpp index 37fe90667f2..99246532b7f 100644 --- a/src/xml/dom/qdomhelpers.cpp +++ b/src/xml/dom/qdomhelpers.cpp @@ -44,6 +44,7 @@ #include "qdomhelpers_p.h" #include "qdom_p.h" #include "qxmlstream.h" +#include "private/qxmlstream_p.h" #include #include @@ -325,9 +326,10 @@ bool QDomParser::parseProlog() if (reader->isStandaloneDocument()) { value += QLatin1String(" standalone='yes'"); } else { - // TODO: Add standalone='no', if 'standalone' is specified. With the current - // QXmlStreamReader there is no way to figure out if it was specified or not. - // QXmlStreamReader needs to be modified for handling that case correctly. + // Add the standalone attribute only if it was specified + QXmlStreamReaderPrivate *priv = QXmlStreamReaderPrivate::get(reader); + if (priv->hasStandalone) + value += QLatin1String(" standalone='no'"); } if (!domBuilder.processingInstruction(QLatin1String("xml"), value)) { diff --git a/tests/auto/xml/dom/qdom/tst_qdom.cpp b/tests/auto/xml/dom/qdom/tst_qdom.cpp index 5045200d054..3f7aba8c334 100644 --- a/tests/auto/xml/dom/qdom/tst_qdom.cpp +++ b/tests/auto/xml/dom/qdom/tst_qdom.cpp @@ -123,6 +123,7 @@ private slots: void DTDInternalSubset() const; void DTDInternalSubset_data() const; void QTBUG49113_dontCrashWithNegativeIndex() const; + void standalone(); void cleanupTestCase() const; @@ -2069,6 +2070,31 @@ void tst_QDom::QTBUG49113_dontCrashWithNegativeIndex() const QVERIFY(node.isNull()); } +void tst_QDom::standalone() +{ + { + QDomDocument doc; + const QString dtd("\n" + "\n"); + doc.setContent(dtd); + QVERIFY(doc.toString().contains("standalone=\'no\'")); + } + { + QDomDocument doc; + const QString dtd("\n" + "\n"); + doc.setContent(dtd); + QVERIFY(!doc.toString().contains("standalone")); + } + { + QDomDocument doc; + const QString dtd("\n" + "\n"); + doc.setContent(dtd); + QVERIFY(doc.toString().contains("standalone=\'yes\'")); + } +} + void tst_QDom::DTDInternalSubset() const { QFETCH( QString, doc );