Android: Fix for URLs with local authorities
The file URLs provided by the native Android can use the "content://" or "assetes://" scheme. That was already handled by urlToLocalFileOrQrc(), but we cannot assume that authority is empty. Some authorities still point to local host, like: - com.android.externalstorage.documents; - com.android.providers.downloads.documents; - com.android.providers.media.documents; This commit treats URLs with the above mentioned authorities as local. Fixes: QTBUG-105110 Pick-to: 6.3 6.4 Change-Id: I9c080305547e5cd2c2d1be489f59d185a5d49658 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
fc683799fe
commit
b65a5a9d06
|
@ -29,6 +29,9 @@ static char file_string[] = "file";
|
|||
#if defined(Q_OS_ANDROID)
|
||||
static char assets_string[] = "assets";
|
||||
static char content_string[] = "content";
|
||||
static char authority_externalstorage[] = "com.android.externalstorage.documents";
|
||||
static char authority_downloads_documents[] = "com.android.providers.downloads.documents";
|
||||
static char authority_media_documents[] = "com.android.providers.media.documents";
|
||||
#endif
|
||||
|
||||
class QQmlFilePrivate;
|
||||
|
@ -469,6 +472,17 @@ bool QQmlFile::isSynchronous(const QString &url)
|
|||
return false;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
static bool hasLocalContentAuthority(const QUrl &url)
|
||||
{
|
||||
const QString authority = url.authority();
|
||||
return authority.isEmpty()
|
||||
|| authority == QLatin1String(authority_externalstorage)
|
||||
|| authority == QLatin1String(authority_downloads_documents)
|
||||
|| authority == QLatin1String(authority_media_documents);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Returns true if \a url is a local file that can be opened with QFile.
|
||||
|
||||
|
@ -490,18 +504,18 @@ bool QQmlFile::isLocalFile(const QUrl &url)
|
|||
return url.authority().isEmpty();
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if ((scheme.length() == 6
|
||||
if (scheme.length() == 6
|
||||
&& scheme.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive))
|
||||
|| (scheme.length() == 7
|
||||
&& scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive))) {
|
||||
return url.authority().isEmpty();
|
||||
}
|
||||
if (scheme.length() == 7
|
||||
&& scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive))
|
||||
return hasLocalContentAuthority(url);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasSchemeAndNoAuthority(const QString &url, const char *scheme, qsizetype schemeLength)
|
||||
static bool hasScheme(const QString &url, const char *scheme, qsizetype schemeLength)
|
||||
{
|
||||
const qsizetype urlLength = url.length();
|
||||
|
||||
|
@ -514,19 +528,41 @@ static bool hasSchemeAndNoAuthority(const QString &url, const char *scheme, qsiz
|
|||
if (url[schemeLength] != QLatin1Char(':'))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static qsizetype authorityOffset(const QString &url, qsizetype schemeLength)
|
||||
{
|
||||
const qsizetype urlLength = url.length();
|
||||
|
||||
if (urlLength < schemeLength + 3)
|
||||
return true;
|
||||
return -1;
|
||||
|
||||
const QLatin1Char slash('/');
|
||||
if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
|
||||
// Exactly two slashes denote an authority. We don't want that.
|
||||
// Exactly two slashes denote an authority.
|
||||
if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
|
||||
return false;
|
||||
return schemeLength + 3;
|
||||
}
|
||||
|
||||
return true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
static bool hasLocalContentAuthority(const QString &url, qsizetype schemeLength)
|
||||
{
|
||||
const qsizetype offset = authorityOffset(url, schemeLength);
|
||||
if (offset == -1)
|
||||
return true; // no authority is a local authority.
|
||||
|
||||
const QString authorityAndPath = url.sliced(offset);
|
||||
return authorityAndPath.startsWith(QLatin1String(authority_externalstorage))
|
||||
|| authorityAndPath.startsWith(QLatin1String(authority_downloads_documents))
|
||||
|| authorityAndPath.startsWith(QLatin1String(authority_media_documents));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Returns true if \a url is a local file that can be opened with QFile.
|
||||
|
||||
|
@ -553,14 +589,17 @@ bool QQmlFile::isLocalFile(const QString &url)
|
|||
}
|
||||
case 'q':
|
||||
case 'Q':
|
||||
return hasSchemeAndNoAuthority(url, qrc_string, strlen(qrc_string));
|
||||
return hasScheme(url, qrc_string, strlen(qrc_string))
|
||||
&& authorityOffset(url, strlen(qrc_string)) == -1;
|
||||
#if defined(Q_OS_ANDROID)
|
||||
case 'a':
|
||||
case 'A':
|
||||
return hasSchemeAndNoAuthority(url, assets_string, strlen(assets_string));
|
||||
return hasScheme(url, assets_string, strlen(assets_string))
|
||||
&& authorityOffset(url, strlen(assets_string)) == -1;
|
||||
case 'c':
|
||||
case 'C':
|
||||
return hasSchemeAndNoAuthority(url, content_string, strlen(content_string));
|
||||
return hasScheme(url, content_string, strlen(content_string))
|
||||
&& hasLocalContentAuthority(url, strlen(content_string));
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
@ -584,10 +623,12 @@ QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
|
|||
#if defined(Q_OS_ANDROID)
|
||||
if (url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0)
|
||||
return url.authority().isEmpty() ? url.toString() : QString();
|
||||
if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0)
|
||||
return url.authority().isEmpty() ? url.toString() : QString();
|
||||
if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) {
|
||||
if (hasLocalContentAuthority(url))
|
||||
return url.toString();
|
||||
return QString();
|
||||
}
|
||||
#endif
|
||||
|
||||
return url.toLocalFile();
|
||||
}
|
||||
|
||||
|
@ -646,8 +687,8 @@ QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
|
|||
#if defined(Q_OS_ANDROID)
|
||||
if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive))
|
||||
return isDoubleSlashed(url, strlen("assets:")) ? QString() : url;
|
||||
if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive))
|
||||
return isDoubleSlashed(url, strlen("content:")) ? QString() : url;
|
||||
if (hasScheme(url, content_string, strlen(content_string)))
|
||||
return hasLocalContentAuthority(url, strlen(content_string)) ? url : QString();
|
||||
#endif
|
||||
|
||||
return toLocalFile(url);
|
||||
|
|
|
@ -118,6 +118,27 @@ void tst_qqmlfile::urlData()
|
|||
QTest::addRow("file:content 1 slash") << QStringLiteral("file:content:/foo/bar") << true << QStringLiteral("content:/foo/bar");
|
||||
QTest::addRow("file:content 2 slashes") << QStringLiteral("file:content://foo/bar") << true << QStringLiteral("content://foo/bar");
|
||||
QTest::addRow("file:content 3 slashes") << QStringLiteral("file:content:///foo/bar") << true << QStringLiteral("content:///foo/bar");
|
||||
|
||||
const QString contentExternalstoragePath = hasAssetsAndContent ?
|
||||
QStringLiteral("content://com.android.externalstorage.documents/foo") : invalid;
|
||||
const QString contentDownloadsPath = hasAssetsAndContent ?
|
||||
QStringLiteral("content://com.android.providers.downloads.documents/foo") : invalid;
|
||||
const QString contentMediaPath = hasAssetsAndContent ?
|
||||
QStringLiteral("content://com.android.providers.media.documents") : invalid;
|
||||
|
||||
QTest::addRow("content externalstorage") << QStringLiteral("content://com.android.externalstorage.documents/foo")
|
||||
<< hasAssetsAndContent << contentExternalstoragePath;
|
||||
QTest::addRow("content downloads documents") << QStringLiteral("content://com.android.providers.downloads.documents/foo")
|
||||
<< hasAssetsAndContent << contentDownloadsPath;
|
||||
QTest::addRow("content media documents") << QStringLiteral("content://com.android.providers.media.documents")
|
||||
<< hasAssetsAndContent << contentMediaPath;
|
||||
|
||||
QTest::addRow("assets externalstorage") << QStringLiteral("assets://com.android.externalstorage.documents/foo")
|
||||
<< false << invalid;
|
||||
QTest::addRow("assets downloads documents") << QStringLiteral("assets://com.android.providers.downloads.documents/foo")
|
||||
<< false << invalid;
|
||||
QTest::addRow("assets media documents") << QStringLiteral("assets://com.android.providers.media.documents")
|
||||
<< false << invalid;
|
||||
}
|
||||
|
||||
void tst_qqmlfile::isLocalFile_data()
|
||||
|
|
Loading…
Reference in New Issue