Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativetypeloader.cpp
index ff6a7c7..b507f20 100644 (file)
@@ -1,34 +1,34 @@
 /****************************************************************************
 **
-** 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.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
 ** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
-** rights.  These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
 **
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
 **
 **
 **
 #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
+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.
@@ -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)
-: 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
 {
-    return m_status;
+    return m_data.status();
 }
 
 /*!
@@ -131,7 +309,7 @@ Returns true if the status is Null.
 */
 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
 {
-    return m_status == Loading;
+    return status() == Loading;
 }
 
 /*!
@@ -147,7 +325,7 @@ Returns true if the status is WaitingForDependencies.
 */
 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
 {
-    return m_status == Complete;
+    return status() == Complete;
 }
 
 /*!
@@ -163,7 +341,7 @@ Returns true if the status is Error.
 */
 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
 {
-    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
 {
-    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.
+
+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;
 }
 
@@ -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 
-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);
@@ -230,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();
 
@@ -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.
+
+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);
 }
@@ -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.
+
+XXX Rename processData() or some such to avoid confusion between done() (processing thread)
+and completed() (main thread)
 */
 void QDeclarativeDataBlob::done()
 {
@@ -366,21 +570,64 @@ 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();
     }
 }
 
@@ -432,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.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.
@@ -466,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))
 {
 }
 
@@ -475,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)
+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_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;
@@ -508,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 {
@@ -518,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();
@@ -532,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);
@@ -549,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;
         }
@@ -566,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;
@@ -610,7 +1121,7 @@ void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArra
         blob->allDependenciesDone();
 
     if (blob->status() != QDeclarativeDataBlob::Error) 
-        blob->m_status = QDeclarativeDataBlob::WaitingForDependencies;
+        blob->m_data.setStatus(QDeclarativeDataBlob::WaitingForDependencies);
 
     blob->m_inCallback = false;
 
@@ -654,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) {
@@ -663,6 +1176,9 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
     }
 
     typeData->addref();
+
+    unlock();
+
     return typeData;
 }
 
@@ -674,30 +1190,40 @@ 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;
 }
 
 /*!
-Returns a QDeclarativeScriptData for \a url.  The QDeclarativeScriptData may be cached.
+Return a QDeclarativeScriptBlob for \a url.  The QDeclarativeScriptData may be cached.
 */
-QDeclarativeScriptData *QDeclarativeTypeLoader::getScript(const QUrl &url)
+QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
 {
     Q_ASSERT(!url.isRelative() && 
             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
 
-    QDeclarativeScriptData *scriptData = m_scriptCache.value(url);
+    lock();
+
+    QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
 
-    if (!scriptData) {
-        scriptData = new QDeclarativeScriptData(url);
-        m_scriptCache.insert(url, scriptData);
-        QDeclarativeDataLoader::load(scriptData);
+    if (!scriptBlob) {
+        scriptBlob = new QDeclarativeScriptBlob(url, this);
+        m_scriptCache.insert(url, scriptBlob);
+        QDeclarativeDataLoader::load(scriptBlob);
     }
 
-    scriptData->addref();
-    return scriptData;
+    scriptBlob->addref();
+
+    unlock();
+
+    return scriptBlob;
 }
 
 /*!
@@ -709,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) {
@@ -718,10 +1246,121 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
     }
 
     qmldirData->addref();
+
+    unlock();
+
     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.
 */
@@ -733,17 +1372,21 @@ void QDeclarativeTypeLoader::clearCache()
         (*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_importDirCache.clear();
+    m_importQmlDirCache.clear();
 }
 
 
 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)
 {
 }
 
@@ -769,7 +1412,7 @@ const QDeclarativeImports &QDeclarativeTypeData::imports() const
     return m_imports;
 }
 
-const QDeclarativeScriptParser &QDeclarativeTypeData::parser() const
+const QDeclarativeScript::Parser &QDeclarativeTypeData::parser() const
 {
     return scriptParser;
 }
@@ -784,6 +1427,11 @@ const QList<QDeclarativeTypeData::ScriptReference> &QDeclarativeTypeData::resolv
     return m_scripts;
 }
 
+const QSet<QString> &QDeclarativeTypeData::namespaces() const
+{
+    return m_namespaces;
+}
+
 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
 {
     if (m_compiledData) 
@@ -807,8 +1455,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);
@@ -849,14 +1495,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)
@@ -868,23 +1515,23 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data)
 
     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;
             }
-        } else if (import.type == QDeclarativeScriptParser::Import::Script) {
+        } else if (import.type == QDeclarativeScript::Import::Script) {
             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
-            QDeclarativeScriptData *data = typeLoader()->getScript(scriptUrl);
-            addDependency(data);
+            QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
+            addDependency(blob);
 
             ScriptReference ref;
             ref.location = import.location.start;
             ref.qualifier = import.qualifier;
-            ref.script = data;
+            ref.script = blob;
             m_scripts << ref;
 
         }
