Implement strict mode for qmldir modules
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlfile.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlfile.h"
43
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>
49
50 /*!
51 \class QQmlFile
52 \brief The QQmlFile class gives access to local and remote files.
53
54 Supports file://, qrc:/, bundle:// uris and whatever QNetworkAccessManager supports.
55 */
56
57 #define QQMLFILE_MAX_REDIRECT_RECURSION 16
58
59 QT_BEGIN_NAMESPACE
60
61 static QString qrc_string(QLatin1String("qrc"));
62 static QString file_string(QLatin1String("file"));
63 static QString bundle_string(QLatin1String("bundle"));
64
65 class QQmlFilePrivate;
66 class QQmlFileNetworkReply : public QObject
67 {
68 Q_OBJECT
69 public:
70     QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *, const QUrl &);
71     ~QQmlFileNetworkReply();
72
73 signals:
74     void finished();
75     void downloadProgress(qint64, qint64);
76
77 public slots:
78     void networkFinished();
79     void networkDownloadProgress(qint64, qint64);
80
81 public:
82     static int finishedIndex;
83     static int downloadProgressIndex;
84     static int networkFinishedIndex;
85     static int networkDownloadProgressIndex;
86     static int replyFinishedIndex;
87     static int replyDownloadProgressIndex;
88
89 private:
90     QQmlEngine *m_engine;
91     QQmlFilePrivate *m_p;
92
93     int m_redirectCount;
94     QNetworkReply *m_reply;
95 };
96
97 class QQmlFilePrivate
98 {
99 public:
100     QQmlFilePrivate();
101
102     mutable QUrl url;
103     mutable QString urlString;
104
105     QQmlBundleData *bundle;
106     const QQmlBundle::FileEntry *file;
107
108     QByteArray data;
109
110     enum Error {
111         None, NotFound, CaseMismatch, Network
112     };
113
114     Error error;
115     QString errorString;
116
117     QQmlFileNetworkReply *reply;
118 };
119
120 int QQmlFileNetworkReply::finishedIndex = -1;
121 int QQmlFileNetworkReply::downloadProgressIndex = -1;
122 int QQmlFileNetworkReply::networkFinishedIndex = -1;
123 int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
124 int QQmlFileNetworkReply::replyFinishedIndex = -1;
125 int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
126
127 QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
128 : m_engine(e), m_p(p), m_redirectCount(0), m_reply(0)
129 {
130     if (finishedIndex == -1) {
131         finishedIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::finished).methodIndex();
132         downloadProgressIndex = QMetaMethod::fromSignal(&QQmlFileNetworkReply::downloadProgress).methodIndex();
133         const QMetaObject *smo = &staticMetaObject;
134         networkFinishedIndex = smo->indexOfMethod("networkFinished()");
135         networkDownloadProgressIndex = smo->indexOfMethod("networkDownloadProgress(qint64,qint64)");
136
137         replyFinishedIndex = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
138         replyDownloadProgressIndex = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
139     }
140     Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
141              networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
142              replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
143
144     QNetworkRequest req(url);
145     req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
146
147     m_reply = m_engine->networkAccessManager()->get(req);
148     QMetaObject::connect(m_reply, replyFinishedIndex, this, networkFinishedIndex);
149     QMetaObject::connect(m_reply, replyDownloadProgressIndex, this, networkDownloadProgressIndex);
150 }
151
152 QQmlFileNetworkReply::~QQmlFileNetworkReply()
153 {
154     if (m_reply) {
155         m_reply->disconnect();
156         m_reply->deleteLater();
157     }
158 }
159
160 void QQmlFileNetworkReply::networkFinished()
161 {
162     ++m_redirectCount;
163     if (m_redirectCount < QQMLFILE_MAX_REDIRECT_RECURSION) {
164         QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
165         if (redirect.isValid()) {
166             QUrl url = m_reply->url().resolved(redirect.toUrl());
167
168             QNetworkRequest req(url);
169             req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
170
171             m_reply->deleteLater();
172             m_reply = m_engine->networkAccessManager()->get(req);
173
174             QMetaObject::connect(m_reply, replyFinishedIndex,
175                                  this, networkFinishedIndex);
176             QMetaObject::connect(m_reply, replyDownloadProgressIndex,
177                                  this, networkDownloadProgressIndex);
178
179             return;
180         }
181     }
182
183     if (m_reply->error()) {
184         m_p->errorString = m_reply->errorString();
185         m_p->error = QQmlFilePrivate::Network;
186     } else {
187         m_p->data = m_reply->readAll();
188     }
189
190     m_reply->deleteLater();
191     m_reply = 0;
192
193     m_p->reply = 0;
194     emit finished();
195     delete this;
196 }
197
198 void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
199 {
200     emit downloadProgress(a, b);
201 }
202
203 QQmlFilePrivate::QQmlFilePrivate()
204 : bundle(0), file(0), error(None), reply(0)
205 {
206 }
207
208 QQmlFile::QQmlFile()
209 : d(new QQmlFilePrivate)
210 {
211 }
212
213 QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url)
214 : d(new QQmlFilePrivate)
215 {
216     load(e, url);
217 }
218
219 QQmlFile::QQmlFile(QQmlEngine *e, const QString &url)
220 : d(new QQmlFilePrivate)
221 {
222     load(e, url);
223 }
224
225 QQmlFile::~QQmlFile()
226 {
227     if (d->reply)
228         delete d->reply;
229     if (d->bundle)
230         d->bundle->release();
231
232     delete d;
233     d = 0;
234 }
235
236 bool QQmlFile::isNull() const
237 {
238     return status() == Null;
239 }
240
241 bool QQmlFile::isReady() const
242 {
243     return status() == Ready;
244 }
245
246 bool QQmlFile::isError() const
247 {
248     return status() == Error;
249 }
250
251 bool QQmlFile::isLoading() const
252 {
253     return status() == Loading;
254 }
255
256 QUrl QQmlFile::url() const
257 {
258     if (!d->urlString.isEmpty()) {
259         d->url = QUrl(d->urlString);
260         d->urlString = QString();
261     }
262     return d->url;
263 }
264
265 QQmlFile::Status QQmlFile::status() const
266 {
267     if (d->url.isEmpty() && d->urlString.isEmpty())
268         return Null;
269     else if (d->reply)
270         return Loading;
271     else if (d->error != QQmlFilePrivate::None)
272         return Error;
273     else
274         return Ready;
275 }
276
277 QString QQmlFile::error() const
278 {
279     switch (d->error) {
280     default:
281     case QQmlFilePrivate::None:
282         return QString();
283     case QQmlFilePrivate::NotFound:
284         return QLatin1String("File not found");
285     case QQmlFilePrivate::CaseMismatch:
286         return QLatin1String("File name case mismatch");
287     }
288 }
289
290 qint64 QQmlFile::size() const
291 {
292     if (d->file) return d->file->fileSize();
293     else return d->data.size();
294 }
295
296 const char *QQmlFile::data() const
297 {
298     if (d->file) return d->file->contents();
299     else return d->data.constData();
300 }
301
302 QByteArray QQmlFile::dataByteArray() const
303 {
304     if (d->file) return QByteArray(d->file->contents(), d->file->fileSize());
305     else return d->data;
306 }
307
308 QByteArray QQmlFile::metaData(const QString &name) const
309 {
310     if (d->file) {
311         Q_ASSERT(d->bundle);
312         const QQmlBundle::FileEntry *meta = d->bundle->link(d->file, name);
313         if (meta)
314             return QByteArray::fromRawData(meta->contents(), meta->fileSize());
315     }
316     return QByteArray();
317 }
318
319 void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
320 {
321     Q_ASSERT(engine);
322
323     QString scheme = url.scheme();
324
325     clear();
326     d->url = url;
327
328     if (isBundle(url)) {
329         // Bundle
330         QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
331         QQmlBundleData *bundle = p->typeLoader.getBundle(url.host());
332
333         d->error = QQmlFilePrivate::NotFound;
334
335         if (bundle) {
336             QString filename = url.path().mid(1);
337             const QQmlBundle::FileEntry *entry = bundle->find(filename);
338             if (entry) {
339                 d->file = entry;
340                 d->bundle = bundle;
341                 d->bundle->addref();
342                 d->error = QQmlFilePrivate::None;
343             }
344             bundle->release();
345         }
346     } else if (isLocalFile(url)) {
347         QString lf = urlToLocalFileOrQrc(url);
348
349         if (!QQml_isFileCaseCorrect(lf)) {
350             d->error = QQmlFilePrivate::CaseMismatch;
351             return;
352         }
353
354         QFile file(lf);
355         if (file.open(QFile::ReadOnly)) {
356             d->data = file.readAll();
357         } else {
358             d->error = QQmlFilePrivate::NotFound;
359         }
360     } else {
361         d->reply = new QQmlFileNetworkReply(engine, d, url);
362     }
363 }
364
365 void QQmlFile::load(QQmlEngine *engine, const QString &url)
366 {
367     Q_ASSERT(engine);
368
369     clear();
370
371     d->urlString = url;
372
373     if (isBundle(url)) {
374         // Bundle
375         QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
376
377         d->error = QQmlFilePrivate::NotFound;
378
379         int index = url.indexOf(QLatin1Char('/'), 9);
380         if (index == -1)
381             return;
382
383         QStringRef identifier(&url, 9, index - 9);
384
385         QQmlBundleData *bundle = p->typeLoader.getBundle(identifier);
386
387         d->error = QQmlFilePrivate::NotFound;
388
389         if (bundle) {
390             QString filename = url.mid(index);
391             const QQmlBundle::FileEntry *entry = bundle->find(filename);
392             if (entry) {
393                 d->data = QByteArray(entry->contents(), entry->fileSize());
394                 d->error = QQmlFilePrivate::None;
395             }
396             bundle->release();
397         }
398
399     } else if (isLocalFile(url)) {
400         QString lf = urlToLocalFileOrQrc(url);
401
402         if (!QQml_isFileCaseCorrect(lf)) {
403             d->error = QQmlFilePrivate::CaseMismatch;
404             return;
405         }
406
407         QFile file(lf);
408         if (file.open(QFile::ReadOnly)) {
409             d->data = file.readAll();
410         } else {
411             d->error = QQmlFilePrivate::NotFound;
412         }
413     } else {
414         QUrl qurl(url);
415         d->url = qurl;
416         d->urlString = QString();
417         d->reply = new QQmlFileNetworkReply(engine, d, qurl);
418     }
419 }
420
421 void QQmlFile::clear()
422 {
423     d->url = QUrl();
424     d->urlString = QString();
425     d->data = QByteArray();
426     if (d->bundle) d->bundle->release();
427     d->bundle = 0;
428     d->file = 0;
429     d->error = QQmlFilePrivate::None;
430 }
431
432 void QQmlFile::clear(QObject *)
433 {
434     clear();
435 }
436
437 bool QQmlFile::connectFinished(QObject *object, const char *method)
438 {
439     if (!d || !d->reply) {
440         qWarning("QQmlFile: connectFinished() called when not loading.");
441         return false;
442     }
443
444     return QObject::connect(d->reply, SIGNAL(finished()),
445                             object, method);
446 }
447
448 bool QQmlFile::connectFinished(QObject *object, int method)
449 {
450     if (!d || !d->reply) {
451         qWarning("QQmlFile: connectFinished() called when not loading.");
452         return false;
453     }
454
455     return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
456                                 object, method);
457 }
458
459 bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
460 {
461     if (!d || !d->reply) {
462         qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
463         return false;
464     }
465
466     return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
467                             object, method);
468 }
469
470 bool QQmlFile::connectDownloadProgress(QObject *object, int method)
471 {
472     if (!d || !d->reply) {
473         qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
474         return false;
475     }
476
477     return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
478                                 object, method);
479 }
480
481 /*!
482 Returns true if QQmlFile will open \a url synchronously.
483
484 Synchronous urls have a qrc:/, file://, or bundle:// scheme.
485 */
486 bool QQmlFile::isSynchronous(const QUrl &url)
487 {
488     QString scheme = url.scheme();
489
490     if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
491         (scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive)) ||
492         (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
493         return true;
494     else
495         return false;
496 }
497
498 /*!
499 Returns true if QQmlFile will open \a url synchronously.
500
501 Synchronous urls have a qrc:/, file://, or bundle:// scheme.
502 */
503 bool QQmlFile::isSynchronous(const QString &url)
504 {
505     if (url.length() < 5 /* qrc:/ */)
506         return false;
507
508     QChar f = url[0];
509
510     if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
511
512         return url.length() >= 7 /* file:// */ &&
513                url.startsWith(file_string, Qt::CaseInsensitive) &&
514                url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
515
516     } else if (f == QLatin1Char('b') || f == QLatin1Char('B')) {
517
518         return url.length() >= 9 /* bundle:// */ &&
519                url.startsWith(bundle_string, Qt::CaseInsensitive) &&
520                url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
521
522     } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
523
524         return url.length() >= 5 /* qrc:/ */ &&
525                url.startsWith(qrc_string, Qt::CaseInsensitive) &&
526                url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
527
528     }
529
530     return false;
531 }
532
533 /*!
534 Returns true if \a url is a bundle.
535
536 Bundle urls have a bundle:// scheme.
537 */
538 bool QQmlFile::isBundle(const QString &url)
539 {
540     return url.length() >= 9 && url.startsWith(bundle_string, Qt::CaseInsensitive) &&
541            url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
542 }
543
544 /*!
545 Returns true if \a url is a bundle.
546
547 Bundle urls have a bundle:// scheme.
548 */
549 bool QQmlFile::isBundle(const QUrl &url)
550 {
551     QString scheme = url.scheme();
552
553     return scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive);
554 }
555
556 /*!
557 Returns true if \a url is a local file that can be opened with QFile.
558
559 Local file urls have either a qrc:/ or file:// scheme.
560 */
561 bool QQmlFile::isLocalFile(const QUrl &url)
562 {
563     QString scheme = url.scheme();
564
565     if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
566         (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
567         return true;
568     else
569         return false;
570 }
571
572 /*!
573 Returns true if \a url is a local file that can be opened with QFile.
574
575 Local file urls have either a qrc:/ or file:// scheme.
576 */
577 bool QQmlFile::isLocalFile(const QString &url)
578 {
579     if (url.length() < 5 /* qrc:/ */)
580         return false;
581
582     QChar f = url[0];
583
584     if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
585
586         return url.length() >= 7 /* file:// */ &&
587                url.startsWith(file_string, Qt::CaseInsensitive) &&
588                url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
589
590     } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
591
592         return url.length() >= 5 /* qrc:/ */ &&
593                url.startsWith(qrc_string, Qt::CaseInsensitive) &&
594                url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/');
595
596     }
597
598     return false;
599 }
600
601 /*!
602 If \a url is a local file returns a path suitable for passing to QFile.  Otherwise returns an
603 empty string.
604 */
605 QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
606 {
607     if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
608         if (url.authority().isEmpty())
609             return QLatin1Char(':') + url.path();
610         return QString();
611     }
612     return url.toLocalFile();
613 }
614
615 static QString toLocalFile(const QString &url)
616 {
617     if (!url.startsWith(QLatin1String("file://"), Qt::CaseInsensitive))
618         return QString();
619
620     QString file = url.mid(7);
621
622     //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt"
623
624     // magic for drives on windows
625     if (file.length() > 2 && file.at(0) == QLatin1Char('/') && file.at(2) == QLatin1Char(':'))
626         file.remove(0, 1);
627
628     return file;
629 }
630
631 /*!
632 If \a url is a local file returns a path suitable for passing to QFile.  Otherwise returns an
633 empty string.
634 */
635 QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
636 {
637     if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
638         if (url.length() > 4)
639             return QLatin1Char(':') + url.mid(4);
640         return QString();
641     }
642
643     return toLocalFile(url);
644 }
645
646 bool QQmlFile::bundleDirectoryExists(const QString &dir, QQmlEngine *e)
647 {
648     if (!isBundle(dir))
649         return false;
650
651     int index = dir.indexOf(QLatin1Char('/'), 9);
652
653     if (index == -1 && dir.length() > 9) // We accept "bundle://<blah>" with no extra path
654         index = dir.length();
655
656     if (index == -1)
657         return false;
658
659     QStringRef identifier(&dir, 9, index - 9);
660     QStringRef path(&dir, index + 1, dir.length() - index - 1);
661
662     QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
663
664     if (bundle) {
665         int lastIndex = dir.lastIndexOf(QLatin1Char('/'));
666
667         if (lastIndex <= index) {
668             bundle->release();
669             return true;
670         }
671
672         QStringRef d(&dir, index + 1, lastIndex - index);
673
674         QList<const QQmlBundle::FileEntry *> entries = bundle->files();
675
676         for (int ii = 0; ii < entries.count(); ++ii) {
677             QString name = entries.at(ii)->fileName();
678             if (name.startsWith(d)) {
679                 bundle->release();
680                 return true;
681             }
682         }
683
684         bundle->release();
685     }
686
687     return false;
688 }
689
690 bool QQmlFile::bundleDirectoryExists(const QUrl &url, QQmlEngine *e)
691 {
692     if (!isBundle(url))
693         return false;
694
695     QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
696
697     if (bundle) {
698         QString path = url.path();
699
700         int lastIndex = path.lastIndexOf(QLatin1Char('/'));
701
702         if (lastIndex == -1) {
703             bundle->release();
704             return true;
705         }
706
707         QStringRef d(&path, 0, lastIndex);
708
709         QList<const QQmlBundle::FileEntry *> entries = bundle->files();
710
711         for (int ii = 0; ii < entries.count(); ++ii) {
712             QString name = entries.at(ii)->fileName();
713             if (name.startsWith(d)) {
714                 bundle->release();
715                 return true;
716             }
717         }
718
719         bundle->release();
720     }
721
722     return false;
723 }
724
725 bool QQmlFile::bundleFileExists(const QString &file, QQmlEngine *e)
726 {
727     if (!isBundle(file))
728         return false;
729
730     int index = file.indexOf(QLatin1Char('/'), 9);
731
732     if (index == -1)
733         return false;
734
735     QStringRef identifier(&file, 9, index - 9);
736     QStringRef path(&file, index + 1, file.length() - index - 1);
737
738     QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
739
740     if (bundle) {
741         const QQmlBundle::FileEntry *entry = bundle->find(path.constData(), path.length());
742         bundle->release();
743
744         return entry != 0;
745     }
746
747     return false;
748 }
749
750 bool QQmlFile::bundleFileExists(const QUrl &, QQmlEngine *)
751 {
752     qFatal("Not implemented");
753     return true;
754 }
755
756 /*!
757 Returns the file name for the bundle file referenced by \a url or an
758 empty string if \a url isn't a bundle url.
759 */
760 QString QQmlFile::bundleFileName(const QString &url, QQmlEngine *e)
761 {
762     if (!isBundle(url))
763         return QString();
764
765     int index = url.indexOf(QLatin1Char('/'), 9);
766
767     if (index == -1)
768         index = url.length();
769
770     QStringRef identifier(&url, 9, index - 9);
771
772     QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
773
774     if (bundle) {
775         QString rv = bundle->fileName;
776         bundle->release();
777         return rv;
778     }
779
780     return QString();
781 }
782
783 /*!
784 Returns the file name for the bundle file referenced by \a url or an
785 empty string if \a url isn't a bundle url.
786 */
787 QString QQmlFile::bundleFileName(const QUrl &url, QQmlEngine *e)
788 {
789     if (!isBundle(url))
790         return QString();
791
792     QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
793
794     if (bundle) {
795         QString rv = bundle->fileName;
796         bundle->release();
797         return rv;
798     }
799
800     return QString();
801 }
802
803 QT_END_NAMESPACE
804
805 #include "qqmlfile.moc"