Revert "QUrl::resolved: switch to using qt_normalizePathSegments"

This reverts commit e7bcf41c0b23d83cfb31f966454945c705589a99.

Reason for revert: Causes Qt.resolvedUrl() to misbehave in QML.

Task-number: QTBUG-128940
Change-Id: I698338d4789ffef5c5a0f08391372b428d4fffb0
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Ulf Hermann 2024-09-18 08:36:47 +02:00
parent 50933122a2
commit c023016ffd
3 changed files with 118 additions and 14 deletions

View File

@ -73,7 +73,7 @@ static qsizetype rootLength(QStringView name, bool allowUncPaths)
return len > 2 && name.at(2) == u'/' ? 3 : 2;
}
#endif
if (len && name.at(0) == u'/')
if (name.at(0) == u'/')
return 1;
return 0;
}

View File

@ -1524,6 +1524,111 @@ inline QString QUrlPrivate::mergePaths(const QString &relativePath) const
return newPath;
}
/*
From http://www.ietf.org/rfc/rfc3986.txt, 5.2.4: Remove dot segments
Removes unnecessary ../ and ./ from the path. Used for normalizing
the URL.
This code has a Qt-specific extension to handle empty path segments (a.k.a.
multiple slashes like "a//b"). We try to keep them wherever possible
because with some protocols they are meaningful, but we still consider them
to be a single directory transition for "." or ".." (e.g., "a/b//c" +
"../" is "a/"). See tst_QUrl::resolved() for the expected behavior.
*/
static void removeDotsFromPath(QString *path)
{
// The input buffer is initialized with the now-appended path
// components and the output buffer is initialized to the empty
// string.
const QChar *in = path->constBegin();
// Scan the input for a "." or ".." segment. If there isn't any, then we
// don't need to modify this path at all.
qsizetype i = 0, n = path->size();
for (bool lastWasSlash = true; i < n; ++i) {
if (lastWasSlash && in[i] == u'.') {
if (i + 1 == n || in[i + 1] == u'/')
break;
if (in[i + 1] == u'.' && (i + 2 == n || in[i + 2] == u'/'))
break;
}
lastWasSlash = in[i] == u'/';
}
if (i == n)
return;
QChar *out = path->data();
const QChar *end = out + path->size();
out += i;
in = out;
// We implement a modified algorithm compared to RFC 3986, for efficiency.
do {
#if 0 // to see in the debugger
QStringView output(path->constBegin(), out);
QStringView input(in, end);
#endif
// First, copy any preceding slashes, so we can look at the segment's
// content.
while (in < end && in[0] == u'/') {
*out++ = *in++;
// Note: we may exit this loop with in == end, in which case we
// *shouldn't* dereference *in. But since we are pointing to a
// detached, non-empty QString, we know there's a u'\0' at the end.
}
// Is this path segment either "." or ".."?
enum { Nothing, Dot, DotDot } type = Nothing;
if (in[0] == u'.') {
if (in + 1 == end || in[1] == u'/')
type = Dot;
else if (in[1] == u'.' && (in + 2 == end || in[2] == u'/'))
type = DotDot;
}
if (type != Nothing) {
// If it is either, we skip it and remove any preceding slashes (if
// any) from the output. If it is "..", we remove the segment
// before that and its preceding slashes (if any) too.
const QChar *start = path->constBegin();
if (type == DotDot) {
while (out > start && *--out != u'/')
;
while (out > start && *--out == u'/')
;
++in; // the first dot
}
in += 2; // one dot and either one slash or the terminating null
while (out > start && *--out != u'/')
;
// And then replace the segment with "/", unless it would make a
// relative path become absolute.
if (out != start) {
// Replacing with a slash won't make the path absolute.
*out++ = u'/';
} else if (*start == u'/') {
// The path is already absolute.
++out;
} else {
// The path is relative, so we must skip any follow-on slashes
// to make sure the next iteration of the loop won't copy them,
// which would make the path become absolute.
while (in < end && *in == u'/')
++in;
}
continue;
}
// If it is neither, then we copy this segment.
while (in < end && in->unicode() != '/')
*out++ = *in++;
} while (in < end);
path->truncate(out - path->constBegin());
}
// Authority-less URLs cannot have paths starting with double slashes (see
// QUrlPrivate::validityError). We refuse to turn a valid URL into invalid by
// way of QUrl::resolved().
@ -2712,7 +2817,7 @@ QUrl QUrl::resolved(const QUrl &relative) const
else
t.d->sectionIsPresent &= ~QUrlPrivate::Fragment;
qt_normalizePathSegments(&t.d->path, isLocalFile() ? QDirPrivate::DefaultNormalization : QDirPrivate::RemotePath);
removeDotsFromPath(&t.d->path);
if (!t.d->hasAuthority())
fixupNonAuthorityPath(&t.d->path);

