1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #include <QtCore/qurl.h>
45 #include <QtCore/qobject.h>
46 #include <QtCore/qmetaobject.h>
47 #include <private/qqmlengine_p.h>
48 #include <private/qqmlglobal_p.h>
52 \brief The QQmlFile class gives access to local and remote files.
56 Supports file://, qrc:/, bundle:// uris and whatever QNetworkAccessManager supports.
59 #define QQMLFILE_MAX_REDIRECT_RECURSION 16
63 static QString qrc_string(QLatin1String("qrc"));
64 static QString file_string(QLatin1String("file"));
65 static QString bundle_string(QLatin1String("bundle"));
67 class QQmlFilePrivate;
68 class QQmlFileNetworkReply : public QObject
72 QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *, const QUrl &);
73 ~QQmlFileNetworkReply();
77 void downloadProgress(qint64, qint64);
80 void networkFinished();
81 void networkDownloadProgress(qint64, qint64);
84 static int finishedIndex;
85 static int downloadProgressIndex;
86 static int networkFinishedIndex;
87 static int networkDownloadProgressIndex;
88 static int replyFinishedIndex;
89 static int replyDownloadProgressIndex;
96 QNetworkReply *m_reply;
105 mutable QString urlString;
107 QQmlBundleData *bundle;
108 const QQmlBundle::FileEntry *file;
113 None, NotFound, CaseMismatch, Network
119 QQmlFileNetworkReply *reply;
122 int QQmlFileNetworkReply::finishedIndex = -1;
123 int QQmlFileNetworkReply::downloadProgressIndex = -1;
124 int QQmlFileNetworkReply::networkFinishedIndex = -1;
125 int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
126 int QQmlFileNetworkReply::replyFinishedIndex = -1;
127 int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
129 QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
130 : m_engine(e), m_p(p), m_redirectCount(0), m_reply(0)
132 if (finishedIndex == -1) {
133 finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
134 downloadProgressIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::downloadProgress).methodIndex();
135 const QMetaObject *smo = &staticMetaObject;
136 networkFinishedIndex = smo->indexOfMethod("networkFinished()");
137 networkDownloadProgressIndex = smo->indexOfMethod("networkDownloadProgress(qint64,qint64)");
139 replyFinishedIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
140 replyDownloadProgressIndex = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
142 Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
143 networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
144 replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
146 QNetworkRequest req(url);
147 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
149 m_reply = m_engine->networkAccessManager()->get(req);
150 QMetaObject::connect(m_reply, replyFinishedIndex, this, networkFinishedIndex);
151 QMetaObject::connect(m_reply, replyDownloadProgressIndex, this, networkDownloadProgressIndex);
154 QQmlFileNetworkReply::~QQmlFileNetworkReply()
157 m_reply->disconnect();
158 m_reply->deleteLater();
162 void QQmlFileNetworkReply::networkFinished()
165 if (m_redirectCount < QQMLFILE_MAX_REDIRECT_RECURSION) {
166 QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
167 if (redirect.isValid()) {
168 QUrl url = m_reply->url().resolved(redirect.toUrl());
170 QNetworkRequest req(url);
171 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
173 m_reply->deleteLater();
174 m_reply = m_engine->networkAccessManager()->get(req);
176 QMetaObject::connect(m_reply, replyFinishedIndex,
177 this, networkFinishedIndex);
178 QMetaObject::connect(m_reply, replyDownloadProgressIndex,
179 this, networkDownloadProgressIndex);
185 if (m_reply->error()) {
186 m_p->errorString = m_reply->errorString();
187 m_p->error = QQmlFilePrivate::Network;
189 m_p->data = m_reply->readAll();
192 m_reply->deleteLater();
200 void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
202 emit downloadProgress(a, b);
205 QQmlFilePrivate::QQmlFilePrivate()
206 : bundle(0), file(0), error(None), reply(0)
211 : d(new QQmlFilePrivate)
215 QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url)
216 : d(new QQmlFilePrivate)
221 QQmlFile::QQmlFile(QQmlEngine *e, const QString &url)
222 : d(new QQmlFilePrivate)
227 QQmlFile::~QQmlFile()
232 d->bundle->release();
238 bool QQmlFile::isNull() const
240 return status() == Null;
243 bool QQmlFile::isReady() const
245 return status() == Ready;
248 bool QQmlFile::isError() const
250 return status() == Error;
253 bool QQmlFile::isLoading() const
255 return status() == Loading;
258 QUrl QQmlFile::url() const
260 if (!d->urlString.isEmpty()) {
261 d->url = QUrl(d->urlString);
262 d->urlString = QString();
267 QQmlFile::Status QQmlFile::status() const
269 if (d->url.isEmpty() && d->urlString.isEmpty())
273 else if (d->error != QQmlFilePrivate::None)
279 QString QQmlFile::error() const
283 case QQmlFilePrivate::None:
285 case QQmlFilePrivate::NotFound:
286 return QLatin1String("File not found");
287 case QQmlFilePrivate::CaseMismatch:
288 return QLatin1String("File name case mismatch");
292 qint64 QQmlFile::size() const
294 if (d->file) return d->file->fileSize();
295 else return d->data.size();
298 const char *QQmlFile::data() const
300 if (d->file) return d->file->contents();
301 else return d->data.constData();
304 QByteArray QQmlFile::dataByteArray() const
306 if (d->file) return QByteArray(d->file->contents(), d->file->fileSize());
310 QByteArray QQmlFile::metaData(const QString &name) const
314 const QQmlBundle::FileEntry *meta = d->bundle->link(d->file, name);
316 return QByteArray::fromRawData(meta->contents(), meta->fileSize());
321 void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
325 QString scheme = url.scheme();
332 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
333 QQmlBundleData *bundle = p->typeLoader.getBundle(url.host());
335 d->error = QQmlFilePrivate::NotFound;
338 QString filename = url.path().mid(1);
339 const QQmlBundle::FileEntry *entry = bundle->find(filename);
344 d->error = QQmlFilePrivate::None;
348 } else if (isLocalFile(url)) {
349 QString lf = urlToLocalFileOrQrc(url);
351 if (!QQml_isFileCaseCorrect(lf)) {
352 d->error = QQmlFilePrivate::CaseMismatch;
357 if (file.open(QFile::ReadOnly)) {
358 d->data = file.readAll();
360 d->error = QQmlFilePrivate::NotFound;
363 d->reply = new QQmlFileNetworkReply(engine, d, url);
367 void QQmlFile::load(QQmlEngine *engine, const QString &url)
377 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
379 d->error = QQmlFilePrivate::NotFound;
381 int index = url.indexOf(QLatin1Char('/'), 9);
385 QStringRef identifier(&url, 9, index - 9);
387 QQmlBundleData *bundle = p->typeLoader.getBundle(identifier);
389 d->error = QQmlFilePrivate::NotFound;
392 QString filename = url.mid(index);
393 const QQmlBundle::FileEntry *entry = bundle->find(filename);
395 d->data = QByteArray(entry->contents(), entry->fileSize());
396 d->error = QQmlFilePrivate::None;
401 } else if (isLocalFile(url)) {
402 QString lf = urlToLocalFileOrQrc(url);
404 if (!QQml_isFileCaseCorrect(lf)) {
405 d->error = QQmlFilePrivate::CaseMismatch;
410 if (file.open(QFile::ReadOnly)) {
411 d->data = file.readAll();
413 d->error = QQmlFilePrivate::NotFound;
418 d->urlString = QString();
419 d->reply = new QQmlFileNetworkReply(engine, d, qurl);
423 void QQmlFile::clear()
426 d->urlString = QString();
427 d->data = QByteArray();
428 if (d->bundle) d->bundle->release();
431 d->error = QQmlFilePrivate::None;
434 void QQmlFile::clear(QObject *)
439 bool QQmlFile::connectFinished(QObject *object, const char *method)
441 if (!d || !d->reply) {
442 qWarning("QQmlFile: connectFinished() called when not loading.");
446 return QObject::connect(d->reply, SIGNAL(finished()),
450 bool QQmlFile::connectFinished(QObject *object, int method)
452 if (!d || !d->reply) {
453 qWarning("QQmlFile: connectFinished() called when not loading.");
457 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
461 bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
463 if (!d || !d->reply) {
464 qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
468 return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
472 bool QQmlFile::connectDownloadProgress(QObject *object, int method)
474 if (!d || !d->reply) {
475 qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
479 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
484 Returns true if QQmlFile will open \a url synchronously.
486 Synchronous urls have a qrc:/, file://, or bundle:// scheme.
488 bool QQmlFile::isSynchronous(const QUrl &url)
490 QString scheme = url.scheme();
492 if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
493 (scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive)) ||
494 (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
501 Returns true if QQmlFile will open \a url synchronously.
503 Synchronous urls have a qrc:/, file://, or bundle:// scheme.
505 bool QQmlFile::isSynchronous(const QString &url)
507 if (url.length() < 5 /* qrc:/ */)
512 if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
514 return url.length() >= 7 /* file:// */ &&
515 url.startsWith(file_string, Qt::CaseInsensitive) &&
516 url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
518 } else if (f == QLatin1Char('b') || f == QLatin1Char('B')) {
520 return url.length() >= 9 /* bundle:// */ &&
521 url.startsWith(bundle_string, Qt::CaseInsensitive) &&
522 url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
524 } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
526 return url.length() >= 5 /* qrc:/ */ &&
527 url.startsWith(qrc_string, Qt::CaseInsensitive) &&
528 url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
536 Returns true if \a url is a bundle.
538 Bundle urls have a bundle:// scheme.
540 bool QQmlFile::isBundle(const QString &url)
542 return url.length() >= 9 && url.startsWith(bundle_string, Qt::CaseInsensitive) &&
543 url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
547 Returns true if \a url is a bundle.
549 Bundle urls have a bundle:// scheme.
551 bool QQmlFile::isBundle(const QUrl &url)
553 QString scheme = url.scheme();
555 return scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive);
559 Returns true if \a url is a local file that can be opened with QFile.
561 Local file urls have either a qrc:/ or file:// scheme.
563 bool QQmlFile::isLocalFile(const QUrl &url)
565 QString scheme = url.scheme();
567 if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
568 (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
575 Returns true if \a url is a local file that can be opened with QFile.
577 Local file urls have either a qrc:/ or file:// scheme.
579 bool QQmlFile::isLocalFile(const QString &url)
581 if (url.length() < 5 /* qrc:/ */)
586 if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
588 return url.length() >= 7 /* file:// */ &&
589 url.startsWith(file_string, Qt::CaseInsensitive) &&
590 url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
592 } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
594 return url.length() >= 5 /* qrc:/ */ &&
595 url.startsWith(qrc_string, Qt::CaseInsensitive) &&
596 url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
604 If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
607 QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
609 if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
610 if (url.authority().isEmpty())
611 return QLatin1Char(':') + url.path();
614 return url.toLocalFile();
617 static QString toLocalFile(const QString &url)
619 if (!url.startsWith(QLatin1String("file://"), Qt::CaseInsensitive))
622 QString file = url.mid(7);
624 //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt"
626 // magic for drives on windows
627 if (file.length() > 2 && file.at(0) == QLatin1Char('/') && file.at(2) == QLatin1Char(':'))
634 If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
637 QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
639 if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
640 if (url.length() > 4)
641 return QLatin1Char(':') + url.mid(4);
645 return toLocalFile(url);
648 bool QQmlFile::bundleDirectoryExists(const QString &dir, QQmlEngine *e)
653 int index = dir.indexOf(QLatin1Char('/'), 9);
655 if (index == -1 && dir.length() > 9) // We accept "bundle://<blah>" with no extra path
656 index = dir.length();
661 QStringRef identifier(&dir, 9, index - 9);
662 QStringRef path(&dir, index + 1, dir.length() - index - 1);
664 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
667 int lastIndex = dir.lastIndexOf(QLatin1Char('/'));
669 if (lastIndex <= index) {
674 QStringRef d(&dir, index + 1, lastIndex - index);
676 QList<const QQmlBundle::FileEntry *> entries = bundle->files();
678 for (int ii = 0; ii < entries.count(); ++ii) {
679 QString name = entries.at(ii)->fileName();
680 if (name.startsWith(d)) {
692 bool QQmlFile::bundleDirectoryExists(const QUrl &url, QQmlEngine *e)
697 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
700 QString path = url.path();
702 int lastIndex = path.lastIndexOf(QLatin1Char('/'));
704 if (lastIndex == -1) {
709 QStringRef d(&path, 0, lastIndex);
711 QList<const QQmlBundle::FileEntry *> entries = bundle->files();
713 for (int ii = 0; ii < entries.count(); ++ii) {
714 QString name = entries.at(ii)->fileName();
715 if (name.startsWith(d)) {
727 bool QQmlFile::bundleFileExists(const QString &file, QQmlEngine *e)
732 int index = file.indexOf(QLatin1Char('/'), 9);
737 QStringRef identifier(&file, 9, index - 9);
738 QStringRef path(&file, index + 1, file.length() - index - 1);
740 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
743 const QQmlBundle::FileEntry *entry = bundle->find(path.constData(), path.length());
752 bool QQmlFile::bundleFileExists(const QUrl &, QQmlEngine *)
754 qFatal("Not implemented");
759 Returns the file name for the bundle file referenced by \a url or an
760 empty string if \a url isn't a bundle url.
762 QString QQmlFile::bundleFileName(const QString &url, QQmlEngine *e)
767 int index = url.indexOf(QLatin1Char('/'), 9);
770 index = url.length();
772 QStringRef identifier(&url, 9, index - 9);
774 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
777 QString rv = bundle->fileName;
786 Returns the file name for the bundle file referenced by \a url or an
787 empty string if \a url isn't a bundle url.
789 QString QQmlFile::bundleFileName(const QUrl &url, QQmlEngine *e)
794 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
797 QString rv = bundle->fileName;
807 #include "qqmlfile.moc"