6#include <QtCore/qurl.h>
7#include <QtCore/qobject.h>
8#include <QtCore/qmetaobject.h>
9#include <QtCore/qfile.h>
10#include <private/qqmlengine_p.h>
11#include <private/qqmlglobal_p.h>
16
17
18
19
20
21
22
23
28#if defined(Q_OS_ANDROID)
29static char assets_string[] =
"assets";
30static char content_string[] =
"content";
31static char authority_externalstorage[] =
"com.android.externalstorage.documents";
32static char authority_downloads_documents[] =
"com.android.providers.downloads.documents";
33static char authority_media_documents[] =
"com.android.providers.media.documents";
36#if QT_DEPRECATED_SINCE(6
, 11
)
38QT_WARNING_DISABLE_DEPRECATED
41
42
43
44
45
46
47
48
49
53#if QT_CONFIG(qml_network)
54class QQmlFileNetworkReply :
public QObject
58 QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *,
const QUrl &);
59 ~QQmlFileNetworkReply();
63 void downloadProgress(qint64, qint64);
66 void networkFinished();
67 void networkDownloadProgress(qint64, qint64);
70 static int finishedIndex;
71 static int downloadProgressIndex;
72 static int networkFinishedIndex;
73 static int networkDownloadProgressIndex;
74 static int replyFinishedIndex;
75 static int replyDownloadProgressIndex;
81 QNetworkReply *m_reply;
91 mutable QString urlString;
96 None, NotFound, Network
101#if QT_CONFIG(qml_network)
102 QQmlFileNetworkReply *reply;
106#if QT_CONFIG(qml_network)
107int QQmlFileNetworkReply::finishedIndex = -1;
108int QQmlFileNetworkReply::downloadProgressIndex = -1;
109int QQmlFileNetworkReply::networkFinishedIndex = -1;
110int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
111int QQmlFileNetworkReply::replyFinishedIndex = -1;
112int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
114QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p,
const QUrl &url)
115: m_engine(e), m_p(p), m_reply(
nullptr)
117 if (finishedIndex == -1) {
118 finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
119 downloadProgressIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::downloadProgress).methodIndex();
120 const QMetaObject *smo = &staticMetaObject;
121 networkFinishedIndex = smo->indexOfMethod(
"networkFinished()");
122 networkDownloadProgressIndex = smo->indexOfMethod(
"networkDownloadProgress(qint64,qint64)");
124 replyFinishedIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
125 replyDownloadProgressIndex = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
127 Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
128 networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
129 replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
131 QNetworkRequest req(url);
132 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
134 m_reply = m_engine->networkAccessManager()->get(req);
135 QMetaObject::connect(m_reply, replyFinishedIndex,
this, networkFinishedIndex);
136 QMetaObject::connect(m_reply, replyDownloadProgressIndex,
this, networkDownloadProgressIndex);
139QQmlFileNetworkReply::~QQmlFileNetworkReply()
142 m_reply->disconnect();
143 m_reply->deleteLater();
147void QQmlFileNetworkReply::networkFinished()
149 if (m_reply->error()) {
150 m_p->errorString = m_reply->errorString();
151 m_p->error = QQmlFilePrivate::Network;
153 m_p->data = m_reply->readAll();
156 m_reply->deleteLater();
159 m_p->reply =
nullptr;
164void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
166 emit downloadProgress(a, b);
170QQmlFilePrivate::QQmlFilePrivate()
172#if QT_CONFIG(qml_network)
179
180
182: d(
new QQmlFilePrivate)
187
188
189
190QQmlFile::QQmlFile(QQmlEngine *engine,
const QUrl &url)
191: d(
new QQmlFilePrivate)
197
198
199
200QQmlFile::QQmlFile(QQmlEngine *engine,
const QString &url)
201 : QQmlFile(engine, QUrl(url))
206
207
210#if QT_CONFIG(qml_network)
218
219
220bool QQmlFile::isNull()
const
222 return status() == Null;
226
227
228bool QQmlFile::isReady()
const
230 return status() == Ready;
234
235
236bool QQmlFile::isError()
const
238 return status() == Error;
242
243
244bool QQmlFile::isLoading()
const
246 return status() == Loading;
250
251
252QUrl QQmlFile::url()
const
254 if (!d->urlString.isEmpty()) {
255 d->url = QUrl(d->urlString);
256 d->urlString = QString();
262
263
264QQmlFile::Status QQmlFile::status()
const
266 if (d->url.isEmpty() && d->urlString.isEmpty())
268#if QT_CONFIG(qml_network)
272 else if (d->error != QQmlFilePrivate::None)
279
280
281
282QString QQmlFile::error()
const
286 case QQmlFilePrivate::None:
288 case QQmlFilePrivate::NotFound:
289 return QLatin1String(
"File not found");
294
295
296
297qint64 QQmlFile::size()
const
299 return d->data.size();
303
304
305
306const char *QQmlFile::data()
const
308 return d->data.constData();
312
313
314
315QByteArray QQmlFile::dataByteArray()
const
321
322
323
324
325
326void QQmlFile::load(QQmlEngine *engine,
const QUrl &url)
333 if (isLocalFile(url)) {
334 const QString lf = urlToLocalFileOrQrc(url);
335 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
336 d->error = QQmlFilePrivate::NotFound;
341 if (file.open(QFile::ReadOnly)) {
342 d->data = file.readAll();
344 d->error = QQmlFilePrivate::NotFound;
347#if QT_CONFIG(qml_network)
348 d->reply =
new QQmlFileNetworkReply(engine, d, url);
350 d->error = QQmlFilePrivate::NotFound;
356
357
358
359
360
361void QQmlFile::load(QQmlEngine *engine,
const QString &url)
369 if (isLocalFile(url)) {
370 const QString lf = urlToLocalFileOrQrc(url);
371 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
372 d->error = QQmlFilePrivate::NotFound;
377 if (file.open(QFile::ReadOnly)) {
378 d->data = file.readAll();
380 d->error = QQmlFilePrivate::NotFound;
383#if QT_CONFIG(qml_network)
386 d->urlString = QString();
387 d->reply =
new QQmlFileNetworkReply(engine, d, qurl);
389 d->error = QQmlFilePrivate::NotFound;
395
396
397
398void QQmlFile::clear()
401 d->urlString = QString();
402 d->data = QByteArray();
403 d->error = QQmlFilePrivate::None;
407
408
409
410
411
412void QQmlFile::clear(QObject *object)
418#if QT_CONFIG(qml_network)
421
422
423
424bool QQmlFile::connectFinished(QObject *object,
const char *method)
426 if (!d || !d->reply) {
427 qWarning(
"QQmlFile: connectFinished() called when not loading.");
431 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
435
436
437
438
439
440bool QQmlFile::connectFinished(QObject *object,
int method)
442 if (!d || !d->reply) {
443 qWarning(
"QQmlFile: connectFinished() called when not loading.");
447 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
452
453
454
455
456
457bool QQmlFile::connectDownloadProgress(QObject *object,
const char *method)
459 if (!d || !d->reply) {
460 qWarning(
"QQmlFile: connectDownloadProgress() called when not loading.");
464 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
469
470
471
472
473
474bool QQmlFile::connectDownloadProgress(QObject *object,
int method)
476 if (!d || !d->reply) {
477 qWarning(
"QQmlFile: connectDownloadProgress() called when not loading.");
481 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
490
491
492
493
494
495
496
497
498
499bool QQmlFile::isSynchronous(
const QUrl &url)
501 QString scheme = url.scheme();
503 if ((scheme.size() == 4 && 0 == scheme.compare(QLatin1String(file_string), Qt::CaseInsensitive)) ||
504 (scheme.size() == 3 && 0 == scheme.compare(QLatin1String(qrc_string), Qt::CaseInsensitive))) {
507#if defined(Q_OS_ANDROID)
508 }
else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) {
510 }
else if (scheme.length() == 7 && 0 == scheme.compare(QLatin1String(content_string), Qt::CaseInsensitive)) {
520
521
522
523
524
525
526
527
528
529bool QQmlFile::isSynchronous(
const QString &url)
536 if (f == QLatin1Char(
'f') || f == QLatin1Char(
'F')) {
538 return url.size() >= 7 &&
539 url.startsWith(QLatin1String(file_string), Qt::CaseInsensitive) &&
540 url[4] == QLatin1Char(
':') && url[5] == QLatin1Char(
'/') && url[6] == QLatin1Char(
'/');
542 }
else if (f == QLatin1Char(
'q') || f == QLatin1Char(
'Q')) {
544 return url.size() >= 5 &&
545 url.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive) &&
546 url[3] == QLatin1Char(
':') && url[4] == QLatin1Char(
'/');
550#if defined(Q_OS_ANDROID)
551 else if (f == QLatin1Char(
'a') || f == QLatin1Char(
'A')) {
552 return url.length() >= 8 &&
553 url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) &&
554 url[6] == QLatin1Char(
':') && url[7] == QLatin1Char(
'/');
555 }
else if (f == QLatin1Char(
'c') || f == QLatin1Char(
'C')) {
556 return url.length() >= 9 &&
557 url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) &&
558 url[7] == QLatin1Char(
':') && url[8] == QLatin1Char(
'/');
565#if defined(Q_OS_ANDROID)
566static bool hasLocalContentAuthority(
const QUrl &url)
568 const QString authority = url.authority();
569 return authority.isEmpty()
570 || authority == QLatin1String(authority_externalstorage)
571 || authority == QLatin1String(authority_downloads_documents)
572 || authority == QLatin1String(authority_media_documents);
577
578
579
580
581
582
583
584bool QQmlFile::isLocalFile(
const QUrl &url)
586 QString scheme = url.scheme();
591 if (scheme.size() == 4 && scheme.startsWith(QLatin1String(file_string), Qt::CaseInsensitive))
594 if (scheme.size() == 3 && scheme.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive))
595 return url.authority().isEmpty();
597#if defined(Q_OS_ANDROID)
598 if (scheme.length() == 6
599 && scheme.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive))
600 return url.authority().isEmpty();
601 if (scheme.length() == 7
602 && scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive))
603 return hasLocalContentAuthority(url);
609static bool hasScheme(
const QString &url,
const char *scheme, qsizetype schemeLength)
611 const qsizetype urlLength = url.size();
613 if (urlLength < schemeLength + 1)
616 if (!url.startsWith(QLatin1String(scheme, scheme + schemeLength), Qt::CaseInsensitive))
619 if (url[schemeLength] != QLatin1Char(
':'))
627 const qsizetype urlLength = url.size();
629 if (urlLength < schemeLength + 3)
632 const QLatin1Char slash(
'/');
633 if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
635 if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
636 return schemeLength + 3;
642#if defined(Q_OS_ANDROID)
643static bool hasLocalContentAuthority(
const QString &url, qsizetype schemeLength)
645 const qsizetype offset = authorityOffset(url, schemeLength);
649 const QString authorityAndPath = url.sliced(offset);
650 return authorityAndPath.startsWith(QLatin1String(authority_externalstorage))
651 || authorityAndPath.startsWith(QLatin1String(authority_downloads_documents))
652 || authorityAndPath.startsWith(QLatin1String(authority_media_documents));
658
659
660
661
662
663
664
665bool QQmlFile::isLocalFile(
const QString &url)
670 switch (url[0].toLatin1()) {
676 const qsizetype fileLength = strlen(file_string);
677 return url.startsWith(QLatin1String(file_string, file_string + fileLength),
679 && url.size() > fileLength
680 && url[fileLength] == QLatin1Char(
':');
684 return hasScheme(url, qrc_string, strlen(qrc_string))
685 && authorityOffset(url, strlen(qrc_string)) == -1;
686#if defined(Q_OS_ANDROID)
689 return hasScheme(url, assets_string, strlen(assets_string))
690 && authorityOffset(url, strlen(assets_string)) == -1;
693 return hasScheme(url, content_string, strlen(content_string))
694 && hasLocalContentAuthority(url, strlen(content_string));
704
705
706
707
708
709QString QQmlFile::urlToLocalFileOrQrc(
const QUrl& url)
711 if (url.scheme().compare(QLatin1String(
"qrc"), Qt::CaseInsensitive) == 0) {
712 if (url.authority().isEmpty())
713 return QLatin1Char(
':') + url.path();
717#if defined(Q_OS_ANDROID)
718 if (url.scheme().compare(QLatin1String(
"assets"), Qt::CaseInsensitive) == 0)
719 return url.authority().isEmpty() ? url.toString() : QString();
720 if (url.scheme().compare(QLatin1String(
"content"), Qt::CaseInsensitive) == 0) {
721 if (hasLocalContentAuthority(url))
722 return url.toString();
726 return url.toLocalFile();
731 const QUrl file(url);
732 if (!file.isLocalFile())
738 return file.toLocalFile();
743 const qsizetype urlLength = url.size();
744 if (urlLength < offset + 2)
747 const QLatin1Char slash(
'/');
748 if (url[offset] != slash || url[offset + 1] != slash)
751 if (urlLength < offset + 3)
754 return url[offset + 2] != slash;
758
759
760
761
762
763QString QQmlFile::urlToLocalFileOrQrc(
const QString& url)
765 if (url.startsWith(QLatin1String(
"qrc://"), Qt::CaseInsensitive)) {
768 if (url.size() == 6 || url[6] != QLatin1Char(
'/')) {
769 Q_ASSERT(isDoubleSlashed(url, strlen(
"qrc:")));
772 Q_ASSERT(!isDoubleSlashed(url, strlen(
"qrc:")));
773 return QLatin1Char(
':') + QStringView{url}.mid(6);
776 if (url.startsWith(QLatin1String(
"qrc:"), Qt::CaseInsensitive)) {
777 Q_ASSERT(!isDoubleSlashed(url, strlen(
"qrc:")));
779 return QLatin1Char(
':') + QStringView{url}.mid(4);
780 return QStringLiteral(
":");
783#if defined(Q_OS_ANDROID)
784 if (url.startsWith(QLatin1String(
"assets:"), Qt::CaseInsensitive))
785 return isDoubleSlashed(url, strlen(
"assets:")) ? QString() : url;
786 if (hasScheme(url, content_string, strlen(content_string)))
787 return hasLocalContentAuthority(url, strlen(content_string)) ? url : QString();
790 return toLocalFile(url);
795#include "qqmlfile.moc"
static QString toLocalFile(const QString &url)
static bool isDoubleSlashed(const QString &url, qsizetype offset)
static qsizetype authorityOffset(const QString &url, qsizetype schemeLength)
static char file_string[]
static bool hasScheme(const QString &url, const char *scheme, qsizetype schemeLength)