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