View File

@ -600,7 +600,7 @@ void tst_QUrl::setUrl()
QUrl url2("../../////kdebase/konqueror");
QCOMPARE(url.resolved(url2).toString(),
QString::fromLatin1("file:///usr/local/src/kde2/kdebase/konqueror"));
QString::fromLatin1("file:///usr/local/src/kde2/////kdebase/konqueror"));
}
{
@ -929,16 +929,16 @@ void tst_QUrl::resolving_data()
QTest::newRow(".-on-//") << "http://a/b/c//" << "." << "http://a/b/c//";
QTest::newRow("./-on-//") << "http://a/b/c//" << "./" << "http://a/b/c//";
QTest::newRow(".//-on-//") << "http://a/b/c//" << ".//" << "http://a/b/c///"; // weird but correct
QTest::newRow("..-on-//") << "http://a/b/c//" << ".." << "http://a/b/c/";
QTest::newRow("../-on-//") << "http://a/b/c//" << "../" << "http://a/b/c/";
QTest::newRow("..//-on-//") << "http://a/b/c//" << "..//" << "http://a/b/c//";
QTest::newRow("../g-on-//") << "http://a/b/c//" << "../g" << "http://a/b/c/g";
QTest::newRow("..//g-on-//") << "http://a/b/c//" << "..//g" << "http://a/b/c//g";
QTest::newRow("../..-on-//") << "http://a/b/c//" << "../.." << "http://a/b/";
QTest::newRow("../../-on-//") << "http://a/b/c//" << "../../" << "http://a/b/";
QTest::newRow("../..//-on-//") << "http://a/b/c//" << "../..//" << "http://a/b//";
QTest::newRow("../../g-on-//") << "http://a/b/c//" << "../../g" << "http://a/b/g";
QTest::newRow("../..//g-on-//") << "http://a/b/c//" << "../..//g" << "http://a/b//g";
QTest::newRow("..-on-//") << "http://a/b/c//" << ".." << "http://a/b/";
QTest::newRow("../-on-//") << "http://a/b/c//" << "../" << "http://a/b/";
QTest::newRow("..//-on-//") << "http://a/b/c//" << "..//" << "http://a/b//";
QTest::newRow("../g-on-//") << "http://a/b/c//" << "../g" << "http://a/b/g";
QTest::newRow("..//g-on-//") << "http://a/b/c//" << "..//g" << "http://a/b//g";
QTest::newRow("../..-on-//") << "http://a/b/c//" << "../.." << "http://a/";
QTest::newRow("../../-on-//") << "http://a/b/c//" << "../../" << "http://a/";
QTest::newRow("../..//-on-//") << "http://a/b/c//" << "../..//" << "http://a//";
QTest::newRow("../../g-on-//") << "http://a/b/c//" << "../../g" << "http://a/g";
QTest::newRow("../..//g-on-//") << "http://a/b/c//" << "../..//g" << "http://a//g";
// 5.4.2 Abnormal Examples (http://www.ietf.org/rfc/rfc3986.txt)
@ -4432,7 +4432,6 @@ void tst_QUrl::normalizeRemotePaths()
QCOMPARE(url.adjusted(QUrl::NormalizePathSegments).toString(), expected);
QCOMPARE(url.adjusted(QUrl::NormalizePathSegments | QUrl::RemoveFilename).toString(),
expectedNoFilename);
QCOMPARE(url.resolved(QUrl(".")).toString(), expectedNoFilename);
}
QTEST_MAIN(tst_QUrl)