1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativetypeloader_p.h"
44 #include <private/qdeclarativeengine_p.h>
45 #include <private/qdeclarativeglobal_p.h>
46 #include <private/qdeclarativethread_p.h>
47 #include <private/qdeclarativecompiler_p.h>
48 #include <private/qdeclarativecomponent_p.h>
49 #include <private/qdeclarativedebugtrace_p.h>
51 #include <QtCore/qdir.h>
52 #include <QtCore/qfile.h>
53 #include <QtCore/qdebug.h>
54 #include <QtCore/qmutex.h>
55 #include <QtCore/qthread.h>
56 #include <QtCore/qdiriterator.h>
57 #include <QtCore/qwaitcondition.h>
58 #include <QtDeclarative/qdeclarativecomponent.h>
59 #include <QtDeclarative/qdeclarativeextensioninterface.h>
61 #if defined (Q_OS_UNIX)
62 #include <sys/types.h>
66 // #define DATABLOB_DEBUG
70 #define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in main thread"); } while(false)
71 #define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in load thread"); } while(false)
72 #define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QDeclarativeDataBlob: An API call was made outside a callback"); } while(false)
76 #define ASSERT_MAINTHREAD()
77 #define ASSERT_LOADTHREAD()
78 #define ASSERT_CALLBACK()
84 // This is a lame object that we need to ensure that slots connected to
85 // QNetworkReply get called in the correct thread (the loader thread).
86 // As QDeclarativeDataLoader lives in the main thread, and we can't use
87 // Qt::DirectConnection connections from a QNetworkReply (because then
88 // sender() wont work), we need to insert this object in the middle.
89 class QDeclarativeDataLoaderNetworkReplyProxy : public QObject
93 QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l);
97 void downloadProgress(qint64, qint64);
100 QDeclarativeDataLoader *l;
103 class QDeclarativeDataLoaderThread : public QDeclarativeThread
105 typedef QDeclarativeDataLoaderThread This;
108 QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader);
109 QNetworkAccessManager *networkAccessManager() const;
110 QDeclarativeDataLoaderNetworkReplyProxy *networkReplyProxy() const;
112 void load(QDeclarativeDataBlob *b);
113 void loadAsync(QDeclarativeDataBlob *b);
114 void loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &);
115 void loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &);
116 void callCompleted(QDeclarativeDataBlob *b);
117 void callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p);
118 void initializeEngine(QDeclarativeExtensionInterface *, const char *);
121 virtual void shutdownThread();
124 void loadThread(QDeclarativeDataBlob *b);
125 void loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &);
126 void callCompletedMain(QDeclarativeDataBlob *b);
127 void callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p);
128 void initializeEngineMain(QDeclarativeExtensionInterface *iface, const char *uri);
130 QDeclarativeDataLoader *m_loader;
131 mutable QNetworkAccessManager *m_networkAccessManager;
132 mutable QDeclarativeDataLoaderNetworkReplyProxy *m_networkReplyProxy;
136 QDeclarativeDataLoaderNetworkReplyProxy::QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l)
141 void QDeclarativeDataLoaderNetworkReplyProxy::finished()
144 Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
145 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
146 l->networkReplyFinished(reply);
149 void QDeclarativeDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
152 Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
153 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
154 l->networkReplyProgress(reply, bytesReceived, bytesTotal);
158 Returns the set of QML files in path (qmldir, *.qml, *.js). The caller
159 is responsible for deleting the returned data.
160 Returns 0 if the directory does not exist.
162 #if defined (Q_OS_UNIX) && !defined(Q_OS_DARWIN)
163 static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
165 QByteArray name(QFile::encodeName(path));
166 DIR *dd = opendir(name);
170 struct dirent *result;
173 char b[offsetof (struct dirent, d_name) + NAME_MAX + 1];
176 QStringHash<bool> *files = new QStringHash<bool>;
177 while (readdir_r(dd, &u.d, &result) == 0 && result != 0) {
178 if (!strcmp(u.d.d_name, "qmldir")) {
179 files->insert(QLatin1String("qmldir"), true);
182 int len = strlen(u.d.d_name);
185 if (!strcmp(u.d.d_name+len-4, ".qml") || !strcmp(u.d.d_name+len-3, ".js"))
186 files->insert(QFile::decodeName(u.d.d_name), true);
187 #if defined(Q_OS_DARWIN)
188 else if ((len > 6 && !strcmp(u.d.d_name+len-6, ".dylib")) || !strcmp(u.d.d_name+len-3, ".so")
189 || (len > 7 && !strcmp(u.d.d_name+len-7, ".bundle")))
190 files->insert(QFile::decodeName(u.d.d_name), true);
192 else if (!strcmp(u.d.d_name+len-3, ".so") || !strcmp(u.d.d_name+len-3, ".sl"))
193 files->insert(QFile::decodeName(u.d.d_name), true);
201 static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
203 QDirIterator dir(path, QDir::Files);
206 QStringHash<bool> *files = new QStringHash<bool>;
207 while (dir.hasNext()) {
209 QString fileName = dir.fileName();
210 if (fileName == QLatin1String("qmldir")
211 || fileName.endsWith(QLatin1String(".qml"))
212 || fileName.endsWith(QLatin1String(".js"))
213 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
214 || fileName.endsWith(QLatin1String(".dll"))
215 #elif defined(Q_OS_DARWIN)
216 || fileName.endsWith(QLatin1String(".dylib"))
217 || fileName.endsWith(QLatin1String(".so"))
218 || fileName.endsWith(QLatin1String(".bundle"))
220 || fileName.endsWith(QLatin1String(".so"))
221 || fileName.endsWith(QLatin1String(".sl"))
224 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
225 fileName = fileName.toLower();
227 files->insert(fileName, true);
236 \class QDeclarativeDataBlob
237 \brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader.
240 QDeclarativeDataBlob's are loaded by a QDeclarativeDataLoader. The user creates the QDeclarativeDataBlob
241 and then calls QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() to load it.
242 The QDeclarativeDataLoader invokes callbacks on the QDeclarativeDataBlob as data becomes available.
246 \enum QDeclarativeDataBlob::Status
248 This enum describes the status of the data blob.
251 \o Null The blob has not yet been loaded by a QDeclarativeDataLoader
252 \o Loading The blob is loading network data. The QDeclarativeDataBlob::setData() callback has not yet been
253 invoked or has not yet returned.
254 \o WaitingForDependencies The blob is waiting for dependencies to be done before continueing. This status
255 only occurs after the QDeclarativeDataBlob::setData() callback has been made, and when the blob has outstanding
257 \o Complete The blob's data has been loaded and all dependencies are done.
258 \o Error An error has been set on this blob.
263 \enum QDeclarativeDataBlob::Type
265 This enum describes the type of the data blob.
268 \o QmlFile This is a QDeclarativeTypeData
269 \o JavaScriptFile This is a QDeclarativeScriptData
270 \o QmldirFile This is a QDeclarativeQmldirData
275 Create a new QDeclarativeDataBlob for \a url and of the provided \a type.
277 QDeclarativeDataBlob::QDeclarativeDataBlob(const QUrl &url, Type type)
278 : m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0),
279 m_inCallback(false), m_isDone(false)
284 QDeclarativeDataBlob::~QDeclarativeDataBlob()
286 Q_ASSERT(m_waitingOnMe.isEmpty());
288 cancelAllWaitingFor();
292 Returns the type provided to the constructor.
294 QDeclarativeDataBlob::Type QDeclarativeDataBlob::type() const
300 Returns the blob's status.
302 QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const
304 return m_data.status();
308 Returns true if the status is Null.
310 bool QDeclarativeDataBlob::isNull() const
312 return status() == Null;
316 Returns true if the status is Loading.
318 bool QDeclarativeDataBlob::isLoading() const
320 return status() == Loading;
324 Returns true if the status is WaitingForDependencies.
326 bool QDeclarativeDataBlob::isWaiting() const
328 return status() == WaitingForDependencies;
332 Returns true if the status is Complete.
334 bool QDeclarativeDataBlob::isComplete() const
336 return status() == Complete;
340 Returns true if the status is Error.
342 bool QDeclarativeDataBlob::isError() const
344 return status() == Error;
348 Returns true if the status is Complete or Error.
350 bool QDeclarativeDataBlob::isCompleteOrError() const
353 return s == Error || s == Complete;
357 Returns the data download progress from 0 to 1.
359 qreal QDeclarativeDataBlob::progress() const
361 quint8 p = m_data.progress();
362 if (p == 0xFF) return 1.;
363 else return qreal(p) / qreal(0xFF);
367 Returns the blob url passed to the constructor. If a network redirect
368 happens while fetching the data, this url remains the same.
372 QUrl QDeclarativeDataBlob::url() const
378 Returns the final url of the data. Initially this is the same as
379 url(), but if a network redirect happens while fetching the data, this url
380 is updated to reflect the new location.
382 May only be called from the load thread, or after the blob isCompleteOrError().
384 QUrl QDeclarativeDataBlob::finalUrl() const
386 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
391 Return the errors on this blob.
393 May only be called from the load thread, or after the blob isCompleteOrError().
395 QList<QDeclarativeError> QDeclarativeDataBlob::errors() const
397 Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
402 Mark this blob as having \a errors.
404 All outstanding dependencies will be cancelled. Requests to add new dependencies
405 will be ignored. Entry into the Error state is irreversable.
407 The setError() method may only be called from within a QDeclarativeDataBlob callback.
409 void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
413 QList<QDeclarativeError> l;
421 void QDeclarativeDataBlob::setError(const QList<QDeclarativeError> &errors)
425 Q_ASSERT(status() != Error);
426 Q_ASSERT(m_errors.isEmpty());
428 m_errors = errors; // Must be set before the m_data fence
429 m_data.setStatus(Error);
431 cancelAllWaitingFor();
438 Wait for \a blob to become complete or to error. If \a blob is already
439 complete or in error, or this blob is already complete, this has no effect.
441 The setError() method may only be called from within a QDeclarativeDataBlob callback.
443 void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob)
447 Q_ASSERT(status() != Null);
450 blob->status() == Error || blob->status() == Complete ||
451 status() == Error || status() == Complete || m_isDone ||
452 m_waitingFor.contains(blob))
457 m_data.setStatus(WaitingForDependencies);
459 m_waitingFor.append(blob);
460 blob->m_waitingOnMe.append(this);
464 \fn void QDeclarativeDataBlob::dataReceived(const QByteArray &data)
466 Invoked when data for the blob is received. Implementors should use this callback
467 to determine a blob's dependencies. Within this callback you may call setError()
472 Invoked once data has either been received or a network error occurred, and all
473 dependencies are complete.
475 You can set an error in this method, but you cannot add new dependencies. Implementors
476 should use this callback to finalize processing of data.
478 The default implementation does nothing.
480 XXX Rename processData() or some such to avoid confusion between done() (processing thread)
481 and completed() (main thread)
483 void QDeclarativeDataBlob::done()
488 Invoked if there is a network error while fetching this blob.
490 The default implementation sets an appropriate QDeclarativeError.
492 void QDeclarativeDataBlob::networkError(QNetworkReply::NetworkError networkError)
494 Q_UNUSED(networkError);
496 QDeclarativeError error;
497 error.setUrl(m_finalUrl);
499 const char *errorString = 0;
500 switch (networkError) {
502 errorString = "Network error";
504 case QNetworkReply::ConnectionRefusedError:
505 errorString = "Connection refused";
507 case QNetworkReply::RemoteHostClosedError:
508 errorString = "Remote host closed the connection";
510 case QNetworkReply::HostNotFoundError:
511 errorString = "Host not found";
513 case QNetworkReply::TimeoutError:
514 errorString = "Timeout";
516 case QNetworkReply::ProxyConnectionRefusedError:
517 case QNetworkReply::ProxyConnectionClosedError:
518 case QNetworkReply::ProxyNotFoundError:
519 case QNetworkReply::ProxyTimeoutError:
520 case QNetworkReply::ProxyAuthenticationRequiredError:
521 case QNetworkReply::UnknownProxyError:
522 errorString = "Proxy error";
524 case QNetworkReply::ContentAccessDenied:
525 errorString = "Access denied";
527 case QNetworkReply::ContentNotFoundError:
528 errorString = "File not found";
530 case QNetworkReply::AuthenticationRequiredError:
531 errorString = "Authentication required";
535 error.setDescription(QLatin1String(errorString));
541 Called if \a blob, which was previously waited for, has an error.
543 The default implementation does nothing.
545 void QDeclarativeDataBlob::dependencyError(QDeclarativeDataBlob *blob)
551 Called if \a blob, which was previously waited for, has completed.
553 The default implementation does nothing.
555 void QDeclarativeDataBlob::dependencyComplete(QDeclarativeDataBlob *blob)
561 Called when all blobs waited for have completed. This occurs regardless of
562 whether they are in error, or complete state.
564 The default implementation does nothing.
566 void QDeclarativeDataBlob::allDependenciesDone()
571 Called when the download progress of this blob changes. \a progress goes
574 This callback is only invoked if an asynchronous load for this blob is
575 made. An asynchronous load is one in which the Asynchronous mode is
576 specified explicitly, or one that is implicitly delayed due to a network
579 The default implementation does nothing.
581 void QDeclarativeDataBlob::downloadProgressChanged(qreal progress)
587 Invoked on the main thread sometime after done() was called on the load thread.
589 You cannot modify the blobs state at all in this callback and cannot depend on the
590 order or timeliness of these callbacks. Implementors should use this callback to notify
591 dependencies on the main thread that the blob is done and not a lot else.
593 This callback is only invoked if an asynchronous load for this blob is
594 made. An asynchronous load is one in which the Asynchronous mode is
595 specified explicitly, or one that is implicitly delayed due to a network
598 The default implementation does nothing.
600 void QDeclarativeDataBlob::completed()
605 void QDeclarativeDataBlob::tryDone()
607 if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
611 #ifdef DATABLOB_DEBUG
612 qWarning("QDeclarativeDataBlob::done() %s", qPrintable(url().toString()));
616 if (status() != Error)
617 m_data.setStatus(Complete);
619 notifyAllWaitingOnMe();
621 // Locking is not required here, as anyone expecting callbacks must
622 // already be protected against the blob being completed (as set above);
623 if (m_data.isAsync()) {
624 #ifdef DATABLOB_DEBUG
625 qWarning("QDeclarativeDataBlob: Dispatching completed");
627 m_manager->m_thread->callCompleted(this);
634 void QDeclarativeDataBlob::cancelAllWaitingFor()
636 while (m_waitingFor.count()) {
637 QDeclarativeDataBlob *blob = m_waitingFor.takeLast();
639 Q_ASSERT(blob->m_waitingOnMe.contains(this));
641 blob->m_waitingOnMe.removeOne(this);
647 void QDeclarativeDataBlob::notifyAllWaitingOnMe()
649 while (m_waitingOnMe.count()) {
650 QDeclarativeDataBlob *blob = m_waitingOnMe.takeLast();
652 Q_ASSERT(blob->m_waitingFor.contains(this));
654 blob->notifyComplete(this);
658 void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob)
660 Q_ASSERT(m_waitingFor.contains(blob));
661 Q_ASSERT(blob->status() == Error || blob->status() == Complete);
665 if (blob->status() == Error) {
666 dependencyError(blob);
667 } else if (blob->status() == Complete) {
668 dependencyComplete(blob);
671 m_waitingFor.removeOne(blob);
674 if (!isError() && m_waitingFor.isEmpty())
675 allDependenciesDone();
677 m_inCallback = false;
682 #define TD_STATUS_MASK 0x0000FFFF
683 #define TD_STATUS_SHIFT 0
684 #define TD_PROGRESS_MASK 0x00FF0000
685 #define TD_PROGRESS_SHIFT 16
686 #define TD_ASYNC_MASK 0x80000000
688 QDeclarativeDataBlob::ThreadData::ThreadData()
693 QDeclarativeDataBlob::Status QDeclarativeDataBlob::ThreadData::status() const
695 return QDeclarativeDataBlob::Status((_p.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
698 void QDeclarativeDataBlob::ThreadData::setStatus(QDeclarativeDataBlob::Status status)
702 int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK);
703 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
707 bool QDeclarativeDataBlob::ThreadData::isAsync() const
709 return _p.load() & TD_ASYNC_MASK;
712 void QDeclarativeDataBlob::ThreadData::setIsAsync(bool v)
716 int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0);
717 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
721 quint8 QDeclarativeDataBlob::ThreadData::progress() const
723 return quint8((_p.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
726 void QDeclarativeDataBlob::ThreadData::setProgress(quint8 v)
730 int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK);
731 if (d == nd || _p.testAndSetOrdered(d, nd)) return;
735 QDeclarativeDataLoaderThread::QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader)
736 : m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
740 QNetworkAccessManager *QDeclarativeDataLoaderThread::networkAccessManager() const
742 Q_ASSERT(isThisThread());
743 if (!m_networkAccessManager) {
744 m_networkAccessManager = QDeclarativeEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0);
745 m_networkReplyProxy = new QDeclarativeDataLoaderNetworkReplyProxy(m_loader);
748 return m_networkAccessManager;
751 QDeclarativeDataLoaderNetworkReplyProxy *QDeclarativeDataLoaderThread::networkReplyProxy() const
753 Q_ASSERT(isThisThread());
754 Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
755 return m_networkReplyProxy;
758 void QDeclarativeDataLoaderThread::load(QDeclarativeDataBlob *b)
761 callMethodInThread(&This::loadThread, b);
764 void QDeclarativeDataLoaderThread::loadAsync(QDeclarativeDataBlob *b)
767 postMethodToThread(&This::loadThread, b);
770 void QDeclarativeDataLoaderThread::loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &d)
773 callMethodInThread(&This::loadWithStaticDataThread, b, d);
776 void QDeclarativeDataLoaderThread::loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &d)
779 postMethodToThread(&This::loadWithStaticDataThread, b, d);
782 void QDeclarativeDataLoaderThread::callCompleted(QDeclarativeDataBlob *b)
785 postMethodToMain(&This::callCompletedMain, b);
788 void QDeclarativeDataLoaderThread::callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p)
791 postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
794 void QDeclarativeDataLoaderThread::initializeEngine(QDeclarativeExtensionInterface *iface,
797 callMethodInMain(&This::initializeEngineMain, iface, uri);
800 void QDeclarativeDataLoaderThread::shutdownThread()
802 delete m_networkAccessManager;
803 m_networkAccessManager = 0;
804 delete m_networkReplyProxy;
805 m_networkReplyProxy = 0;
808 void QDeclarativeDataLoaderThread::loadThread(QDeclarativeDataBlob *b)
810 m_loader->loadThread(b);
814 void QDeclarativeDataLoaderThread::loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &d)
816 m_loader->loadWithStaticDataThread(b, d);
820 void QDeclarativeDataLoaderThread::callCompletedMain(QDeclarativeDataBlob *b)
822 #ifdef DATABLOB_DEBUG
823 qWarning("QDeclarativeDataLoaderThread: %s completed() callback", qPrintable(b->url().toString()));
829 void QDeclarativeDataLoaderThread::callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p)
831 #ifdef DATABLOB_DEBUG
832 qWarning("QDeclarativeDataLoaderThread: %s downloadProgressChanged(%f) callback",
833 qPrintable(b->url().toString()), p);
835 b->downloadProgressChanged(p);
839 void QDeclarativeDataLoaderThread::initializeEngineMain(QDeclarativeExtensionInterface *iface,
842 Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
843 iface->initializeEngine(m_loader->engine(), uri);
847 \class QDeclarativeDataLoader
848 \brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network.
851 The QDeclarativeDataLoader class is provided for the exclusive use of the QDeclarativeTypeLoader class.
853 Clients create QDeclarativeDataBlob instances and submit them to the QDeclarativeDataLoader class
854 through the QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() methods.
855 The loader then fetches the data over the network or from the local file system in an efficient way.
856 QDeclarativeDataBlob is an abstract class, so should always be specialized.
858 Once data is received, the QDeclarativeDataBlob::dataReceived() method is invoked on the blob. The
859 derived class should use this callback to process the received data. Processing of the data can
860 result in an error being set (QDeclarativeDataBlob::setError()), or one or more dependencies being
861 created (QDeclarativeDataBlob::addDependency()). Dependencies are other QDeclarativeDataBlob's that
862 are required before processing can fully complete.
864 To complete processing, the QDeclarativeDataBlob::done() callback is invoked. done() is called when
865 one of these three preconditions are met.
868 \o The QDeclarativeDataBlob has no dependencies.
869 \o The QDeclarativeDataBlob has an error set.
870 \o All the QDeclarativeDataBlob's dependencies are themselves "done()".
873 Thus QDeclarativeDataBlob::done() will always eventually be called, even if the blob has an error set.
877 Create a new QDeclarativeDataLoader for \a engine.
879 QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine)
880 : m_engine(engine), m_thread(new QDeclarativeDataLoaderThread(this))
885 QDeclarativeDataLoader::~QDeclarativeDataLoader()
887 for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter)
890 m_thread->shutdown();
894 void QDeclarativeDataLoader::lock()
899 void QDeclarativeDataLoader::unlock()
905 Load the provided \a blob from the network or filesystem.
907 The loader must be locked.
909 void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob, Mode mode)
911 #ifdef DATABLOB_DEBUG
912 qWarning("QDeclarativeDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()),
913 m_thread->isThisThread()?"Compile":"Engine");
916 Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
917 Q_ASSERT(blob->m_manager == 0);
919 blob->m_data.setStatus(QDeclarativeDataBlob::Loading);
920 blob->m_manager = this;
922 if (m_thread->isThisThread()) {
926 } else if (mode == PreferSynchronous) {
928 m_thread->load(blob);
930 if (!blob->isCompleteOrError())
931 blob->m_data.setIsAsync(true);
933 Q_ASSERT(mode == Asynchronous);
934 blob->m_data.setIsAsync(true);
936 m_thread->loadAsync(blob);
942 Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case.
944 The loader must be locked.
946 void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data, Mode mode)
948 #ifdef DATABLOB_DEBUG
949 qWarning("QDeclarativeDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()),
950 m_thread->isThisThread()?"Compile":"Engine");
953 Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
954 Q_ASSERT(blob->m_manager == 0);
956 blob->m_data.setStatus(QDeclarativeDataBlob::Loading);
957 blob->m_manager = this;
959 if (m_thread->isThisThread()) {
961 loadWithStaticDataThread(blob, data);
963 } else if (mode == PreferSynchronous) {
965 m_thread->loadWithStaticData(blob, data);
967 if (!blob->isCompleteOrError())
968 blob->m_data.setIsAsync(true);
970 Q_ASSERT(mode == Asynchronous);
971 blob->m_data.setIsAsync(true);
973 m_thread->loadWithStaticDataAsync(blob, data);
978 void QDeclarativeDataLoader::loadWithStaticDataThread(QDeclarativeDataBlob *blob, const QByteArray &data)
985 void QDeclarativeDataLoader::loadThread(QDeclarativeDataBlob *blob)
989 if (blob->m_url.isEmpty()) {
990 QDeclarativeError error;
991 error.setDescription(QLatin1String("Invalid null URL"));
992 blob->setError(error);
996 QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(blob->m_url);
999 if (!QDeclarative_isFileCaseCorrect(lf)) {
1000 QDeclarativeError error;
1001 error.setUrl(blob->m_url);
1002 error.setDescription(QLatin1String("File name case mismatch"));
1003 blob->setError(error);
1007 if (file.open(QFile::ReadOnly)) {
1008 QByteArray data = file.readAll();
1010 blob->m_data.setProgress(0xFF);
1011 if (blob->m_data.isAsync())
1012 m_thread->callDownloadProgressChanged(blob, 1.);
1014 setData(blob, data);
1016 blob->networkError(QNetworkReply::ContentNotFoundError);
1021 QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
1022 QObject *nrp = m_thread->networkReplyProxy();
1023 QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
1024 nrp, SLOT(downloadProgress(qint64,qint64)));
1025 QObject::connect(reply, SIGNAL(finished()),
1026 nrp, SLOT(finished()));
1027 m_networkReplies.insert(reply, blob);
1033 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
1035 void QDeclarativeDataLoader::networkReplyFinished(QNetworkReply *reply)
1037 Q_ASSERT(m_thread->isThisThread());
1039 reply->deleteLater();
1041 QDeclarativeDataBlob *blob = m_networkReplies.take(reply);
1045 blob->m_redirectCount++;
1047 if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
1048 QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
1049 if (redirect.isValid()) {
1050 QUrl url = reply->url().resolved(redirect.toUrl());
1051 blob->m_finalUrl = url;
1053 QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url));
1054 QObject *nrp = m_thread->networkReplyProxy();
1055 QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished()));
1056 m_networkReplies.insert(reply, blob);
1061 if (reply->error()) {
1062 blob->networkError(reply->error());
1064 QByteArray data = reply->readAll();
1065 setData(blob, data);
1071 void QDeclarativeDataLoader::networkReplyProgress(QNetworkReply *reply,
1072 qint64 bytesReceived, qint64 bytesTotal)
1074 Q_ASSERT(m_thread->isThisThread());
1076 QDeclarativeDataBlob *blob = m_networkReplies.value(reply);
1080 if (bytesTotal != 0) {
1081 quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal));
1082 blob->m_data.setProgress(progress);
1083 if (blob->m_data.isAsync())
1084 m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
1089 Return the QDeclarativeEngine associated with this loader
1091 QDeclarativeEngine *QDeclarativeDataLoader::engine() const
1097 Call the initializeEngine() method on \a iface. Used by QDeclarativeImportDatabase to ensure it
1098 gets called in the correct thread.
1100 void QDeclarativeDataLoader::initializeEngine(QDeclarativeExtensionInterface *iface,
1103 Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread());
1105 if (m_thread->isThisThread()) {
1106 m_thread->initializeEngine(iface, uri);
1108 Q_ASSERT(engine()->thread() == QThread::currentThread());
1109 iface->initializeEngine(engine(), uri);
1114 void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data)
1116 blob->m_inCallback = true;
1118 blob->dataReceived(data);
1120 if (!blob->isError() && !blob->isWaiting())
1121 blob->allDependenciesDone();
1123 if (blob->status() != QDeclarativeDataBlob::Error)
1124 blob->m_data.setStatus(QDeclarativeDataBlob::WaitingForDependencies);
1126 blob->m_inCallback = false;
1132 Constructs a new type loader that uses the given \a engine.
1134 QDeclarativeTypeLoader::QDeclarativeTypeLoader(QDeclarativeEngine *engine)
1135 : QDeclarativeDataLoader(engine)
1140 Destroys the type loader, first clearing the cache of any information about
1143 QDeclarativeTypeLoader::~QDeclarativeTypeLoader()
1149 \enum QDeclarativeTypeLoader::Option
1151 This enum defines the options that control the way type data is handled.
1153 \value None The default value, indicating that no other options
1155 \value PreserveParser The parser used to handle the type data is preserved
1156 after the data has been parsed.
1160 Returns a QDeclarativeTypeData for the specified \a url. The QDeclarativeTypeData may be cached.
1162 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
1164 Q_ASSERT(!url.isRelative() &&
1165 (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
1166 !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
1170 QDeclarativeTypeData *typeData = m_typeCache.value(url);
1173 typeData = new QDeclarativeTypeData(url, None, this);
1174 m_typeCache.insert(url, typeData);
1175 QDeclarativeDataLoader::load(typeData);
1186 Returns a QDeclarativeTypeData for the given \a data with the provided base \a url. The
1187 QDeclarativeTypeData will not be cached.
1189 The specified \a options control how the loader handles type data.
1191 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
1195 QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
1196 QDeclarativeDataLoader::loadWithStaticData(typeData, data);
1204 Return a QDeclarativeScriptBlob for \a url. The QDeclarativeScriptData may be cached.
1206 QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
1208 Q_ASSERT(!url.isRelative() &&
1209 (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
1210 !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
1214 QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
1217 scriptBlob = new QDeclarativeScriptBlob(url, this);
1218 m_scriptCache.insert(url, scriptBlob);
1219 QDeclarativeDataLoader::load(scriptBlob);
1222 scriptBlob->addref();
1230 Returns a QDeclarativeQmldirData for \a url. The QDeclarativeQmldirData may be cached.
1232 QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
1234 Q_ASSERT(!url.isRelative() &&
1235 (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
1236 !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
1240 QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
1243 qmldirData = new QDeclarativeQmldirData(url);
1244 m_qmldirCache.insert(url, qmldirData);
1245 QDeclarativeDataLoader::load(qmldirData);
1248 qmldirData->addref();
1256 Returns the absolute filename of path via a directory cache for files named
1257 "qmldir", "*.qml", "*.js", and plugins.
1258 Returns a empty string if the path does not exist.
1260 QString QDeclarativeTypeLoader::absoluteFilePath(const QString &path)
1264 if (path.at(0) == QLatin1Char(':')) {
1266 QFileInfo fileInfo(path);
1267 return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
1269 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1270 QString lowPath = path.toLower();
1271 int lastSlash = lowPath.lastIndexOf(QLatin1Char('/'));
1272 QString dirPath = lowPath.left(lastSlash);
1274 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1275 QStringRef dirPath(&path, 0, lastSlash);
1278 StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1280 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1281 QHashedString dirPathString(dirPath);
1283 QHashedString dirPathString(dirPath.toString());
1285 StringSet *files = qmlFilesInDirectory(dirPathString);
1286 m_importDirCache.insert(dirPathString, files);
1287 fileSet = m_importDirCache.value(dirPathString);
1292 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1293 QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(lowPath.constData()+lastSlash+1, lowPath.length()-lastSlash-1)) ? path : QString();
1295 QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(path.constData()+lastSlash+1, path.length()-lastSlash-1)) ? path : QString();
1297 if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':'))
1298 absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath();
1300 return absoluteFilePath;
1304 Returns true if the path is a directory via a directory cache. Cache is
1305 shared with absoluteFilePath().
1307 bool QDeclarativeTypeLoader::directoryExists(const QString &path)
1311 if (path.at(0) == QLatin1Char(':')) {
1313 QFileInfo fileInfo(path);
1314 return fileInfo.exists() && fileInfo.isDir();
1317 int length = path.length();
1318 if (path.endsWith(QLatin1Char('/')))
1320 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1321 QString dirPath = path.left(length).toLower();
1323 QStringRef dirPath(&path, 0, length);
1326 StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1328 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1329 QHashedString dirPathString(dirPath);
1331 QHashedString dirPathString(dirPath.toString());
1333 StringSet *files = qmlFilesInDirectory(dirPathString);
1334 m_importDirCache.insert(dirPathString, files);
1335 fileSet = m_importDirCache.value(dirPathString);
1343 Return a QDeclarativeDirParser for absoluteFilePath. The QDeclarativeDirParser may be cached.
1345 const QDeclarativeDirParser *QDeclarativeTypeLoader::qmlDirParser(const QString &absoluteFilePath)
1347 QDeclarativeDirParser *qmldirParser;
1348 QDeclarativeDirParser **val = m_importQmlDirCache.value(absoluteFilePath);
1350 qmldirParser = new QDeclarativeDirParser;
1351 qmldirParser->setFileSource(absoluteFilePath);
1352 qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath));
1353 qmldirParser->parse();
1354 m_importQmlDirCache.insert(absoluteFilePath, qmldirParser);
1356 qmldirParser = *val;
1359 return qmldirParser;
1364 Clears cached information about loaded files, including any type data, scripts
1365 and qmldir information.
1367 void QDeclarativeTypeLoader::clearCache()
1369 for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter)
1371 for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter)
1373 for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter)
1375 qDeleteAll(m_importDirCache);
1376 qDeleteAll(m_importQmlDirCache);
1378 m_typeCache.clear();
1379 m_scriptCache.clear();
1380 m_qmldirCache.clear();
1381 m_importDirCache.clear();
1382 m_importQmlDirCache.clear();
1386 QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options,
1387 QDeclarativeTypeLoader *manager)
1388 : QDeclarativeDataBlob(url, QmlFile), m_options(options), m_imports(manager), m_typesResolved(false),
1389 m_compiledData(0), m_typeLoader(manager)
1393 QDeclarativeTypeData::~QDeclarativeTypeData()
1395 for (int ii = 0; ii < m_scripts.count(); ++ii)
1396 m_scripts.at(ii).script->release();
1397 for (int ii = 0; ii < m_qmldirs.count(); ++ii)
1398 m_qmldirs.at(ii)->release();
1399 for (int ii = 0; ii < m_types.count(); ++ii)
1400 if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
1402 m_compiledData->release();
1405 QDeclarativeTypeLoader *QDeclarativeTypeData::typeLoader() const
1407 return m_typeLoader;
1410 const QDeclarativeImports &QDeclarativeTypeData::imports() const
1415 const QDeclarativeScript::Parser &QDeclarativeTypeData::parser() const
1417 return scriptParser;
1420 const QList<QDeclarativeTypeData::TypeReference> &QDeclarativeTypeData::resolvedTypes() const
1425 const QList<QDeclarativeTypeData::ScriptReference> &QDeclarativeTypeData::resolvedScripts() const
1430 const QSet<QString> &QDeclarativeTypeData::namespaces() const
1432 return m_namespaces;
1435 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
1438 m_compiledData->addref();
1440 return m_compiledData;
1443 void QDeclarativeTypeData::registerCallback(TypeDataCallback *callback)
1445 Q_ASSERT(!m_callbacks.contains(callback));
1446 m_callbacks.append(callback);
1449 void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback)
1451 Q_ASSERT(m_callbacks.contains(callback));
1452 m_callbacks.removeOne(callback);
1453 Q_ASSERT(!m_callbacks.contains(callback));
1456 void QDeclarativeTypeData::done()
1458 // Check all script dependencies for errors
1459 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1460 const ScriptReference &script = m_scripts.at(ii);
1461 Q_ASSERT(script.script->isCompleteOrError());
1462 if (script.script->isError()) {
1463 QList<QDeclarativeError> errors = script.script->errors();
1464 QDeclarativeError error;
1465 error.setUrl(finalUrl());
1466 error.setLine(script.location.line);
1467 error.setColumn(script.location.column);
1468 error.setDescription(QDeclarativeTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
1469 errors.prepend(error);
1474 // Check all type dependencies for errors
1475 for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
1476 const TypeReference &type = m_types.at(ii);
1477 Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
1478 if (type.typeData && type.typeData->isError()) {
1479 QString typeName = scriptParser.referencedTypes().at(ii)->name;
1481 QList<QDeclarativeError> errors = type.typeData->errors();
1482 QDeclarativeError error;
1483 error.setUrl(finalUrl());
1484 error.setLine(type.location.line);
1485 error.setColumn(type.location.column);
1486 error.setDescription(QDeclarativeTypeLoader::tr("Type %1 unavailable").arg(typeName));
1487 errors.prepend(error);
1492 // Compile component
1496 if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
1497 scriptParser.clear();
1500 void QDeclarativeTypeData::completed()
1503 while (!m_callbacks.isEmpty()) {
1504 TypeDataCallback *callback = m_callbacks.takeFirst();
1505 callback->typeDataReady(this);
1509 void QDeclarativeTypeData::dataReceived(const QByteArray &data)
1511 if (!scriptParser.parse(data, finalUrl())) {
1512 setError(scriptParser.errors());
1516 m_imports.setBaseUrl(finalUrl());
1518 foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) {
1519 if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) {
1520 QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1521 if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
1522 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
1523 addDependency(data);
1526 } else if (import.type == QDeclarativeScript::Import::Script) {
1527 QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1528 QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1529 addDependency(blob);
1531 ScriptReference ref;
1532 ref.location = import.location.start;
1533 ref.qualifier = import.qualifier;
1540 if (!finalUrl().scheme().isEmpty()) {
1541 QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
1542 if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
1543 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
1544 addDependency(data);
1550 void QDeclarativeTypeData::allDependenciesDone()
1552 if (!m_typesResolved) {
1554 m_typesResolved = true;
1558 void QDeclarativeTypeData::downloadProgressChanged(qreal p)
1560 for (int ii = 0; ii < m_callbacks.count(); ++ii) {
1561 TypeDataCallback *callback = m_callbacks.at(ii);
1562 callback->typeDataProgress(this, p);
1566 void QDeclarativeTypeData::compile()
1568 Q_ASSERT(m_compiledData == 0);
1569 QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Compiling);
1571 m_compiledData = new QDeclarativeCompiledData(typeLoader()->engine());
1572 m_compiledData->url = m_imports.baseUrl();
1573 m_compiledData->name = m_compiledData->url.toString();
1574 QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name);
1576 QDeclarativeCompiler compiler(&scriptParser._pool);
1577 if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
1578 setError(compiler.errors());
1579 m_compiledData->release();
1582 QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Compiling);
1585 void QDeclarativeTypeData::resolveTypes()
1587 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
1588 QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
1590 // For local urls, add an implicit import "." as first (most overridden) lookup.
1591 // This will also trigger the loading of the qmldir and the import of any native
1592 // types from available plugins.
1593 QList<QDeclarativeError> errors;
1594 if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
1595 m_imports.addImport(importDatabase, QLatin1String("."),
1596 QString(), -1, -1, QDeclarativeScript::Import::File,
1597 qmldir->dirComponents(), &errors);
1599 m_imports.addImport(importDatabase, QLatin1String("."),
1600 QString(), -1, -1, QDeclarativeScript::Import::File,
1601 QDeclarativeDirComponents(), &errors);
1604 // remove any errors which are due to the implicit import which aren't real errors.
1605 // for example, if the implicitly included qmldir file doesn't exist, that is not an error.
1606 QList<QDeclarativeError> realErrors;
1607 for (int i = 0; i < errors.size(); ++i) {
1608 if (errors.at(i).description() != QDeclarativeImportDatabase::tr("import \".\" has no qmldir and no namespace")
1609 && errors.at(i).description() != QDeclarativeImportDatabase::tr("\".\": no such directory")) {
1610 realErrors.prepend(errors.at(i)); // this is a real error.
1614 // report any real errors which occurred during plugin loading or qmldir parsing.
1615 if (!realErrors.isEmpty()) {
1616 setError(realErrors);
1620 foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) {
1621 QDeclarativeDirComponents qmldircomponentsnetwork;
1622 if (import.type == QDeclarativeScript::Import::Script)
1625 if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) {
1626 QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1627 if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl))
1628 qmldircomponentsnetwork = qmldir->dirComponents();
1633 import.extractVersion(&vmaj, &vmin);
1635 QList<QDeclarativeError> errors;
1636 if (!m_imports.addImport(importDatabase, import.uri, import.qualifier,
1637 vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) {
1638 QDeclarativeError error;
1639 if (errors.size()) {
1640 error = errors.takeFirst();
1642 // this should not be possible!
1643 // Description should come from error provided by addImport() function.
1644 error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
1646 error.setUrl(m_imports.baseUrl());
1647 error.setLine(import.location.start.line);
1648 error.setColumn(import.location.start.column);
1649 errors.prepend(error); // put it back on the list after filling out information.
1656 // Add any imported scripts to our resolved set
1657 foreach (const QDeclarativeImports::ScriptReference &script, m_imports.resolvedScripts())
1659 QDeclarativeScriptBlob *blob = typeLoader()->getScript(script.location);
1660 addDependency(blob);
1662 ScriptReference ref;
1663 //ref.location = ...
1664 ref.qualifier = script.nameSpace;
1665 if (!script.qualifier.isEmpty())
1667 ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
1669 // Add a reference to the enclosing namespace
1670 m_namespaces.insert(script.qualifier);
1677 foreach (QDeclarativeScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
1683 QDeclarativeImportedNamespace *typeNamespace = 0;
1684 QList<QDeclarativeError> errors;
1686 if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
1687 &typeNamespace, &errors) || typeNamespace) {
1688 // Known to not be a type:
1689 // - known to be a namespace (Namespace {})
1690 // - type with unknown namespace (UnknownNamespace.SomeType {})
1691 QDeclarativeError error;
1692 QString userTypeName = parserRef->name;
1693 userTypeName.replace(QLatin1Char('/'),QLatin1Char('.'));
1694 if (typeNamespace) {
1695 error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName));
1697 if (errors.size()) {
1698 error = errors.takeFirst();
1700 // this should not be possible!
1701 // Description should come from error provided by addImport() function.
1702 error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
1704 error.setUrl(m_imports.baseUrl());
1705 error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description()));
1708 if (!parserRef->refObjects.isEmpty()) {
1709 QDeclarativeScript::Object *obj = parserRef->refObjects.first();
1710 error.setLine(obj->location.start.line);
1711 error.setColumn(obj->location.start.column);
1714 errors.prepend(error);
1720 ref.majorVersion = majorVersion;
1721 ref.minorVersion = minorVersion;
1723 ref.typeData = typeLoader()->get(QUrl(url));
1724 addDependency(ref.typeData);
1727 if (parserRef->refObjects.count())
1728 ref.location = parserRef->refObjects.first()->location.start;
1734 QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
1736 for (int ii = 0; ii < m_qmldirs.count(); ++ii) {
1737 if (m_qmldirs.at(ii)->url() == url)
1738 return m_qmldirs.at(ii);
1743 QDeclarativeScriptData::QDeclarativeScriptData()
1744 : importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), m_loaded(false)
1748 QDeclarativeScriptData::~QDeclarativeScriptData()
1752 void QDeclarativeScriptData::clear()
1755 importCache->release();
1759 for (int ii = 0; ii < scripts.count(); ++ii)
1760 scripts.at(ii)->release();
1763 qPersistentDispose(m_program);
1764 qPersistentDispose(m_value);
1766 // An addref() was made when the QDeclarativeCleanup was added to the engine.
1770 QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader)
1771 : QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeScript::Object::ScriptBlock::None),
1772 m_imports(loader), m_scriptData(0), m_typeLoader(loader)
1776 QDeclarativeScriptBlob::~QDeclarativeScriptBlob()
1779 m_scriptData->release();
1784 QDeclarativeScript::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const
1789 QString QDeclarativeScriptBlob::scriptSource() const
1794 QDeclarativeTypeLoader *QDeclarativeScriptBlob::typeLoader() const
1796 return m_typeLoader;
1799 const QDeclarativeImports &QDeclarativeScriptBlob::imports() const
1804 QDeclarativeScriptData *QDeclarativeScriptBlob::scriptData() const
1806 return m_scriptData;
1809 void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
1811 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
1812 QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
1814 m_source = QString::fromUtf8(data);
1816 QDeclarativeScript::Parser::JavaScriptMetaData metadata =
1817 QDeclarativeScript::Parser::extractMetaData(m_source);
1819 m_imports.setBaseUrl(finalUrl());
1821 m_pragmas = metadata.pragmas;
1823 foreach (const QDeclarativeScript::Import &import, metadata.imports) {
1824 Q_ASSERT(import.type != QDeclarativeScript::Import::File);
1826 if (import.type == QDeclarativeScript::Import::Script) {
1827 QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1828 QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1829 addDependency(blob);
1831 ScriptReference ref;
1832 ref.location = import.location.start;
1833 ref.qualifier = import.qualifier;
1837 Q_ASSERT(import.type == QDeclarativeScript::Import::Library);
1840 import.extractVersion(&vmaj, &vmin);
1842 QList<QDeclarativeError> errors;
1843 if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
1844 import.type, QDeclarativeDirComponents(), &errors)) {
1845 QDeclarativeError error = errors.takeFirst();
1846 // description should be set by addImport().
1847 error.setUrl(m_imports.baseUrl());
1848 error.setLine(import.location.start.line);
1849 error.setColumn(import.location.start.column);
1850 errors.prepend(error);
1859 void QDeclarativeScriptBlob::done()
1861 // Check all script dependencies for errors
1862 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1863 const ScriptReference &script = m_scripts.at(ii);
1864 Q_ASSERT(script.script->isCompleteOrError());
1865 if (script.script->isError()) {
1866 QList<QDeclarativeError> errors = script.script->errors();
1867 QDeclarativeError error;
1868 error.setUrl(finalUrl());
1869 error.setLine(script.location.line);
1870 error.setColumn(script.location.column);
1871 error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
1872 errors.prepend(error);
1880 QDeclarativeEngine *engine = typeLoader()->engine();
1881 m_scriptData = new QDeclarativeScriptData();
1882 m_scriptData->url = finalUrl();
1883 m_scriptData->importCache = new QDeclarativeTypeNameCache();
1885 for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1886 const ScriptReference &script = m_scripts.at(ii);
1888 m_scriptData->scripts.append(script.script);
1889 m_scriptData->importCache->add(script.qualifier, ii);
1892 m_imports.populateCache(m_scriptData->importCache, engine);
1894 m_scriptData->pragmas = m_pragmas;
1895 m_scriptData->m_programSource = m_source;
1898 QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
1899 : QDeclarativeDataBlob(url, QmldirFile)
1903 const QDeclarativeDirComponents &QDeclarativeQmldirData::dirComponents() const
1905 return m_components;
1908 void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
1910 QDeclarativeDirParser parser;
1911 parser.setSource(QString::fromUtf8(data));
1913 m_components = parser.components();
1918 #include "qdeclarativetypeloader.moc"