Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativetypeloader.cpp
index 97c5b38..b507f20 100644 (file)
@@ -1,8 +1,7 @@
 /****************************************************************************
 **
 /****************************************************************************
 **
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
 **
 ** This file is part of the QtDeclarative module of the Qt Toolkit.
 **
 **
 ** This file is part of the QtDeclarative module of the Qt Toolkit.
 **
@@ -35,6 +34,7 @@
 **
 **
 **
 **
 **
 **
+**
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/
 #include "qdeclarativetypeloader_p.h"
 
 #include <private/qdeclarativeengine_p.h>
 #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/qdeclarativecompiler_p.h>
 #include <private/qdeclarativecomponent_p.h>
-#include <private/qdeclarativeglobal_p.h>
 #include <private/qdeclarativedebugtrace_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/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
 
 
 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
+is responsible for deleting the returned data.
+Returns 0 if the directory does not exist.
+*/
+#if defined (Q_OS_UNIX) && !defined(Q_OS_DARWIN)
+static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
+{
+    QByteArray name(QFile::encodeName(path));
+    DIR *dd = opendir(name);
+    if (!dd)
+        return 0;
+
+    struct dirent *result;
+    union {
+        struct dirent d;
+        char b[offsetof (struct dirent, d_name) + NAME_MAX + 1];
+    } u;
+
+    QStringHash<bool> *files = new QStringHash<bool>;
+    while (readdir_r(dd, &u.d, &result) == 0 && result != 0) {
+        if (!strcmp(u.d.d_name, "qmldir")) {
+            files->insert(QLatin1String("qmldir"), true);
+            continue;
+        }
+        int len = strlen(u.d.d_name);
+        if (len < 4)
+            continue;
+        if (!strcmp(u.d.d_name+len-4, ".qml") || !strcmp(u.d.d_name+len-3, ".js"))
+            files->insert(QFile::decodeName(u.d.d_name), true);
+#if defined(Q_OS_DARWIN)
+        else if ((len > 6 && !strcmp(u.d.d_name+len-6, ".dylib")) || !strcmp(u.d.d_name+len-3, ".so")
+                  || (len > 7 && !strcmp(u.d.d_name+len-7, ".bundle")))
+            files->insert(QFile::decodeName(u.d.d_name), true);
+#else  // Unix
+        else if (!strcmp(u.d.d_name+len-3, ".so") || !strcmp(u.d.d_name+len-3, ".sl"))
+            files->insert(QFile::decodeName(u.d.d_name), true);
+#endif
+    }
+
+    closedir(dd);
+    return files;
+}
+#else
+static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
+{
+    QDirIterator dir(path, QDir::Files);
+    if (!dir.hasNext())
+        return 0;
+    QStringHash<bool> *files = new QStringHash<bool>;
+    while (dir.hasNext()) {
+        dir.next();
+        QString fileName = dir.fileName();
+        if (fileName == QLatin1String("qmldir")
+                || fileName.endsWith(QLatin1String(".qml"))
+                || fileName.endsWith(QLatin1String(".js"))
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+                || fileName.endsWith(QLatin1String(".dll"))
+#elif defined(Q_OS_DARWIN)
+                || fileName.endsWith(QLatin1String(".dylib"))
+                || fileName.endsWith(QLatin1String(".so"))
+                || fileName.endsWith(QLatin1String(".bundle"))
+#else  // Unix
+                || fileName.endsWith(QLatin1String(".so"))
+                || fileName.endsWith(QLatin1String(".sl"))
+#endif
+                ) {
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
+            fileName = fileName.toLower();
+#endif
+            files->insert(fileName, true);
+        }
+    }
+    return files;
+}
+#endif
+
+
 /*!
 \class QDeclarativeDataBlob
 \brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader.
 /*!
 \class QDeclarativeDataBlob
 \brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader.
@@ -97,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)
 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)
 {
 }
 
 {
 }
 
@@ -123,7 +301,7 @@ Returns the blob's status.
 */
 QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const
 {
 */
 QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const
 {
-    return m_status;
+    return m_data.status();
 }
 
 /*!
 }
 
 /*!
@@ -131,7 +309,7 @@ Returns true if the status is Null.
 */
 bool QDeclarativeDataBlob::isNull() const
 {
 */
 bool QDeclarativeDataBlob::isNull() const
 {
-    return m_status == Null;
+    return status() == Null;
 }
 
 /*!
 }
 
 /*!
@@ -139,7 +317,7 @@ Returns true if the status is Loading.
 */
 bool QDeclarativeDataBlob::isLoading() const
 {
 */
 bool QDeclarativeDataBlob::isLoading() const
 {
-    return m_status == Loading;
+    return status() == Loading;
 }
 
 /*!
 }
 
 /*!
@@ -147,7 +325,7 @@ Returns true if the status is WaitingForDependencies.
 */
 bool QDeclarativeDataBlob::isWaiting() const
 {
 */
 bool QDeclarativeDataBlob::isWaiting() const
 {
-    return m_status == WaitingForDependencies;
+    return status() == WaitingForDependencies;
 }
 
 /*!
 }
 
 /*!
@@ -155,7 +333,7 @@ Returns true if the status is Complete.
 */
 bool QDeclarativeDataBlob::isComplete() const
 {
 */
 bool QDeclarativeDataBlob::isComplete() const
 {
-    return m_status == Complete;
+    return status() == Complete;
 }
 
 /*!
 }
 
 /*!
@@ -163,7 +341,7 @@ Returns true if the status is Error.
 */
 bool QDeclarativeDataBlob::isError() const
 {
 */
 bool QDeclarativeDataBlob::isError() const
 {
-    return m_status == Error;
+    return status() == Error;
 }
 
 /*!
 }
 
 /*!
@@ -171,7 +349,8 @@ Returns true if the status is Complete or Error.
 */
 bool QDeclarativeDataBlob::isCompleteOrError() const
 {
 */
 bool QDeclarativeDataBlob::isCompleteOrError() const
 {
-    return isComplete() || isError();
+    Status s = status();
+    return s == Error || s == Complete;
 }
 
 /*!
 }
 
 /*!
@@ -179,7 +358,9 @@ Returns the data download progress from 0 to 1.
 */
 qreal QDeclarativeDataBlob::progress() const
 {
 */
 qreal QDeclarativeDataBlob::progress() const
 {
-    return m_progress;
+    quint8 p = m_data.progress();
+    if (p == 0xFF) return 1.;
+    else return qreal(p) / qreal(0xFF);
 }
 
 /*!
 }
 
 /*!
@@ -197,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.
 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
 {
 */
 QUrl QDeclarativeDataBlob::finalUrl() const
 {
+    Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
     return m_finalUrl;
 }
 
 /*!
 Return the errors on this blob.
     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
 {
 */
 QList<QDeclarativeError> QDeclarativeDataBlob::errors() const
 {
+    Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
     return m_errors;
 }
 
     return m_errors;
 }
 
@@ -215,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 
 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)
 {
 */
 void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
 {
+    ASSERT_CALLBACK();
+
     QList<QDeclarativeError> l;
     l << errors;
     setError(l);
     QList<QDeclarativeError> l;
     l << errors;
     setError(l);
@@ -230,8 +420,13 @@ void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
 */
 void QDeclarativeDataBlob::setError(const QList<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();
 
 
     cancelAllWaitingFor();
 
@@ -242,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.
 /*! 
 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)
 {
 */
 void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob)
 {
+    ASSERT_CALLBACK();
+
     Q_ASSERT(status() != Null);
 
     if (!blob ||
         blob->status() == Error || blob->status() == Complete ||
     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_waitingFor.contains(blob))
         return;
 
     blob->addref();
-    m_status = WaitingForDependencies;
+
+    m_data.setStatus(WaitingForDependencies);
+
     m_waitingFor.append(blob);
     blob->m_waitingOnMe.append(this);
 }
     m_waitingFor.append(blob);
     blob->m_waitingOnMe.append(this);
 }
@@ -275,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.
 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()
 {
 */
 void QDeclarativeDataBlob::done()
 {
@@ -366,22 +570,63 @@ void QDeclarativeDataBlob::allDependenciesDone()
 /*!
 Called when the download progress of this blob changes.  \a progress goes
 from 0 to 1.
 /*!
 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);
 }
 
 */
 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) {
 void QDeclarativeDataBlob::tryDone()
 {
     if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
-        if (status() != Error)
-            m_status = Complete;
-
         m_isDone = true;
         addref();
         m_isDone = true;
         addref();
+
+#ifdef DATABLOB_DEBUG
+        qWarning("QDeclarativeDataBlob::done() %s", qPrintable(url().toString()));
+#endif
         done();
         done();
+
+        if (status() != Error)
+            m_data.setStatus(Complete);
+
         notifyAllWaitingOnMe();
         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();
     }
 }
         release();
     }
 }
@@ -434,6 +679,170 @@ void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob)
     tryDone();
 }
 
     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.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
+}
+
+void QDeclarativeDataBlob::ThreadData::setStatus(QDeclarativeDataBlob::Status status)
+{
+    while (true) {
+        int d = _p.load();
+        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.load() & TD_ASYNC_MASK;
+}
+
+void QDeclarativeDataBlob::ThreadData::setIsAsync(bool v)
+{
+    while (true) {
+        int d = _p.load();
+        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.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
+}
+
+void QDeclarativeDataBlob::ThreadData::setProgress(quint8 v)
+{
+    while (true) {
+        int d = _p.load();
+        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.
 /*!
 \class QDeclarativeDataLoader
 \brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network.
@@ -468,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)
 Create a new QDeclarativeDataLoader for \a engine.
 */
 QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine)
-: m_engine(engine)
+: m_engine(engine), m_thread(new QDeclarativeDataLoaderThread(this))
 {
 }
 
 {
 }
 
@@ -477,17 +886,105 @@ QDeclarativeDataLoader::~QDeclarativeDataLoader()
 {
     for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) 
         (*iter)->release();
 {
     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.
 }
 
 /*!
 Load the provided \a blob from the network or filesystem.
+
+The loader must be locked.
 */
 */
-void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
+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);
 
     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
     Q_ASSERT(blob->m_manager == 0);
 
-    blob->m_status = QDeclarativeDataBlob::Loading;
+    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::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();
+    }
+}
+
+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;
 
     if (blob->m_url.isEmpty()) {
         QDeclarativeError error;
@@ -510,8 +1007,9 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
         if (file.open(QFile::ReadOnly)) {
             QByteArray data = file.readAll();
 
         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 {
 
             setData(blob, data);
         } else {
@@ -520,12 +1018,12 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
 
     } else {
 
 
     } 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)), 
         QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), 
-                         this, SLOT(networkReplyProgress(qint64,qint64)));
+                         nrp, SLOT(downloadProgress(qint64,qint64)));
         QObject::connect(reply, SIGNAL(finished()), 
         QObject::connect(reply, SIGNAL(finished()), 
-                         this, SLOT(networkReplyFinished()));
+                         nrp, SLOT(finished()));
         m_networkReplies.insert(reply, blob);
 
         blob->addref();
         m_networkReplies.insert(reply, blob);
 
         blob->addref();
