diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index d206df7c6c4..1e1a992719b 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -2717,8 +2717,12 @@ QUrl QUrl::resolved(const QUrl &relative) const qt_normalizePathSegments( &t.d->path, isLocalFile() ? QDirPrivate::DefaultNormalization : QDirPrivate::RemotePath); - if (!t.d->hasAuthority()) - fixupNonAuthorityPath(&t.d->path); + if (!t.d->hasAuthority()) { + if (t.d->isLocalFile() && t.d->path.startsWith(u'/')) + t.d->sectionIsPresent |= QUrlPrivate::Host; + else + fixupNonAuthorityPath(&t.d->path); + } #if defined(QURL_DEBUG) qDebug("QUrl(\"%ls\").resolved(\"%ls\") = \"%ls\"", @@ -2893,6 +2897,11 @@ QUrl QUrl::adjusted(QUrl::FormattingOptions options) const d->appendPath(path, options | FullyEncoded, QUrlPrivate::Path); that.d->setPath(path, 0, path.size()); } + if (that.d->isLocalFile() && that.d->path.startsWith(u'/')) { + // ensure absolute file URLs have an empty authority to comply with the + // XDG file spec (note this may undo a RemoveAuthority) + that.d->sectionIsPresent |= QUrlPrivate::Host; + } return that; } @@ -3307,15 +3316,18 @@ static QString fromNativeSeparators(const QString &pathName) QUrl QUrl::fromLocalFile(const QString &localFile) { QUrl url; - if (localFile.isEmpty()) + QString deslashified = fromNativeSeparators(localFile); + if (deslashified.isEmpty()) return url; QString scheme = fileScheme(); - QString deslashified = fromNativeSeparators(localFile); + char16_t firstChar = deslashified.at(0).unicode(); + char16_t secondChar = deslashified.size() > 1 ? deslashified.at(1).unicode() : u'\0'; // magic for drives on windows - if (deslashified.size() > 1 && deslashified.at(1) == u':' && deslashified.at(0) != u'/') { + if (firstChar != u'/' && secondChar == u':') { deslashified.prepend(u'/'); - } else if (deslashified.startsWith("//"_L1)) { + firstChar = u'/'; + } else if (firstChar == u'/' && secondChar == u'/') { // magic for shared drive on windows qsizetype indexOfPath = deslashified.indexOf(u'/', 2); QStringView hostSpec = QStringView{deslashified}.mid(2, indexOfPath - 2); @@ -3339,9 +3351,15 @@ QUrl QUrl::fromLocalFile(const QString &localFile) deslashified.clear(); } } + if (firstChar == u'/') { + // ensure absolute file URLs have an empty authority to comply with the XDG file spec + url.detach(); + url.d->sectionIsPresent |= QUrlPrivate::Host; + } url.setScheme(scheme); url.setPath(deslashified, DecodedMode); + return url; } diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 79c225ba64a..d47dfa2b08a 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -1500,10 +1500,13 @@ void tst_QUrl::fromLocalFileNormalize_data() QTest::newRow("relative-dot") << QString::fromLatin1("./a.txt") << QString::fromLatin1("file:./a.txt") << QString::fromLatin1("file:a.txt"); QTest::newRow("relative-dot-dot") << QString::fromLatin1("././a.txt") << QString::fromLatin1("file:././a.txt") << QString::fromLatin1("file:a.txt"); QTest::newRow("relative-path-dotdot") << QString::fromLatin1("b/../a.txt") << QString::fromLatin1("file:b/../a.txt") << QString::fromLatin1("file:a.txt"); + QTest::newRow("relative-path-dotdot-dotdot") << QString::fromLatin1("b/../../a.txt") << QString::fromLatin1("file:b/../../a.txt") << QString::fromLatin1("file:../a.txt"); QTest::newRow("absolute-path-dotdot") << QString::fromLatin1("/b/../a.txt") << QString::fromLatin1("file:///b/../a.txt") << QString::fromLatin1("file:///a.txt"); + QTest::newRow("absolute-path-dotdot-dotdot") << QString::fromLatin1("/b/../../a.txt") << QString::fromLatin1("file:///b/../../a.txt") << QString::fromLatin1("file:///../a.txt"); QTest::newRow("absolute-path-slash") << QString::fromLatin1("/b/") << QString::fromLatin1("file:///b/") << QString::fromLatin1("file:///b/"); QTest::newRow("absolute-path-slahs-dot") << QString::fromLatin1("/b/.") << QString::fromLatin1("file:///b/.") << QString::fromLatin1("file:///b/"); QTest::newRow("absolute-path-slahs-dot-slash") << QString::fromLatin1("/b/./") << QString::fromLatin1("file:///b/./") << QString::fromLatin1("file:///b/"); + QTest::newRow("absolute-path-dotdot-slashslash") << QString::fromLatin1("/b/..//") << QString::fromLatin1("file:///b/..//") << QString::fromLatin1("file:////"); } void tst_QUrl::fromLocalFileNormalize()