Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlfile.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
5#include "qqmlfile.h"
6
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>
14
16
17/*!
18 \class QQmlFile
19 \inmodule QtQml
20 \since 5.0
21 \brief The QQmlFile class provides static utility methods to categorize URLs.
22
23 QQmlFile provides some static utility methods to categorize URLs
24 and file names the way \l{QQmlEngine} does when loading content from them.
25*/
26
27static constexpr QLatin1StringView qrc_string { "qrc" };
28static constexpr QLatin1StringView file_string { "file" };
29
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";
36#endif
37
38#if defined(Q_OS_WASM)
39static constexpr QLatin1StringView weblocalfile_string { "weblocalfile" };
40#endif
41
42#if QT_DEPRECATED_SINCE(6, 11)
43QT_WARNING_PUSH
44QT_WARNING_DISABLE_DEPRECATED
45
46/*!
47 \internal
48 \deprecated [6.11]
49
50 \enum QQmlFile::Status
51 \value Null
52 \value Ready
53 \value Error
54 \value Loading
55 */
56
57class QQmlFilePrivate;
58
59#if QT_CONFIG(qml_network)
60class QQmlFileNetworkReply : public QObject
61{
62Q_OBJECT
63public:
64 QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *, const QUrl &);
65 ~QQmlFileNetworkReply();
66
67signals:
68 void finished();
69 void downloadProgress(qint64, qint64);
70
71public slots:
72 void networkFinished();
73 void networkDownloadProgress(qint64, qint64);
74
75public:
76 static int finishedIndex;
77 static int downloadProgressIndex;
78 static int networkFinishedIndex;
79 static int networkDownloadProgressIndex;
80 static int replyFinishedIndex;
81 static int replyDownloadProgressIndex;
82
83private:
84 QQmlEngine *m_engine;
85 QQmlFilePrivate *m_p;
86
87 QNetworkReply *m_reply;
88};
89#endif
90
91class QQmlFilePrivate
92{
93public:
94 QQmlFilePrivate();
95
96 mutable QUrl url;
97 mutable QString urlString;
98
99 QByteArray data;
100
101 enum Error {
102 None, NotFound, Network
103 };
104
105 Error error;
106 QString errorString;
107#if QT_CONFIG(qml_network)
108 QQmlFileNetworkReply *reply;
109#endif
110};
111
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;
119
120QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
121: m_engine(e), m_p(p), m_reply(nullptr)
122{
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)");
129
130 replyFinishedIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
131 replyDownloadProgressIndex = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
132 }
133 Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
134 networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
135 replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
136
137 QNetworkRequest req(url);
138 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
139
140 m_reply = m_engine->networkAccessManager()->get(req);
141 QMetaObject::connect(m_reply, replyFinishedIndex, this, networkFinishedIndex);
142 QMetaObject::connect(m_reply, replyDownloadProgressIndex, this, networkDownloadProgressIndex);
143}
144
145QQmlFileNetworkReply::~QQmlFileNetworkReply()
146{
147 if (m_reply) {
148 m_reply->disconnect();
149 m_reply->deleteLater();
150 }
151}
152
153void QQmlFileNetworkReply::networkFinished()
154{
155 if (m_reply->error()) {
156 m_p->errorString = m_reply->errorString();
157 m_p->error = QQmlFilePrivate::Network;
158 } else {
159 m_p->data = m_reply->readAll();
160 }
161
162 m_reply->deleteLater();
163 m_reply = nullptr;
164
165 m_p->reply = nullptr;
166 emit finished();
167 delete this;
168}
169
170void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
171{
172 emit downloadProgress(a, b);
173}
174#endif // qml_network
175
176QQmlFilePrivate::QQmlFilePrivate()
177: error(None)
178#if QT_CONFIG(qml_network)
179, reply(nullptr)
180#endif
181{
182}
183
184/*!
185 \internal
186 */
187QQmlFile::QQmlFile()
188: d(new QQmlFilePrivate)
189{
190}
191
192/*!
193 \internal
194 Constructs a QQmlFile for content at \a url, using \a engine to retrieve it.
195 */
196QQmlFile::QQmlFile(QQmlEngine *engine, const QUrl &url)
197: d(new QQmlFilePrivate)
198{
199 load(engine, url);
200}
201
202/*!
203 \internal
204 Constructs a QQmlFile for content at \a url, using \a engine to retrieve it.
205 */
206QQmlFile::QQmlFile(QQmlEngine *engine, const QString &url)
207 : QQmlFile(engine, QUrl(url))
208{
209}
210
211/*!
212 \internal
213 */
214QQmlFile::~QQmlFile()
215{
216#if QT_CONFIG(qml_network)
217 delete d->reply;
218#endif
219 delete d;
220 d = nullptr;
221}
222
223/*!
224 \internal
225 */
226bool QQmlFile::isNull() const
227{
228 return status() == Null;
229}
230
231/*!
232 \internal
233 */
234bool QQmlFile::isReady() const
235{
236 return status() == Ready;
237}
238
239/*!
240 \internal
241 */
242bool QQmlFile::isError() const
243{
244 return status() == Error;
245}
246
247/*!
248 \internal
249 */
250bool QQmlFile::isLoading() const
251{
252 return status() == Loading;
253}
254
255/*!
256 \internal
257 */
258QUrl QQmlFile::url() const
259{
260 if (!d->urlString.isEmpty()) {
261 d->url = QUrl(d->urlString);
262 d->urlString = QString();
263 }
264 return d->url;
265}
266
267/*!
268 \internal
269 */
270QQmlFile::Status QQmlFile::status() const
271{
272 if (d->url.isEmpty() && d->urlString.isEmpty())
273 return Null;
274#if QT_CONFIG(qml_network)
275 else if (d->reply)
276 return Loading;
277#endif
278 else if (d->error != QQmlFilePrivate::None)
279 return Error;
280 else
281 return Ready;
282}
283
284/*!
285 \internal
286 \deprecated [6.11]
287 */
288QString QQmlFile::error() const
289{
290 switch (d->error) {
291 default:
292 case QQmlFilePrivate::None:
293 return QString();
294 case QQmlFilePrivate::NotFound:
295 return QLatin1String("File not found");
296 }
297}
298
299/*!
300 \internal
301 \deprecated [6.11]
302 */
303qint64 QQmlFile::size() const
304{
305 return d->data.size();
306}
307
308/*!
309 \internal
310 \deprecated [6.11]
311 */
312const char *QQmlFile::data() const
313{
314 return d->data.constData();
315}
316
317/*!
318 \internal
319 \deprecated [6.11]
320 */
321QByteArray QQmlFile::dataByteArray() const
322{
323 return d->data;
324}
325
326/*!
327 \internal
328 \deprecated [6.11]
329
330 Loads content at \a url using \a engine.
331 */
332void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
333{
334 Q_ASSERT(engine);
335
336 clear();
337 d->url = url;
338
339 if (isLocalFile(url)) {
340 const QString lf = urlToLocalFileOrQrc(url);
341 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
342 d->error = QQmlFilePrivate::NotFound;
343 return;
344 }
345
346 QFile file(lf);
347 if (file.open(QFile::ReadOnly)) {
348 d->data = file.readAll();
349 } else {
350 d->error = QQmlFilePrivate::NotFound;
351 }
352 } else {
353#if QT_CONFIG(qml_network)
354 d->reply = new QQmlFileNetworkReply(engine, d, url);
355#else
356 d->error = QQmlFilePrivate::NotFound;
357#endif
358 }
359}
360
361/*!
362 \internal
363 \deprecated [6.11]
364
365 Loads content at \a url using \a engine.
366 */
367void QQmlFile::load(QQmlEngine *engine, const QString &url)
368{
369 Q_ASSERT(engine);
370
371 clear();
372
373 d->urlString = url;
374
375 if (isLocalFile(url)) {
376 const QString lf = urlToLocalFileOrQrc(url);
377 if (!QQmlTypeLoader::get(engine)->fileExists(lf)) {
378 d->error = QQmlFilePrivate::NotFound;
379 return;
380 }
381
382 QFile file(lf);
383 if (file.open(QFile::ReadOnly)) {
384 d->data = file.readAll();
385 } else {
386 d->error = QQmlFilePrivate::NotFound;
387 }
388 } else {
389#if QT_CONFIG(qml_network)
390 QUrl qurl(url);
391 d->url = qurl;
392 d->urlString = QString();
393 d->reply = new QQmlFileNetworkReply(engine, d, qurl);
394#else
395 d->error = QQmlFilePrivate::NotFound;
396#endif
397 }
398}
399
400/*!
401 \internal
402 \deprecated [6.11]
403 */
404void QQmlFile::clear()
405{
406 d->url = QUrl();
407 d->urlString = QString();
408 d->data = QByteArray();
409 d->error = QQmlFilePrivate::None;
410}
411
412/*!
413 \internal
414 \deprecated [6.11]
415
416 Redirects to the other clear() overload, ignoring \a object.
417 */
418void QQmlFile::clear(QObject *object)
419{
420 Q_UNUSED(object);
421 clear();
422}
423
424#if QT_CONFIG(qml_network)
425
426/*!
427 \internal
428 Connects \a method of \a object to the internal \c{finished} signal.
429 */
430bool QQmlFile::connectFinished(QObject *object, const char *method)
431{
432 if (!d || !d->reply) {
433 qWarning("QQmlFile: connectFinished() called when not loading.");
434 return false;
435 }
436
437 return QObject::connect(d->reply, SIGNAL(finished()), object, method);
438}
439
440/*!
441 \internal
442 \deprecated [6.11]
443
444 Connects \a method of \a object to the internal \c{finished} signal.
445 */
446bool QQmlFile::connectFinished(QObject *object, int method)
447{
448 if (!d || !d->reply) {
449 qWarning("QQmlFile: connectFinished() called when not loading.");
450 return false;
451 }
452
453 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
454 object, method);
455}
456
457/*!
458 \internal
459 \deprecated [6.11]
460
461 Connects \a method of \a object to the internal \c{downloadProgress} signal.
462 */
463bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
464{
465 if (!d || !d->reply) {
466 qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
467 return false;
468 }
469
470 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
471 object, method);
472}
473
474/*!
475 \internal
476 \deprecated [6.11]
477
478 Connects \a method of \a object to the internal \c{downloadProgress} signal.
479 */
480bool QQmlFile::connectDownloadProgress(QObject *object, int method)
481{
482 if (!d || !d->reply) {
483 qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
484 return false;
485 }
486
487 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
488 object, method);
489}
490#endif
491
492QT_WARNING_POP
493#endif // QT_DEPRECATED_SINCE(6, 11)
494
495/*!
496 \internal
497
498 Returns \c true if QQmlFile will open \a url synchronously.
499 Otherwise returns \c false. Synchronous urls have a \c{qrc:} or \c{file:}
500 scheme.
501
502 \note On Android, urls with \c{assets:} or \c{content:} scheme are also
503 considered synchronous.
504*/
505bool QQmlFile::isSynchronous(const QUrl &url)
506{
507 if (url.isLocalFile())
508 return true;
509 QString scheme = url.scheme();
510
511 if (scheme == qrc_string) {
512 return true;
513
514#if defined(Q_OS_ANDROID)
515 } else if (scheme == assets_string) {
516 return true;
517 } else if (scheme == content_string) {
518 return true;
519#endif
520
521#if defined(Q_OS_WASM)
522 } else if (scheme == weblocalfile_string) {
523 return true;
524#endif
525
526 } else {
527 return false;
528 }
529}
530
531/*!
532 \internal
533
534 Returns \c true if QQmlFile will open \a url synchronously.
535 Otherwise returns \c false. Synchronous urls have a \c{qrc:} or \c{file:}
536 scheme.
537
538 \note On Android, urls with \c{assets:} or \c{content:} scheme are also
539 considered synchronous.
540*/
541bool QQmlFile::isSynchronous(const QString &url)
542{
543 if (url.size() < 5 /* qrc:/ */)
544 return false;
545
546 QChar f = url[0];
547
548 if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
549
550 return url.size() >= file_string.size() + 2 /* file:/ */ &&
551 url.startsWith(file_string, Qt::CaseInsensitive) &&
552 url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/');
553
554 } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
555
556 return url.size() >= qrc_string.size() + 2 /* qrc:/ */ &&
557 url.startsWith(qrc_string, Qt::CaseInsensitive) &&
558 url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
559
560 }
561
562#if defined(Q_OS_ANDROID)
563 else if (f == QLatin1Char('a') || f == QLatin1Char('A')) {
564 return url.length() >= assets_string.size() + 2 /* assets:/ */ &&
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 /* content:/ */ &&
569 url.startsWith(content_string, Qt::CaseInsensitive) &&
570 url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/');
571 }
572#endif
573
574#if defined(Q_OS_WASM)
575 else if (f == QLatin1Char('w') || f == QLatin1Char('W')) {
576 return url.length() >= weblocalfile_string.size() + 2 /* weblocalfile:/ */ &&
577 url.startsWith(QLatin1String(weblocalfile_string), Qt::CaseInsensitive) &&
578 url[12] == QLatin1Char(':') && url[13] == QLatin1Char('/');
579 }
580#endif
581
582 return false;
583}
584
585#if defined(Q_OS_ANDROID)
586static bool isLocalContentAuthority(QStringView authority)
587{
588 return authority.isEmpty()
589 || authority == QLatin1String(authority_externalstorage)
590 || authority == QLatin1String(authority_downloads_documents)
591 || authority == QLatin1String(authority_media_documents);
592}
593
594static bool hasLocalContentAuthority(const QUrl &url)
595{
596 return isLocalContentAuthority(url.authority());
597}
598#endif
599
600/*!
601 Returns \c true if \a url is a local file that can be opened with \l{QFile}.
602 Otherwise returns \c false. Local file urls have either a \c{qrc:} or
603 \c{file:} scheme.
604
605 \note On Android, urls with \c{assets:} or \c{content:} scheme are also
606 considered local files.
607*/
608bool QQmlFile::isLocalFile(const QUrl &url)
609{
610 if (url.isLocalFile())
611 return true;
612 QString scheme = url.scheme();
613
614 if (scheme == qrc_string)
615 return url.authority().isEmpty();
616
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);
622#endif
623
624#if defined(Q_OS_WASM)
625 if (scheme == weblocalfile_string)
626 return true;
627#endif
628
629 return false;
630}
631
632static bool hasScheme(const QString &url, QLatin1StringView scheme)
633{
634 const qsizetype urlLength = url.size();
635 const qsizetype schemeLength = scheme.size();
636
637 if (urlLength < schemeLength + 1)
638 return false;
639
640 if (!url.startsWith(scheme, Qt::CaseInsensitive))
641 return false;
642
643 if (url[schemeLength] != QLatin1Char(':'))
644 return false;
645
646 return true;
647}
648
649static qsizetype authorityOffset(const QString &url, qsizetype schemeLength)
650{
651 const qsizetype urlLength = url.size();
652
653 if (urlLength < schemeLength + 3)
654 return -1;
655
656 const QLatin1Char slash('/');
657 if (url[schemeLength + 1] == slash && url[schemeLength + 2] == slash) {
658 // Exactly two slashes denote an authority.
659 if (urlLength < schemeLength + 4 || url[schemeLength + 3] != slash)
660 return schemeLength + 3;
661 }
662
663 return -1;
664}
665
666#if defined(Q_OS_ANDROID)
667static bool hasLocalContentAuthority(const QString &url, qsizetype schemeLength)
668{
669 const qsizetype offset = authorityOffset(url, schemeLength);
670 if (offset == -1)
671 return true; // no authority is a local authority.
672
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);
677}
678
679#endif
680
681/*!
682 Returns \c true if \a url is a local file that can be opened with \l{QFile}.
683 Otherwise returns \c false. Local file urls have either a \c{qrc:} or
684 \c{file:} scheme.
685
686 \note On Android, urls with \c{assets:} or \c{content:} scheme are also considered
687 local files.
688*/
689bool QQmlFile::isLocalFile(const QString &url)
690{
691 if (url.size() < 4 /* qrc: */)
692 return false;
693
694 switch (url[0].toLatin1()) {
695 case 'f':
696 case 'F': {
697 // file: URLs with two slashes following the scheme can be interpreted as local files
698 // where the slashes are part of the path. Therefore, disregard the authority.
699 // See QUrl::toLocalFile().
700 const qsizetype fileLength = file_string.size();
701 return url.startsWith(file_string, Qt::CaseInsensitive)
702 && url.size() > fileLength
703 && url[fileLength] == QLatin1Char(':');
704 }
705 case 'q':
706 case 'Q':
707 return hasScheme(url, qrc_string)
708 && authorityOffset(url, qrc_string.size()) == -1;
709#if defined(Q_OS_ANDROID)
710 case 'a':
711 case 'A':
712 return hasScheme(url, assets_string)
713 && authorityOffset(url, assets_string.size()) == -1;
714 case 'c':
715 case 'C':
716 return hasScheme(url, content_string)
717 && hasLocalContentAuthority(url, content_string.size());
718#endif
719#if defined(Q_OS_WASM)
720 case 'w':
721 case 'W':
722 return hasScheme(url, weblocalfile_string);
723#endif
724 default:
725 break;
726 }
727
728 return false;
729}
730
731/*!
732 If \a url is a local file returns a path suitable for passing to \l{QFile}.
733 Otherwise returns an empty string.
734
735 \sa isLocalFile
736*/
737QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
738{
739 if (url.scheme() == qrc_string) {
740 if (!url.authority().isEmpty())
741 return QString();
742 const QString path = url.path(QUrlDecodeForLocalFile);
743 if (!path.isEmpty())
744 return QLatin1Char(':') + path;
745 return url.path(QUrl::PrettyDecoded).isEmpty() ? QStringLiteral(":") : QString();
746 }
747
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();
754 return QString();
755 }
756#endif
757
758#if defined(Q_OS_WASM)
759 if (url.scheme() == weblocalfile_string)
760 return url.toString();
761#endif
762
763 return url.toLocalFile();
764}
765
766static QString toLocalFile(const QString &url)
767{
768 const QUrl file(url);
769 if (!file.isLocalFile())
770 return QString();
771
772 // QUrl::toLocalFile() interprets two slashes as part of the path.
773 // Therefore windows hostnames like "//servername/path/to/file.txt" are preserved.
774
775 return file.toLocalFile();
776}
777
778static bool isDoubleSlashed(const QString &url, qsizetype offset)
779{
780 const qsizetype urlLength = url.size();
781 if (urlLength < offset + 2)
782 return false;
783
784 const QLatin1Char slash('/');
785 if (url[offset] != slash || url[offset + 1] != slash)
786 return false;
787
788 if (urlLength < offset + 3)
789 return true;
790
791 return url[offset + 2] != slash;
792}
793
794/*!
795 If \a url is a local file returns a path suitable for passing to \l{QFile}.
796 Otherwise returns an empty string.
797
798 \sa isLocalFile
799*/
800QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
801{
802 if (url.startsWith(QLatin1String("qrc://"), Qt::CaseInsensitive)) {
803 // Exactly two slashes are bad because that's a URL authority.
804 // One slash is fine and >= 3 slashes are file.
805 if (url.size() == 6 || url[6] != QLatin1Char('/')) {
806 Q_ASSERT(isDoubleSlashed(url, strlen("qrc:")));
807 return QString();
808 }
809 Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
810 return QLatin1Char(':') + QStringView{url}.mid(6);
811 }
812
813 if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
814 Q_ASSERT(!isDoubleSlashed(url, strlen("qrc:")));
815 if (url.size() > 4)
816 return QLatin1Char(':') + QStringView{url}.mid(4);
817 return QStringLiteral(":");
818 }
819
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();
825#endif
826
827#if defined(Q_OS_WASM)
828 if (hasScheme(url, weblocalfile_string))
829 return url;
830#endif
831
832 return toLocalFile(url);
833}
834
835QT_END_NAMESPACE
836
837#include "qqmlfile.moc"
Combined button and popup list for selecting options.
static bool hasScheme(const QString &url, QLatin1StringView scheme)
Definition qqmlfile.cpp:632
static constexpr QLatin1StringView file_string
Definition qqmlfile.cpp:28
static constexpr QLatin1StringView qrc_string
Definition qqmlfile.cpp:27
static QString toLocalFile(const QString &url)
Definition qqmlfile.cpp:766
static bool isDoubleSlashed(const QString &url, qsizetype offset)
Definition qqmlfile.cpp:778
static qsizetype authorityOffset(const QString &url, qsizetype schemeLength)
Definition qqmlfile.cpp:649