diff options
Diffstat (limited to 'src/corelib/io/qurl.cpp')
-rw-r--r-- | src/corelib/io/qurl.cpp | 198 |
1 files changed, 110 insertions, 88 deletions
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 3cebb68561..4360b5b076 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -14,14 +14,18 @@ \ingroup network \ingroup shared + \compares weak It can parse and construct URLs in both encoded and unencoded form. QUrl also has support for internationalized domain names (IDNs). - The most common way to use QUrl is to initialize it via the - constructor by passing a QString. Otherwise, setUrl() can also - be used. + The most common way to use QUrl is to initialize it via the constructor by + passing a QString containing a full URL. QUrl objects can also be created + from a QByteArray containing a full URL using QUrl::fromEncoded(), or + heuristically from incomplete URLs using QUrl::fromUserInput(). The URL + representation can be obtained from a QUrl using either QUrl::toString() or + QUrl::toEncoded(). URLs can be represented in two forms: encoded or unencoded. The unencoded representation is suitable for showing to users, but @@ -400,18 +404,17 @@ #include "private/qipaddress_p.h" #include "qurlquery.h" #include "private/qdir_p.h" +#include <private/qtools_p.h> QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; - -// in qstring.cpp: -void qt_from_latin1(char16_t *dst, const char *str, size_t size) noexcept; +using namespace QtMiscUtils; inline static bool isHex(char c) { c |= 0x20; - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); + return isAsciiDigit(c) || (c >= 'a' && c <= 'f'); } static inline QString ftpScheme() @@ -819,14 +822,16 @@ recodeFromUser(const QString &input, const ushort *actions, qsizetype from, qsiz static inline void appendToUser(QString &appendTo, QStringView value, QUrl::FormattingOptions options, const ushort *actions) { - // Test ComponentFormattingOptions, ignore FormattingOptions. - if ((options & 0xFFFF0000) == QUrl::PrettyDecoded) { + // The stored value is already QUrl::PrettyDecoded, so there's nothing to + // do if that's what the user asked for (test only + // ComponentFormattingOptions, ignore FormattingOptions). + if ((options & 0xFFFF0000) == QUrl::PrettyDecoded || + !qt_urlRecode(appendTo, value, options, actions)) appendTo += value; - return; - } - if (!qt_urlRecode(appendTo, value, options, actions)) - appendTo += value; + // copy nullness, if necessary, because QString::operator+=(QStringView) doesn't + if (appendTo.isNull() && !value.isNull()) + appendTo.detach(); } inline void QUrlPrivate::appendAuthority(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const @@ -918,7 +923,7 @@ inline void QUrlPrivate::appendPath(QString &appendTo, QUrl::FormattingOptions o } // check if we need to remove trailing slashes if (options & QUrl::StripTrailingSlash) { - while (thePathView.length() > 1 && thePathView.endsWith(u'/')) + while (thePathView.size() > 1 && thePathView.endsWith(u'/')) thePathView.chop(1); } @@ -960,14 +965,14 @@ inline bool QUrlPrivate::setScheme(const QString &value, qsizetype len, bool doS qsizetype needsLowercasing = -1; const ushort *p = reinterpret_cast<const ushort *>(value.data()); for (qsizetype i = 0; i < len; ++i) { - if (p[i] >= 'a' && p[i] <= 'z') + if (isAsciiLower(p[i])) continue; - if (p[i] >= 'A' && p[i] <= 'Z') { + if (isAsciiUpper(p[i])) { needsLowercasing = i; continue; } if (i) { - if (p[i] >= '0' && p[i] <= '9') + if (isAsciiDigit(p[i])) continue; if (p[i] == '+' || p[i] == '-' || p[i] == '.') continue; @@ -988,7 +993,7 @@ inline bool QUrlPrivate::setScheme(const QString &value, qsizetype len, bool doS QChar *schemeData = scheme.data(); // force detaching here for (qsizetype i = needsLowercasing; i >= 0; --i) { ushort c = schemeData[i].unicode(); - if (c >= 'A' && c <= 'Z') + if (isAsciiUpper(c)) schemeData[i] = QChar(c + 0x20); } } @@ -1040,7 +1045,7 @@ inline void QUrlPrivate::setAuthority(const QString &auth, qsizetype from, qsize unsigned long x = 0; for (qsizetype i = colonIndex + 1; i < end; ++i) { ushort c = auth.at(i).unicode(); - if (c >= '0' && c <= '9') { + if (isAsciiDigit(c)) { x *= 10; x += c - '0'; } else { @@ -1182,9 +1187,7 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar const QChar *const origBegin = begin; if (begin[3].unicode() != '.') return &begin[3]; - if ((begin[2].unicode() >= 'A' && begin[2].unicode() <= 'F') || - (begin[2].unicode() >= 'a' && begin[2].unicode() <= 'f') || - (begin[2].unicode() >= '0' && begin[2].unicode() <= '9')) { + if (isHexDigit(begin[2].unicode())) { // this is so unlikely that we'll just go down the slow path // decode the whole string, skipping the "[vH." and "]" which we already know to be there host += QStringView(begin, 4); @@ -1203,11 +1206,7 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar } for ( ; begin != end; ++begin) { - if (begin->unicode() >= 'A' && begin->unicode() <= 'Z') - host += *begin; - else if (begin->unicode() >= 'a' && begin->unicode() <= 'z') - host += *begin; - else if (begin->unicode() >= '0' && begin->unicode() <= '9') + if (isAsciiLetterOrNumber(begin->unicode())) host += *begin; else if (begin->unicode() < 0x80 && strchr(acceptable, begin->unicode()) != nullptr) host += *begin; @@ -1792,7 +1791,20 @@ inline void QUrlPrivate::validate() const /*! - Constructs a URL by parsing \a url. QUrl will automatically percent encode + Constructs a URL by parsing \a url. Note this constructor expects a proper + URL or URL-Reference and will not attempt to guess intent. For example, the + following declaration: + + \snippet code/src_corelib_io_qurl.cpp constructor-url-reference + + Will construct a valid URL but it may not be what one expects, as the + scheme() part of the input is missing. For a string like the above, + applications may want to use fromUserInput(). For this constructor or + setUrl(), the following is probably what was intended: + + \snippet code/src_corelib_io_qurl.cpp constructor-url + + QUrl will automatically percent encode all characters that are not allowed in a URL and decode the percent-encoded sequences that represent an unreserved character (letters, digits, hyphens, underscores, dots and tildes). All other characters are left in their @@ -2958,19 +2970,23 @@ QByteArray QUrl::toEncoded(FormattingOptions options) const } /*! - \fn QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode parsingMode) - Parses \a input and returns the corresponding QUrl. \a input is assumed to be in encoded form, containing only ASCII characters. - Parses the URL using \a parsingMode. See setUrl() for more information on + Parses the URL using \a mode. See setUrl() for more information on this parameter. QUrl::DecodedMode is not permitted in this context. + \note In Qt versions prior to 6.7, this function took a QByteArray, not + QByteArrayView. If you experience compile errors, it's because your code + is passing objects that are implicitly convertible to QByteArray, but not + QByteArrayView. Wrap the corresponding argument in \c{QByteArray{~~~}} to + make the cast explicit. This is backwards-compatible with old Qt versions. + \sa toEncoded(), setUrl() */ -QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode mode) +QUrl QUrl::fromEncoded(QByteArrayView input, ParsingMode mode) { - return QUrl(QString::fromUtf8(input.constData(), input.size()), mode); + return QUrl(QString::fromUtf8(input), mode); } /*! @@ -3053,88 +3069,101 @@ QByteArray QUrl::toAce(const QString &domain, AceProcessingOptions options) /*! \internal - Returns \c true if this URL is "less than" the given \a url. This + \fn bool QUrl::operator<(const QUrl &lhs, const QUrl &rhs) + + Returns \c true if URL \a lhs is "less than" URL \a rhs. This provides a means of ordering URLs. */ -bool QUrl::operator <(const QUrl &url) const + +Qt::weak_ordering compareThreeWay(const QUrl &lhs, const QUrl &rhs) { - if (!d || !url.d) { - bool thisIsEmpty = !d || d->isEmpty(); - bool thatIsEmpty = !url.d || url.d->isEmpty(); + if (!lhs.d || !rhs.d) { + bool thisIsEmpty = !lhs.d || lhs.d->isEmpty(); + bool thatIsEmpty = !rhs.d || rhs.d->isEmpty(); // sort an empty URL first - return thisIsEmpty && !thatIsEmpty; + if (thisIsEmpty) { + if (!thatIsEmpty) + return Qt::weak_ordering::less; + else + return Qt::weak_ordering::equivalent; + } else { + return Qt::weak_ordering::greater; + } } int cmp; - cmp = d->scheme.compare(url.d->scheme); + cmp = lhs.d->scheme.compare(rhs.d->scheme); if (cmp != 0) - return cmp < 0; + return Qt::compareThreeWay(cmp, 0); - cmp = d->userName.compare(url.d->userName); + cmp = lhs.d->userName.compare(rhs.d->userName); if (cmp != 0) - return cmp < 0; + return Qt::compareThreeWay(cmp, 0); - cmp = d->password.compare(url.d->password); + cmp = lhs.d->password.compare(rhs.d->password); if (cmp != 0) - return cmp < 0; + return Qt::compareThreeWay(cmp, 0); - cmp = d->host.compare(url.d->host); + cmp = lhs.d->host.compare(rhs.d->host); if (cmp != 0) - return cmp < 0; + return Qt::compareThreeWay(cmp, 0); - if (d->port != url.d->port) - return d->port < url.d->port; + if (lhs.d->port != rhs.d->port) + return Qt::compareThreeWay(lhs.d->port, rhs.d->port); - cmp = d->path.compare(url.d->path); + cmp = lhs.d->path.compare(rhs.d->path); if (cmp != 0) - return cmp < 0; + return Qt::compareThreeWay(cmp, 0); - if (d->hasQuery() != url.d->hasQuery()) - return url.d->hasQuery(); + if (lhs.d->hasQuery() != rhs.d->hasQuery()) + return rhs.d->hasQuery() ? Qt::weak_ordering::less : Qt::weak_ordering::greater; - cmp = d->query.compare(url.d->query); + cmp = lhs.d->query.compare(rhs.d->query); if (cmp != 0) - return cmp < 0; + return Qt::compareThreeWay(cmp, 0); - if (d->hasFragment() != url.d->hasFragment()) - return url.d->hasFragment(); + if (lhs.d->hasFragment() != rhs.d->hasFragment()) + return rhs.d->hasFragment() ? Qt::weak_ordering::less : Qt::weak_ordering::greater; - cmp = d->fragment.compare(url.d->fragment); - return cmp < 0; + cmp = lhs.d->fragment.compare(rhs.d->fragment); + return Qt::compareThreeWay(cmp, 0); } /*! - Returns \c true if this URL and the given \a url are equal; + \fn bool QUrl::operator==(const QUrl &lhs, const QUrl &rhs) + + Returns \c true if \a lhs and \a rhs URLs are equivalent; otherwise returns \c false. \sa matches() */ -bool QUrl::operator ==(const QUrl &url) const + +bool comparesEqual(const QUrl &lhs, const QUrl &rhs) { - if (!d && !url.d) + if (!lhs.d && !rhs.d) return true; - if (!d) - return url.d->isEmpty(); - if (!url.d) - return d->isEmpty(); + if (!lhs.d) + return rhs.d->isEmpty(); + if (!rhs.d) + return lhs.d->isEmpty(); // First, compare which sections are present, since it speeds up the // processing considerably. We just have to ignore the host-is-present flag // for local files (the "file" protocol), due to the requirements of the // XDG file URI specification. int mask = QUrlPrivate::FullUrl; - if (isLocalFile()) + if (lhs.isLocalFile()) mask &= ~QUrlPrivate::Host; - return (d->sectionIsPresent & mask) == (url.d->sectionIsPresent & mask) && - d->scheme == url.d->scheme && - d->userName == url.d->userName && - d->password == url.d->password && - d->host == url.d->host && - d->port == url.d->port && - d->path == url.d->path && - d->query == url.d->query && - d->fragment == url.d->fragment; + return (lhs.d->sectionIsPresent & mask) == (rhs.d->sectionIsPresent & mask) && + lhs.d->scheme == rhs.d->scheme && + lhs.d->userName == rhs.d->userName && + lhs.d->password == rhs.d->password && + lhs.d->host == rhs.d->host && + lhs.d->port == rhs.d->port && + lhs.d->path == rhs.d->path && + lhs.d->query == rhs.d->query && + lhs.d->fragment == rhs.d->fragment; } /*! @@ -3214,15 +3243,13 @@ bool QUrl::matches(const QUrl &url, FormattingOptions options) const } /*! - Returns \c true if this URL and the given \a url are not equal; + \fn bool QUrl::operator !=(const QUrl &lhs, const QUrl &rhs) + + Returns \c true if \a lhs and \a rhs URLs are not equal; otherwise returns \c false. \sa matches() */ -bool QUrl::operator !=(const QUrl &url) const -{ - return !(*this == url); -} /*! Assigns the specified \a url to this object. @@ -3493,10 +3520,7 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err switch (errorCode) { case QUrlPrivate::NoError: - Q_ASSERT_X(false, "QUrl::errorString", - "Impossible: QUrl::errorString should have treated this condition"); - Q_UNREACHABLE(); - return QString(); + Q_UNREACHABLE_RETURN(QString()); // QUrl::errorString should have treated this condition case QUrlPrivate::InvalidSchemeError: { auto msg = "Invalid scheme (character '%1' not permitted)"_L1; @@ -3553,9 +3577,7 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err return QStringLiteral("Relative URL's path component contains ':' before any '/'"); } - Q_ASSERT_X(false, "QUrl::errorString", "Cannot happen, unknown error"); - Q_UNREACHABLE(); - return QString(); + Q_UNREACHABLE_RETURN(QString()); } static inline void appendComponentIfPresent(QString &msg, bool present, const char *componentName, |