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 ****************************************************************************/
42 #include "qqmltypeloader_p.h"
44 #include <private/qqmlengine_p.h>
45 #include <private/qqmlglobal_p.h>
46 #include <private/qqmlthread_p.h>
47 #include <private/qqmlcompiler_p.h>
48 #include <private/qqmlcomponent_p.h>
49 #include <private/qqmlprofilerservice_p.h>
51 #include <QtCore/qdir.h>
52 #include <QtCore/qfile.h>
53 #include <QtCore/qdebug.h>
54 #include <QtCore/qmutex.h>
55 #include <QtCore/qthread.h>
56 #include <QtCore/qdiriterator.h>
57 #include <QtCore/qwaitcondition.h>
58 #include <QtQml/qqmlcomponent.h>
59 #include <QtQml/qqmlextensioninterface.h>
61 #if defined (Q_OS_UNIX)
62 #include <sys/types.h>
66 // #define DATABLOB_DEBUG
70 #define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in main thread"); } while(false)
71 #define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in load thread"); } while(false)
72 #define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while(false)
76 #define ASSERT_MAINTHREAD()
77 #define ASSERT_LOADTHREAD()
78 #define ASSERT_CALLBACK()
84 // This is a lame object that we need to ensure that slots connected to
85 // QNetworkReply get called in the correct thread (the loader thread).
86 // As QQmlDataLoader lives in the main thread, and we can't use
87 // Qt::DirectConnection connections from a QNetworkReply (because then
88 // sender() wont work), we need to insert this object in the middle.
89 class QQmlDataLoaderNetworkReplyProxy : public QObject
93 QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l);
97 void downloadProgress(qint64, qint64);
103 class QQmlDataLoaderThread : public QQmlThread
105 typedef QQmlDataLoaderThread This;
108 QQmlDataLoaderThread(QQmlDataLoader *loader);
109 QNetworkAccessManager *networkAccessManager() const;
110 QQmlDataLoaderNetworkReplyProxy *networkReplyProxy() const;
112 void load(QQmlDataBlob *b);
113 void loadAsync(QQmlDataBlob *b);
114 void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
115 void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &);
116 void callCompleted(QQmlDataBlob *b);
117 void callDownloadProgressChanged(QQmlDataBlob *b, qreal p);
118 void initializeEngine(QQmlExtensionInterface *, const char *);
121 virtual void shutdownThread();
124 void loadThread(QQmlDataBlob *b);
125 void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &);
126 void callCompletedMain(QQmlDataBlob *b);
127 void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p);
128 void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
130 QQmlDataLoader *m_loader;
131 mutable QNetworkAccessManager *m_networkAccessManager;
132 mutable QQmlDataLoaderNetworkReplyProxy *m_networkReplyProxy;
136 QQmlDataLoaderNetworkReplyProxy::QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l)
141 void QQmlDataLoaderNetworkReplyProxy::finished()
144 Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
145 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
146 l->networkReplyFinished(reply);
149 void QQmlDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
152 Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
153 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
154 l->networkReplyProgress(reply, bytesReceived, bytesTotal);
158 Returns the set of QML files in path (qmldir, *.qml, *.js). The caller
159 is responsible for deleting the returned data.
160 Returns 0 if the directory does not exist.
162 #if defined (Q_OS_UNIX) && !defined(Q_OS_DARWIN)
163 static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
165 QByteArray name(QFile::encodeName(path));
166 DIR *dd = opendir(name);
170 struct dirent *result;
173 char b[offsetof (struct dirent, d_name) + NAME_MAX + 1];
176 QStringHash<bool> *files = new QStringHash<bool>;
177 while (readdir_r(dd, &u.d, &result) == 0 && result != 0) {
178 if (!strcmp(u.d.d_name, "qmldir")) {
179 files->insert(QLatin1String("qmldir"), true);
182 int len = strlen(u.d.d_name);
185 if (!strcmp(u.d.d_name+len-4, ".qml") || !strcmp(u.d.d_name+len-3, ".js"))
186 files->insert(QFile::decodeName(u.d.d_name), true);
187 #if defined(Q_OS_DARWIN)
188 else if ((len > 6 && !strcmp(u.d.d_name+len-6, ".dylib")) || !strcmp(u.d.d_name+len-3, ".so")
189 || (len > 7 && !strcmp(u.d.d_name+len-7, ".bundle")))
190 files->insert(QFile::decodeName(u.d.d_name), true);
192 else if (!strcmp(u.d.d_name+len-3, ".so") || !strcmp(u.d.d_name+len-3, ".sl"))
193 files->insert(QFile::decodeName(u.d.d_name), true);
201 static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
203 QDirIterator dir(path, QDir::Files);
206 QStringHash<bool> *files = new QStringHash<bool>;
207 while (dir.hasNext()) {
209 QString fileName = dir.fileName();
210 if (fileName == QLatin1String("qmldir")
211 || fileName.endsWith(QLatin1String(".qml"))
212 || fileName.endsWith(QLatin1String(".js"))
213 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
214 || fileName.endsWith(QLatin1String(".dll"))
215 #elif defined(Q_OS_DARWIN)
216 || fileName.endsWith(QLatin1String(".dylib"))
217 || fileName.endsWith(QLatin1String(".so"))
218 || fileName.endsWith(QLatin1String(".bundle"))
220 || fileName.endsWith(QLatin1String(".so"))
221 || fileName.endsWith(QLatin1String(".sl"))
224 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
225 fileName = fileName.toLower();
227 files->insert(fileName, true);
237 \brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlDataLoader.
240 QQmlDataBlob's are loaded by a QQmlDataLoader. The user creates the QQmlDataBlob
241 and then calls QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() to load it.
242 The QQmlDataLoader invokes callbacks on the QQmlDataBlob as data becomes available.
246 \enum QQmlDataBlob::Status
248 This enum describes the status of the data blob.
251 \o Null The blob has not yet been loaded by a QQmlDataLoader
252 \o Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been
253 invoked or has not yet returned.
254 \o WaitingForDependencies The blob is waiting for dependencies to be done before continueing. This status
255 only occurs after the QQmlDataBlob::setData() callback has been made, and when the blob has outstanding
257 \o Complete The blob's data has been loaded and all dependencies are done.
258 \o Error An error has been set on this blob.
263 \enum QQmlDataBlob::Type
265 This enum describes the type of the data blob.
268 \o QmlFile This is a QQmlTypeData
269 \o JavaScriptFile This is a QQmlScriptData
270 \o QmldirFile This is a QQmlQmldirData
275 Create a new QQmlDataBlob for \a url and of the provided \a type.
277 QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type)
278 : m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0),
279 m_inCallback(false), m_isDone(false)
284 QQmlDataBlob::~QQmlDataBlob()
286 Q_ASSERT(m_waitingOnMe.isEmpty());
288 cancelAllWaitingFor();
292 Returns the type provided to the constructor.
294 QQmlDataBlob::Type QQmlDataBlob::type() const
300 Returns the blob's status.
302 QQmlDataBlob::Status QQmlDataBlob::status() const
304 return m_data.status();
308 Returns true if the status is Null.
310 bool QQmlDataBlob::isNull() const
312 return status() == Null;
316 Returns true if the status is Loading.
318 bool QQmlDataBlob::isLoading() const
320 return status() == Loading;
324 Returns true if the status is WaitingForDependencies.
326 bool QQmlDataBlob::isWaiting() const
328 return status() == WaitingForDependencies;
332 Returns true if the status is Complete.
334 bool QQmlDataBlob::isComplete() const
336 return status() == Complete;
340 Returns true if the status is Error.
342 bool QQmlDataBlob::isError() const
344 return status() == Error;
348 Returns true if the status is Complete or Error.
350 bool QQmlDataBlob::isCompleteOrError() const
353 return s == Error || s == Complete;
357 Returns the data download progress from 0 to 1.
359 qreal QQmlDataBlob::progress() const
361 quint8 p = m_data.progress();
362 if (p == 0xFF) return 1.;
363 else return qreal(p) / qreal(0xFF);
367 Returns the blob url passed to the constructor. If a network redirect
368 happens while fetching the data, this url remains the same.
372 QUrl QQmlDataBlob::url() const
378 Returns the final url of the data. Initially this is the same as
379 url(), but if a network redirect happens while fetching the data, this url
380 is updated to reflect the new location.
382 May only be called from the load thread, or after the blob isCompleteOrError().
384 QUrl QQmlDataBlob::finalUrl() const
386 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
391 Returns the finalUrl() as a string.
393 QString QQmlDataBlob::finalUrlString() const
395 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
396 if (m_finalUrlString.isEmpty())
397 m_finalUrlString = m_finalUrl.toString();
399 return m_finalUrlString;
403 Return the errors on this blob.
405 May only be called from the load thread, or after the blob isCompleteOrError().
407 QList<QQmlError> QQmlDataBlob::errors() const
409 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
414 Mark this blob as having \a errors.
416 All outstanding dependencies will be cancelled. Requests to add new dependencies
417 will be ignored. Entry into the Error state is irreversable.
419 The setError() method may only be called from within a QQmlDataBlob callback.
421 void QQmlDataBlob::setError(const QQmlError &errors)
433 void QQmlDataBlob::setError(const QList<QQmlError> &errors)
437 Q_ASSERT(status() != Error);
438 Q_ASSERT(m_errors.isEmpty());
440 m_errors = errors; // Must be set before the m_data fence
441 m_data.setStatus(Error);
443 cancelAllWaitingFor();
450 Wait for \a blob to become complete or to error. If \a blob is already
451 complete or in error, or this blob is already complete, this has no effect.
453 The setError() method may only be called from within a QQmlDataBlob callback.
455 void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
459 Q_ASSERT(status() != Null);
462 blob->status() == Error || blob->status() == Complete ||
463 status() == Error || status() == Complete || m_isDone ||
464 m_waitingFor.contains(blob))
469 m_data.setStatus(WaitingForDependencies);
471 m_waitingFor.append(blob);
472 blob->m_waitingOnMe.append(this);
476 \fn void QQmlDataBlob::dataReceived(const QByteArray &data)
478 Invoked when data for the blob is received. Implementors should use this callback
479 to determine a blob's dependencies. Within this callback you may call setError()
484 Invoked once data has either been received or a network error occurred, and all
485 dependencies are complete.
487 You can set an error in this method, but you cannot add new dependencies. Implementors
488 should use this callback to finalize processing of data.
490 The default implementation does nothing.
492 XXX Rename processData() or some such to avoid confusion between done() (processing thread)
493 and completed() (main thread)
495 void QQmlDataBlob::done()
500 Invoked if there is a network error while fetching this blob.
502 The default implementation sets an appropriate QQmlError.
504 void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
506 Q_UNUSED(networkError);
509 error.setUrl(m_finalUrl);
511 const char *errorString = 0;
512 switch (networkError) {
514 errorString = "Network error";
516 case QNetworkReply::ConnectionRefusedError:
517 errorString = "Connection refused";
519 case QNetworkReply::RemoteHostClosedError:
520 errorString = "Remote host closed the connection";
522 case QNetworkReply::HostNotFoundError:
523 errorString = "Host not found";
525 case QNetworkReply::TimeoutError:
526 errorString = "Timeout";
528 case QNetworkReply::ProxyConnectionRefusedError:
529 case QNetworkReply::ProxyConnectionClosedError:
530 case QNetworkReply::ProxyNotFoundError:
531 case QNetworkReply::ProxyTimeoutError:
532 case QNetworkReply::ProxyAuthenticationRequiredError:
533 case QNetworkReply::UnknownProxyError:
534 errorString = "Proxy error";
536 case QNetworkReply::ContentAccessDenied:
537 errorString = "Access denied";
539 case QNetworkReply::ContentNotFoundError:
540 errorString = "File not found";
542 case QNetworkReply::AuthenticationRequiredError:
543 errorString = "Authentication required";
547 error.setDescription(QLatin1String(errorString));
553 Called if \a blob, which was previously waited for, has an error.
555 The default implementation does nothing.
557 void QQmlDataBlob::dependencyError(QQmlDataBlob *blob)
563 Called if \a blob, which was previously waited for, has completed.
565 The default implementation does nothing.
567 void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob)
573 Called when all blobs waited for have completed. This occurs regardless of
574 whether they are in error, or complete state.
576 The default implementation does nothing.
578 void QQmlDataBlob::allDependenciesDone()
583 Called when the download progress of this blob changes. \a progress goes
586 This callback is only invoked if an asynchronous load for this blob is
587 made. An asynchronous load is one in which the Asynchronous mode is
588 specified explicitly, or one that is implicitly delayed due to a network
591 The default implementation does nothing.
593 void QQmlDataBlob::downloadProgressChanged(qreal progress)
599 Invoked on the main thread sometime after done() was called on the load thread.
601 You cannot modify the blobs state at all in this callback and cannot depend on the
602 order or timeliness of these callbacks. Implementors should use this callback to notify
603 dependencies on the main thread that the blob is done and not a lot else.
605 This callback is only invoked if an asynchronous load for this blob is
606 made. An asynchronous load is one in which the Asynchronous mode is
607 specified explicitly, or one that is implicitly delayed due to a network
610 The default implementation does nothing.
612 void QQmlDataBlob::completed()
617 void QQmlDataBlob::tryDone()
619 if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
623 #ifdef DATABLOB_DEBUG
624 qWarning("QQmlDataBlob::done() %s", qPrintable(url().toString()));
628 if (status() != Error)
629 m_data.setStatus(Complete);
631 notifyAllWaitingOnMe();
633 // Locking is not required here, as anyone expecting callbacks must
634 // already be protected against the blob being completed (as set above);
635 if (m_data.isAsync()) {
636 #ifdef DATABLOB_DEBUG
637 qWarning("QQmlDataBlob: Dispatching completed");
639 m_manager->m_thread->callCompleted(this);
646 void QQmlDataBlob::cancelAllWaitingFor()
648 while (m_waitingFor.count()) {
649 QQmlDataBlob *blob = m_waitingFor.takeLast();
651 Q_ASSERT(blob->m_waitingOnMe.contains(this));
653 blob->m_waitingOnMe.removeOne(this);
659 void QQmlDataBlob::notifyAllWaitingOnMe()
661 while (m_waitingOnMe.count()) {
662 QQmlDataBlob *blob = m_waitingOnMe.takeLast();
664 Q_ASSERT(blob->m_waitingFor.contains(this));
666 blob->notifyComplete(this);
670 void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
672 Q_ASSERT(m_waitingFor.contains(blob));
673 Q_ASSERT(blob->status() == Error || blob->status() == Complete);
677 if (blob->status() == Error) {
678 dependencyError(blob);
679 } else if (blob->status() == Complete) {
680 dependencyComplete(blob);
683 m_waitingFor.removeOne(blob);
686 if (!isError() && m_waitingFor.isEmpty())
687 allDependenciesDone();
689 m_inCallback = false;
694 #define TD_STATUS_MASK 0x0000FFFF
695 #define TD_STATUS_SHIFT 0
696 #define TD_PROGRESS_MASK 0x00FF0000
697 #define TD_PROGRESS_SHIFT 16
698 #define TD_ASYNC_MASK 0x80000000
700 QQmlDataBlob::ThreadData::ThreadData()
705 QQmlDataBlob::Status QQmlDataBlob::ThreadData::status() const
707 return QQmlDataBlob::Status((_p.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
710 void QQmlDataBlob::ThreadData::setStatus(QQmlDataBlob::Status status)
714 int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK);
715 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
719 bool QQmlDataBlob::ThreadData::isAsync() const
721 return _p.load() & TD_ASYNC_MASK;
724 void QQmlDataBlob::ThreadData::setIsAsync(bool v)
728 int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0);
729 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
733 quint8 QQmlDataBlob::ThreadData::progress() const
735 return quint8((_p.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
738 void QQmlDataBlob::ThreadData::setProgress(quint8 v)
742 int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK);
743 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
747 QQmlDataLoaderThread::QQmlDataLoaderThread(QQmlDataLoader *loader)
748 : m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
752 QNetworkAccessManager *QQmlDataLoaderThread::networkAccessManager() const
754 Q_ASSERT(isThisThread());
755 if (!m_networkAccessManager) {
756 m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0);
757 m_networkReplyProxy = new QQmlDataLoaderNetworkReplyProxy(m_loader);
760 return m_networkAccessManager;
763 QQmlDataLoaderNetworkReplyProxy *QQmlDataLoaderThread::networkReplyProxy() const
765 Q_ASSERT(isThisThread());
766 Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
767 return m_networkReplyProxy;
770 void QQmlDataLoaderThread::load(QQmlDataBlob *b)
773 callMethodInThread(&This::loadThread, b);
776 void QQmlDataLoaderThread::loadAsync(QQmlDataBlob *b)
779 postMethodToThread(&This::loadThread, b);
782 void QQmlDataLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d)
785 callMethodInThread(&This::loadWithStaticDataThread, b, d);
788 void QQmlDataLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d)
791 postMethodToThread(&This::loadWithStaticDataThread, b, d);
794 void QQmlDataLoaderThread::callCompleted(QQmlDataBlob *b)
797 postMethodToMain(&This::callCompletedMain, b);
800 void QQmlDataLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p)
803 postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
806 void QQmlDataLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
809 callMethodInMain(&This::initializeEngineMain, iface, uri);
812 void QQmlDataLoaderThread::shutdownThread()
814 delete m_networkAccessManager;
815 m_networkAccessManager = 0;
816 delete m_networkReplyProxy;
817 m_networkReplyProxy = 0;
820 void QQmlDataLoaderThread::loadThread(QQmlDataBlob *b)
822 m_loader->loadThread(b);
826 void QQmlDataLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d)
828 m_loader->loadWithStaticDataThread(b, d);
832 void QQmlDataLoaderThread::callCompletedMain(QQmlDataBlob *b)
834 #ifdef DATABLOB_DEBUG
835 qWarning("QQmlDataLoaderThread: %s completed() callback", qPrintable(b->url().toString()));
841 void QQmlDataLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p)
843 #ifdef DATABLOB_DEBUG
844 qWarning("QQmlDataLoaderThread: %s downloadProgressChanged(%f) callback",
845 qPrintable(b->url().toString()), p);
847 b->downloadProgressChanged(p);
851 void QQmlDataLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface,
854 Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
855 iface->initializeEngine(m_loader->engine(), uri);
859 \class QQmlDataLoader
860 \brief The QQmlDataLoader class abstracts loading files and their dependencies over the network.
863 The QQmlDataLoader class is provided for the exclusive use of the QQmlTypeLoader class.
865 Clients create QQmlDataBlob instances and submit them to the QQmlDataLoader class
866 through the QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() methods.
867 The loader then fetches the data over the network or from the local file system in an efficient way.
868 QQmlDataBlob is an abstract class, so should always be specialized.
870 Once data is received, the QQmlDataBlob::dataReceived() method is invoked on the blob. The
871 derived class should use this callback to process the received data. Processing of the data can
872 result in an error being set (QQmlDataBlob::setError()), or one or more dependencies being
873 created (QQmlDataBlob::addDependency()). Dependencies are other QQmlDataBlob's that
874 are required before processing can fully complete.
876 To complete processing, the QQmlDataBlob::done() callback is invoked. done() is called when
877 one of these three preconditions are met.
880 \o The QQmlDataBlob has no dependencies.
881 \o The QQmlDataBlob has an error set.
882 \o All the QQmlDataBlob's dependencies are themselves "done()".
885 Thus QQmlDataBlob::done() will always eventually be called, even if the blob has an error set.
889 Create a new QQmlDataLoader for \a engine.
891 QQmlDataLoader::QQmlDataLoader(QQmlEngine *engine)
892 : m_engine(engine), m_thread(new QQmlDataLoaderThread(this))
897 QQmlDataLoader::~QQmlDataLoader()
899 for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter)
902 m_thread->shutdown();
906 void QQmlDataLoader::lock()
911 void QQmlDataLoader::unlock()
917 Load the provided \a blob from the network or filesystem.
919 The loader must be locked.
921 void QQmlDataLoader::load(QQmlDataBlob *blob, Mode mode)
923 #ifdef DATABLOB_DEBUG
924 qWarning("QQmlDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()),
925 m_thread->isThisThread()?"Compile":"Engine");
928 Q_ASSERT(blob->status() == QQmlDataBlob::Null);
929 Q_ASSERT(blob->m_manager == 0);
931 blob->m_data.setStatus(QQmlDataBlob::Loading);
932 blob->m_manager = this;
934 if (m_thread->isThisThread()) {
938 } else if (mode == PreferSynchronous) {
940 m_thread->load(blob);
942 if (!blob->isCompleteOrError())
943 blob->m_data.setIsAsync(true);
945 Q_ASSERT(mode == Asynchronous);
946 blob->m_data.setIsAsync(true);
948 m_thread->loadAsync(blob);
954 Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case.
956 The loader must be locked.
958 void QQmlDataLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode)
960 #ifdef DATABLOB_DEBUG
961 qWarning("QQmlDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()),
962 m_thread->isThisThread()?"Compile":"Engine");
965 Q_ASSERT(blob->status() == QQmlDataBlob::Null);
966 Q_ASSERT(blob->m_manager == 0);
968 blob->m_data.setStatus(QQmlDataBlob::Loading);
969 blob->m_manager = this;
971 if (m_thread->isThisThread()) {
973 loadWithStaticDataThread(blob, data);
975 } else if (mode == PreferSynchronous) {
977 m_thread->loadWithStaticData(blob, data);
979 if (!blob->isCompleteOrError())
980 blob->m_data.setIsAsync(true);
982 Q_ASSERT(mode == Asynchronous);
983 blob->m_data.setIsAsync(true);
985 m_thread->loadWithStaticDataAsync(blob, data);
990 void QQmlDataLoader::loadWithStaticDataThread(QQmlDataBlob *blob, const QByteArray &data)
997 void QQmlDataLoader::loadThread(QQmlDataBlob *blob)
1001 if (blob->m_url.isEmpty()) {
1003 error.setDescription(QLatin1String("Invalid null URL"));
1004 blob->setError(error);
1008 QString lf = QQmlEnginePrivate::urlToLocalFileOrQrc(blob->m_url);
1010 if (!lf.isEmpty()) {
1011 if (!QQml_isFileCaseCorrect(lf)) {
1013 error.setUrl(blob->m_url);
1014 error.setDescription(QLatin1String("File name case mismatch"));
1015 blob->setError(error);
1019 if (file.open(QFile::ReadOnly)) {
1020 QByteArray data = file.readAll();
1022 blob->m_data.setProgress(0xFF);
1023 if (blob->m_data.isAsync())
1024 m_thread->callDownloadProgressChanged(blob, 1.);
1026 setData(blob, data);
1028 blob->networkError(QNetworkReply::ContentNotFoundError);
1033 QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
1034 QObject *nrp = m_thread->networkReplyProxy();
1035 QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
1036 nrp, SLOT(downloadProgress(qint64,qint64)));
1037 QObject::connect(reply, SIGNAL(finished()),
1038 nrp, SLOT(finished()));
1039 m_networkReplies.insert(reply, blob);
1045 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
1047 void QQmlDataLoader::networkReplyFinished(QNetworkReply *reply)
1049 Q_ASSERT(m_thread->isThisThread());
1051 reply->deleteLater();
1053 QQmlDataBlob *blob = m_networkReplies.take(reply);
1057 blob->m_redirectCount++;
1059 if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
1060 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
1061 if (redirect.isValid()) {
1062 QUrl url = reply->url().resolved(redirect.toUrl());
1063 blob->m_finalUrl = url;
1065 QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url));
1066 QObject *nrp = m_thread->networkReplyProxy();
1067 QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished()));
1068 m_networkReplies.insert(reply, blob);
1073 if (reply->error()) {
1074 blob->networkError(reply->error());
1076 QByteArray data = reply->readAll();
1077 setData(blob, data);
1083 void QQmlDataLoader::networkReplyProgress(QNetworkReply *reply,
1084 qint64 bytesReceived, qint64 bytesTotal)
1086 Q_ASSERT(m_thread->isThisThread());
1088 QQmlDataBlob *blob = m_networkReplies.value(reply);
1092 if (bytesTotal != 0) {
1093 quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal));
1094 blob->m_data.setProgress(progress);
1095 if (blob->m_data.isAsync())
1096 m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
1101 Return the QQmlEngine associated with this loader
1103 QQmlEngine *QQmlDataLoader::engine() const
1109 Call the initializeEngine() method on \a iface. Used by QQmlImportDatabase to ensure it
1110 gets called in the correct thread.
1112 void QQmlDataLoader::initializeEngine(QQmlExtensionInterface *iface,
1115 Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread());
1117 if (m_thread->isThisThread()) {
1118 m_thread->initializeEngine(iface, uri);
1120 Q_ASSERT(engine()->thread() == QThread::currentThread());
1121 iface->initializeEngine(engine(), uri);
1126 void QQmlDataLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
1128 blob->m_inCallback = true;
1130 blob->dataReceived(data);
1132 if (!blob->isError() && !blob->isWaiting())
1133 blob->allDependenciesDone();
1135 if (blob->status() != QQmlDataBlob::Error)
1136 blob->m_data.setStatus(QQmlDataBlob::WaitingForDependencies);
1138 blob->m_inCallback = false;
1144 Constructs a new type loader that uses the given \a engine.
1146 QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine)
1147 : QQmlDataLoader(engine)
1152 Destroys the type loader, first clearing the cache of any information about
1155 QQmlTypeLoader::~QQmlTypeLoader()
1161 \enum QQmlTypeLoader::Option
1163 This enum defines the options that control the way type data is handled.
1165 \value None The default value, indicating that no other options
1167 \value PreserveParser The parser used to handle the type data is preserved
1168 after the data has been parsed.
1172 Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached.
1174 QQmlTypeData *QQmlTypeLoader::get(const QUrl &url)
1176 Q_ASSERT(!url.isRelative() &&
1177 (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
1178 !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url))));
1182 QQmlTypeData *typeData = m_typeCache.value(url);
1185 typeData = new QQmlTypeData(url, None, this);
1186 m_typeCache.insert(url, typeData);
1187 QQmlDataLoader::load(typeData);
1198 Returns a QQmlTypeData for the given \a data with the provided base \a url. The
1199 QQmlTypeData will not be cached.
1201 The specified \a options control how the loader handles type data.
1203 QQmlTypeData *QQmlTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
1207 QQmlTypeData *typeData = new QQmlTypeData(url, options, this);
1208 QQmlDataLoader::loadWithStaticData(typeData, data);
1216 Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
1218 QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url)
1220 Q_ASSERT(!url.isRelative() &&
1221 (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
1222 !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url))));
1226 QQmlScriptBlob *scriptBlob = m_scriptCache.value(url);
1229 scriptBlob = new QQmlScriptBlob(url, this);
1230 m_scriptCache.insert(url, scriptBlob);
1231 QQmlDataLoader::load(scriptBlob);
1234 scriptBlob->addref();
1242 Returns a QQmlQmldirData for \a url. The QQmlQmldirData may be cached.
1244 QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
1246 Q_ASSERT(!url.isRelative() &&
1247 (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
1248 !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url))));
1252 QQmlQmldirData *qmldirData = m_qmldirCache.value(url);
1255 qmldirData = new QQmlQmldirData(url);
1256 m_qmldirCache.insert(url, qmldirData);
1257 QQmlDataLoader::load(qmldirData);
1260 qmldirData->addref();
1268 Returns the absolute filename of path via a directory cache for files named
1269 "qmldir", "*.qml", "*.js", and plugins.
1270 Returns a empty string if the path does not exist.
1272 QString QQmlTypeLoader::absoluteFilePath(const QString &path)
1276 if (path.at(0) == QLatin1Char(':')) {
1278 QFileInfo fileInfo(path);
1279 return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
1281 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1282 QString lowPath = path.toLower();
1283 int lastSlash = lowPath.lastIndexOf(QLatin1Char('/'));
1284 QString dirPath = lowPath.left(lastSlash);
1286 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1287 QStringRef dirPath(&path, 0, lastSlash);
1290 StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1292 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1293 QHashedString dirPathString(dirPath);
1295 QHashedString dirPathString(dirPath.toString());
1297 StringSet *files = qmlFilesInDirectory(dirPathString);
1298 m_importDirCache.insert(dirPathString, files);
1299 fileSet = m_importDirCache.value(dirPathString);
1304 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1305 QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(lowPath.constData()+lastSlash+1, lowPath.length()-lastSlash-1)) ? path : QString();
1307 QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(path.constData()+lastSlash+1, path.length()-lastSlash-1)) ? path : QString();
1309 if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':'))
1310 absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath();
1312 return absoluteFilePath;
1316 Returns true if the path is a directory via a directory cache. Cache is
1317 shared with absoluteFilePath().
1319 bool QQmlTypeLoader::directoryExists(const QString &path)
1323 if (path.at(0) == QLatin1Char(':')) {
1325 QFileInfo fileInfo(path);
1326 return fileInfo.exists() && fileInfo.isDir();
1329 int length = path.length();
1330 if (path.endsWith(QLatin1Char('/')))
1332 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1333 QString dirPath = path.left(length).toLower();
1335 QStringRef dirPath(&path, 0, length);
1338 StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1340 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1341 QHashedString dirPathString(dirPath);
1343 QHashedString dirPathString(dirPath.toString());
1345 StringSet *files = qmlFilesInDirectory(dirPathString);
1346 m_importDirCache.insert(dirPathString, files);
1347 fileSet = m_importDirCache.value(dirPathString);
1355 Return a QQmlDirParser for absoluteFilePath. The QQmlDirParser may be cached.
1357 const QQmlDirParser *QQmlTypeLoader::qmlDirParser(const QString &absoluteFilePath)
1359 QQmlDirParser *qmldirParser;
1360 QQmlDirParser **val = m_importQmlDirCache.value(absoluteFilePath);
1362 qmldirParser = new QQmlDirParser;
1363 qmldirParser->setFileSource(absoluteFilePath);
1364 qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath));
1365 qmldirParser->parse();
1366 m_importQmlDirCache.insert(absoluteFilePath, qmldirParser);
1368 qmldirParser = *val;
1371 return qmldirParser;
1376 Clears cached information about loaded files, including any type data, scripts
1377 and qmldir information.
1379 void QQmlTypeLoader::clearCache()
1381 for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter)
1383 for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter)
1385 for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter)
1387 qDeleteAll(m_importDirCache);
1388 qDeleteAll(m_importQmlDirCache);
1390 m_typeCache.clear();
1391 m_scriptCache.clear();
1392 m_qmldirCache.clear();
1393 m_importDirCache.clear();
1394 m_importQmlDirCache.clear();
1398 QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options,
1399 QQmlTypeLoader *manager)
1400 : QQmlDataBlob(url, QmlFile), m_options(options), m_imports(manager), m_typesResolved(false),
1401 m_compiledData(0), m_typeLoader(manager)
1405 QQmlTypeData::~QQmlTypeData()
1407 for (int ii = 0; ii < m_scripts.count(); ++ii)
1408 m_scripts.at(ii).script->release();
1409 for (int ii = 0; ii < m_qmldirs.count(); ++ii)
1410 m_qmldirs.at(ii)->release();
1411 for (int ii = 0; ii < m_types.count(); ++ii)
1412 if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
1414 m_compiledData->release();
1417 QQmlTypeLoader *QQmlTypeData::typeLoader() const
1419 return m_typeLoader;
1422 const QQmlImports &QQmlTypeData::imports() const
1427 const QQmlScript::Parser &QQmlTypeData::parser() const
1429 return scriptParser;
1432 const QList<QQmlTypeData::TypeReference> &QQmlTypeData::resolvedTypes() const
1437 const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
1442 const QSet<QString> &QQmlTypeData::namespaces() const
1444 return m_namespaces;
1447 QQmlCompiledData *QQmlTypeData::compiledData() const
1450 m_compiledData->addref();
1452 return m_compiledData;
1455 void QQmlTypeData::registerCallback(TypeDataCallback *callback)
1457 Q_ASSERT(!m_callbacks.contains(callback));
1458 m_callbacks.append(callback);
1461 void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
1463 Q_ASSERT(m_callbacks.contains(callback));
1464 m_callbacks.removeOne(callback);
1465 Q_ASSERT(!m_callbacks.contains(callback));
1468 void QQmlTypeData::done()
1470 // Check all script dependencies for errors
1471 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1472 const ScriptReference &script = m_scripts.at(ii);
1473 Q_ASSERT(script.script->isCompleteOrError());
1474 if (script.script->isError()) {
1475 QList<QQmlError> errors = script.script->errors();
1477 error.setUrl(finalUrl());
1478 error.setLine(script.location.line);
1479 error.setColumn(script.location.column);
1480 error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
1481 errors.prepend(error);
1486 // Check all type dependencies for errors
1487 for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
1488 const TypeReference &type = m_types.at(ii);
1489 Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
1490 if (type.typeData && type.typeData->isError()) {
1491 QString typeName = scriptParser.referencedTypes().at(ii)->name;
1493 QList<QQmlError> errors = type.typeData->errors();
1495 error.setUrl(finalUrl());
1496 error.setLine(type.location.line);
1497 error.setColumn(type.location.column);
1498 error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
1499 errors.prepend(error);
1504 // Compile component
1508 if (!(m_options & QQmlTypeLoader::PreserveParser))
1509 scriptParser.clear();
1512 void QQmlTypeData::completed()
1515 while (!m_callbacks.isEmpty()) {
1516 TypeDataCallback *callback = m_callbacks.takeFirst();
1517 callback->typeDataReady(this);
1521 void QQmlTypeData::dataReceived(const QByteArray &data)
1523 if (!scriptParser.parse(data, finalUrl(), finalUrlString())) {
1524 setError(scriptParser.errors());
1528 m_imports.setBaseUrl(finalUrl(), finalUrlString());
1530 foreach (const QQmlScript::Import &import, scriptParser.imports()) {
1531 if (import.type == QQmlScript::Import::File && import.qualifier.isEmpty()) {
1532 QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1533 if (QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
1534 QQmlQmldirData *data = typeLoader()->getQmldir(importUrl);
1535 addDependency(data);
1538 } else if (import.type == QQmlScript::Import::Script) {
1539 QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1540 QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1541 addDependency(blob);
1543 ScriptReference ref;
1544 ref.location = import.location.start;
1545 ref.qualifier = import.qualifier;
1552 if (!finalUrl().scheme().isEmpty()) {
1553 QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
1554 if (QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
1555 QQmlQmldirData *data = typeLoader()->getQmldir(importUrl);
1556 addDependency(data);
1562 void QQmlTypeData::allDependenciesDone()
1564 if (!m_typesResolved) {
1566 m_typesResolved = true;
1570 void QQmlTypeData::downloadProgressChanged(qreal p)
1572 for (int ii = 0; ii < m_callbacks.count(); ++ii) {
1573 TypeDataCallback *callback = m_callbacks.at(ii);
1574 callback->typeDataProgress(this, p);
1578 void QQmlTypeData::compile()
1580 Q_ASSERT(m_compiledData == 0);
1582 m_compiledData = new QQmlCompiledData(typeLoader()->engine());
1583 m_compiledData->url = finalUrl();
1584 m_compiledData->name = finalUrlString();
1586 QQmlCompilingProfiler prof(m_compiledData->name);
1588 QQmlCompiler compiler(&scriptParser._pool);
1589 if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
1590 setError(compiler.errors());
1591 m_compiledData->release();
1596 void QQmlTypeData::resolveTypes()
1598 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_typeLoader->engine());
1599 QQmlImportDatabase *importDatabase = &ep->importDatabase;
1601 // For local urls, add an implicit import "." as first (most overridden) lookup.
1602 // This will also trigger the loading of the qmldir and the import of any native
1603 // types from available plugins.
1604 QList<QQmlError> errors;
1605 if (QQmlQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
1606 m_imports.addImport(importDatabase, QLatin1String("."),
1607 QString(), -1, -1, QQmlScript::Import::File,
1608 qmldir->dirComponents(), &errors);
1610 m_imports.addImport(importDatabase, QLatin1String("."),
1611 QString(), -1, -1, QQmlScript::Import::File,
1612 QQmlDirComponents(), &errors);
1615 // remove any errors which are due to the implicit import which aren't real errors.
1616 // for example, if the implicitly included qmldir file doesn't exist, that is not an error.
1617 QList<QQmlError> realErrors;
1618 for (int i = 0; i < errors.size(); ++i) {
1619 if (errors.at(i).description() != QQmlImportDatabase::tr("import \".\" has no qmldir and no namespace")
1620 && errors.at(i).description() != QQmlImportDatabase::tr("\".\": no such directory")) {
1621 realErrors.prepend(errors.at(i)); // this is a real error.
1625 // report any real errors which occurred during plugin loading or qmldir parsing.
1626 if (!realErrors.isEmpty()) {
1627 setError(realErrors);
1631 foreach (const QQmlScript::Import &import, scriptParser.imports()) {
1632 QQmlDirComponents qmldircomponentsnetwork;
1633 if (import.type == QQmlScript::Import::Script)
1636 if (import.type == QQmlScript::Import::File && import.qualifier.isEmpty()) {
1637 QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1638 if (QQmlQmldirData *qmldir = qmldirForUrl(qmldirUrl))
1639 qmldircomponentsnetwork = qmldir->dirComponents();
1644 import.extractVersion(&vmaj, &vmin);
1646 QList<QQmlError> errors;
1647 if (!m_imports.addImport(importDatabase, import.uri, import.qualifier,
1648 vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) {
1650 if (errors.size()) {
1651 error = errors.takeFirst();
1653 // this should not be possible!
1654 // Description should come from error provided by addImport() function.
1655 error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
1657 error.setUrl(m_imports.baseUrl());
1658 error.setLine(import.location.start.line);
1659 error.setColumn(import.location.start.column);
1660 errors.prepend(error); // put it back on the list after filling out information.
1667 // Add any imported scripts to our resolved set
1668 foreach (const QQmlImports::ScriptReference &script, m_imports.resolvedScripts())
1670 QQmlScriptBlob *blob = typeLoader()->getScript(script.location);
1671 addDependency(blob);
1673 ScriptReference ref;
1674 //ref.location = ...
1675 ref.qualifier = script.nameSpace;
1676 if (!script.qualifier.isEmpty())
1678 ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
1680 // Add a reference to the enclosing namespace
1681 m_namespaces.insert(script.qualifier);
1688 foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
1694 QQmlImportedNamespace *typeNamespace = 0;
1695 QList<QQmlError> errors;
1697 if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
1698 &typeNamespace, &errors) || typeNamespace) {
1699 // Known to not be a type:
1700 // - known to be a namespace (Namespace {})
1701 // - type with unknown namespace (UnknownNamespace.SomeType {})
1703 QString userTypeName = parserRef->name;
1704 userTypeName.replace(QLatin1Char('/'),QLatin1Char('.'));
1705 if (typeNamespace) {
1706 error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName));
1708 if (errors.size()) {
1709 error = errors.takeFirst();
1711 // this should not be possible!
1712 // Description should come from error provided by addImport() function.
1713 error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
1715 error.setUrl(m_imports.baseUrl());
1716 error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description()));
1719 if (!parserRef->refObjects.isEmpty()) {
1720 QQmlScript::Object *obj = parserRef->refObjects.first();
1721 error.setLine(obj->location.start.line);
1722 error.setColumn(obj->location.start.column);
1725 errors.prepend(error);
1731 ref.majorVersion = majorVersion;
1732 ref.minorVersion = minorVersion;
1734 ref.typeData = typeLoader()->get(QUrl(url));
1735 addDependency(ref.typeData);
1738 if (parserRef->refObjects.count())
1739 ref.location = parserRef->refObjects.first()->location.start;
1745 QQmlQmldirData *QQmlTypeData::qmldirForUrl(const QUrl &url)
1747 for (int ii = 0; ii < m_qmldirs.count(); ++ii) {
1748 if (m_qmldirs.at(ii)->url() == url)
1749 return m_qmldirs.at(ii);
1754 QQmlScriptData::QQmlScriptData()
1755 : importCache(0), pragmas(QQmlScript::Object::ScriptBlock::None), m_loaded(false)
1759 QQmlScriptData::~QQmlScriptData()
1763 void QQmlScriptData::clear()
1766 importCache->release();
1770 for (int ii = 0; ii < scripts.count(); ++ii)
1771 scripts.at(ii)->release();
1774 qPersistentDispose(m_program);
1775 qPersistentDispose(m_value);
1777 // An addref() was made when the QQmlCleanup was added to the engine.
1781 QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
1782 : QQmlDataBlob(url, JavaScriptFile), m_pragmas(QQmlScript::Object::ScriptBlock::None),
1783 m_imports(loader), m_scriptData(0), m_typeLoader(loader)
1787 QQmlScriptBlob::~QQmlScriptBlob()
1790 m_scriptData->release();
1795 QQmlScript::Object::ScriptBlock::Pragmas QQmlScriptBlob::pragmas() const
1800 QQmlTypeLoader *QQmlScriptBlob::typeLoader() const
1802 return m_typeLoader;
1805 const QQmlImports &QQmlScriptBlob::imports() const
1810 QQmlScriptData *QQmlScriptBlob::scriptData() const
1812 return m_scriptData;
1815 void QQmlScriptBlob::dataReceived(const QByteArray &data)
1817 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_typeLoader->engine());
1818 QQmlImportDatabase *importDatabase = &ep->importDatabase;
1820 m_source = QString::fromUtf8(data);
1822 QQmlScript::Parser::JavaScriptMetaData metadata =
1823 QQmlScript::Parser::extractMetaData(m_source);
1825 m_imports.setBaseUrl(finalUrl(), finalUrlString());
1827 m_pragmas = metadata.pragmas;
1829 foreach (const QQmlScript::Import &import, metadata.imports) {
1830 Q_ASSERT(import.type != QQmlScript::Import::File);
1832 if (import.type == QQmlScript::Import::Script) {
1833 QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1834 QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1835 addDependency(blob);
1837 ScriptReference ref;
1838 ref.location = import.location.start;
1839 ref.qualifier = import.qualifier;
1843 Q_ASSERT(import.type == QQmlScript::Import::Library);
1846 import.extractVersion(&vmaj, &vmin);
1848 QList<QQmlError> errors;
1849 if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
1850 import.type, QQmlDirComponents(), &errors)) {
1851 QQmlError error = errors.takeFirst();
1852 // description should be set by addImport().
1853 error.setUrl(m_imports.baseUrl());
1854 error.setLine(import.location.start.line);
1855 error.setColumn(import.location.start.column);
1856 errors.prepend(error);
1865 void QQmlScriptBlob::done()
1867 // Check all script dependencies for errors
1868 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1869 const ScriptReference &script = m_scripts.at(ii);
1870 Q_ASSERT(script.script->isCompleteOrError());
1871 if (script.script->isError()) {
1872 QList<QQmlError> errors = script.script->errors();
1874 error.setUrl(finalUrl());
1875 error.setLine(script.location.line);
1876 error.setColumn(script.location.column);
1877 error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
1878 errors.prepend(error);
1886 QQmlEngine *engine = typeLoader()->engine();
1887 m_scriptData = new QQmlScriptData();
1888 m_scriptData->url = finalUrl();
1889 m_scriptData->urlString = finalUrlString();
1890 m_scriptData->importCache = new QQmlTypeNameCache();
1892 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1893 const ScriptReference &script = m_scripts.at(ii);
1895 m_scriptData->scripts.append(script.script);
1896 m_scriptData->importCache->add(script.qualifier, ii);
1899 m_imports.populateCache(m_scriptData->importCache, engine);
1901 m_scriptData->pragmas = m_pragmas;
1902 m_scriptData->m_programSource = m_source.toUtf8();
1906 QQmlQmldirData::QQmlQmldirData(const QUrl &url)
1907 : QQmlDataBlob(url, QmldirFile)
1911 const QQmlDirComponents &QQmlQmldirData::dirComponents() const
1913 return m_components;
1916 void QQmlQmldirData::dataReceived(const QByteArray &data)
1918 QQmlDirParser parser;
1919 parser.setSource(QString::fromUtf8(data));
1921 m_components = parser.components();
1926 #include "qqmltypeloader.moc"