@@ -534,9 +1032,10 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
 
 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
 
 
 #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);
     reply->deleteLater();
 
     QDeclarativeDataBlob *blob = m_networkReplies.take(reply);
@@ -551,8 +1050,9 @@ void QDeclarativeDataLoader::networkReplyFinished()
             QUrl url = reply->url().resolved(redirect.toUrl());
             blob->m_finalUrl = url;
 
             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;
         }
             m_networkReplies.insert(reply, blob);
             return;
         }
@@ -568,40 +1068,49 @@ void QDeclarativeDataLoader::networkReplyFinished()
     blob->release();
 }
 
     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) {
     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;
 void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data)
 {
     blob->m_inCallback = true;
@@ -611,8 +1120,8 @@ void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArra
     if (!blob->isError() && !blob->isWaiting())
         blob->allDependenciesDone();
 
     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;
 
 
     blob->m_inCallback = false;
 
@@ -656,6 +1165,8 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
 
             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
 
+    lock();
+    
     QDeclarativeTypeData *typeData = m_typeCache.value(url);
 
     if (!typeData) {
     QDeclarativeTypeData *typeData = m_typeCache.value(url);
 
     if (!typeData) {
@@ -665,6 +1176,9 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
     }
 
     typeData->addref();
     }
 
     typeData->addref();
+
+    unlock();
+
     return typeData;
 }
 
     return typeData;
 }
 
