summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qurl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qurl.cpp')
-rw-r--r--src/corelib/io/qurl.cpp198
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,