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>
17
18
19
20
21
22
23
24
29#if defined(Q_OS_ANDROID)
30static char assets_string[] =
"assets";
31static char content_string[] =
"content";
32static char authority_externalstorage[] =
"com.android.externalstorage.documents";
33static char authority_downloads_documents[] =
"com.android.providers.downloads.documents";
34static char authority_media_documents[] =
"com.android.providers.media.documents";
37#if QT_DEPRECATED_SINCE(6
, 11
)
39QT_WARNING_DISABLE_DEPRECATED
42
43
44
45
46
47
48
49
50
54#if QT_CONFIG(qml_network)
55class QQmlFileNetworkReply :
public QObject
59 QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *,
const QUrl &);
60 ~QQmlFileNetworkReply();
64 void downloadProgress(qint64, qint64);
67 void networkFinished();
68 void networkDownloadProgress(qint64, qint64);
71 static int finishedIndex;
72 static int downloadProgressIndex;
73 static int networkFinishedIndex;
74 static int networkDownloadProgressIndex;
75 static int replyFinishedIndex;
76 static int replyDownloadProgressIndex;
82 QNetworkReply *m_reply;
92 mutable QString urlString;
97 None, NotFound, Network
102#if QT_CONFIG(qml_network)
103 QQmlFileNetworkReply *reply;
107#if QT_CONFIG(qml_network)
108int QQmlFileNetworkReply::finishedIndex = -1;
109int QQmlFileNetworkReply::downloadProgressIndex = -1;
110int QQmlFileNetworkReply::networkFinishedIndex = -1;
111int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
112int QQmlFileNetworkReply::replyFinishedIndex = -1;
113int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
115QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p,
const QUrl &url)
116: m_engine(e), m_p(p), m_reply(
nullptr)
118 if (finishedIndex == -1) {
119 finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
120 downloadProgressIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::downloadProgress).methodIndex();
121 const QMetaObject *smo = &staticMetaObject;
122 networkFinishedIndex = smo->indexOfMethod(
"networkFinished()");
123 networkDownloadProgressIndex = smo->indexOfMethod(
"networkDownloadProgress(qint64,qint64)");
125 replyFinishedIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
126 replyDownloadProgressIndex = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
128 Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
129 networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
130 replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
132 QNetworkRequest req(url);
133 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
135 m_reply = m_engine->networkAccessManager()->get(req);
136 QMetaObject::connect(m_reply, replyFinishedIndex,
this, networkFinishedIndex);
137 QMetaObject::connect(m_reply, replyDownloadProgressIndex,
this, networkDownloadProgressIndex);
140QQmlFileNetworkReply::~QQmlFileNetworkReply()
143 m_reply->disconnect();
144 m_reply->deleteLater();
148void QQmlFileNetworkReply::networkFinished()
150 if (m_reply->error()) {
151 m_p->errorString = m_reply->errorString();
152 m_p->error = QQmlFilePrivate::Network;
154 m_p->data = m_reply->readAll();
157 m_reply->deleteLater();
160 m_p->reply =
nullptr;
165void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
167 emit downloadProgress(a, b);
171QQmlFilePrivate::QQmlFilePrivate()
173#if QT_CONFIG(qml_network)
180
181
183: d(
new QQmlFilePrivate)
188
189
190
191QQmlFile::QQmlFile(QQmlEngine *engine,
const QUrl &url)
192: d(
new QQmlFilePrivate)
198
199
200
201QQmlFile::QQmlFile(QQmlEngine *engine,
const QString &url)
202 : QQmlFile(engine, QUrl(url))
207
208
211#if QT_CONFIG(qml_network)
219
220
221bool QQmlFile::isNull()
const
223 return status() == Null;
227
228
229bool QQmlFile::isReady()
const
231 return status() == Ready;
235
236
237bool QQmlFile::isError()
const
239 return status() == Error;
243
244
245bool QQmlFile::isLoading()
const
247 return status() == Loading;
251
252
253QUrl QQmlFile::url()
const
255 if (!d->urlString.isEmpty()) {
256 d->url = QUrl(d->urlString);
257 d->urlString = QString();
263
264
265QQmlFile::Status QQmlFile::status()
const
267 if (d->url.isEmpty() && d->urlString.isEmpty())
269#if QT_CONFIG(qml_network)
273 else if (d->error != QQmlFilePrivate::None)
280
281
282
283QString QQmlFile::error()
const
287 case QQmlFilePrivate::None:
289 case QQmlFilePrivate::NotFound:
290 return QLatin1String(
"File not found");
295
296
297
298qint64 QQmlFile::size()
const
300 return d->data.size();
304
305
306
307const char *QQmlFile::data()
const
309 return d->data.constData();
313
314
315
316QByteArray QQmlFile::dataByteArray()
const
322
323
324
325
326
327void QQmlFile::load(QQmlEngine *engine,
const QUrl &url)
334 if (isLocalFile(url)) {
335 const QString lf = urlToLocalFileOrQrc(url);
336 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
337 d->error = QQmlFilePrivate::NotFound;
342 if (file.open(QFile::ReadOnly)) {
343 d->data = file.readAll();
345 d->error = QQmlFilePrivate::NotFound;
348#if QT_CONFIG(qml_network)
349 d->reply =
new QQmlFileNetworkReply(engine, d, url);
351 d->error = QQmlFilePrivate::NotFound;
357
358
359
360
361
362void QQmlFile::load(QQmlEngine *engine,
const QString &url)
370 if (isLocalFile(url)) {
371 const QString lf = urlToLocalFileOrQrc(url);
372 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
373 d->error = QQmlFilePrivate::NotFound;
378 if (file.open(QFile::ReadOnly)) {
379 d->data = file.readAll();
381 d->error = QQmlFilePrivate::NotFound;
384#if QT_CONFIG(qml_network)
387 d->urlString = QString();
388 d->reply =
new QQmlFileNetworkReply(engine, d, qurl);
390 d->error = QQmlFilePrivate::NotFound;
396
397
398
399void QQmlFile::clear()
402 d->urlString = QString();
403 d->data = QByteArray();
404 d->error = QQmlFilePrivate::None;
408
409
410
411
412
413void QQmlFile::clear(QObject *object)
419#if QT_CONFIG(qml_network)
422
423
424
425bool QQmlFile::connectFinished(QObject *object,
const char *method)
427 if (!d || !d->reply) {
428 qWarning(
"QQmlFile: connectFinished() called when not loading.");
432 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
436
437
438
439
440
441bool QQmlFile::connectFinished(QObject *object,
int method)
443 if (!d || !d->reply) {
444 qWarning(
"QQmlFile: connectFinished() called when not loading.");
448 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
453
454
455
456
457
458bool QQmlFile::connectDownloadProgress(QObject *object,
const char *method)
460 if (!d || !d->reply) {
461 qWarning(
"QQmlFile: connectDownloadProgress() called when not loading.");
465 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
470
471
472
473
474
475bool QQmlFile::connectDownloadProgress(QObject *object,
int method)
477 if (!d || !d->reply) {
478 qWarning(
"QQmlFile: connectDownloadProgress() called when not loading.");
482 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
491
492
493
494
495
496
497
498
499
500bool QQmlFile::isSynchronous(
const QUrl &url)
502 QString scheme = url.scheme();
504 if ((scheme.size() == 4 && 0 == scheme.compare(QLatin1String(file_string), Qt::CaseInsensitive)) ||
505 (scheme.size() == 3 && 0 == scheme.compare(QLatin1String(qrc_string), Qt::CaseInsensitive))) {
508#if defined(Q_OS_ANDROID)
509 }
else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) {
511 }
else if (scheme.length() == 7 && 0 == scheme.compare(QLatin1String(content_string), Qt::CaseInsensitive)) {
521
522
523
524
525
526
527
528
529
530bool QQmlFile::isSynchronous(
const QString &url)
537 if (f == QLatin1Char(
'f') || f == QLatin1Char(
'F')) {
539 return url.size() >= 7 &&
540 url.startsWith(QLatin1String(file_string), Qt::CaseInsensitive) &&
541 url[4] == QLatin1Char(
':') && url[5] == QLatin1Char(
'/') && url[6] == QLatin1Char(
'/');
543 }
else if (f == QLatin1Char(
'q') || f == QLatin1Char(
'Q')) {
545 return url.size() >= 5 &&
546 url.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive) &&
547 url[3] == QLatin1Char(
':') && url[4] == QLatin1Char(
'/');
551#if defined(Q_OS_ANDROID)
552 else if (f == QLatin1Char(
'a') || f == QLatin1Char(
'A')) {
553 return url.length() >= 8 &&
554 url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) &&
555 url[6] == QLatin1Char(
':') && url[7] == QLatin1Char(
'/');
556 }
else if (f == QLatin1Char(
'c') || f == QLatin1Char(
'C')) {
557 return url.length() >= 9 &&
558 url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) &&
559 url[7] == QLatin1Char(
':') && url[8] == QLatin1Char(
'/');
566#if defined(Q_OS_ANDROID)
567static bool hasLocalContentAuthority(
const QUrl &url)
569 const QString authority = url.authority();
570 return authority.isEmpty()
571 || authority == QLatin1String(authority_externalstorage)
572 || authority == QLatin1String(authority_downloads_documents)
573 || authority == QLatin1String(authority_media_documents);
578
579
580
581
582
583
584
585bool QQmlFile::isLocalFile(
const QUrl &url)
587 QString scheme = url.scheme();
592 if (scheme.size() == 4 && scheme.startsWith(QLatin1String(file_string), Qt::CaseInsensitive))
595 if (scheme.size() == 3 && scheme.startsWith(QLatin1String(qrc_string), Qt::CaseInsensitive))
596 return url.authority().isEmpty();
598#if defined(Q_OS_ANDROID)
599 if (scheme.length() == 6
600 && scheme.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive))
601 return url.authority().isEmpty();
602 if (scheme.length() == 7
603 && scheme.startsWith(QLatin1String(content_string), Qt::CaseInsensitive))
604 return hasLocalContentAuthority(url);
610static bool hasScheme(
const QString &url,
const char *scheme, qsizetype schemeLength)
612 const qsizetype urlLength = url.size();
614 if (urlLength < schemeLength + 1)
617 if (!url.startsWith(QLatin1String(scheme, scheme + schemeLength), Qt::CaseInsensitive))
620 if (url[schemeLength] != QLatin1Char(
':'))
628 const qsizetype urlLength = url.size();
630 if (urlLength < schemeLength + 3)
633 const QLatin1Char slash(
'/');
634 if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
636 if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
637 return schemeLength + 3;
643#if defined(Q_OS_ANDROID)
644static bool hasLocalContentAuthority(
const QString &url, qsizetype schemeLength)
646 const qsizetype offset = authorityOffset(url, schemeLength);
650 const QString authorityAndPath = url.sliced(offset);
651 return authorityAndPath.startsWith(QLatin1String(authority_externalstorage))
652 || authorityAndPath.startsWith(QLatin1String(authority_downloads_documents))
653 || authorityAndPath.startsWith(QLatin1String(authority_media_documents));
659
660
661
662
663
664
665
666bool QQmlFile::isLocalFile(
const QString &url)
671 switch (url[0].toLatin1()) {
677 const qsizetype fileLength = strlen(file_string);
678 return url.startsWith(QLatin1String(file_string, file_string + fileLength),
680 && url.size() > fileLength
681 && url[fileLength] == QLatin1Char(
':');
685 return hasScheme(url, qrc_string, strlen(qrc_string))
686 && authorityOffset(url, strlen(qrc_string)) == -1;
687#if defined(Q_OS_ANDROID)
690 return hasScheme(url, assets_string, strlen(assets_string))
691 && authorityOffset(url, strlen(assets_string)) == -1;
694 return hasScheme(url, content_string, strlen(content_string))
695 && hasLocalContentAuthority(url, strlen(content_string));
705
706
707
708
709
710QString QQmlFile::urlToLocalFileOrQrc(
const QUrl& url)
712 if (url.scheme().compare(QLatin1String(
"qrc"), Qt::CaseInsensitive) == 0) {
713 if (url.authority().isEmpty())
714 return QLatin1Char(
':') + url.path();
718#if defined(Q_OS_ANDROID)
719 if (url.scheme().compare(QLatin1String(
"assets"), Qt::CaseInsensitive) == 0)
720 return url.authority().isEmpty() ? url.toString() : QString();
721 if (url.scheme().compare(QLatin1String(
"content"), Qt::CaseInsensitive) == 0) {
722 if (hasLocalContentAuthority(url))
723 return url.toString();
727 return url.toLocalFile();
732 const QUrl file(url);
733 if (!file.isLocalFile())
739 return file.toLocalFile();
744 const qsizetype urlLength = url.size();
745 if (urlLength < offset + 2)
748 const QLatin1Char slash(
'/');
749 if (url[offset] != slash || url[offset + 1] != slash)
752 if (urlLength < offset + 3)
755 return url[offset + 2] != slash;
759
760
761
762
763
764QString QQmlFile::urlToLocalFileOrQrc(
const QString& url)
766 if (url.startsWith(QLatin1String(
"qrc://"), Qt::CaseInsensitive)) {
769 if (url.size() == 6 || url[6] != QLatin1Char(
'/')) {
770 Q_ASSERT(isDoubleSlashed(url, strlen(
"qrc:")));
773 Q_ASSERT(!isDoubleSlashed(url, strlen(
"qrc:")));
774 return QLatin1Char(
':') + QStringView{url}.mid(6);
777 if (url.startsWith(QLatin1String(
"qrc:"), Qt::CaseInsensitive)) {
778 Q_ASSERT(!isDoubleSlashed(url, strlen(
"qrc:")));
780 return QLatin1Char(
':') + QStringView{url}.mid(4);
781 return QStringLiteral(
":");
784#if defined(Q_OS_ANDROID)
785 if (url.startsWith(QLatin1String(
"assets:"), Qt::CaseInsensitive))
786 return isDoubleSlashed(url, strlen(
"assets:")) ? QString() : url;
787 if (hasScheme(url, content_string, strlen(content_string)))
788 return hasLocalContentAuthority(url, strlen(content_string)) ? url : QString();
791 return toLocalFile(url);
796#include "qqmlfile.moc"
Combined button and popup list for selecting options.
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)