@@ -676,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)
 {
 */
 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
 {
+    lock();
+
     QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
     QDeclarativeDataLoader::loadWithStaticData(typeData, data);
     QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
     QDeclarativeDataLoader::loadWithStaticData(typeData, data);
+
+    unlock();
+
     return typeData;
 }
 
     return typeData;
 }
 
@@ -690,6 +1209,8 @@ QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
 
             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
 
+    lock();
+
     QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
 
     if (!scriptBlob) {
     QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
 
     if (!scriptBlob) {
@@ -698,6 +1219,10 @@ QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
         QDeclarativeDataLoader::load(scriptBlob);
     }
 
         QDeclarativeDataLoader::load(scriptBlob);
     }
 
+    scriptBlob->addref();
+
+    unlock();
+
     return scriptBlob;
 }
 
     return scriptBlob;
 }
 
@@ -710,6 +1235,8 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
 
             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
 
+    lock();
+
     QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
 
     if (!qmldirData) {
     QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
 
     if (!qmldirData) {
@@ -719,10 +1246,121 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
     }
 
     qmldirData->addref();
     }
 
     qmldirData->addref();
+
+    unlock();
+
     return qmldirData;
 }
 
 /*!
     return qmldirData;
 }
 
 /*!
+Returns the absolute filename of path via a directory cache for files named
+"qmldir", "*.qml", "*.js", and plugins.
+Returns a empty string if the path does not exist.
+*/
+QString QDeclarativeTypeLoader::absoluteFilePath(const QString &path)
+{
+    if (path.isEmpty())
+        return QString();
+    if (path.at(0) == QLatin1Char(':')) {
+        // qrc resource
+        QFileInfo fileInfo(path);
+        return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
+    }
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
+    QString lowPath = path.toLower();
+    int lastSlash = lowPath.lastIndexOf(QLatin1Char('/'));
+    QString dirPath = lowPath.left(lastSlash);
+#else
+    int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+    QStringRef dirPath(&path, 0, lastSlash);
+#endif
+
+    StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
+    if (!fileSet) {
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
+        QHashedString dirPathString(dirPath);
+#else
+        QHashedString dirPathString(dirPath.toString());
+#endif
+        StringSet *files = qmlFilesInDirectory(dirPathString);
+        m_importDirCache.insert(dirPathString, files);
+        fileSet = m_importDirCache.value(dirPathString);
+    }
+    if (!(*fileSet))
+        return QString();
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
+    QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(lowPath.constData()+lastSlash+1, lowPath.length()-lastSlash-1)) ? path : QString();
+#else
+    QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(path.constData()+lastSlash+1, path.length()-lastSlash-1)) ? path : QString();
+#endif
+    if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':'))
+        absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath();
+
+    return absoluteFilePath;
+}
+
+/*!
+Returns true if the path is a directory via a directory cache.  Cache is
+shared with absoluteFilePath().
+*/
+bool QDeclarativeTypeLoader::directoryExists(const QString &path)
+{
+    if (path.isEmpty())
+        return false;
+    if (path.at(0) == QLatin1Char(':')) {
+        // qrc resource
+        QFileInfo fileInfo(path);
+        return fileInfo.exists() && fileInfo.isDir();
+    }
+
+    int length = path.length();
+    if (path.endsWith(QLatin1Char('/')))
+        --length;
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
+    QString dirPath = path.left(length).toLower();
+#else
+    QStringRef dirPath(&path, 0, length);
+#endif
+
+    StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
+    if (!fileSet) {
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
+        QHashedString dirPathString(dirPath);
+#else
+        QHashedString dirPathString(dirPath.toString());
+#endif
+        StringSet *files = qmlFilesInDirectory(dirPathString);
+        m_importDirCache.insert(dirPathString, files);
+        fileSet = m_importDirCache.value(dirPathString);
+    }
+
+    return (*fileSet);
+}
+
+
+/*!
+Return a QDeclarativeDirParser for absoluteFilePath.  The QDeclarativeDirParser may be cached.
+*/
+const QDeclarativeDirParser *QDeclarativeTypeLoader::qmlDirParser(const QString &absoluteFilePath)
+{
+    QDeclarativeDirParser *qmldirParser;
+    QDeclarativeDirParser **val = m_importQmlDirCache.value(absoluteFilePath);
+    if (!val) {
+        qmldirParser = new QDeclarativeDirParser;
+        qmldirParser->setFileSource(absoluteFilePath);
+        qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath));
+        qmldirParser->parse();
+        m_importQmlDirCache.insert(absoluteFilePath, qmldirParser);
+    } else {
+        qmldirParser = *val;
+    }
+
+    return qmldirParser;
+}
+
+
+/*!
 Clears cached information about loaded files, including any type data, scripts
 and qmldir information.
 */
 Clears cached information about loaded files, including any type data, scripts
 and qmldir information.
 */
