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
Pick-to: 6.5 6.2
Change-Id: I06a0f230a2d69597dd6453f8fd3b036943d08735
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
This commit is contained in:
Axel Spoerl 2023-04-18 13:44:00 +02:00
parent 3fd7086878
commit 11f14c3b0d
4 changed files with 35 additions and 4 deletions

View File

@ -827,6 +827,7 @@ void QXmlStreamReaderPrivate::init()
isWhitespace = true;
isCDATA = false;
standalone = false;
hasStandalone = false;
tos = 0;
resumeReduction = 0;
state_stack[tos++] = 0;
@ -1777,7 +1778,6 @@ void QXmlStreamReaderPrivate::startDocument()
* proper order:
*
* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' */
bool hasStandalone = false;
for (qsizetype i = 0; err.isNull() && i < n; ++i) {
Attribute &attrib = attributeStack[i];

View File

@ -383,6 +383,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);
@ -509,6 +510,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.

View File

@ -8,6 +8,7 @@
#include "qdomhelpers_p.h"
#include "qdom_p.h"
#include "qxmlstream.h"
#include "private/qxmlstream_p.h"
#include <memory>
#include <stack>
@ -264,9 +265,10 @@ bool QDomParser::parseProlog()
if (reader->isStandaloneDocument()) {
value += u" standalone='yes'"_s;
} 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 += u" standalone='no'"_s;
}
if (!domBuilder.processingInstruction(u"xml"_s, value)) {

View File

@ -105,6 +105,7 @@ private slots:
void DTDInternalSubset() const;
void DTDInternalSubset_data() const;
void QTBUG49113_dontCrashWithNegativeIndex() const;
void standalone();
void cleanupTestCase() const;
@ -2225,6 +2226,31 @@ void tst_QDom::QTBUG49113_dontCrashWithNegativeIndex() const
QVERIFY(node.isNull());
}
void tst_QDom::standalone()
{
{
QDomDocument doc;
const QString dtd("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
"<Test/>\n");
doc.setContent(dtd);
QVERIFY(doc.toString().contains("standalone=\'no\'"));
}
{
QDomDocument doc;
const QString dtd("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<Test/>\n");
doc.setContent(dtd);
QVERIFY(!doc.toString().contains("standalone"));
}
{
QDomDocument doc;
const QString dtd("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
"<Test/>\n");
doc.setContent(dtd);
QVERIFY(doc.toString().contains("standalone=\'yes\'"));
}
}
void tst_QDom::DTDInternalSubset() const
{
QFETCH( QString, doc );