@@ -926,7 +1573,7 @@ void QDeclarativeTypeData::compile()
     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();
@@ -943,100 +1590,137 @@ void QDeclarativeTypeData::resolveTypes()
     // For local urls, add an implicit import "." as first (most overridden) lookup. 
     // This will also trigger the loading of the qmldir and the import of any native 
     // types from available plugins.
+    QList<QDeclarativeError> errors;
     if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
         m_imports.addImport(importDatabase, QLatin1String("."),
-                            QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
-                            qmldir->dirComponents(), 0);
+                            QString(), -1, -1, QDeclarativeScript::Import::File, 
+                            qmldir->dirComponents(), &errors);
     } else {
         m_imports.addImport(importDatabase, QLatin1String("."), 
-                            QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
-                            QDeclarativeDirComponents(), 0);
+                            QString(), -1, -1, QDeclarativeScript::Import::File, 
+                            QDeclarativeDirComponents(), &errors);
     }
 
-    foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
+    // remove any errors which are due to the implicit import which aren't real errors.
+    // for example, if the implicitly included qmldir file doesn't exist, that is not an error.
+    QList<QDeclarativeError> realErrors;
+    for (int i = 0; i < errors.size(); ++i) {
+        if (errors.at(i).description() != QDeclarativeImportDatabase::tr("import \".\" has no qmldir and no namespace")
+                && errors.at(i).description() != QDeclarativeImportDatabase::tr("\".\": no such directory")) {
+            realErrors.prepend(errors.at(i)); // this is a real error.
+        }
+    }
+
+    // report any real errors which occurred during plugin loading or qmldir parsing.
+    if (!realErrors.isEmpty()) {
+        setError(realErrors);
+        return;
+    }
+
+    foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) {
         QDeclarativeDirComponents qmldircomponentsnetwork;
-        if (import.type == QDeclarativeScriptParser::Import::Script)
+        if (import.type == QDeclarativeScript::Import::Script)
             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)) 
+            if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl))
                 qmldircomponentsnetwork = qmldir->dirComponents();
         }
 
         int vmaj = -1;
         int vmin = -1;
+        import.extractVersion(&vmaj, &vmin);
 
-        if (!import.version.isEmpty()) {
-            int dot = import.version.indexOf(QLatin1Char('.'));
-            if (dot < 0) {
-                vmaj = import.version.toInt();
-                vmin = 0;
-            } else {
-                vmaj = import.version.left(dot).toInt();
-                vmin = import.version.mid(dot+1).toInt();
-            }
-        }
-
-        QString errorString;
+        QList<QDeclarativeError> errors;
         if (!m_imports.addImport(importDatabase, import.uri, import.qualifier,
-                                 vmaj, vmin, import.type, qmldircomponentsnetwork, &errorString)) {
+                                 vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) {
             QDeclarativeError error;
+            if (errors.size()) {
+                error = errors.takeFirst();
+            } else {
+                // this should not be possible!
+                // Description should come from error provided by addImport() function.
+                error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
+            }
             error.setUrl(m_imports.baseUrl());
-            error.setDescription(errorString);
             error.setLine(import.location.start.line);
             error.setColumn(import.location.start.column);
+            errors.prepend(error); // put it back on the list after filling out information.
 
-            setError(error);
+            setError(errors);
             return;
         }
     }
 
-    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;
 
-        QUrl url;
+        QString url;
         int majorVersion;
         int minorVersion;
         QDeclarativeImportedNamespace *typeNamespace = 0;
-        QString errorString;
+        QList<QDeclarativeError> errors;
 
-        if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion,
-                                   &typeNamespace, &errorString) || typeNamespace) {
+        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 {})
             //  - type with unknown namespace (UnknownNamespace.SomeType {})
             QDeclarativeError error;
-            error.setUrl(m_imports.baseUrl());
             QString userTypeName = parserRef->name;
             userTypeName.replace(QLatin1Char('/'),QLatin1Char('.'));
-            if (typeNamespace)
+            if (typeNamespace) {
                 error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName));
-            else
-                error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(errorString));
+            } else {
+                if (errors.size()) {
+                    error = errors.takeFirst();
+                } else {
+                    // this should not be possible!
+                    // Description should come from error provided by addImport() function.
+                    error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
+                }
+                error.setUrl(m_imports.baseUrl());
+                error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description()));
+            }
 
             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);
             }
-            
-            setError(error);
+
+            errors.prepend(error);
+            setError(errors);
             return;
         }
 
         if (ref.type) {
             ref.majorVersion = majorVersion;
             ref.minorVersion = minorVersion;
-            foreach (QDeclarativeParser::Object *obj, parserRef->refObjects) {
-               // store namespace for DOM
-               obj->majorVersion = majorVersion;
-               obj->minorVersion = minorVersion;
-            }
         } else {
-            ref.typeData = typeLoader()->get(url);
+            ref.typeData = typeLoader()->get(QUrl(url));
             addDependency(ref.typeData);
         }
 