@@ -734,17 +1372,21 @@ void QDeclarativeTypeLoader::clearCache()
         (*iter)->release();
     for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) 
         (*iter)->release();
         (*iter)->release();
     for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) 
         (*iter)->release();
+    qDeleteAll(m_importDirCache);
+    qDeleteAll(m_importQmlDirCache);
 
     m_typeCache.clear();
     m_scriptCache.clear();
     m_qmldirCache.clear();
 
     m_typeCache.clear();
     m_scriptCache.clear();
     m_qmldirCache.clear();
+    m_importDirCache.clear();
+    m_importQmlDirCache.clear();
 }
 
 
 QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, 
                                            QDeclarativeTypeLoader *manager)
 }
 
 
 QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, 
                                            QDeclarativeTypeLoader *manager)
-: QDeclarativeDataBlob(url, QmlFile), m_options(options), m_typesResolved(false), 
-  m_compiledData(0), m_typeLoader(manager)
+: QDeclarativeDataBlob(url, QmlFile), m_options(options), m_imports(manager), m_typesResolved(false),
+   m_compiledData(0), m_typeLoader(manager)
 {
 }
 
 {
 }
 
@@ -770,7 +1412,7 @@ const QDeclarativeImports &QDeclarativeTypeData::imports() const
     return m_imports;
 }
 
     return m_imports;
 }
 
