1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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' existence 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);
1883 QQmlTypeData::TypeDataCallback::~TypeDataCallback()
1887 QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options,
1888 QQmlTypeLoader *manager)
1889 : QQmlTypeLoader::Blob(url, QmlFile, manager), m_options(options),
1890 m_typesResolved(false), m_compiledData(0), m_implicitImport(0)
1894 QQmlTypeData::~QQmlTypeData()
1896 for (int ii = 0; ii < m_scripts.count(); ++ii)
1897 m_scripts.at(ii).script->release();
1898 for (int ii = 0; ii < m_types.count(); ++ii)
1899 if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
1901 m_compiledData->release();
1902 delete m_implicitImport;
1905 const QQmlScript::Parser &QQmlTypeData::parser() const
1907 return scriptParser;
1910 const QList<QQmlTypeData::TypeReference> &QQmlTypeData::resolvedTypes() const
1915 const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
1920 const QSet<QString> &QQmlTypeData::namespaces() const
1922 return m_namespaces;
1925 QQmlCompiledData *QQmlTypeData::compiledData() const
1927 return m_compiledData;
1930 void QQmlTypeData::registerCallback(TypeDataCallback *callback)
1932 Q_ASSERT(!m_callbacks.contains(callback));
1933 m_callbacks.append(callback);
1936 void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
1938 Q_ASSERT(m_callbacks.contains(callback));
1939 m_callbacks.removeOne(callback);
1940 Q_ASSERT(!m_callbacks.contains(callback));
1943 void QQmlTypeData::done()
1945 // Check all script dependencies for errors
1946 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1947 const ScriptReference &script = m_scripts.at(ii);
1948 Q_ASSERT(script.script->isCompleteOrError());
1949 if (script.script->isError()) {
1950 QList<QQmlError> errors = script.script->errors();
1952 error.setUrl(finalUrl());
1953 error.setLine(script.location.line);
1954 error.setColumn(script.location.column);
1955 error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
1956 errors.prepend(error);
1961 // Check all type dependencies for errors
1962 for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
1963 const TypeReference &type = m_types.at(ii);
1964 Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
1965 if (type.typeData && type.typeData->isError()) {
1966 QString typeName = scriptParser.referencedTypes().at(ii)->name;
1968 QList<QQmlError> errors = type.typeData->errors();
1970 error.setUrl(finalUrl());
1971 error.setLine(type.location.line);
1972 error.setColumn(type.location.column);
1973 error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
1974 errors.prepend(error);
1979 // Compile component
1983 if (!(m_options & QQmlTypeLoader::PreserveParser))
1984 scriptParser.clear();
1987 void QQmlTypeData::completed()
1990 while (!m_callbacks.isEmpty()) {
1991 TypeDataCallback *callback = m_callbacks.takeFirst();
1992 callback->typeDataReady(this);
1996 void QQmlTypeData::dataReceived(const Data &data)
1998 QString code = QString::fromUtf8(data.data(), data.size());
1999 QByteArray preparseData;
2001 if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
2003 if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) {
2004 setError(scriptParser.errors());
2008 m_imports.setBaseUrl(finalUrl(), finalUrlString());
2010 QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
2012 // For local urls, add an implicit import "." as first (most overridden) lookup.
2013 // This will also trigger the loading of the qmldir and the import of any native
2014 // types from available plugins.
2015 QList<QQmlError> implicitImportErrors;
2016 m_imports.addImplicitImport(importDatabase, &implicitImportErrors);
2018 if (!implicitImportErrors.isEmpty()) {
2019 setError(implicitImportErrors);
2023 QList<QQmlError> errors;
2025 if (!finalUrl().scheme().isEmpty()) {
2026 QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
2027 if (!QQmlImports::isLocal(qmldirUrl)) {
2028 // This qmldir is for the implicit import
2029 m_implicitImport = new QQmlScript::Import;
2030 m_implicitImport->uri = QLatin1String(".");
2031 m_implicitImport->qualifier = QString();
2032 m_implicitImport->majorVersion = -1;
2033 m_implicitImport->minorVersion = -1;
2035 if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) {
2042 foreach (const QQmlScript::Import &import, scriptParser.imports()) {
2043 if (!addImport(import, &errors)) {
2044 Q_ASSERT(errors.size());
2045 QQmlError error(errors.takeFirst());
2046 error.setUrl(m_imports.baseUrl());
2047 error.setLine(import.location.start.line);
2048 error.setColumn(import.location.start.column);
2049 errors.prepend(error); // put it back on the list after filling out information.
2056 void QQmlTypeData::allDependenciesDone()
2058 if (!m_typesResolved) {
2059 // Check that all imports were resolved
2060 QList<QQmlError> errors;
2061 QHash<const QQmlScript::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
2062 for ( ; it != end; ++it) {
2064 // This import was not resolved
2065 foreach (const QQmlScript::Import *import, m_unresolvedImports.keys()) {
2067 error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
2068 error.setUrl(m_imports.baseUrl());
2069 error.setLine(import->location.start.line);
2070 error.setColumn(import->location.start.column);
2071 errors.prepend(error);
2075 if (errors.size()) {
2081 m_typesResolved = true;
2085 void QQmlTypeData::downloadProgressChanged(qreal p)
2087 for (int ii = 0; ii < m_callbacks.count(); ++ii) {
2088 TypeDataCallback *callback = m_callbacks.at(ii);
2089 callback->typeDataProgress(this, p);
2093 void QQmlTypeData::compile()
2095 Q_ASSERT(m_compiledData == 0);
2097 m_compiledData = new QQmlCompiledData(typeLoader()->engine());
2098 m_compiledData->url = finalUrl();
2099 m_compiledData->name = finalUrlString();
2101 QQmlCompilingProfiler prof(m_compiledData->name);
2103 QQmlCompiler compiler(&scriptParser._pool);
2104 if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
2105 setError(compiler.errors());
2106 m_compiledData->release();
2111 void QQmlTypeData::resolveTypes()
2113 // Add any imported scripts to our resolved set
2114 foreach (const QQmlImports::ScriptReference &script, m_imports.resolvedScripts())
2116 QQmlScriptBlob *blob = typeLoader()->getScript(script.location);
2117 addDependency(blob);
2119 ScriptReference ref;
2120 //ref.location = ...
2121 ref.qualifier = script.nameSpace;
2122 if (!script.qualifier.isEmpty())
2124 ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
2126 // Add a reference to the enclosing namespace
2127 m_namespaces.insert(script.qualifier);
2134 foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
2140 QQmlImportNamespace *typeNamespace = 0;
2141 QList<QQmlError> errors;
2143 if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
2144 &typeNamespace, &errors) || typeNamespace) {
2145 // Known to not be a type:
2146 // - known to be a namespace (Namespace {})
2147 // - type with unknown namespace (UnknownNamespace.SomeType {})
2149 if (typeNamespace) {
2150 error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(parserRef->name));
2152 if (errors.size()) {
2153 error = errors.takeFirst();
2155 // this should not be possible!
2156 // Description should come from error provided by addImport() function.
2157 error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
2159 error.setUrl(m_imports.baseUrl());
2160 error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(parserRef->name).arg(error.description()));
2163 Q_ASSERT(parserRef->firstUse);
2164 error.setLine(parserRef->firstUse->location.start.line);
2165 error.setColumn(parserRef->firstUse->location.start.column);
2167 errors.prepend(error);
2173 ref.majorVersion = majorVersion;
2174 ref.minorVersion = minorVersion;
2176 ref.typeData = typeLoader()->getType(QUrl(url));
2177 addDependency(ref.typeData);
2180 Q_ASSERT(parserRef->firstUse);
2181 ref.location = parserRef->firstUse->location.start;
2187 void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
2189 ScriptReference ref;
2191 ref.location = location;
2192 ref.qualifier = qualifier;
2197 QQmlScriptData::QQmlScriptData()
2198 : importCache(0), pragmas(QQmlScript::Object::ScriptBlock::None), m_loaded(false)
2202 QQmlScriptData::~QQmlScriptData()
2206 void QQmlScriptData::clear()
2209 importCache->release();
2213 for (int ii = 0; ii < scripts.count(); ++ii)
2214 scripts.at(ii)->release();
2217 qPersistentDispose(m_program);
2218 qPersistentDispose(m_value);
2220 // An addref() was made when the QQmlCleanup was added to the engine.
2224 QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
2225 : QQmlTypeLoader::Blob(url, JavaScriptFile, loader), m_scriptData(0)
2229 QQmlScriptBlob::~QQmlScriptBlob()
2232 m_scriptData->release();
2237 QQmlScript::Object::ScriptBlock::Pragmas QQmlScriptBlob::pragmas() const
2239 return m_metadata.pragmas;
2242 QQmlScriptData *QQmlScriptBlob::scriptData() const
2244 return m_scriptData;
2247 void QQmlScriptBlob::dataReceived(const Data &data)
2249 m_source = QString::fromUtf8(data.data(), data.size());
2251 m_scriptData = new QQmlScriptData();
2252 m_scriptData->url = finalUrl();
2253 m_scriptData->urlString = finalUrlString();
2255 QQmlError metaDataError;
2256 m_metadata = QQmlScript::Parser::extractMetaData(m_source, &metaDataError);
2257 if (metaDataError.isValid()) {
2258 metaDataError.setUrl(finalUrl());
2259 m_scriptData->setError(metaDataError);
2262 m_imports.setBaseUrl(finalUrl(), finalUrlString());
2264 QList<QQmlError> errors;
2266 foreach (const QQmlScript::Import &import, m_metadata.imports) {
2267 if (!addImport(import, &errors)) {
2268 Q_ASSERT(errors.size());
2269 QQmlError error(errors.takeFirst());
2270 error.setUrl(m_imports.baseUrl());
2271 error.setLine(import.location.start.line);
2272 error.setColumn(import.location.start.column);
2273 errors.prepend(error); // put it back on the list after filling out information.
2280 void QQmlScriptBlob::done()
2282 // Check all script dependencies for errors
2283 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
2284 const ScriptReference &script = m_scripts.at(ii);
2285 Q_ASSERT(script.script->isCompleteOrError());
2286 if (script.script->isError()) {
2287 QList<QQmlError> errors = script.script->errors();
2289 error.setUrl(finalUrl());
2290 error.setLine(script.location.line);
2291 error.setColumn(script.location.column);
2292 error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
2293 errors.prepend(error);
2301 m_scriptData->importCache = new QQmlTypeNameCache();
2305 for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) {
2306 const ScriptReference &script = m_scripts.at(scriptIndex);
2308 m_scriptData->scripts.append(script.script);
2310 if (!script.nameSpace.isNull()) {
2311 if (!ns.contains(script.nameSpace)) {
2312 ns.insert(script.nameSpace);
2313 m_scriptData->importCache->add(script.nameSpace);
2316 m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace);
2319 m_imports.populateCache(m_scriptData->importCache);
2321 m_scriptData->pragmas = m_metadata.pragmas;
2322 m_scriptData->m_programSource = m_source.toUtf8();
2326 void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace)
2328 ScriptReference ref;
2330 ref.location = location;
2331 ref.qualifier = qualifier;
2332 ref.nameSpace = nameSpace;
2337 QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
2338 : QQmlTypeLoader::Blob(url, QmldirFile, loader), m_import(0), m_priority(0)
2342 const QString &QQmlQmldirData::content() const
2347 const QQmlScript::Import *QQmlQmldirData::import() const
2352 void QQmlQmldirData::setImport(const QQmlScript::Import *import)
2357 int QQmlQmldirData::priority() const
2362 void QQmlQmldirData::setPriority(int priority)
2364 m_priority = priority;
2367 void QQmlQmldirData::dataReceived(const Data &data)
2369 m_content = QString::fromUtf8(data.data(), data.size());
2374 #include "qqmltypeloader.moc"