aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/qml/qdeclarativetypeloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/qml/qdeclarativetypeloader.cpp')
-rw-r--r--src/declarative/qml/qdeclarativetypeloader.cpp581
1 files changed, 510 insertions, 71 deletions
diff --git a/src/declarative/qml/qdeclarativetypeloader.cpp b/src/declarative/qml/qdeclarativetypeloader.cpp
index 8bb40ce7a5..ebda11fa31 100644
--- a/src/declarative/qml/qdeclarativetypeloader.cpp
+++ b/src/declarative/qml/qdeclarativetypeloader.cpp
@@ -42,24 +42,117 @@
#include "qdeclarativetypeloader_p.h"
#include <private/qdeclarativeengine_p.h>
+#include <private/qdeclarativeglobal_p.h>
+#include <private/qdeclarativethread_p.h>
#include <private/qdeclarativecompiler_p.h>
#include <private/qdeclarativecomponent_p.h>
-#include <private/qdeclarativeglobal_p.h>
#include <private/qdeclarativedebugtrace_p.h>
-#include <QtDeclarative/qdeclarativecomponent.h>
-#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qthread.h>
#include <QtCore/qdiriterator.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qdeclarativeextensioninterface.h>
#if defined (Q_OS_UNIX)
#include <sys/types.h>
#include <dirent.h>
#endif
+// #define DATABLOB_DEBUG
+
+#ifdef DATABLOB_DEBUG
+
+#define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in main thread"); } while(false)
+#define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in load thread"); } while(false)
+#define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QDeclarativeDataBlob: An API call was made outside a callback"); } while(false)
+
+#else
+
+#define ASSERT_MAINTHREAD()
+#define ASSERT_LOADTHREAD()
+#define ASSERT_CALLBACK()
+
+#endif
+
QT_BEGIN_NAMESPACE
+// This is a lame object that we need to ensure that slots connected to
+// QNetworkReply get called in the correct thread (the loader thread).
+// As QDeclarativeDataLoader lives in the main thread, and we can't use
+// Qt::DirectConnection connections from a QNetworkReply (because then
+// sender() wont work), we need to insert this object in the middle.
+class QDeclarativeDataLoaderNetworkReplyProxy : public QObject
+{
+ Q_OBJECT
+public:
+ QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l);
+
+public slots:
+ void finished();
+ void downloadProgress(qint64, qint64);
+
+private:
+ QDeclarativeDataLoader *l;
+};
+
+class QDeclarativeDataLoaderThread : public QDeclarativeThread
+{
+ typedef QDeclarativeDataLoaderThread This;
+
+public:
+ QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader);
+ QNetworkAccessManager *networkAccessManager() const;
+ QDeclarativeDataLoaderNetworkReplyProxy *networkReplyProxy() const;
+
+ void load(QDeclarativeDataBlob *b);
+ void loadAsync(QDeclarativeDataBlob *b);
+ void loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &);
+ void loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &);
+ void callCompleted(QDeclarativeDataBlob *b);
+ void callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p);
+ void initializeEngine(QDeclarativeExtensionInterface *, const char *);
+
+protected:
+ virtual void shutdownThread();
+
+private:
+ void loadThread(QDeclarativeDataBlob *b);
+ void loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &);
+ void callCompletedMain(QDeclarativeDataBlob *b);
+ void callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p);
+ void initializeEngineMain(QDeclarativeExtensionInterface *iface, const char *uri);
+
+ QDeclarativeDataLoader *m_loader;
+ mutable QNetworkAccessManager *m_networkAccessManager;
+ mutable QDeclarativeDataLoaderNetworkReplyProxy *m_networkReplyProxy;
+};
+
+
+QDeclarativeDataLoaderNetworkReplyProxy::QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l)
+: l(l)
+{
+}
+
+void QDeclarativeDataLoaderNetworkReplyProxy::finished()
+{
+ Q_ASSERT(sender());
+ Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
+ QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
+ l->networkReplyFinished(reply);
+}
+
+void QDeclarativeDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ Q_ASSERT(sender());
+ Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
+ QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
+ l->networkReplyProgress(reply, bytesReceived, bytesTotal);
+}
/*
Returns the set of QML files in path (qmldir, *.qml, *.js). The caller
@@ -182,8 +275,8 @@ This enum describes the type of the data blob.
Create a new QDeclarativeDataBlob for \a url and of the provided \a type.
*/
QDeclarativeDataBlob::QDeclarativeDataBlob(const QUrl &url, Type type)
-: m_type(type), m_status(Null), m_progress(0), m_url(url), m_finalUrl(url), m_manager(0),
- m_redirectCount(0), m_inCallback(false), m_isDone(false)
+: m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0),
+ m_inCallback(false), m_isDone(false)
{
}
@@ -208,7 +301,7 @@ Returns the blob's status.
*/
QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const
{
- return m_status;
+ return m_data.status();
}
/*!
@@ -216,7 +309,7 @@ Returns true if the status is Null.
*/
bool QDeclarativeDataBlob::isNull() const
{
- return m_status == Null;
+ return status() == Null;
}
/*!
@@ -224,7 +317,7 @@ Returns true if the status is Loading.
*/
bool QDeclarativeDataBlob::isLoading() const
{
- return m_status == Loading;
+ return status() == Loading;
}
/*!
@@ -232,7 +325,7 @@ Returns true if the status is WaitingForDependencies.
*/
bool QDeclarativeDataBlob::isWaiting() const
{
- return m_status == WaitingForDependencies;
+ return status() == WaitingForDependencies;
}
/*!
@@ -240,7 +333,7 @@ Returns true if the status is Complete.
*/
bool QDeclarativeDataBlob::isComplete() const
{
- return m_status == Complete;
+ return status() == Complete;
}
/*!
@@ -248,7 +341,7 @@ Returns true if the status is Error.
*/
bool QDeclarativeDataBlob::isError() const
{
- return m_status == Error;
+ return status() == Error;
}
/*!
@@ -256,7 +349,8 @@ Returns true if the status is Complete or Error.
*/
bool QDeclarativeDataBlob::isCompleteOrError() const
{
- return isComplete() || isError();
+ Status s = status();
+ return s == Error || s == Complete;
}
/*!
@@ -264,7 +358,9 @@ Returns the data download progress from 0 to 1.
*/
qreal QDeclarativeDataBlob::progress() const
{
- return m_progress;
+ quint8 p = m_data.progress();
+ if (p == 0xFF) return 1.;
+ else return qreal(p) / qreal(0xFF);
}
/*!
@@ -282,17 +378,23 @@ QUrl QDeclarativeDataBlob::url() const
Returns the final url of the data. Initially this is the same as
url(), but if a network redirect happens while fetching the data, this url
is updated to reflect the new location.
+
+May only be called from the load thread, or after the blob isCompleteOrError().
*/
QUrl QDeclarativeDataBlob::finalUrl() const
{
+ Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
return m_finalUrl;
}
/*!
Return the errors on this blob.
+
+May only be called from the load thread, or after the blob isCompleteOrError().
*/
QList<QDeclarativeError> QDeclarativeDataBlob::errors() const
{
+ Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
return m_errors;
}
@@ -300,11 +402,14 @@ QList<QDeclarativeError> QDeclarativeDataBlob::errors() const
Mark this blob as having \a errors.
All outstanding dependencies will be cancelled. Requests to add new dependencies
-will be ignored. Entry into the Error state is irreversable, although you can change the
-specific errors by additional calls to setError.
+will be ignored. Entry into the Error state is irreversable.
+
+The setError() method may only be called from within a QDeclarativeDataBlob callback.
*/
void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
{
+ ASSERT_CALLBACK();
+
QList<QDeclarativeError> l;
l << errors;
setError(l);
@@ -315,8 +420,13 @@ void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
*/
void QDeclarativeDataBlob::setError(const QList<QDeclarativeError> &errors)
{
- m_status = Error;
- m_errors = errors;
+ ASSERT_CALLBACK();
+
+ Q_ASSERT(status() != Error);
+ Q_ASSERT(m_errors.isEmpty());
+
+ m_errors = errors; // Must be set before the m_data fence
+ m_data.setStatus(Error);
cancelAllWaitingFor();
@@ -327,19 +437,25 @@ void QDeclarativeDataBlob::setError(const QList<QDeclarativeError> &errors)
/*!
Wait for \a blob to become complete or to error. If \a blob is already
complete or in error, or this blob is already complete, this has no effect.
+
+The setError() method may only be called from within a QDeclarativeDataBlob callback.
*/
void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob)
{
+ ASSERT_CALLBACK();
+
Q_ASSERT(status() != Null);
if (!blob ||
blob->status() == Error || blob->status() == Complete ||
- status() == Error || status() == Complete ||
+ status() == Error || status() == Complete || m_isDone ||
m_waitingFor.contains(blob))
return;
blob->addref();
- m_status = WaitingForDependencies;
+
+ m_data.setStatus(WaitingForDependencies);
+
m_waitingFor.append(blob);
blob->m_waitingOnMe.append(this);
}
@@ -360,6 +476,9 @@ You can set an error in this method, but you cannot add new dependencies. Imple
should use this callback to finalize processing of data.
The default implementation does nothing.
+
+XXX Rename processData() or some such to avoid confusion between done() (processing thread)
+and completed() (main thread)
*/
void QDeclarativeDataBlob::done()
{
@@ -451,22 +570,63 @@ void QDeclarativeDataBlob::allDependenciesDone()
/*!
Called when the download progress of this blob changes. \a progress goes
from 0 to 1.
+
+This callback is only invoked if an asynchronous load for this blob is
+made. An asynchronous load is one in which the Asynchronous mode is
+specified explicitly, or one that is implicitly delayed due to a network
+operation.
+
+The default implementation does nothing.
*/
void QDeclarativeDataBlob::downloadProgressChanged(qreal progress)
{
Q_UNUSED(progress);
}
+/*!
+Invoked on the main thread sometime after done() was called on the load thread.
+
+You cannot modify the blobs state at all in this callback and cannot depend on the
+order or timeliness of these callbacks. Implementors should use this callback to notify
+dependencies on the main thread that the blob is done and not a lot else.
+
+This callback is only invoked if an asynchronous load for this blob is
+made. An asynchronous load is one in which the Asynchronous mode is
+specified explicitly, or one that is implicitly delayed due to a network
+operation.
+
+The default implementation does nothing.
+*/
+void QDeclarativeDataBlob::completed()
+{
+}
+
+
void QDeclarativeDataBlob::tryDone()
{
if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
- if (status() != Error)
- m_status = Complete;
-
m_isDone = true;
addref();
+
+#ifdef DATABLOB_DEBUG
+ qWarning("QDeclarativeDataBlob::done() %s", qPrintable(url().toString()));
+#endif
done();
+
+ if (status() != Error)
+ m_data.setStatus(Complete);
+
notifyAllWaitingOnMe();
+
+ // Locking is not required here, as anyone expecting callbacks must
+ // already be protected against the blob being completed (as set above);
+ if (m_data.isAsync()) {
+#ifdef DATABLOB_DEBUG
+ qWarning("QDeclarativeDataBlob: Dispatching completed");
+#endif
+ m_manager->m_thread->callCompleted(this);
+ }
+
release();
}
}
@@ -519,6 +679,170 @@ void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob)
tryDone();
}
+#define TD_STATUS_MASK 0x0000FFFF
+#define TD_STATUS_SHIFT 0
+#define TD_PROGRESS_MASK 0x00FF0000
+#define TD_PROGRESS_SHIFT 16
+#define TD_ASYNC_MASK 0x80000000
+
+QDeclarativeDataBlob::ThreadData::ThreadData()
+: _p(0)
+{
+}
+
+QDeclarativeDataBlob::Status QDeclarativeDataBlob::ThreadData::status() const
+{
+ return QDeclarativeDataBlob::Status((_p & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
+}
+
+void QDeclarativeDataBlob::ThreadData::setStatus(QDeclarativeDataBlob::Status status)
+{
+ while (true) {
+ int d = _p;
+ int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK);
+ if (d == nd || _p.testAndSetOrdered(d, nd)) return;
+ }
+}
+
+bool QDeclarativeDataBlob::ThreadData::isAsync() const
+{
+ return _p & TD_ASYNC_MASK;
+}
+
+void QDeclarativeDataBlob::ThreadData::setIsAsync(bool v)
+{
+ while (true) {
+ int d = _p;
+ int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0);
+ if (d == nd || _p.testAndSetOrdered(d, nd)) return;
+ }
+}
+
+quint8 QDeclarativeDataBlob::ThreadData::progress() const
+{
+ return quint8((_p & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
+}
+
+void QDeclarativeDataBlob::ThreadData::setProgress(quint8 v)
+{
+ while (true) {
+ int d = _p;
+ int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK);
+ if (d == nd || _p.testAndSetOrdered(d, nd)) return;
+ }
+}
+
+QDeclarativeDataLoaderThread::QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader)
+: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
+{
+}
+
+QNetworkAccessManager *QDeclarativeDataLoaderThread::networkAccessManager() const
+{
+ Q_ASSERT(isThisThread());
+ if (!m_networkAccessManager) {
+ m_networkAccessManager = QDeclarativeEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0);
+ m_networkReplyProxy = new QDeclarativeDataLoaderNetworkReplyProxy(m_loader);
+ }
+
+ return m_networkAccessManager;
+}
+
+QDeclarativeDataLoaderNetworkReplyProxy *QDeclarativeDataLoaderThread::networkReplyProxy() const
+{
+ Q_ASSERT(isThisThread());
+ Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
+ return m_networkReplyProxy;
+}
+
+void QDeclarativeDataLoaderThread::load(QDeclarativeDataBlob *b)
+{
+ b->addref();
+ callMethodInThread(&This::loadThread, b);
+}
+
+void QDeclarativeDataLoaderThread::loadAsync(QDeclarativeDataBlob *b)
+{
+ b->addref();
+ postMethodToThread(&This::loadThread, b);
+}
+
+void QDeclarativeDataLoaderThread::loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &d)
+{
+ b->addref();
+ callMethodInThread(&This::loadWithStaticDataThread, b, d);
+}
+
+void QDeclarativeDataLoaderThread::loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &d)
+{
+ b->addref();
+ postMethodToThread(&This::loadWithStaticDataThread, b, d);
+}
+
+void QDeclarativeDataLoaderThread::callCompleted(QDeclarativeDataBlob *b)
+{
+ b->addref();
+ postMethodToMain(&This::callCompletedMain, b);
+}
+
+void QDeclarativeDataLoaderThread::callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p)
+{
+ b->addref();
+ postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
+}
+
+void QDeclarativeDataLoaderThread::initializeEngine(QDeclarativeExtensionInterface *iface,
+ const char *uri)
+{
+ callMethodInMain(&This::initializeEngineMain, iface, uri);
+}
+
+void QDeclarativeDataLoaderThread::shutdownThread()
+{
+ delete m_networkAccessManager;
+ m_networkAccessManager = 0;
+ delete m_networkReplyProxy;
+ m_networkReplyProxy = 0;
+}
+
+void QDeclarativeDataLoaderThread::loadThread(QDeclarativeDataBlob *b)
+{
+ m_loader->loadThread(b);
+ b->release();
+}
+
+void QDeclarativeDataLoaderThread::loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &d)
+{
+ m_loader->loadWithStaticDataThread(b, d);
+ b->release();
+}
+
+void QDeclarativeDataLoaderThread::callCompletedMain(QDeclarativeDataBlob *b)
+{
+#ifdef DATABLOB_DEBUG
+ qWarning("QDeclarativeDataLoaderThread: %s completed() callback", qPrintable(b->url().toString()));
+#endif
+ b->completed();
+ b->release();
+}
+
+void QDeclarativeDataLoaderThread::callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p)
+{
+#ifdef DATABLOB_DEBUG
+ qWarning("QDeclarativeDataLoaderThread: %s downloadProgressChanged(%f) callback",
+ qPrintable(b->url().toString()), p);
+#endif
+ b->downloadProgressChanged(p);
+ b->release();
+}
+
+void QDeclarativeDataLoaderThread::initializeEngineMain(QDeclarativeExtensionInterface *iface,
+ const char *uri)
+{
+ Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
+ iface->initializeEngine(m_loader->engine(), uri);
+}
+
/*!
\class QDeclarativeDataLoader
\brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network.
@@ -553,7 +877,7 @@ Thus QDeclarativeDataBlob::done() will always eventually be called, even if the
Create a new QDeclarativeDataLoader for \a engine.
*/
QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine)
-: m_engine(engine)
+: m_engine(engine), m_thread(new QDeclarativeDataLoaderThread(this))
{
}
@@ -562,17 +886,105 @@ QDeclarativeDataLoader::~QDeclarativeDataLoader()
{
for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter)
(*iter)->release();
+
+ m_thread->shutdown();
+ delete m_thread;
+}
+
+void QDeclarativeDataLoader::lock()
+{
+ m_thread->lock();
+}
+
+void QDeclarativeDataLoader::unlock()
+{
+ m_thread->unlock();
}
/*!
Load the provided \a blob from the network or filesystem.
+
+The loader must be locked.
+*/
+void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob, Mode mode)
+{
+#ifdef DATABLOB_DEBUG
+ qWarning("QDeclarativeDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()),
+ m_thread->isThisThread()?"Compile":"Engine");
+#endif
+
+ Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
+ Q_ASSERT(blob->m_manager == 0);
+
+ blob->m_data.setStatus(QDeclarativeDataBlob::Loading);
+ blob->m_manager = this;
+
+ if (m_thread->isThisThread()) {
+ unlock();
+ loadThread(blob);
+ lock();
+ } else if (mode == PreferSynchronous) {
+ unlock();
+ m_thread->load(blob);
+ lock();
+ if (!blob->isCompleteOrError())
+ blob->m_data.setIsAsync(true);
+ } else {
+ Q_ASSERT(mode == Asynchronous);
+ blob->m_data.setIsAsync(true);
+ unlock();
+ m_thread->loadAsync(blob);
+ lock();
+ }
+}
+
+/*!
+Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case.
+
+The loader must be locked.
*/
-void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
+void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data, Mode mode)
{
+#ifdef DATABLOB_DEBUG
+ qWarning("QDeclarativeDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()),
+ m_thread->isThisThread()?"Compile":"Engine");
+#endif
+
Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
Q_ASSERT(blob->m_manager == 0);
+
+ blob->m_data.setStatus(QDeclarativeDataBlob::Loading);
+ blob->m_manager = this;
+
+ if (m_thread->isThisThread()) {
+ unlock();
+ loadWithStaticDataThread(blob, data);
+ lock();
+ } else if (mode == PreferSynchronous) {
+ unlock();
+ m_thread->loadWithStaticData(blob, data);
+ lock();
+ if (!blob->isCompleteOrError())
+ blob->m_data.setIsAsync(true);
+ } else {
+ Q_ASSERT(mode == Asynchronous);
+ blob->m_data.setIsAsync(true);
+ unlock();
+ m_thread->loadWithStaticDataAsync(blob, data);
+ lock();
+ }
+}
- blob->m_status = QDeclarativeDataBlob::Loading;
+void QDeclarativeDataLoader::loadWithStaticDataThread(QDeclarativeDataBlob *blob, const QByteArray &data)
+{
+ ASSERT_LOADTHREAD();
+
+ setData(blob, data);
+}
+
+void QDeclarativeDataLoader::loadThread(QDeclarativeDataBlob *blob)
+{
+ ASSERT_LOADTHREAD();
if (blob->m_url.isEmpty()) {
QDeclarativeError error;
@@ -595,8 +1007,9 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
if (file.open(QFile::ReadOnly)) {
QByteArray data = file.readAll();
- blob->m_progress = 1.;
- blob->downloadProgressChanged(1.);
+ blob->m_data.setProgress(0xFF);
+ if (blob->m_data.isAsync())
+ m_thread->callDownloadProgressChanged(blob, 1.);
setData(blob, data);
} else {
@@ -605,12 +1018,12 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
} else {
- blob->m_manager = this;
- QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(blob->m_url));
+ QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
+ QObject *nrp = m_thread->networkReplyProxy();
QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
- this, SLOT(networkReplyProgress(qint64,qint64)));
+ nrp, SLOT(downloadProgress(qint64,qint64)));
QObject::connect(reply, SIGNAL(finished()),
- this, SLOT(networkReplyFinished()));
+ nrp, SLOT(finished()));
m_networkReplies.insert(reply, blob);
blob->addref();
@@ -619,9 +1032,10 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
-void QDeclarativeDataLoader::networkReplyFinished()
+void QDeclarativeDataLoader::networkReplyFinished(QNetworkReply *reply)
{
- QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
+ Q_ASSERT(m_thread->isThisThread());
+
reply->deleteLater();
QDeclarativeDataBlob *blob = m_networkReplies.take(reply);
@@ -636,8 +1050,9 @@ void QDeclarativeDataLoader::networkReplyFinished()
QUrl url = reply->url().resolved(redirect.toUrl());
blob->m_finalUrl = url;
- QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(url));
- QObject::connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished()));
+ QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url));
+ QObject *nrp = m_thread->networkReplyProxy();
+ QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished()));
m_networkReplies.insert(reply, blob);
return;
}
@@ -653,40 +1068,49 @@ void QDeclarativeDataLoader::networkReplyFinished()
blob->release();
}
-void QDeclarativeDataLoader::networkReplyProgress(qint64 bytesReceived, qint64 bytesTotal)
+void QDeclarativeDataLoader::networkReplyProgress(QNetworkReply *reply,
+ qint64 bytesReceived, qint64 bytesTotal)
{
- QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
+ Q_ASSERT(m_thread->isThisThread());
+
QDeclarativeDataBlob *blob = m_networkReplies.value(reply);
Q_ASSERT(blob);
if (bytesTotal != 0) {
- blob->m_progress = bytesReceived / bytesTotal;
- blob->downloadProgressChanged(blob->m_progress);
+ quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal));
+ blob->m_data.setProgress(progress);
+ if (blob->m_data.isAsync())
+ m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
}
}
/*!
-Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case.
+Return the QDeclarativeEngine associated with this loader
*/
-void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data)
+QDeclarativeEngine *QDeclarativeDataLoader::engine() const
{
- Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
- Q_ASSERT(blob->m_manager == 0);
-
- blob->m_status = QDeclarativeDataBlob::Loading;
-
- setData(blob, data);
+ return m_engine;
}
/*!
-Return the QDeclarativeEngine associated with this loader
+Call the initializeEngine() method on \a iface. Used by QDeclarativeImportDatabase to ensure it
+gets called in the correct thread.
*/
-QDeclarativeEngine *QDeclarativeDataLoader::engine() const
+void QDeclarativeDataLoader::initializeEngine(QDeclarativeExtensionInterface *iface,
+ const char *uri)
{
- return m_engine;
+ Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread());
+
+ if (m_thread->isThisThread()) {
+ m_thread->initializeEngine(iface, uri);
+ } else {
+ Q_ASSERT(engine()->thread() == QThread::currentThread());
+ iface->initializeEngine(engine(), uri);
+ }
}
+
void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data)
{
blob->m_inCallback = true;
@@ -696,8 +1120,8 @@ void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArra
if (!blob->isError() && !blob->isWaiting())
blob->allDependenciesDone();
- if (blob->status() != QDeclarativeDataBlob::Error)
- blob->m_status = QDeclarativeDataBlob::WaitingForDependencies;
+ if (blob->status() != QDeclarativeDataBlob::Error)
+ blob->m_data.setStatus(QDeclarativeDataBlob::WaitingForDependencies);
blob->m_inCallback = false;
@@ -741,6 +1165,8 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
!QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
+ lock();
+
QDeclarativeTypeData *typeData = m_typeCache.value(url);
if (!typeData) {
@@ -750,6 +1176,9 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
}
typeData->addref();
+
+ unlock();
+
return typeData;
}
@@ -761,8 +1190,13 @@ The specified \a options control how the loader handles type data.
*/
QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
{
+ lock();
+
QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
QDeclarativeDataLoader::loadWithStaticData(typeData, data);
+
+ unlock();
+
return typeData;
}
@@ -775,6 +1209,8 @@ QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
!QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
+ lock();
+
QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
if (!scriptBlob) {
@@ -783,6 +1219,10 @@ QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
QDeclarativeDataLoader::load(scriptBlob);
}
+ scriptBlob->addref();
+
+ unlock();
+
return scriptBlob;
}
@@ -795,6 +1235,8 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
!QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
+ lock();
+
QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
if (!qmldirData) {
@@ -804,6 +1246,9 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
}
qmldirData->addref();
+
+ unlock();
+
return qmldirData;
}
@@ -914,6 +1359,7 @@ const QDeclarativeDirParser *QDeclarativeTypeLoader::qmlDirParser(const QString
return qmldirParser;
}
+
/*!
Clears cached information about loaded files, including any type data, scripts
and qmldir information.
@@ -1004,8 +1450,6 @@ void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback)
void QDeclarativeTypeData::done()
{
- addref();
-
// Check all script dependencies for errors
for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
const ScriptReference &script = m_scripts.at(ii);
@@ -1046,14 +1490,15 @@ void QDeclarativeTypeData::done()
if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
scriptParser.clear();
+}
+void QDeclarativeTypeData::completed()
+{
// Notify callbacks
while (!m_callbacks.isEmpty()) {
TypeDataCallback *callback = m_callbacks.takeFirst();
callback->typeDataReady(this);
}
-
- release();
}
void QDeclarativeTypeData::dataReceived(const QByteArray &data)
@@ -1082,7 +1527,6 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data)
ref.location = import.location.start;
ref.qualifier = import.qualifier;
ref.script = blob;
- blob->addref();
m_scripts << ref;
}
@@ -1270,15 +1714,13 @@ QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
return 0;
}
-QDeclarativeScriptData::QDeclarativeScriptData(QDeclarativeEngine *engine)
-: QDeclarativeCleanup(engine), importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None),
- m_loaded(false)
+QDeclarativeScriptData::QDeclarativeScriptData()
+: importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), m_loaded(false)
{
}
QDeclarativeScriptData::~QDeclarativeScriptData()
{
- clear();
}
void QDeclarativeScriptData::clear()
@@ -1294,6 +1736,9 @@ void QDeclarativeScriptData::clear()
qPersistentDispose(m_program);
qPersistentDispose(m_value);
+
+ // An addref() was made when the QDeclarativeCleanup was added to the engine.
+ release();
}
QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader)
@@ -1361,7 +1806,6 @@ void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
ref.location = import.location.start;
ref.qualifier = import.qualifier;
ref.script = blob;
- blob->addref();
m_scripts << ref;
} else {
Q_ASSERT(import.type == QDeclarativeScript::Import::Library);
@@ -1408,9 +1852,9 @@ void QDeclarativeScriptBlob::done()
return;
QDeclarativeEngine *engine = typeLoader()->engine();
- m_scriptData = new QDeclarativeScriptData(engine);
+ m_scriptData = new QDeclarativeScriptData();
m_scriptData->url = finalUrl();
- m_scriptData->importCache = new QDeclarativeTypeNameCache(engine);
+ m_scriptData->importCache = new QDeclarativeTypeNameCache();
for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
const ScriptReference &script = m_scripts.at(ii);
@@ -1422,13 +1866,7 @@ void QDeclarativeScriptBlob::done()
m_imports.populateCache(m_scriptData->importCache, engine);
m_scriptData->pragmas = m_pragmas;
-
- // XXX TODO: Handle errors that occur duing the script compile
- QV8Engine *v8engine = QDeclarativeEnginePrivate::get(engine)->v8engine();
- v8::HandleScope handle_scope;
- v8::Context::Scope scope(v8engine->context());
- v8::Local<v8::Script> program = v8engine->qmlModeCompile(m_source, finalUrl().toString(), 1);
- m_scriptData->m_program = qPersistentNew<v8::Script>(program);
+ m_scriptData->m_programSource = m_source;
}
QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
@@ -1451,3 +1889,4 @@ void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
QT_END_NAMESPACE
+#include "qdeclarativetypeloader.moc"