-const QDeclarativeScriptParser &QDeclarativeTypeData::parser() const
+const QDeclarativeScript::Parser &QDeclarativeTypeData::parser() const
 {
     return scriptParser;
 }
 {
     return scriptParser;
 }
@@ -785,6 +1427,11 @@ const QList<QDeclarativeTypeData::ScriptReference> &QDeclarativeTypeData::resolv
     return m_scripts;
 }
 
     return m_scripts;
 }
 
+const QSet<QString> &QDeclarativeTypeData::namespaces() const
+{
+    return m_namespaces;
+}
+
 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
 {
     if (m_compiledData) 
 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
 {
     if (m_compiledData) 
@@ -808,8 +1455,6 @@ void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback)
 
 void QDeclarativeTypeData::done()
 {
 
 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);
     // Check all script dependencies for errors
     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
         const ScriptReference &script = m_scripts.at(ii);
@@ -850,14 +1495,15 @@ void QDeclarativeTypeData::done()
 
     if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
         scriptParser.clear();
 
     if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
         scriptParser.clear();
+}
 
 
+void QDeclarativeTypeData::completed()
+{
     // Notify callbacks
     while (!m_callbacks.isEmpty()) {
         TypeDataCallback *callback = m_callbacks.takeFirst();
         callback->typeDataReady(this);
     }
     // Notify callbacks
     while (!m_callbacks.isEmpty()) {
         TypeDataCallback *callback = m_callbacks.takeFirst();
         callback->typeDataReady(this);
     }
-
-    release();
 }
 
 void QDeclarativeTypeData::dataReceived(const QByteArray &data)
 }
 
 void QDeclarativeTypeData::dataReceived(const QByteArray &data)