@@ -1056,25 +1740,159 @@ QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
     return 0;
 }
 
-QDeclarativeScriptData::QDeclarativeScriptData(const QUrl &url)
-: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None)
+QDeclarativeScriptData::QDeclarativeScriptData()
+: importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), m_loaded(false) 
 {
 }
 
-QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptData::pragmas() const
+QDeclarativeScriptData::~QDeclarativeScriptData()
+{
+}
+
+void QDeclarativeScriptData::clear()
+{
+    if (importCache) {
+        importCache->release();
+        importCache = 0;
+    }
+
+    for (int ii = 0; ii < scripts.count(); ++ii)
+        scripts.at(ii)->release();
+    scripts.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)
+: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeScript::Object::ScriptBlock::None),
+  m_imports(loader), m_scriptData(0), m_typeLoader(loader)
+{
+}
+
+QDeclarativeScriptBlob::~QDeclarativeScriptBlob()
+{
+    if (m_scriptData) {
+        m_scriptData->release();
+        m_scriptData = 0;
+    }
+}
+
+QDeclarativeScript::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const
 {
     return m_pragmas;
 }
 
-QString QDeclarativeScriptData::scriptSource() const
+QString QDeclarativeScriptBlob::scriptSource() const
 {
     return m_source;
 }
 
-void QDeclarativeScriptData::dataReceived(const QByteArray &data)
+QDeclarativeTypeLoader *QDeclarativeScriptBlob::typeLoader() const
+{
+    return m_typeLoader;
+}
+
+const QDeclarativeImports &QDeclarativeScriptBlob::imports() const
 {
+    return m_imports;
+}
+
+QDeclarativeScriptData *QDeclarativeScriptBlob::scriptData() const
+{
+    return m_scriptData;
+}
+
+void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
+{
+    QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
+    QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
+
     m_source = QString::fromUtf8(data);
-    m_pragmas = QDeclarativeScriptParser::extractPragmas(m_source);
+
+    QDeclarativeScript::Parser::JavaScriptMetaData metadata =
+        QDeclarativeScript::Parser::extractMetaData(m_source);
+
+    m_imports.setBaseUrl(finalUrl());
+
+    m_pragmas = metadata.pragmas;
+
+    foreach (const QDeclarativeScript::Import &import, metadata.imports) {
+        Q_ASSERT(import.type != QDeclarativeScript::Import::File);
+
+        if (import.type == QDeclarativeScript::Import::Script) {
+            QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
+            QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
+            addDependency(blob);
+
+            ScriptReference ref;
+            ref.location = import.location.start;
+            ref.qualifier = import.qualifier;
+            ref.script = blob;
+            m_scripts << ref;
+        } else {
+            Q_ASSERT(import.type == QDeclarativeScript::Import::Library);
+            int vmaj = -1;
+            int vmin = -1;
+            import.extractVersion(&vmaj, &vmin);
+
+            QList<QDeclarativeError> errors;
+            if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
+                                     import.type, QDeclarativeDirComponents(), &errors)) {
+                QDeclarativeError error = errors.takeFirst();
+                // description should be set by addImport().
+                error.setUrl(m_imports.baseUrl());
+                error.setLine(import.location.start.line);
+                error.setColumn(import.location.start.column);
+                errors.prepend(error);
+
+                setError(errors);
+                return;
+            }
+        }
+    }
+}
+
+void QDeclarativeScriptBlob::done()
+{
+    // Check all script dependencies for errors
+    for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
+        const ScriptReference &script = m_scripts.at(ii);
+        Q_ASSERT(script.script->isCompleteOrError());
+        if (script.script->isError()) {
+            QList<QDeclarativeError> errors = script.script->errors();
+            QDeclarativeError error;
+            error.setUrl(finalUrl());
+            error.setLine(script.location.line);
+            error.setColumn(script.location.column);
+            error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
+            errors.prepend(error);
+            setError(errors);
+        }
+    }
+
+    if (isError())
+        return;
+
+    QDeclarativeEngine *engine = typeLoader()->engine();
+    m_scriptData = new QDeclarativeScriptData();
+    m_scriptData->url = finalUrl();
+    m_scriptData->importCache = new QDeclarativeTypeNameCache();
+
+    for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
+        const ScriptReference &script = m_scripts.at(ii);
+
+        m_scriptData->scripts.append(script.script);
+        m_scriptData->importCache->add(script.qualifier, ii);
+    }
+
+    m_imports.populateCache(m_scriptData->importCache, engine);
+
+    m_scriptData->pragmas = m_pragmas;
+    m_scriptData->m_programSource = m_source;
 }
 
 QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
@@ -1097,3 +1915,4 @@ void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
 
 QT_END_NAMESPACE
 
+#include "qdeclarativetypeloader.moc"