7#include <QtCore/qurl.h>
8#include <QtCore/qobject.h>
9#include <QtCore/qmetaobject.h>
10#include <QtCore/qfile.h>
11#include <private/qqmlengine_p.h>
12#include <private/qqmlglobal_p.h>
13#include <private/qurl_p.h>
18
19
20
21
22
23
24
25
30#if defined(Q_OS_ANDROID)
31static constexpr QLatin1StringView assets_string {
"assets" };
32static constexpr QLatin1StringView content_string {
"content" };
33static char authority_externalstorage[] =
"com.android.externalstorage.documents";
34static char authority_downloads_documents[] =
"com.android.providers.downloads.documents";
35static char authority_media_documents[] =
"com.android.providers.media.documents";
39static constexpr QLatin1StringView weblocalfile_string {
"weblocalfile" };
42#if QT_DEPRECATED_SINCE(6
, 11
)
44QT_WARNING_DISABLE_DEPRECATED
47
48
49
50
51
52
53
54
55
59#if QT_CONFIG(qml_network)
60class QQmlFileNetworkReply :
public QObject
64 QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *,
const QUrl &);
65 ~QQmlFileNetworkReply();
69 void downloadProgress(qint64, qint64);
72 void networkFinished();
73 void networkDownloadProgress(qint64, qint64);
76 static int finishedIndex;
77 static int downloadProgressIndex;
78 static int networkFinishedIndex;
79 static int networkDownloadProgressIndex;
80 static int replyFinishedIndex;
81 static int replyDownloadProgressIndex;
87 QNetworkReply *m_reply;
97 mutable QString urlString;
102 None, NotFound, Network
107#if QT_CONFIG(qml_network)
108 QQmlFileNetworkReply *reply;
112#if QT_CONFIG(qml_network)
113int QQmlFileNetworkReply::finishedIndex = -1;
114int QQmlFileNetworkReply::downloadProgressIndex = -1;
115int QQmlFileNetworkReply::networkFinishedIndex = -1;
116int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
117int QQmlFileNetworkReply::replyFinishedIndex = -1;
118int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
120QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p,
const QUrl &url)
121: m_engine(e), m_p(p), m_reply(
nullptr)
123 if (finishedIndex == -1) {
124 finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
125 downloadProgressIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::downloadProgress).methodIndex();
126 const QMetaObject *smo = &staticMetaObject;
127 networkFinishedIndex = smo->indexOfMethod(
"networkFinished()");
128 networkDownloadProgressIndex = smo->indexOfMethod(
"networkDownloadProgress(qint64,qint64)");
130 replyFinishedIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
131 replyDownloadProgressIndex = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
133 Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
134 networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
135 replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
137 QNetworkRequest req(url);
138 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
140 m_reply = m_engine->networkAccessManager()->get(req);
141 QMetaObject::connect(m_reply, replyFinishedIndex,
this, networkFinishedIndex);
142 QMetaObject::connect(m_reply, replyDownloadProgressIndex,
this, networkDownloadProgressIndex);
145QQmlFileNetworkReply::~QQmlFileNetworkReply()
148 m_reply->disconnect();
149 m_reply->deleteLater();
153void QQmlFileNetworkReply::networkFinished()
155 if (m_reply->error()) {
156 m_p->errorString = m_reply->errorString();
157 m_p->error = QQmlFilePrivate::Network;
159 m_p->data = m_reply->readAll();
162 m_reply->deleteLater();
165 m_p->reply =
nullptr;
170void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
172 emit downloadProgress(a, b);
176QQmlFilePrivate::QQmlFilePrivate()
178#if QT_CONFIG(qml_network)
185
186
188: d(
new QQmlFilePrivate)
193
194
195
196QQmlFile::QQmlFile(QQmlEngine *engine,
const QUrl &url)
197: d(
new QQmlFilePrivate)
203
204
205
206QQmlFile::QQmlFile(QQmlEngine *engine,
const QString &url)
207 : QQmlFile(engine, QUrl(url))
212
213
216#if QT_CONFIG(qml_network)
224
225
226bool QQmlFile::isNull()
const
228 return status() == Null;
232
233
234bool QQmlFile::isReady()
const
236 return status() == Ready;
240
241
242bool QQmlFile::isError()
const
244 return status() == Error;
248
249
250bool QQmlFile::isLoading()
const
252 return status() == Loading;
256
257
258QUrl QQmlFile::url()
const
260 if (!d->urlString.isEmpty()) {
261 d->url = QUrl(d->urlString);
262 d->urlString = QString();
268
269
270QQmlFile::Status QQmlFile::status()
const
272 if (d->url.isEmpty() && d->urlString.isEmpty())
274#if QT_CONFIG(qml_network)
278 else if (d->error != QQmlFilePrivate::None)
285
286
287
288QString QQmlFile::error()
const
292 case QQmlFilePrivate::None:
294 case QQmlFilePrivate::NotFound:
295 return QLatin1String(
"File not found");
300
301
302
303qint64 QQmlFile::size()
const
305 return d->data.size();
309
310
311
312const char *QQmlFile::data()
const
314 return d->data.constData();
318
319
320
321QByteArray QQmlFile::dataByteArray()
const
327
328
329
330
331
332void QQmlFile::load(QQmlEngine *engine,
const QUrl &url)
339 if (isLocalFile(url)) {
340 const QString lf = urlToLocalFileOrQrc(url);
341 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
342 d->error = QQmlFilePrivate::NotFound;
347 if (file.open(QFile::ReadOnly)) {
348 d->data = file.readAll();
350 d->error = QQmlFilePrivate::NotFound;
353#if QT_CONFIG(qml_network)
354 d->reply =
new QQmlFileNetworkReply(engine, d, url);
356 d->error = QQmlFilePrivate::NotFound;
362
363
364
365
366
367void QQmlFile::load(QQmlEngine *engine,
const QString &url)
375 if (isLocalFile(url)) {
376 const QString lf = urlToLocalFileOrQrc(url);
377 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
378 d->error = QQmlFilePrivate::NotFound;
383 if (file.open(QFile::ReadOnly)) {
384 d->data = file.readAll();
386 d->error = QQmlFilePrivate::NotFound;
389#if QT_CONFIG(qml_network)
392 d->urlString = QString();
393 d->reply =
new QQmlFileNetworkReply(engine, d, qurl);
395 d->error = QQmlFilePrivate::NotFound;
401
402
403
404void QQmlFile::clear()
407 d->urlString = QString();
408 d->data = QByteArray();
409 d->error = QQmlFilePrivate::None;
413
414
415
416
417
418void QQmlFile::clear(QObject *object)
424#if QT_CONFIG(qml_network)
427
428
429
430bool QQmlFile::connectFinished(QObject *object,
const char *method)
432 if (!d || !d->reply) {
433 qWarning(
"QQmlFile: connectFinished() called when not loading.");
437 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
441
442
443
444
445
446bool QQmlFile::connectFinished(QObject *object,
int method)
448 if (!d || !d->reply) {
449 qWarning(
"QQmlFile: connectFinished() called when not loading.");
453 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
458
459
460
461
462
463bool QQmlFile::connectDownloadProgress(QObject *object,
const char *method)
465 if (!d || !d->reply) {
466 qWarning(
"QQmlFile: connectDownloadProgress() called when not loading.");
470 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
475
476
477
478
479
480bool QQmlFile::connectDownloadProgress(QObject *object,
int method)
482 if (!d || !d->reply) {
483 qWarning(
"QQmlFile: connectDownloadProgress() called when not loading.");
487 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
496
497
498
499
500
501
502
503
504
505bool QQmlFile::isSynchronous(
const QUrl &url)
507 if (url.isLocalFile())
509 QString scheme = url.scheme();
511 if (scheme == qrc_string) {
514#if defined(Q_OS_ANDROID)
515 }
else if (scheme == assets_string) {
517 }
else if (scheme == content_string) {
521#if defined(Q_OS_WASM)
522 }
else if (scheme == weblocalfile_string) {
532
533
534
535
536
537
538
539
540
541bool QQmlFile::isSynchronous(
const QString &url)
548 if (f == QLatin1Char(
'f') || f == QLatin1Char(
'F')) {
550 return url.size() >= file_string.size() + 2 &&
551 url.startsWith(file_string, Qt::CaseInsensitive) &&
552 url[4] == QLatin1Char(
':') && url[5] == QLatin1Char(
'/');
554 }
else if (f == QLatin1Char(
'q') || f == QLatin1Char(
'Q')) {
556 return url.size() >= qrc_string.size() + 2 &&
557 url.startsWith(qrc_string, Qt::CaseInsensitive) &&
558 url[3] == QLatin1Char(
':') && url[4] == QLatin1Char(
'/');
562#if defined(Q_OS_ANDROID)
563 else if (f == QLatin1Char(
'a') || f == QLatin1Char(
'A')) {
564 return url.length() >= assets_string.size() + 2 &&
565 url.startsWith(assets_string, Qt::CaseInsensitive) &&
566 url[6] == QLatin1Char(
':') && url[7] == QLatin1Char(
'/');
567 }
else if (f == QLatin1Char(
'c') || f == QLatin1Char(
'C')) {
568 return url.length() >= content_string.size() + 2 &&
569 url.startsWith(content_string, Qt::CaseInsensitive) &&
570 url[7] == QLatin1Char(
':') && url[8] == QLatin1Char(
'/');
574#if defined(Q_OS_WASM)
575 else if (f == QLatin1Char(
'w') || f == QLatin1Char(
'W')) {
576 return url.length() >= weblocalfile_string.size() + 2 &&
577 url.startsWith(QLatin1String(weblocalfile_string), Qt::CaseInsensitive) &&
578 url[12] == QLatin1Char(
':') && url[13] == QLatin1Char(
'/');
585#if defined(Q_OS_ANDROID)
586static bool isLocalContentAuthority(QStringView authority)
588 return authority.isEmpty()
589 || authority == QLatin1String(authority_externalstorage)
590 || authority == QLatin1String(authority_downloads_documents)
591 || authority == QLatin1String(authority_media_documents);
594static bool hasLocalContentAuthority(
const QUrl &url)
596 return isLocalContentAuthority(url.authority());
601
602
603
604
605
606
607
608bool QQmlFile::isLocalFile(
const QUrl &url)
610 if (url.isLocalFile())
612 QString scheme = url.scheme();
614 if (scheme == qrc_string)
615 return url.authority().isEmpty();
617#if defined(Q_OS_ANDROID)
618 if (scheme == assets_string)
619 return url.authority().isEmpty();
620 if (scheme == content_string)
621 return hasLocalContentAuthority(url);
624#if defined(Q_OS_WASM)
625 if (scheme == weblocalfile_string)
632static bool hasScheme(
const QString &url, QLatin1StringView scheme)
634 const qsizetype urlLength = url.size();
635 const qsizetype schemeLength = scheme.size();
637 if (urlLength < schemeLength + 1)
640 if (!url.startsWith(scheme, Qt::CaseInsensitive))
643 if (url[schemeLength] != QLatin1Char(
':'))
651 const qsizetype urlLength = url.size();
653 if (urlLength < schemeLength + 3)
656 const QLatin1Char slash(
'/');
657 if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
659 if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
660 return schemeLength + 3;
666#if defined(Q_OS_ANDROID)
667static bool hasLocalContentAuthority(
const QString &url, qsizetype schemeLength)
669 const qsizetype offset = authorityOffset(url, schemeLength);
673 const qsizetype slash = url.indexOf(QLatin1Char(
'/'), offset);
674 const qsizetype end = slash == -1 ? url.size() : slash;
675 const QStringView authority = QStringView(url).sliced(offset, end - offset);
676 return isLocalContentAuthority(authority);
682
683
684
685
686
687
688
689bool QQmlFile::isLocalFile(
const QString &url)
694 switch (url[0].toLatin1()) {
700 const qsizetype fileLength = file_string.size();
701 return url.startsWith(file_string, Qt::CaseInsensitive)
702 && url.size() > fileLength
703 && url[fileLength] == QLatin1Char(
':');
707 return hasScheme(url, qrc_string)
708 && authorityOffset(url, qrc_string.size()) == -1;
709#if defined(Q_OS_ANDROID)
712 return hasScheme(url, assets_string)
713 && authorityOffset(url, assets_string.size()) == -1;
716 return hasScheme(url, content_string)
717 && hasLocalContentAuthority(url, content_string.size());
719#if defined(Q_OS_WASM)
722 return hasScheme(url, weblocalfile_string);
732
733
734
735
736
737QString QQmlFile::urlToLocalFileOrQrc(
const QUrl& url)
739 if (url.scheme() == qrc_string) {
740 if (!url.authority().isEmpty())
742 const QString path = url.path(QUrlDecodeForLocalFile);
744 return QLatin1Char(
':') + path;
745 return url.path(QUrl::PrettyDecoded).isEmpty() ? QStringLiteral(
":") : QString();
748#if defined(Q_OS_ANDROID)
749 if (url.scheme() == assets_string)
750 return url.authority().isEmpty() ? url.toString() : QString();
751 if (url.scheme() == content_string) {
752 if (hasLocalContentAuthority(url))
753 return url.toString();
758#if defined(Q_OS_WASM)
759 if (url.scheme() == weblocalfile_string)
760 return url.toString();
763 return url.toLocalFile();
768 const QUrl file(url);
769 if (!file.isLocalFile())
775 return file.toLocalFile();
780 const qsizetype urlLength = url.size();
781 if (urlLength < offset + 2)
784 const QLatin1Char slash(
'/');
785 if (url[offset] != slash || url[offset + 1] != slash)
788 if (urlLength < offset + 3)
791 return url[offset + 2] != slash;
795
796
797
798
799
800QString QQmlFile::urlToLocalFileOrQrc(
const QString& url)
802 if (url.startsWith(QLatin1String(
"qrc://"), Qt::CaseInsensitive)) {
805 if (url.size() == 6 || url[6] != QLatin1Char(
'/')) {
806 Q_ASSERT(isDoubleSlashed(url, strlen(
"qrc:")));
809 Q_ASSERT(!isDoubleSlashed(url, strlen(
"qrc:")));
810 return QLatin1Char(
':') + QStringView{url}.mid(6);
813 if (url.startsWith(QLatin1String(
"qrc:"), Qt::CaseInsensitive)) {
814 Q_ASSERT(!isDoubleSlashed(url, strlen(
"qrc:")));
816 return QLatin1Char(
':') + QStringView{url}.mid(4);
817 return QStringLiteral(
":");
820#if defined(Q_OS_ANDROID)
821 if (hasScheme(url, assets_string))
822 return authorityOffset(url, assets_string.size()) == -1 ? url : QString();
823 if (hasScheme(url, content_string))
824 return hasLocalContentAuthority(url, content_string.size()) ? url : QString();
827#if defined(Q_OS_WASM)
828 if (hasScheme(url, weblocalfile_string))
832 return toLocalFile(url);
837#include "qqmlfile.moc"
Combined button and popup list for selecting options.
static bool hasScheme(const QString &url, QLatin1StringView scheme)
static constexpr QLatin1StringView file_string
static constexpr QLatin1StringView qrc_string
static QString toLocalFile(const QString &url)
static bool isDoubleSlashed(const QString &url, qsizetype offset)
static qsizetype authorityOffset(const QString &url, qsizetype schemeLength)