@@ -869,15 +1515,15 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data)
 
     m_imports.setBaseUrl(finalUrl());
 
 
     m_imports.setBaseUrl(finalUrl());
 
-    foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
-        if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
+    foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) {
+        if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) {
             QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
             if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
                 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
                 addDependency(data);
                 m_qmldirs << data;
             }
             QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
             if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
                 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
                 addDependency(data);
                 m_qmldirs << data;
             }
-        } else if (import.type == QDeclarativeScriptParser::Import::Script) {
+        } else if (import.type == QDeclarativeScript::Import::Script) {
             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
             addDependency(blob);
             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
             addDependency(blob);
@@ -886,7 +1532,6 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data)
             ref.location = import.location.start;
             ref.qualifier = import.qualifier;
             ref.script = blob;
             ref.location = import.location.start;
             ref.qualifier = import.qualifier;
             ref.script = blob;
-            blob->addref();
             m_scripts << ref;
 
         }
             m_scripts << ref;
 
         }
@@ -928,7 +1573,7 @@ void QDeclarativeTypeData::compile()
     m_compiledData->name = m_compiledData->url.toString();
     QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name);
 
     m_compiledData->name = m_compiledData->url.toString();
     QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name);
 
-    QDeclarativeCompiler compiler;
+    QDeclarativeCompiler compiler(&scriptParser._pool);
     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
         setError(compiler.errors());
         m_compiledData->release();
     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
         setError(compiler.errors());
         m_compiledData->release();
@@ -948,11 +1593,11 @@ void QDeclarativeTypeData::resolveTypes()
     QList<QDeclarativeError> errors;
     if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
         m_imports.addImport(importDatabase, QLatin1String("."),
     QList<QDeclarativeError> errors;
     if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
         m_imports.addImport(importDatabase, QLatin1String("."),
-                            QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
+                            QString(), -1, -1, QDeclarativeScript::Import::File, 
                             qmldir->dirComponents(), &errors);
     } else {
         m_imports.addImport(importDatabase, QLatin1String("."), 
                             qmldir->dirComponents(), &errors);
     } else {
         m_imports.addImport(importDatabase, QLatin1String("."), 
-                            QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
+                            QString(), -1, -1, QDeclarativeScript::Import::File, 
                             QDeclarativeDirComponents(), &errors);
     }
 
                             QDeclarativeDirComponents(), &errors);
     }
 
@@ -972,12 +1617,12 @@ void QDeclarativeTypeData::resolveTypes()
         return;
     }
 
         return;
     }
 
