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>
50 #include <private/qqmlmemoryprofiler_p.h>
52 #include <QtCore/qdir.h>
53 #include <QtCore/qfile.h>
54 #include <QtCore/qdebug.h>
55 #include <QtCore/qmutex.h>
56 #include <QtCore/qthread.h>
57 #include <QtQml/qqmlfile.h>
58 #include <QtCore/qdiriterator.h>
59 #include <QtQml/qqmlcomponent.h>
60 #include <QtCore/qwaitcondition.h>
61 #include <QtQml/qqmlextensioninterface.h>
63 #if defined (Q_OS_UNIX)
64 #include <sys/types.h>
69 #if defined (QT_LINUXBASE)
70 // LSB doesn't declare NAME_MAX. Use SYMLINK_MAX instead, which seems to
71 // always be identical to NAME_MAX
73 # define NAME_MAX _POSIX_SYMLINK_MAX
76 // LSB has a broken version of offsetof that can't be used at compile time
77 // https://lsbbugs.linuxfoundation.org/show_bug.cgi?id=3462
79 #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
82 // #define DATABLOB_DEBUG
86 #define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in main thread"); } while(false)
87 #define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in load thread"); } while(false)
88 #define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while(false)
92 #define ASSERT_MAINTHREAD()
93 #define ASSERT_LOADTHREAD()
94 #define ASSERT_CALLBACK()
98 DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
104 template<typename LockType>
108 LockHolder(LockType *l) : lock(*l) { lock.lock(); }
109 ~LockHolder() { lock.unlock(); }
113 // This is a lame object that we need to ensure that slots connected to
114 // QNetworkReply get called in the correct thread (the loader thread).
115 // As QQmlDataLoader lives in the main thread, and we can't use
116 // Qt::DirectConnection connections from a QNetworkReply (because then
117 // sender() wont work), we need to insert this object in the middle.
118 class QQmlDataLoaderNetworkReplyProxy : public QObject
122 QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l);
126 void downloadProgress(qint64, qint64);
132 class QQmlDataLoaderThread : public QQmlThread
134 typedef QQmlDataLoaderThread This;
137 QQmlDataLoaderThread(QQmlDataLoader *loader);
138 QNetworkAccessManager *networkAccessManager() const;
139 QQmlDataLoaderNetworkReplyProxy *networkReplyProxy() const;
141 void load(QQmlDataBlob *b);
142 void loadAsync(QQmlDataBlob *b);
143 void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
144 void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &);
145 void callCompleted(QQmlDataBlob *b);
146 void callDownloadProgressChanged(QQmlDataBlob *b, qreal p);
147 void initializeEngine(QQmlExtensionInterface *, const char *);
150 virtual void shutdownThread();
153 void loadThread(QQmlDataBlob *b);
154 void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &);
155 void callCompletedMain(QQmlDataBlob *b);
156 void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p);
157 void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
159 QQmlDataLoader *m_loader;
160 mutable QNetworkAccessManager *m_networkAccessManager;
161 mutable QQmlDataLoaderNetworkReplyProxy *m_networkReplyProxy;
165 QQmlDataLoaderNetworkReplyProxy::QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l)
170 void QQmlDataLoaderNetworkReplyProxy::finished()
173 Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
174 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
175 l->networkReplyFinished(reply);
178 void QQmlDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
181 Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
182 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
183 l->networkReplyProgress(reply, bytesReceived, bytesTotal);
189 \brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlDataLoader.
192 QQmlDataBlob's are loaded by a QQmlDataLoader. The user creates the QQmlDataBlob
193 and then calls QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() to load it.
194 The QQmlDataLoader invokes callbacks on the QQmlDataBlob as data becomes available.
198 \enum QQmlDataBlob::Status
200 This enum describes the status of the data blob.
203 \li Null The blob has not yet been loaded by a QQmlDataLoader
204 \li Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been
205 invoked or has not yet returned.
206 \li WaitingForDependencies The blob is waiting for dependencies to be done before continueing. This status
207 only occurs after the QQmlDataBlob::setData() callback has been made, and when the blob has outstanding
209 \li Complete The blob's data has been loaded and all dependencies are done.
210 \li Error An error has been set on this blob.
215 \enum QQmlDataBlob::Type
217 This enum describes the type of the data blob.
220 \li QmlFile This is a QQmlTypeData
221 \li JavaScriptFile This is a QQmlScriptData
222 \li QmldirFile This is a QQmlQmldirData
227 Create a new QQmlDataBlob for \a url and of the provided \a type.
229 QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type)
230 : m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0),
231 m_inCallback(false), m_isDone(false)
236 QQmlDataBlob::~QQmlDataBlob()
238 Q_ASSERT(m_waitingOnMe.isEmpty());
240 cancelAllWaitingFor();
244 Returns the type provided to the constructor.
246 QQmlDataBlob::Type QQmlDataBlob::type() const
252 Returns the blob's status.
254 QQmlDataBlob::Status QQmlDataBlob::status() const
256 return m_data.status();
260 Returns true if the status is Null.
262 bool QQmlDataBlob::isNull() const
264 return status() == Null;
268 Returns true if the status is Loading.
270 bool QQmlDataBlob::isLoading() const
272 return status() == Loading;
276 Returns true if the status is WaitingForDependencies.
278 bool QQmlDataBlob::isWaiting() const
280 return status() == WaitingForDependencies;
284 Returns true if the status is Complete.
286 bool QQmlDataBlob::isComplete() const
288 return status() == Complete;
292 Returns true if the status is Error.
294 bool QQmlDataBlob::isError() const
296 return status() == Error;
300 Returns true if the status is Complete or Error.
302 bool QQmlDataBlob::isCompleteOrError() const
305 return s == Error || s == Complete;
309 Returns the data download progress from 0 to 1.
311 qreal QQmlDataBlob::progress() const
313 quint8 p = m_data.progress();
314 if (p == 0xFF) return 1.;
315 else return qreal(p) / qreal(0xFF);
319 Returns the blob url passed to the constructor. If a network redirect
320 happens while fetching the data, this url remains the same.
324 QUrl QQmlDataBlob::url() const
330 Returns the final url of the data. Initially this is the same as
331 url(), but if a network redirect happens while fetching the data, this url
332 is updated to reflect the new location.
334 May only be called from the load thread, or after the blob isCompleteOrError().
336 QUrl QQmlDataBlob::finalUrl() const
338 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
343 Returns the finalUrl() as a string.
345 QString QQmlDataBlob::finalUrlString() const
347 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
348 if (m_finalUrlString.isEmpty())
349 m_finalUrlString = m_finalUrl.toString();
351 return m_finalUrlString;
355 Return the errors on this blob.
357 May only be called from the load thread, or after the blob isCompleteOrError().
359 QList<QQmlError> QQmlDataBlob::errors() const
361 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
366 Mark this blob as having \a errors.
368 All outstanding dependencies will be cancelled. Requests to add new dependencies
369 will be ignored. Entry into the Error state is irreversable.
371 The setError() method may only be called from within a QQmlDataBlob callback.
373 void QQmlDataBlob::setError(const QQmlError &errors)
385 void QQmlDataBlob::setError(const QList<QQmlError> &errors)
389 Q_ASSERT(status() != Error);
390 Q_ASSERT(m_errors.isEmpty());
392 m_errors = errors; // Must be set before the m_data fence
393 m_data.setStatus(Error);
396 qWarning().nospace() << "Errors for " << m_finalUrl.toString();
397 for (int ii = 0; ii < errors.count(); ++ii)
398 qWarning().nospace() << " " << qPrintable(errors.at(ii).toString());
400 cancelAllWaitingFor();
407 Wait for \a blob to become complete or to error. If \a blob is already
408 complete or in error, or this blob is already complete, this has no effect.
410 The setError() method may only be called from within a QQmlDataBlob callback.
412 void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
416 Q_ASSERT(status() != Null);
419 blob->status() == Error || blob->status() == Complete ||
420 status() == Error || status() == Complete || m_isDone ||
421 m_waitingFor.contains(blob))
426 m_data.setStatus(WaitingForDependencies);
428 m_waitingFor.append(blob);
429 blob->m_waitingOnMe.append(this);
433 \fn void QQmlDataBlob::dataReceived(const Data &data)
435 Invoked when data for the blob is received. Implementors should use this callback
436 to determine a blob's dependencies. Within this callback you may call setError()
441 Invoked once data has either been received or a network error occurred, and all
442 dependencies are complete.
444 You can set an error in this method, but you cannot add new dependencies. Implementors
445 should use this callback to finalize processing of data.
447 The default implementation does nothing.
449 XXX Rename processData() or some such to avoid confusion between done() (processing thread)
450 and completed() (main thread)
452 void QQmlDataBlob::done()
457 Invoked if there is a network error while fetching this blob.
459 The default implementation sets an appropriate QQmlError.
461 void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
463 Q_UNUSED(networkError);
466 error.setUrl(m_finalUrl);
468 const char *errorString = 0;
469 switch (networkError) {
471 errorString = "Network error";
473 case QNetworkReply::ConnectionRefusedError:
474 errorString = "Connection refused";
476 case QNetworkReply::RemoteHostClosedError:
477 errorString = "Remote host closed the connection";
479 case QNetworkReply::HostNotFoundError:
480 errorString = "Host not found";
482 case QNetworkReply::TimeoutError:
483 errorString = "Timeout";
485 case QNetworkReply::ProxyConnectionRefusedError:
486 case QNetworkReply::ProxyConnectionClosedError:
487 case QNetworkReply::ProxyNotFoundError:
488 case QNetworkReply::ProxyTimeoutError:
489 case QNetworkReply::ProxyAuthenticationRequiredError:
490 case QNetworkReply::UnknownProxyError:
491 errorString = "Proxy error";
493 case QNetworkReply::ContentAccessDenied:
494 errorString = "Access denied";
496 case QNetworkReply::ContentNotFoundError:
497 errorString = "File not found";
499 case QNetworkReply::AuthenticationRequiredError:
500 errorString = "Authentication required";
504 error.setDescription(QLatin1String(errorString));
510 Called if \a blob, which was previously waited for, has an error.
512 The default implementation does nothing.
514 void QQmlDataBlob::dependencyError(QQmlDataBlob *blob)
520 Called if \a blob, which was previously waited for, has completed.
522 The default implementation does nothing.
524 void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob)
530 Called when all blobs waited for have completed. This occurs regardless of
531 whether they are in error, or complete state.
533 The default implementation does nothing.
535 void QQmlDataBlob::allDependenciesDone()
540 Called when the download progress of this blob changes. \a progress goes
543 This callback is only invoked if an asynchronous load for this blob is
544 made. An asynchronous load is one in which the Asynchronous mode is
545 specified explicitly, or one that is implicitly delayed due to a network
548 The default implementation does nothing.
550 void QQmlDataBlob::downloadProgressChanged(qreal progress)
556 Invoked on the main thread sometime after done() was called on the load thread.
558 You cannot modify the blobs state at all in this callback and cannot depend on the
559 order or timeliness of these callbacks. Implementors should use this callback to notify
560 dependencies on the main thread that the blob is done and not a lot else.
562 This callback is only invoked if an asynchronous load for this blob is
563 made. An asynchronous load is one in which the Asynchronous mode is
564 specified explicitly, or one that is implicitly delayed due to a network
567 The default implementation does nothing.
569 void QQmlDataBlob::completed()
574 void QQmlDataBlob::tryDone()
576 if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
580 #ifdef DATABLOB_DEBUG
581 qWarning("QQmlDataBlob::done() %s", qPrintable(url().toString()));
585 if (status() != Error)
586 m_data.setStatus(Complete);
588 notifyAllWaitingOnMe();
590 // Locking is not required here, as anyone expecting callbacks must
591 // already be protected against the blob being completed (as set above);
592 if (m_data.isAsync()) {
593 #ifdef DATABLOB_DEBUG
594 qWarning("QQmlDataBlob: Dispatching completed");
596 m_manager->m_thread->callCompleted(this);
603 void QQmlDataBlob::cancelAllWaitingFor()
605 while (m_waitingFor.count()) {
606 QQmlDataBlob *blob = m_waitingFor.takeLast();
608 Q_ASSERT(blob->m_waitingOnMe.contains(this));
610 blob->m_waitingOnMe.removeOne(this);
616 void QQmlDataBlob::notifyAllWaitingOnMe()
618 while (m_waitingOnMe.count()) {
619 QQmlDataBlob *blob = m_waitingOnMe.takeLast();
621 Q_ASSERT(blob->m_waitingFor.contains(this));
623 blob->notifyComplete(this);
627 void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
629 Q_ASSERT(m_waitingFor.contains(blob));
630 Q_ASSERT(blob->status() == Error || blob->status() == Complete);
634 m_waitingFor.removeOne(blob);
636 if (blob->status() == Error) {
637 dependencyError(blob);
638 } else if (blob->status() == Complete) {
639 dependencyComplete(blob);
644 if (!isError() && m_waitingFor.isEmpty())
645 allDependenciesDone();
647 m_inCallback = false;
652 #define TD_STATUS_MASK 0x0000FFFF
653 #define TD_STATUS_SHIFT 0
654 #define TD_PROGRESS_MASK 0x00FF0000
655 #define TD_PROGRESS_SHIFT 16
656 #define TD_ASYNC_MASK 0x80000000
658 QQmlDataBlob::ThreadData::ThreadData()
663 QQmlDataBlob::Status QQmlDataBlob::ThreadData::status() const
665 return QQmlDataBlob::Status((_p.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
668 void QQmlDataBlob::ThreadData::setStatus(QQmlDataBlob::Status status)
672 int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK);
673 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
677 bool QQmlDataBlob::ThreadData::isAsync() const
679 return _p.load() & TD_ASYNC_MASK;
682 void QQmlDataBlob::ThreadData::setIsAsync(bool v)
686 int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0);
687 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
691 quint8 QQmlDataBlob::ThreadData::progress() const
693 return quint8((_p.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
696 void QQmlDataBlob::ThreadData::setProgress(quint8 v)
700 int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK);
701 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
705 QQmlDataLoaderThread::QQmlDataLoaderThread(QQmlDataLoader *loader)
706 : m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
710 QNetworkAccessManager *QQmlDataLoaderThread::networkAccessManager() const
712 Q_ASSERT(isThisThread());
713 if (!m_networkAccessManager) {
714 m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0);
715 m_networkReplyProxy = new QQmlDataLoaderNetworkReplyProxy(m_loader);
718 return m_networkAccessManager;
721 QQmlDataLoaderNetworkReplyProxy *QQmlDataLoaderThread::networkReplyProxy() const
723 Q_ASSERT(isThisThread());
724 Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
725 return m_networkReplyProxy;
728 void QQmlDataLoaderThread::load(QQmlDataBlob *b)
731 callMethodInThread(&This::loadThread, b);
734 void QQmlDataLoaderThread::loadAsync(QQmlDataBlob *b)
737 postMethodToThread(&This::loadThread, b);
740 void QQmlDataLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d)
743 callMethodInThread(&This::loadWithStaticDataThread, b, d);
746 void QQmlDataLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d)
749 postMethodToThread(&This::loadWithStaticDataThread, b, d);
752 void QQmlDataLoaderThread::callCompleted(QQmlDataBlob *b)
755 postMethodToMain(&This::callCompletedMain, b);
758 void QQmlDataLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p)
761 postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
764 void QQmlDataLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
767 callMethodInMain(&This::initializeEngineMain, iface, uri);
770 void QQmlDataLoaderThread::shutdownThread()
772 delete m_networkAccessManager;
773 m_networkAccessManager = 0;
774 delete m_networkReplyProxy;
775 m_networkReplyProxy = 0;
778 void QQmlDataLoaderThread::loadThread(QQmlDataBlob *b)
780 m_loader->loadThread(b);
784 void QQmlDataLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d)
786 m_loader->loadWithStaticDataThread(b, d);
790 void QQmlDataLoaderThread::callCompletedMain(QQmlDataBlob *b)
792 QML_MEMORY_SCOPE_URL(b->url());
793 #ifdef DATABLOB_DEBUG
794 qWarning("QQmlDataLoaderThread: %s completed() callback", qPrintable(b->url().toString()));
800 void QQmlDataLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p)
802 #ifdef DATABLOB_DEBUG
803 qWarning("QQmlDataLoaderThread: %s downloadProgressChanged(%f) callback",
804 qPrintable(b->url().toString()), p);
806 b->downloadProgressChanged(p);
810 void QQmlDataLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface,
813 Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
814 iface->initializeEngine(m_loader->engine(), uri);
818 \class QQmlDataLoader
819 \brief The QQmlDataLoader class abstracts loading files and their dependencies over the network.
822 The QQmlDataLoader class is provided for the exclusive use of the QQmlTypeLoader class.
824 Clients create QQmlDataBlob instances and submit them to the QQmlDataLoader class
825 through the QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() methods.
826 The loader then fetches the data over the network or from the local file system in an efficient way.
827 QQmlDataBlob is an abstract class, so should always be specialized.
829 Once data is received, the QQmlDataBlob::dataReceived() method is invoked on the blob. The
830 derived class should use this callback to process the received data. Processing of the data can
831 result in an error being set (QQmlDataBlob::setError()), or one or more dependencies being
832 created (QQmlDataBlob::addDependency()). Dependencies are other QQmlDataBlob's that
833 are required before processing can fully complete.
835 To complete processing, the QQmlDataBlob::done() callback is invoked. done() is called when
836 one of these three preconditions are met.
839 \li The QQmlDataBlob has no dependencies.
840 \li The QQmlDataBlob has an error set.
841 \li All the QQmlDataBlob's dependencies are themselves "done()".
844 Thus QQmlDataBlob::done() will always eventually be called, even if the blob has an error set.
848 Create a new QQmlDataLoader for \a engine.
850 QQmlDataLoader::QQmlDataLoader(QQmlEngine *engine)
851 : m_engine(engine), m_thread(new QQmlDataLoaderThread(this))
856 QQmlDataLoader::~QQmlDataLoader()
858 for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter)
865 void QQmlDataLoader::lock()
870 void QQmlDataLoader::unlock()
876 Load the provided \a blob from the network or filesystem.
878 The loader must be locked.
880 void QQmlDataLoader::load(QQmlDataBlob *blob, Mode mode)
882 #ifdef DATABLOB_DEBUG
883 qWarning("QQmlDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()),
884 m_thread->isThisThread()?"Compile":"Engine");
887 Q_ASSERT(blob->status() == QQmlDataBlob::Null);
888 Q_ASSERT(blob->m_manager == 0);
890 blob->m_data.setStatus(QQmlDataBlob::Loading);
891 blob->m_manager = this;
893 if (m_thread->isThisThread()) {
897 } else if (mode == PreferSynchronous) {
899 m_thread->load(blob);
901 if (!blob->isCompleteOrError())
902 blob->m_data.setIsAsync(true);
904 Q_ASSERT(mode == Asynchronous);
905 blob->m_data.setIsAsync(true);
907 m_thread->loadAsync(blob);
913 Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case.
915 The loader must be locked.
917 void QQmlDataLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode)
919 #ifdef DATABLOB_DEBUG
920 qWarning("QQmlDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()),
921 m_thread->isThisThread()?"Compile":"Engine");
924 Q_ASSERT(blob->status() == QQmlDataBlob::Null);
925 Q_ASSERT(blob->m_manager == 0);
927 blob->m_data.setStatus(QQmlDataBlob::Loading);
928 blob->m_manager = this;
930 if (m_thread->isThisThread()) {
932 loadWithStaticDataThread(blob, data);
934 } else if (mode == PreferSynchronous) {
936 m_thread->loadWithStaticData(blob, data);
938 if (!blob->isCompleteOrError())
939 blob->m_data.setIsAsync(true);
941 Q_ASSERT(mode == Asynchronous);
942 blob->m_data.setIsAsync(true);
944 m_thread->loadWithStaticDataAsync(blob, data);
949 void QQmlDataLoader::loadWithStaticDataThread(QQmlDataBlob *blob, const QByteArray &data)
956 void QQmlDataLoader::loadThread(QQmlDataBlob *blob)
960 // Don't continue loading if we've been shutdown
961 if (m_thread->isShutdown()) {
963 error.setDescription(QLatin1String("Interrupted by shutdown"));
964 blob->setError(error);
968 if (blob->m_url.isEmpty()) {
970 error.setDescription(QLatin1String("Invalid null URL"));
971 blob->setError(error);
975 QML_MEMORY_SCOPE_URL(blob->m_url);
976 QQmlEnginePrivate *engine_d = QQmlEnginePrivate::get(m_engine);
977 QHash<QUrl, QByteArray> debugCache = engine_d->debugChangesCache();
979 if (!debugCache.isEmpty()) {
980 foreach (const QUrl &url, debugCache.keys()) {
981 if (blob->m_url == blob->m_url.resolved(url)) {
982 blob->m_data.setProgress(0xFF);
983 if (blob->m_data.isAsync())
984 m_thread->callDownloadProgressChanged(blob, 1.);
985 setData(blob, debugCache.value(url, QByteArray()));
991 if (QQmlFile::isSynchronous(blob->m_url)) {
992 QQmlFile file(m_engine, blob->m_url);
994 if (file.isError()) {
996 error.setUrl(blob->m_url);
997 error.setDescription(file.error());
998 blob->setError(error);
1002 blob->m_data.setProgress(0xFF);
1003 if (blob->m_data.isAsync())
1004 m_thread->callDownloadProgressChanged(blob, 1.);
1006 setData(blob, &file);
1010 QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
1011 QObject *nrp = m_thread->networkReplyProxy();
1012 QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
1013 nrp, SLOT(downloadProgress(qint64,qint64)));
1014 QObject::connect(reply, SIGNAL(finished()),
1015 nrp, SLOT(finished()));
1016 m_networkReplies.insert(reply, blob);
1017 #ifdef DATABLOB_DEBUG
1018 qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString()));
1025 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
1027 void QQmlDataLoader::networkReplyFinished(QNetworkReply *reply)
1029 Q_ASSERT(m_thread->isThisThread());
1031 reply->deleteLater();
1033 QQmlDataBlob *blob = m_networkReplies.take(reply);
1037 blob->m_redirectCount++;
1039 if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
1040 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
1041 if (redirect.isValid()) {
1042 QUrl url = reply->url().resolved(redirect.toUrl());
1043 blob->m_finalUrl = url;
1045 QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url));
1046 QObject *nrp = m_thread->networkReplyProxy();
1047 QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished()));
1048 m_networkReplies.insert(reply, blob);
1049 #ifdef DATABLOB_DEBUG
1050 qWarning("QQmlDataBlob: redirected to %s", qPrintable(blob->m_finalUrl.toString()));
1056 if (reply->error()) {
1057 blob->networkError(reply->error());
1059 QByteArray data = reply->readAll();
1060 setData(blob, data);
1066 void QQmlDataLoader::networkReplyProgress(QNetworkReply *reply,
1067 qint64 bytesReceived, qint64 bytesTotal)
1069 Q_ASSERT(m_thread->isThisThread());
1071 QQmlDataBlob *blob = m_networkReplies.value(reply);
1075 if (bytesTotal != 0) {
1076 quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal));
1077 blob->m_data.setProgress(progress);
1078 if (blob->m_data.isAsync())
1079 m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
1084 Return the QQmlEngine associated with this loader
1086 QQmlEngine *QQmlDataLoader::engine() const
1092 Call the initializeEngine() method on \a iface. Used by QQmlImportDatabase to ensure it
1093 gets called in the correct thread.
1095 void QQmlDataLoader::initializeEngine(QQmlExtensionInterface *iface,
1098 Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread());
1100 if (m_thread->isThisThread()) {
1101 m_thread->initializeEngine(iface, uri);
1103 Q_ASSERT(engine()->thread() == QThread::currentThread());
1104 iface->initializeEngine(engine(), uri);
1109 void QQmlDataLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
1111 QML_MEMORY_SCOPE_URL(blob->url());
1112 QQmlDataBlob::Data d;
1117 void QQmlDataLoader::setData(QQmlDataBlob *blob, QQmlFile *file)
1119 QML_MEMORY_SCOPE_URL(blob->url());
1120 QQmlDataBlob::Data d;
1125 void QQmlDataLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::Data &d)
1127 QML_MEMORY_SCOPE_URL(blob->url());
1128 blob->m_inCallback = true;
1130 blob->dataReceived(d);
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;
1143 void QQmlDataLoader::shutdownThread()
1145 if (!m_thread->isShutdown())
1146 m_thread->shutdown();
1149 QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
1150 : QQmlDataBlob(url, type), m_typeLoader(loader), m_imports(loader)
1154 QQmlTypeLoader::Blob::~Blob()
1156 for (int ii = 0; ii < m_qmldirs.count(); ++ii)
1157 m_qmldirs.at(ii)->release();
1160 bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QQmlScript::Import *import, int priority, QList<QQmlError> *errors)
1162 QQmlQmldirData *data = typeLoader()->getQmldir(url);
1164 data->setImport(import);
1165 data->setPriority(priority);
1167 if (data->status() == Error) {
1168 // This qmldir must not exist - which is not an error
1171 } else if (data->status() == Complete) {
1172 // This data is already available
1173 return qmldirDataAvailable(data, errors);
1176 // Wait for this data to become available
1177 addDependency(data);
1181 bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QQmlScript::Import *import, QList<QQmlError> *errors)
1183 QString qmldirIdentifier = data->url().toString();
1184 QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1);
1186 typeLoader()->setQmldirContent(qmldirIdentifier, data->content());
1188 if (!m_imports.updateQmldirContent(typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors))
1191 QHash<const QQmlScript::Import *, int>::iterator it = m_unresolvedImports.find(import);
1192 if (it != m_unresolvedImports.end()) {
1193 *it = data->priority();
1196 // Release this reference at destruction
1199 if (!import->qualifier.isEmpty()) {
1200 // Does this library contain any qualified scripts?
1201 QUrl libraryUrl(qmldirUrl);
1202 const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier, qmldirUrl);
1203 foreach (const QQmlDirParser::Script &script, qmldir->scripts()) {
1204 QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
1205 QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1206 addDependency(blob);
1208 scriptImported(blob, import->location.start, script.nameSpace, import->qualifier);
1215 bool QQmlTypeLoader::Blob::addImport(const QQmlScript::Import &import, QList<QQmlError> *errors)
1219 QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
1221 if (import.type == QQmlScript::Import::Script) {
1222 QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1223 QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1224 addDependency(blob);
1226 scriptImported(blob, import.location.start, import.qualifier, QString());
1227 } else if (import.type == QQmlScript::Import::Library) {
1228 QString qmldirFilePath;
1231 if (m_imports.locateQmldir(importDatabase, import.uri, import.majorVersion, import.minorVersion,
1232 &qmldirFilePath, &qmldirUrl)) {
1233 // This is a local library import
1234 if (!m_imports.addLibraryImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1235 import.minorVersion, qmldirFilePath, qmldirUrl, false, errors))
1238 if (!import.qualifier.isEmpty()) {
1239 // Does this library contain any qualified scripts?
1240 QUrl libraryUrl(qmldirUrl);
1241 const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath, qmldirUrl);
1242 foreach (const QQmlDirParser::Script &script, qmldir->scripts()) {
1243 QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
1244 QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1245 addDependency(blob);
1247 scriptImported(blob, import.location.start, script.nameSpace, import.qualifier);
1251 // Is this a module?
1252 if (QQmlMetaType::isAnyModule(import.uri)) {
1253 if (!m_imports.addLibraryImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1254 import.minorVersion, QString(), QString(), false, errors))
1257 // We haven't yet resolved this import
1258 m_unresolvedImports.insert(&import, 0);
1260 // Query any network import paths for this library
1261 QStringList remotePathList = importDatabase->importPathList(QQmlImportDatabase::Remote);
1262 if (!remotePathList.isEmpty()) {
1263 // Add this library and request the possible locations for it
1264 if (!m_imports.addLibraryImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1265 import.minorVersion, QString(), QString(), true, errors))
1268 // Probe for all possible locations
1270 for (int version = QQmlImports::FullyVersioned; version <= QQmlImports::Unversioned; ++version) {
1271 foreach (const QString &path, remotePathList) {
1272 QString qmldirUrl = QQmlImports::completeQmldirPath(import.uri, path, import.majorVersion, import.minorVersion,
1273 static_cast<QQmlImports::ImportVersion>(version));
1274 if (!fetchQmldir(QUrl(qmldirUrl), &import, ++priority, errors))
1282 Q_ASSERT(import.type == QQmlScript::Import::File);
1284 bool incomplete = false;
1287 if (import.qualifier.isEmpty()) {
1288 qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1289 if (!QQmlImports::isLocal(qmldirUrl)) {
1290 // This is a remote file; the import is currently incomplete
1295 if (!m_imports.addFileImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1296 import.minorVersion, incomplete, errors))
1300 if (!fetchQmldir(qmldirUrl, &import, 1, errors))
1308 void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob)
1310 if (blob->type() == QQmlDataBlob::QmldirFile) {
1311 QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
1316 void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
1318 if (blob->type() == QQmlDataBlob::QmldirFile) {
1319 QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
1321 const QQmlScript::Import *import = data->import();
1323 QList<QQmlError> errors;
1324 if (!qmldirDataAvailable(data, &errors)) {
1325 Q_ASSERT(errors.size());
1326 QQmlError error(errors.takeFirst());
1327 error.setUrl(m_imports.baseUrl());
1328 error.setLine(import->location.start.line);
1329 error.setColumn(import->location.start.column);
1330 errors.prepend(error); // put it back on the list after filling out information.
1336 bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors)
1338 bool resolve = true;
1340 const QQmlScript::Import *import = data->import();
1343 int priority = data->priority();
1344 data->setPriority(0);
1347 // Do we need to resolve this import?
1348 QHash<const QQmlScript::Import *, int>::iterator it = m_unresolvedImports.find(import);
1349 if (it != m_unresolvedImports.end()) {
1350 resolve = (*it == 0) || (*it > priority);
1354 // This is the (current) best resolution for this import
1355 if (!updateQmldir(data, import, errors)) {
1370 QQmlTypeLoader::QmldirContent::QmldirContent()
1374 bool QQmlTypeLoader::QmldirContent::hasError() const
1376 return m_parser.hasError();
1379 QList<QQmlError> QQmlTypeLoader::QmldirContent::errors(const QString &uri) const
1381 return m_parser.errors(uri);
1384 QString QQmlTypeLoader::QmldirContent::typeNamespace() const
1386 return m_parser.typeNamespace();
1389 void QQmlTypeLoader::QmldirContent::setContent(const QString &location, const QString &content)
1391 m_location = location;
1392 m_parser.parse(content);
1395 void QQmlTypeLoader::QmldirContent::setError(const QQmlError &error)
1397 m_parser.setError(error);
1400 QQmlDirComponents QQmlTypeLoader::QmldirContent::components() const
1402 return m_parser.components();
1405 QQmlDirScripts QQmlTypeLoader::QmldirContent::scripts() const
1407 return m_parser.scripts();
1410 QQmlDirPlugins QQmlTypeLoader::QmldirContent::plugins() const
1412 return m_parser.plugins();
1415 QString QQmlTypeLoader::QmldirContent::pluginLocation() const
1422 Constructs a new type loader that uses the given \a engine.
1424 QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine)
1425 : QQmlDataLoader(engine)
1430 Destroys the type loader, first clearing the cache of any information about
1433 QQmlTypeLoader::~QQmlTypeLoader()
1435 // Stop the loader thread before releasing resources
1441 QQmlImportDatabase *QQmlTypeLoader::importDatabase()
1443 return &QQmlEnginePrivate::get(engine())->importDatabase;
1447 \enum QQmlTypeLoader::Option
1449 This enum defines the options that control the way type data is handled.
1451 \value None The default value, indicating that no other options
1453 \value PreserveParser The parser used to handle the type data is preserved
1454 after the data has been parsed.
1458 Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached.
1460 QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode)
1462 Q_ASSERT(!url.isRelative() &&
1463 (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
1464 !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
1466 LockHolder<QQmlTypeLoader> holder(this);
1468 QQmlTypeData *typeData = m_typeCache.value(url);
1471 typeData = new QQmlTypeData(url, None, this);
1472 // TODO: if (compiledData == 0), is it safe to omit this insertion?
1473 m_typeCache.insert(url, typeData);
1474 QQmlDataLoader::load(typeData, mode);
1483 Returns a QQmlTypeData for the given \a data with the provided base \a url. The
1484 QQmlTypeData will not be cached.
1486 The specified \a options control how the loader handles type data.
1488 QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Options options)
1490 LockHolder<QQmlTypeLoader> holder(this);
1492 QQmlTypeData *typeData = new QQmlTypeData(url, options, this);
1493 QQmlDataLoader::loadWithStaticData(typeData, data);
1499 Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
1501 QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url)
1503 Q_ASSERT(!url.isRelative() &&
1504 (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
1505 !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
1507 LockHolder<QQmlTypeLoader> holder(this);
1509 QQmlScriptBlob *scriptBlob = m_scriptCache.value(url);
1512 scriptBlob = new QQmlScriptBlob(url, this);
1513 m_scriptCache.insert(url, scriptBlob);
1514 QQmlDataLoader::load(scriptBlob);
1517 scriptBlob->addref();
1523 Returns a QQmlQmldirData for \a url. The QQmlQmldirData may be cached.
1525 QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
1527 Q_ASSERT(!url.isRelative() &&
1528 (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
1529 !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
1531 LockHolder<QQmlTypeLoader> holder(this);
1533 QQmlQmldirData *qmldirData = m_qmldirCache.value(url);
1536 qmldirData = new QQmlQmldirData(url, this);
1537 m_qmldirCache.insert(url, qmldirData);
1538 QQmlDataLoader::load(qmldirData);
1541 qmldirData->addref();
1547 Returns a QQmlBundleData for \a identifier.
1549 QQmlBundleData *QQmlTypeLoader::getBundle(const QString &identifier)
1551 return getBundle(QHashedStringRef(identifier));
1554 QQmlBundleData *QQmlTypeLoader::getBundle(const QHashedStringRef &identifier)
1558 QQmlBundleData *rv = 0;
1559 QQmlBundleData **bundle = m_bundleCache.value(identifier);
1570 QQmlBundleData::QQmlBundleData(const QString &file)
1571 : QQmlBundle(file), fileName(file)
1575 // XXX check for errors etc.
1576 void QQmlTypeLoader::addBundle(const QString &identifier, const QString &fileName)
1579 addBundleNoLock(identifier, fileName);
1583 void QQmlTypeLoader::addBundleNoLock(const QString &identifier, const QString &fileName)
1585 QQmlBundleData *data = new QQmlBundleData(fileName);
1588 m_bundleCache.insert(identifier, data);
1595 QString QQmlTypeLoader::bundleIdForQmldir(const QString &name, const QString &uriHint)
1598 QString *bundleId = m_qmldirBundleIdCache.value(name);
1600 QString newBundleId = QLatin1String("qml.") + uriHint.toLower() /* XXX toLower()? */;
1601 if (m_qmldirBundleIdCache.contains(newBundleId))
1602 newBundleId += QString::number(m_qmldirBundleIdCache.count());
1603 m_qmldirBundleIdCache.insert(name, newBundleId);
1604 addBundleNoLock(newBundleId, name);
1613 bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName)
1617 if (name.startsWith(QLatin1String("qml."))) // reserved
1620 d->typeLoader.addBundle(name, fileName);
1625 Returns the absolute filename of path via a directory cache.
1626 Returns a empty string if the path does not exist.
1628 Why a directory cache? QML checks for files in many paths with
1629 invalid directories. By caching whether a directory exists
1630 we avoid many stats. We also cache the files' existance in the
1631 directory, for the same reason.
1633 QString QQmlTypeLoader::absoluteFilePath(const QString &path)
1637 if (path.at(0) == QLatin1Char(':')) {
1639 QFileInfo fileInfo(path);
1640 return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
1641 } else if (path.count() > 3 && path.at(3) == QLatin1Char(':') &&
1642 path.startsWith(QLatin1String("qrc"), Qt::CaseInsensitive)) {
1644 QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path));
1645 return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
1647 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1648 QStringRef dirPath(&path, 0, lastSlash);
1650 StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1652 QHashedString dirPathString(dirPath.toString());
1653 bool exists = false;
1655 struct stat statBuf;
1656 if (::stat(QFile::encodeName(dirPathString).constData(), &statBuf) == 0)
1657 exists = S_ISDIR(statBuf.st_mode);
1659 exists = QDir(dirPathString).exists();
1661 QStringHash<bool> *files = exists ? new QStringHash<bool> : 0;
1662 m_importDirCache.insert(dirPathString, files);
1663 fileSet = m_importDirCache.value(dirPathString);
1668 QString absoluteFilePath;
1669 QHashedStringRef fileName(path.constData()+lastSlash+1, path.length()-lastSlash-1);
1671 bool *value = (*fileSet)->value(fileName);
1674 absoluteFilePath = path;
1676 bool exists = false;
1678 struct stat statBuf;
1679 // XXX Avoid encoding entire path. Should store encoded dirpath in cache
1680 if (::stat(QFile::encodeName(path).constData(), &statBuf) == 0)
1681 exists = S_ISREG(statBuf.st_mode);
1683 exists = QFile::exists(path);
1685 (*fileSet)->insert(fileName.toString(), exists);
1687 absoluteFilePath = path;
1690 if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':'))
1691 absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath();
1693 return absoluteFilePath;
1698 Returns true if the path is a directory via a directory cache. Cache is
1699 shared with absoluteFilePath().
1701 bool QQmlTypeLoader::directoryExists(const QString &path)
1705 if (path.at(0) == QLatin1Char(':')) {
1707 QFileInfo fileInfo(path);
1708 return fileInfo.exists() && fileInfo.isDir();
1711 int length = path.length();
1712 if (path.endsWith(QLatin1Char('/')))
1714 QStringRef dirPath(&path, 0, length);
1716 StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1718 QHashedString dirPathString(dirPath.toString());
1719 bool exists = false;
1721 struct stat statBuf;
1722 if (::stat(QFile::encodeName(dirPathString).constData(), &statBuf) == 0)
1723 exists = S_ISDIR(statBuf.st_mode);
1725 exists = QDir(dirPathString).exists();
1727 QStringHash<bool> *files = exists ? new QStringHash<bool> : 0;
1728 m_importDirCache.insert(dirPathString, files);
1729 fileSet = m_importDirCache.value(dirPathString);
1737 Return a QmldirContent for absoluteFilePath. The QmldirContent may be cached.
1739 \a filePath is either a bundle URL, or a local file path.
1741 const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePath, const QString &uriHint)
1743 QmldirContent *qmldir;
1744 QmldirContent **val = m_importQmlDirCache.value(filePath);
1746 qmldir = new QmldirContent;
1748 #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); }
1749 #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable"))
1750 #define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\""))
1752 if (QQmlFile::isBundle(filePath)) {
1756 QQmlFile file(engine(), url);
1757 if (file.isError()) {
1758 ERROR(NOT_READABLE_ERROR.arg(filePath));
1760 QString content(QString::fromUtf8(file.data(), file.size()));
1761 qmldir->setContent(filePath, content);
1766 QFile file(filePath);
1767 if (!QQml_isFileCaseCorrect(filePath)) {
1768 ERROR(CASE_MISMATCH_ERROR.arg(filePath));
1769 } else if (file.open(QFile::ReadOnly)) {
1770 QByteArray data = file.read(QQmlBundle::bundleHeaderLength());
1772 if (QQmlBundle::isBundleHeader(data.constData(), data.length())) {
1773 QString id = bundleIdForQmldir(filePath, uriHint);
1775 QString bundleUrl = QLatin1String("bundle://") + id + QLatin1Char('/');
1777 QUrl url(bundleUrl + QLatin1String("qmldir"));
1779 QQmlFile file(engine(), url);
1780 if (file.isError()) {
1781 ERROR(NOT_READABLE_ERROR.arg(filePath));
1783 QString content(QString::fromUtf8(file.data(), file.size()));
1784 qmldir->setContent(QQmlFile::bundleFileName(bundleUrl, engine()), content);
1787 data += file.readAll();
1788 qmldir->setContent(filePath, QString::fromUtf8(data));
1791 ERROR(NOT_READABLE_ERROR.arg(filePath));
1797 #undef NOT_READABLE_ERROR
1798 #undef CASE_MISMATCH_ERROR
1800 m_importQmlDirCache.insert(filePath, qmldir);
1808 void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content)
1810 QmldirContent *qmldir;
1811 QmldirContent **val = m_importQmlDirCache.value(url);
1815 qmldir = new QmldirContent;
1816 m_importQmlDirCache.insert(url, qmldir);
1819 qmldir->setContent(url, content);
1823 Clears cached information about loaded files, including any type data, scripts
1824 and qmldir information.
1826 void QQmlTypeLoader::clearCache()
1828 for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter)
1830 for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter)
1832 for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter)
1834 qDeleteAll(m_importDirCache);
1835 qDeleteAll(m_importQmlDirCache);
1837 m_typeCache.clear();
1838 m_scriptCache.clear();
1839 m_qmldirCache.clear();
1840 m_importDirCache.clear();
1841 m_importQmlDirCache.clear();
1844 void QQmlTypeLoader::trimCache()
1847 QList<TypeCache::Iterator> unneededTypes;
1848 for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter) {
1849 QQmlTypeData *typeData = iter.value();
1850 if (typeData->m_compiledData && typeData->m_compiledData->count() == 1) {
1851 // There are no live objects of this type
1852 unneededTypes.append(iter);
1856 if (unneededTypes.isEmpty())
1859 while (!unneededTypes.isEmpty()) {
1860 TypeCache::Iterator iter = unneededTypes.last();
1861 unneededTypes.removeLast();
1863 iter.value()->release();
1864 m_typeCache.erase(iter);
1868 // TODO: release any scripts which are no longer referenced by any types
1871 bool QQmlTypeLoader::isTypeLoaded(const QUrl &url) const
1873 LockHolder<QQmlTypeLoader> holder(const_cast<QQmlTypeLoader *>(this));
1874 return m_typeCache.contains(url);
1877 bool QQmlTypeLoader::isScriptLoaded(const QUrl &url) const
1879 LockHolder<QQmlTypeLoader> holder(const_cast<QQmlTypeLoader *>(this));
1880 return m_scriptCache.contains(url);
1884 QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options,
1885 QQmlTypeLoader *manager)
1886 : QQmlTypeLoader::Blob(url, QmlFile, manager), m_options(options),
1887 m_typesResolved(false), m_compiledData(0), m_implicitImport(0)
1891 QQmlTypeData::~QQmlTypeData()
1893 for (int ii = 0; ii < m_scripts.count(); ++ii)
1894 m_scripts.at(ii).script->release();
1895 for (int ii = 0; ii < m_types.count(); ++ii)
1896 if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
1898 m_compiledData->release();
1899 delete m_implicitImport;
1902 const QQmlScript::Parser &QQmlTypeData::parser() const
1904 return scriptParser;
1907 const QList<QQmlTypeData::TypeReference> &QQmlTypeData::resolvedTypes() const
1912 const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
1917 const QSet<QString> &QQmlTypeData::namespaces() const
1919 return m_namespaces;
1922 QQmlCompiledData *QQmlTypeData::compiledData() const
1924 return m_compiledData;
1927 void QQmlTypeData::registerCallback(TypeDataCallback *callback)
1929 Q_ASSERT(!m_callbacks.contains(callback));
1930 m_callbacks.append(callback);
1933 void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
1935 Q_ASSERT(m_callbacks.contains(callback));
1936 m_callbacks.removeOne(callback);
1937 Q_ASSERT(!m_callbacks.contains(callback));
1940 void QQmlTypeData::done()
1942 // Check all script dependencies for errors
1943 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1944 const ScriptReference &script = m_scripts.at(ii);
1945 Q_ASSERT(script.script->isCompleteOrError());
1946 if (script.script->isError()) {
1947 QList<QQmlError> errors = script.script->errors();
1949 error.setUrl(finalUrl());
1950 error.setLine(script.location.line);
1951 error.setColumn(script.location.column);
1952 error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
1953 errors.prepend(error);
1958 // Check all type dependencies for errors
1959 for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
1960 const TypeReference &type = m_types.at(ii);
1961 Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
1962 if (type.typeData && type.typeData->isError()) {
1963 QString typeName = scriptParser.referencedTypes().at(ii)->name;
1965 QList<QQmlError> errors = type.typeData->errors();
1967 error.setUrl(finalUrl());
1968 error.setLine(type.location.line);
1969 error.setColumn(type.location.column);
1970 error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
1971 errors.prepend(error);
1976 // Compile component
1980 if (!(m_options & QQmlTypeLoader::PreserveParser))
1981 scriptParser.clear();
1984 void QQmlTypeData::completed()
1987 while (!m_callbacks.isEmpty()) {
1988 TypeDataCallback *callback = m_callbacks.takeFirst();
1989 callback->typeDataReady(this);
1993 void QQmlTypeData::dataReceived(const Data &data)
1995 QString code = QString::fromUtf8(data.data(), data.size());
1996 QByteArray preparseData;
1998 if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
2000 if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) {
2001 setError(scriptParser.errors());
2005 m_imports.setBaseUrl(finalUrl(), finalUrlString());
2007 QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
2009 // For local urls, add an implicit import "." as first (most overridden) lookup.
2010 // This will also trigger the loading of the qmldir and the import of any native
2011 // types from available plugins.
2012 QList<QQmlError> implicitImportErrors;
2013 m_imports.addImplicitImport(importDatabase, &implicitImportErrors);
2015 if (!implicitImportErrors.isEmpty()) {
2016 setError(implicitImportErrors);
2020 QList<QQmlError> errors;
2022 if (!finalUrl().scheme().isEmpty()) {
2023 QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
2024 if (!QQmlImports::isLocal(qmldirUrl)) {
2025 // This qmldir is for the implicit import
2026 m_implicitImport = new QQmlScript::Import;
2027 m_implicitImport->uri = QLatin1String(".");
2028 m_implicitImport->qualifier = QString();
2029 m_implicitImport->majorVersion = -1;
2030 m_implicitImport->minorVersion = -1;
2032 if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) {
2039 foreach (const QQmlScript::Import &import, scriptParser.imports()) {
2040 if (!addImport(import, &errors)) {
2041 Q_ASSERT(errors.size());
2042 QQmlError error(errors.takeFirst());
2043 error.setUrl(m_imports.baseUrl());
2044 error.setLine(import.location.start.line);
2045 error.setColumn(import.location.start.column);
2046 errors.prepend(error); // put it back on the list after filling out information.
2053 void QQmlTypeData::allDependenciesDone()
2055 if (!m_typesResolved) {
2056 // Check that all imports were resolved
2057 QList<QQmlError> errors;
2058 QHash<const QQmlScript::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
2059 for ( ; it != end; ++it) {
2061 // This import was not resolved
2062 foreach (const QQmlScript::Import *import, m_unresolvedImports.keys()) {
2064 error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
2065 error.setUrl(m_imports.baseUrl());
2066 error.setLine(import->location.start.line);
2067 error.setColumn(import->location.start.column);
2068 errors.prepend(error);
2072 if (errors.size()) {
2078 m_typesResolved = true;
2082 void QQmlTypeData::downloadProgressChanged(qreal p)
2084 for (int ii = 0; ii < m_callbacks.count(); ++ii) {
2085 TypeDataCallback *callback = m_callbacks.at(ii);
2086 callback->typeDataProgress(this, p);
2090 void QQmlTypeData::compile()
2092 Q_ASSERT(m_compiledData == 0);
2094 m_compiledData = new QQmlCompiledData(typeLoader()->engine());
2095 m_compiledData->url = finalUrl();
2096 m_compiledData->name = finalUrlString();
2098 QQmlCompilingProfiler prof(m_compiledData->name);
2100 QQmlCompiler compiler(&scriptParser._pool);
2101 if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
2102 setError(compiler.errors());
2103 m_compiledData->release();
2108 void QQmlTypeData::resolveTypes()
2110 // Add any imported scripts to our resolved set
2111 foreach (const QQmlImports::ScriptReference &script, m_imports.resolvedScripts())
2113 QQmlScriptBlob *blob = typeLoader()->getScript(script.location);
2114 addDependency(blob);
2116 ScriptReference ref;
2117 //ref.location = ...
2118 ref.qualifier = script.nameSpace;
2119 if (!script.qualifier.isEmpty())
2121 ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
2123 // Add a reference to the enclosing namespace
2124 m_namespaces.insert(script.qualifier);
2131 foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
2137 QQmlImportNamespace *typeNamespace = 0;
2138 QList<QQmlError> errors;
2140 if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
2141 &typeNamespace, &errors) || typeNamespace) {
2142 // Known to not be a type:
2143 // - known to be a namespace (Namespace {})
2144 // - type with unknown namespace (UnknownNamespace.SomeType {})
2146 if (typeNamespace) {
2147 error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(parserRef->name));
2149 if (errors.size()) {
2150 error = errors.takeFirst();
2152 // this should not be possible!
2153 // Description should come from error provided by addImport() function.
2154 error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
2156 error.setUrl(m_imports.baseUrl());
2157 error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(parserRef->name).arg(error.description()));
2160 Q_ASSERT(parserRef->firstUse);
2161 error.setLine(parserRef->firstUse->location.start.line);
2162 error.setColumn(parserRef->firstUse->location.start.column);
2164 errors.prepend(error);
2170 ref.majorVersion = majorVersion;
2171 ref.minorVersion = minorVersion;
2173 ref.typeData = typeLoader()->getType(QUrl(url));
2174 addDependency(ref.typeData);
2177 Q_ASSERT(parserRef->firstUse);
2178 ref.location = parserRef->firstUse->location.start;
2184 void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
2186 ScriptReference ref;
2188 ref.location = location;
2189 ref.qualifier = qualifier;
2194 QQmlScriptData::QQmlScriptData()
2195 : importCache(0), pragmas(QQmlScript::Object::ScriptBlock::None), m_loaded(false)
2199 QQmlScriptData::~QQmlScriptData()
2203 void QQmlScriptData::clear()
2206 importCache->release();
2210 for (int ii = 0; ii < scripts.count(); ++ii)
2211 scripts.at(ii)->release();
2214 qPersistentDispose(m_program);
2215 qPersistentDispose(m_value);
2217 // An addref() was made when the QQmlCleanup was added to the engine.
2221 QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
2222 : QQmlTypeLoader::Blob(url, JavaScriptFile, loader), m_scriptData(0)
2226 QQmlScriptBlob::~QQmlScriptBlob()
2229 m_scriptData->release();
2234 QQmlScript::Object::ScriptBlock::Pragmas QQmlScriptBlob::pragmas() const
2236 return m_metadata.pragmas;
2239 QQmlScriptData *QQmlScriptBlob::scriptData() const
2241 return m_scriptData;
2244 void QQmlScriptBlob::dataReceived(const Data &data)
2246 m_source = QString::fromUtf8(data.data(), data.size());
2248 m_scriptData = new QQmlScriptData();
2249 m_scriptData->url = finalUrl();
2250 m_scriptData->urlString = finalUrlString();
2252 QQmlError metaDataError;
2253 m_metadata = QQmlScript::Parser::extractMetaData(m_source, &metaDataError);
2254 if (metaDataError.isValid()) {
2255 metaDataError.setUrl(finalUrl());
2256 m_scriptData->setError(metaDataError);
2259 m_imports.setBaseUrl(finalUrl(), finalUrlString());
2261 QList<QQmlError> errors;
2263 foreach (const QQmlScript::Import &import, m_metadata.imports) {
2264 if (!addImport(import, &errors)) {
2265 Q_ASSERT(errors.size());
2266 QQmlError error(errors.takeFirst());
2267 error.setUrl(m_imports.baseUrl());
2268 error.setLine(import.location.start.line);
2269 error.setColumn(import.location.start.column);
2270 errors.prepend(error); // put it back on the list after filling out information.
2277 void QQmlScriptBlob::done()
2279 // Check all script dependencies for errors
2280 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
2281 const ScriptReference &script = m_scripts.at(ii);
2282 Q_ASSERT(script.script->isCompleteOrError());
2283 if (script.script->isError()) {
2284 QList<QQmlError> errors = script.script->errors();
2286 error.setUrl(finalUrl());
2287 error.setLine(script.location.line);
2288 error.setColumn(script.location.column);
2289 error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
2290 errors.prepend(error);
2298 m_scriptData->importCache = new QQmlTypeNameCache();
2302 for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) {
2303 const ScriptReference &script = m_scripts.at(scriptIndex);
2305 m_scriptData->scripts.append(script.script);
2307 if (!script.nameSpace.isNull()) {
2308 if (!ns.contains(script.nameSpace)) {
2309 ns.insert(script.nameSpace);
2310 m_scriptData->importCache->add(script.nameSpace);
2313 m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace);
2316 m_imports.populateCache(m_scriptData->importCache);
2318 m_scriptData->pragmas = m_metadata.pragmas;
2319 m_scriptData->m_programSource = m_source.toUtf8();
2323 void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace)
2325 ScriptReference ref;
2327 ref.location = location;
2328 ref.qualifier = qualifier;
2329 ref.nameSpace = nameSpace;
2334 QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
2335 : QQmlTypeLoader::Blob(url, QmldirFile, loader), m_import(0), m_priority(0)
2339 const QString &QQmlQmldirData::content() const
2344 const QQmlScript::Import *QQmlQmldirData::import() const
2349 void QQmlQmldirData::setImport(const QQmlScript::Import *import)
2354 int QQmlQmldirData::priority() const
2359 void QQmlQmldirData::setPriority(int priority)
2361 m_priority = priority;
2364 void QQmlQmldirData::dataReceived(const Data &data)
2366 m_content = QString::fromUtf8(data.data(), data.size());
2371 #include "qqmltypeloader.moc"