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 <private/qqmlengine_p.h>
47 #include <private/qqmlglobal_p.h>
50 \class The QQmlFile class gives access to local and remote files.
52 Supports file://, qrc://, bundle:// uris and whatever QNetworkAccessManager supports.
55 #define QQMLFILE_MAX_REDIRECT_RECURSION 16
59 static QString qrc_string(QLatin1String("qrc"));
60 static QString file_string(QLatin1String("file"));
61 static QString bundle_string(QLatin1String("bundle"));
63 class QQmlFilePrivate;
64 class QQmlFileNetworkReply : public QObject
68 QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *, const QUrl &);
69 ~QQmlFileNetworkReply();
73 void downloadProgress(qint64, qint64);
76 void networkFinished();
77 void networkDownloadProgress(qint64, qint64);
80 static int finishedIndex;
81 static int downloadProgressIndex;
82 static int networkFinishedIndex;
83 static int networkDownloadProgressIndex;
84 static int replyFinishedIndex;
85 static int replyDownloadProgressIndex;
92 QNetworkReply *m_reply;
101 mutable QString urlString;
103 QQmlBundleData *bundle;
104 const QQmlBundle::FileEntry *file;
109 None, NotFound, CaseMismatch, Network
115 QQmlFileNetworkReply *reply;
118 int QQmlFileNetworkReply::finishedIndex = -1;
119 int QQmlFileNetworkReply::downloadProgressIndex = -1;
120 int QQmlFileNetworkReply::networkFinishedIndex = -1;
121 int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
122 int QQmlFileNetworkReply::replyFinishedIndex = -1;
123 int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
125 QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
126 : m_engine(e), m_p(p), m_redirectCount(0), m_reply(0)
128 if (finishedIndex == -1) {
129 const QMetaObject *smo = &staticMetaObject;
130 finishedIndex = smo->indexOfSignal("finished()");
131 downloadProgressIndex = smo->indexOfSignal("downloadProgress(qint64,qint64)");
132 networkFinishedIndex = smo->indexOfMethod("networkFinished()");
133 networkDownloadProgressIndex = smo->indexOfMethod("networkDownloadProgress(qint64,qint64)");
135 const QMetaObject *rsmo = &QNetworkReply::staticMetaObject;
136 replyFinishedIndex = rsmo->indexOfSignal("finished()");
137 replyDownloadProgressIndex = rsmo->indexOfSignal("downloadProgress(qint64,qint64)");
139 Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
140 networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
141 replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
143 QNetworkRequest req(url);
144 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
146 m_reply = m_engine->networkAccessManager()->get(req);
147 QMetaObject::connect(m_reply, replyFinishedIndex, this, networkFinishedIndex);
148 QMetaObject::connect(m_reply, replyDownloadProgressIndex, this, networkDownloadProgressIndex);
151 QQmlFileNetworkReply::~QQmlFileNetworkReply()
154 m_reply->disconnect();
155 m_reply->deleteLater();
159 void QQmlFileNetworkReply::networkFinished()
162 if (m_redirectCount < QQMLFILE_MAX_REDIRECT_RECURSION) {
163 QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
164 if (redirect.isValid()) {
165 QUrl url = m_reply->url().resolved(redirect.toUrl());
167 QNetworkRequest req(url);
168 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
170 m_reply->deleteLater();
171 m_reply = m_engine->networkAccessManager()->get(req);
173 QMetaObject::connect(m_reply, replyFinishedIndex,
174 this, networkFinishedIndex);
175 QMetaObject::connect(m_reply, replyDownloadProgressIndex,
176 this, networkDownloadProgressIndex);
182 if (m_reply->error()) {
183 m_p->errorString = m_reply->errorString();
184 m_p->error = QQmlFilePrivate::Network;
186 m_p->data = m_reply->readAll();
189 m_reply->deleteLater();
197 void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
199 emit downloadProgress(a, b);
202 QQmlFilePrivate::QQmlFilePrivate()
203 : bundle(0), file(0), error(None), reply(0)
208 : d(new QQmlFilePrivate)
212 QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url)
213 : d(new QQmlFilePrivate)
218 QQmlFile::QQmlFile(QQmlEngine *e, const QString &url)
219 : d(new QQmlFilePrivate)
224 QQmlFile::~QQmlFile()
229 d->bundle->release();
235 bool QQmlFile::isNull() const
237 return status() == Null;
240 bool QQmlFile::isReady() const
242 return status() == Ready;
245 bool QQmlFile::isError() const
247 return status() == Error;
250 bool QQmlFile::isLoading() const
252 return status() == Loading;
255 QUrl QQmlFile::url() const
257 if (!d->urlString.isEmpty()) {
258 d->url = QUrl(d->urlString);
259 d->urlString = QString();
264 QQmlFile::Status QQmlFile::status() const
266 if (d->url.isEmpty() && d->urlString.isEmpty())
270 else if (d->error != QQmlFilePrivate::None)
276 QString QQmlFile::error() const
280 case QQmlFilePrivate::None:
282 case QQmlFilePrivate::NotFound:
283 return QLatin1String("File not found");
284 case QQmlFilePrivate::CaseMismatch:
285 return QLatin1String("File name case mismatch");
289 qint64 QQmlFile::size() const
291 if (d->file) return d->file->fileSize();
292 else return d->data.size();
295 const char *QQmlFile::data() const
297 if (d->file) return d->file->contents();
298 else return d->data.constData();
301 QByteArray QQmlFile::dataByteArray() const
303 if (d->file) return QByteArray(d->file->contents(), d->file->fileSize());
307 QByteArray QQmlFile::metaData(const QString &name) const
311 const QQmlBundle::FileEntry *meta = d->bundle->link(d->file, name);
313 return QByteArray::fromRawData(meta->contents(), meta->fileSize());
318 void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
322 QString scheme = url.scheme();
329 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
330 QQmlBundleData *bundle = p->typeLoader.getBundle(url.host());
332 d->error = QQmlFilePrivate::NotFound;
335 QString filename = url.path().mid(1);
336 const QQmlBundle::FileEntry *entry = bundle->find(filename);
341 d->error = QQmlFilePrivate::None;
345 } else if (isLocalFile(url)) {
346 QString lf = urlToLocalFileOrQrc(url);
348 if (!QQml_isFileCaseCorrect(lf)) {
349 d->error = QQmlFilePrivate::CaseMismatch;
354 if (file.open(QFile::ReadOnly)) {
355 d->data = file.readAll();
357 d->error = QQmlFilePrivate::NotFound;
360 d->reply = new QQmlFileNetworkReply(engine, d, url);
364 void QQmlFile::load(QQmlEngine *engine, const QString &url)
374 QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
376 d->error = QQmlFilePrivate::NotFound;
378 int index = url.indexOf(QLatin1Char('/'), 9);
382 QStringRef identifier(&url, 9, index - 9);
384 QQmlBundleData *bundle = p->typeLoader.getBundle(identifier);
386 d->error = QQmlFilePrivate::NotFound;
389 QString filename = url.mid(index);
390 const QQmlBundle::FileEntry *entry = bundle->find(filename);
392 d->data = QByteArray(entry->contents(), entry->fileSize());
393 d->error = QQmlFilePrivate::None;
398 } else if (isLocalFile(url)) {
399 QString lf = urlToLocalFileOrQrc(url);
401 if (!QQml_isFileCaseCorrect(lf)) {
402 d->error = QQmlFilePrivate::CaseMismatch;
407 if (file.open(QFile::ReadOnly)) {
408 d->data = file.readAll();
410 d->error = QQmlFilePrivate::NotFound;
415 d->urlString = QString();
416 d->reply = new QQmlFileNetworkReply(engine, d, qurl);
420 void QQmlFile::clear()
423 d->urlString = QString();
424 d->data = QByteArray();
425 if (d->bundle) d->bundle->release();
428 d->error = QQmlFilePrivate::None;
431 void QQmlFile::clear(QObject *)
436 bool QQmlFile::connectFinished(QObject *object, const char *method)
438 if (!d || !d->reply) {
439 qWarning("QQmlFile: connectFinished() called when not loading.");
443 return QObject::connect(d->reply, SIGNAL(finished()),
447 bool QQmlFile::connectFinished(QObject *object, int method)
449 if (!d || !d->reply) {
450 qWarning("QQmlFile: connectFinished() called when not loading.");
454 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
458 bool 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)),
469 bool QQmlFile::connectDownloadProgress(QObject *object, int method)
471 if (!d || !d->reply) {
472 qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
476 return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
481 Returns true if QQmlFile will open \a url synchronously.
483 Synchronous urls have a qrc://, file://, or bundle:// scheme.
485 bool QQmlFile::isSynchronous(const QUrl &url)
487 QString scheme = url.scheme();
489 if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
490 (scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive)) ||
491 (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
498 Returns true if QQmlFile will open \a url synchronously.
500 Synchronous urls have a qrc://, file://, or bundle:// scheme.
502 bool QQmlFile::isSynchronous(const QString &url)
504 if (url.length() < 6 /* qrc:// */)
509 if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
511 return url.length() >= 7 /* file:// */ &&
512 url.startsWith(file_string, Qt::CaseInsensitive) &&
513 url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
515 } else if (f == QLatin1Char('b') || f == QLatin1Char('B')) {
517 return url.length() >= 9 /* bundle:// */ &&
518 url.startsWith(bundle_string, Qt::CaseInsensitive) &&
519 url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
521 } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
523 return url.length() >= 6 /* bundle:// */ &&
524 url.startsWith(qrc_string, Qt::CaseInsensitive) &&
525 url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/') && url[5] == QLatin1Char('/');
533 Returns true if \a url is a bundle.
535 Bundle urls have a bundle:// scheme.
537 bool QQmlFile::isBundle(const QString &url)
539 return url.length() >= 9 && url.startsWith(bundle_string, Qt::CaseInsensitive) &&
540 url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
544 Returns true if \a url is a bundle.
546 Bundle urls have a bundle:// scheme.
548 bool QQmlFile::isBundle(const QUrl &url)
550 QString scheme = url.scheme();
552 return scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive);
556 Returns true if \a url is a local file that can be opened with QFile.
558 Local file urls have either a qrc:// or file:// scheme.
560 bool QQmlFile::isLocalFile(const QUrl &url)
562 QString scheme = url.scheme();
564 if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
565 (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
572 Returns true if \a url is a local file that can be opened with QFile.
574 Local file urls have either a qrc:// or file:// scheme.
576 bool QQmlFile::isLocalFile(const QString &url)
578 if (url.length() < 6 /* qrc:// */)
583 if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
585 return url.length() >= 7 /* file:// */ &&
586 url.startsWith(file_string, Qt::CaseInsensitive) &&
587 url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
589 } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
591 return url.length() >= 6 /* bundle:// */ &&
592 url.startsWith(qrc_string, Qt::CaseInsensitive) &&
593 url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/') && url[5] == QLatin1Char('/');
601 If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
604 QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
606 if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
607 if (url.authority().isEmpty())
608 return QLatin1Char(':') + url.path();
611 return url.toLocalFile();
614 static QString toLocalFile(const QString &url)
616 if (!url.startsWith(QLatin1String("file://"), Qt::CaseInsensitive))
619 QString file = url.mid(7);
621 //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt"
623 // magic for drives on windows
624 if (file.length() > 2 && file.at(0) == QLatin1Char('/') && file.at(2) == QLatin1Char(':'))
631 If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
634 QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
636 if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
637 if (url.length() > 4)
638 return QLatin1Char(':') + url.mid(4);
642 return toLocalFile(url);
645 bool QQmlFile::bundleDirectoryExists(const QString &dir, QQmlEngine *e)
650 int index = dir.indexOf(QLatin1Char('/'), 9);
652 if (index == -1 && dir.length() > 9) // We accept "bundle://<blah>" with no extra path
653 index = dir.length();
658 QStringRef identifier(&dir, 9, index - 9);
659 QStringRef path(&dir, index + 1, dir.length() - index - 1);
661 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
664 int lastIndex = dir.lastIndexOf(QLatin1Char('/'));
666 if (lastIndex <= index) {
671 QStringRef d(&dir, index + 1, lastIndex - index);
673 QList<const QQmlBundle::FileEntry *> entries = bundle->files();
675 for (int ii = 0; ii < entries.count(); ++ii) {
676 QString name = entries.at(ii)->fileName();
677 if (name.startsWith(d)) {
689 bool QQmlFile::bundleDirectoryExists(const QUrl &url, QQmlEngine *e)
694 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
697 QString path = url.path();
699 int lastIndex = path.lastIndexOf(QLatin1Char('/'));
701 if (lastIndex == -1) {
706 QStringRef d(&path, 0, lastIndex);
708 QList<const QQmlBundle::FileEntry *> entries = bundle->files();
710 for (int ii = 0; ii < entries.count(); ++ii) {
711 QString name = entries.at(ii)->fileName();
712 if (name.startsWith(d)) {
724 bool QQmlFile::bundleFileExists(const QString &file, QQmlEngine *e)
729 int index = file.indexOf(QLatin1Char('/'), 9);
734 QStringRef identifier(&file, 9, index - 9);
735 QStringRef path(&file, index + 1, file.length() - index - 1);
737 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
740 const QQmlBundle::FileEntry *entry = bundle->find(path.constData(), path.length());
749 bool QQmlFile::bundleFileExists(const QUrl &, QQmlEngine *)
751 qFatal("Not implemented");
756 Returns the file name for the bundle file referenced by \a url or an
757 empty string if \a url isn't a bundle url.
759 QString QQmlFile::bundleFileName(const QString &url, QQmlEngine *e)
764 int index = url.indexOf(QLatin1Char('/'), 9);
767 index = url.length();
769 QStringRef identifier(&url, 9, index - 9);
771 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
774 QString rv = bundle->fileName;
783 Returns the file name for the bundle file referenced by \a url or an
784 empty string if \a url isn't a bundle url.
786 QString QQmlFile::bundleFileName(const QUrl &url, QQmlEngine *e)
791 QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
794 QString rv = bundle->fileName;
804 #include "qqmlfile.moc"