-    foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
+    foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) {
         QDeclarativeDirComponents qmldircomponentsnetwork;
         QDeclarativeDirComponents qmldircomponentsnetwork;
-        if (import.type == QDeclarativeScriptParser::Import::Script)
+        if (import.type == QDeclarativeScript::Import::Script)
             continue;
 
             continue;
 
-        if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
+        if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) {
             QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
             if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl))
                 qmldircomponentsnetwork = qmldir->dirComponents();
             QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
             if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl))
                 qmldircomponentsnetwork = qmldir->dirComponents();
@@ -1008,18 +1653,37 @@ void QDeclarativeTypeData::resolveTypes()
         }
     }
 
         }
     }
 
-    foreach (QDeclarativeScriptParser::TypeReference *parserRef, scriptParser.referencedTypes()) {
-        QByteArray typeName = parserRef->name.toUtf8();
+    // Add any imported scripts to our resolved set
+    foreach (const QDeclarativeImports::ScriptReference &script, m_imports.resolvedScripts())
+    {
+        QDeclarativeScriptBlob *blob = typeLoader()->getScript(script.location);
+        addDependency(blob);
+
+        ScriptReference ref;
+        //ref.location = ...
+        ref.qualifier = script.nameSpace;
+        if (!script.qualifier.isEmpty())
+        {
+            ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
+
+            // Add a reference to the enclosing namespace
+            m_namespaces.insert(script.qualifier);
+        }
+
+        ref.script = blob;
+        m_scripts << ref;
+    }
 
 
+    foreach (QDeclarativeScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
         TypeReference ref;
 
         TypeReference ref;
 
-        QUrl url;
+        QString url;
         int majorVersion;
         int minorVersion;
         QDeclarativeImportedNamespace *typeNamespace = 0;
         QList<QDeclarativeError> errors;
 
         int majorVersion;
         int minorVersion;
         QDeclarativeImportedNamespace *typeNamespace = 0;
         QList<QDeclarativeError> errors;
 
-        if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion,
+        if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
                                    &typeNamespace, &errors) || typeNamespace) {
             // Known to not be a type:
             //  - known to be a namespace (Namespace {})
                                    &typeNamespace, &errors) || typeNamespace) {
             // Known to not be a type:
             //  - known to be a namespace (Namespace {})
@@ -1042,7 +1706,7 @@ void QDeclarativeTypeData::resolveTypes()
             }
 
             if (!parserRef->refObjects.isEmpty()) {
             }
 
             if (!parserRef->refObjects.isEmpty()) {
-                QDeclarativeParser::Object *obj = parserRef->refObjects.first();
+                QDeclarativeScript::Object *obj = parserRef->refObjects.first();
                 error.setLine(obj->location.start.line);
                 error.setColumn(obj->location.start.column);
             }
                 error.setLine(obj->location.start.line);
                 error.setColumn(obj->location.start.column);
             }
@@ -1056,7 +1720,7 @@ void QDeclarativeTypeData::resolveTypes()
             ref.majorVersion = majorVersion;
             ref.minorVersion = minorVersion;
         } else {
             ref.majorVersion = majorVersion;
             ref.minorVersion = minorVersion;
         } else {
-            ref.typeData = typeLoader()->get(url);
+            ref.typeData = typeLoader()->get(QUrl(url));
             addDependency(ref.typeData);
         }
 
             addDependency(ref.typeData);
         }
 
@@ -1076,15 +1740,13 @@ QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
     return 0;
 }
 
     return 0;
 }
 
-QDeclarativeScriptData::QDeclarativeScriptData(QDeclarativeEngine *engine)
-: QDeclarativeCleanup(engine), importCache(0), pragmas(QDeclarativeParser::Object::ScriptBlock::None),
-  m_loaded(false)
+QDeclarativeScriptData::QDeclarativeScriptData()
+: importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), m_loaded(false) 
 {
 }
 
 QDeclarativeScriptData::~QDeclarativeScriptData()
 {
 {
 }
 
 QDeclarativeScriptData::~QDeclarativeScriptData()
 {
-    clear();
 }
 
 void QDeclarativeScriptData::clear()
 }
 
 void QDeclarativeScriptData::clear()
@@ -1100,11 +1762,14 @@ void QDeclarativeScriptData::clear()
 
     qPersistentDispose(m_program);
     qPersistentDispose(m_value);
 
     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)
 }
 
 QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader)
-: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None),
-  m_scriptData(0), m_typeLoader(loader)
+: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeScript::Object::ScriptBlock::None),
+  m_imports(loader), m_scriptData(0), m_typeLoader(loader)
 {
 }
 
 {
 }
 
@@ -1116,7 +1781,7 @@ QDeclarativeScriptBlob::~QDeclarativeScriptBlob()
     }
 }
 
     }
 }
 
-QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const
+QDeclarativeScript::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const
 {
     return m_pragmas;
 }
 {
     return m_pragmas;
 }
@@ -1148,17 +1813,17 @@ void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
 
     m_source = QString::fromUtf8(data);
 
 
     m_source = QString::fromUtf8(data);
 
-    QDeclarativeScriptParser::JavaScriptMetaData metadata =
-        QDeclarativeScriptParser::extractMetaData(m_source);
+    QDeclarativeScript::Parser::JavaScriptMetaData metadata =
+        QDeclarativeScript::Parser::extractMetaData(m_source);
 
     m_imports.setBaseUrl(finalUrl());
 
     m_pragmas = metadata.pragmas;
 
 
     m_imports.setBaseUrl(finalUrl());
 
     m_pragmas = metadata.pragmas;
 
-    foreach (const QDeclarativeScriptParser::Import &import, metadata.imports) {
-        Q_ASSERT(import.type != QDeclarativeScriptParser::Import::File);
+    foreach (const QDeclarativeScript::Import &import, metadata.imports) {
+        Q_ASSERT(import.type != QDeclarativeScript::Import::File);
 
 
-        if (import.type == QDeclarativeScriptParser::Import::Script) {
+        if (import.type == QDeclarativeScript::Import::Script) {
             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
             addDependency(blob);
             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
             addDependency(blob);
@@ -1167,10 +1832,9 @@ void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
             ref.location = import.location.start;
             ref.qualifier = import.qualifier;
             ref.script = blob;
             ref.location = import.location.start;
             ref.qualifier = import.qualifier;
             ref.script = blob;
-            blob->addref();
             m_scripts << ref;
         } else {
             m_scripts << ref;
         } else {
-            Q_ASSERT(import.type == QDeclarativeScriptParser::Import::Library);
+            Q_ASSERT(import.type == QDeclarativeScript::Import::Library);
             int vmaj = -1;
             int vmin = -1;
             import.extractVersion(&vmaj, &vmin);
             int vmaj = -1;
             int vmin = -1;
             import.extractVersion(&vmaj, &vmin);
@@ -1214,9 +1878,9 @@ void QDeclarativeScriptBlob::done()
         return;
 
     QDeclarativeEngine *engine = typeLoader()->engine();
         return;
 
     QDeclarativeEngine *engine = typeLoader()->engine();
-    m_scriptData = new QDeclarativeScriptData(engine);
+    m_scriptData = new QDeclarativeScriptData();
     m_scriptData->url = finalUrl();
     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);
 
     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
         const ScriptReference &script = m_scripts.at(ii);
@@ -1228,13 +1892,7 @@ void QDeclarativeScriptBlob::done()
     m_imports.populateCache(m_scriptData->importCache, engine);
 
     m_scriptData->pragmas = m_pragmas;
     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)
 }
 
 QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
@@ -1257,3 +1915,4 @@ void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
 
 QT_END_NAMESPACE
 
 
 QT_END_NAMESPACE
 
+#include "qdeclarativetypeloader.moc"