summaryrefslogtreecommitdiffstats
path: root/src/declarative/qml
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-11-30 06:25:37 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-11-30 06:25:37 (GMT)
commit1c8cf1b080c81c40bedc9d7c342555585fc394b0 (patch)
tree3aa4b9eec3c31773ee878ec8c4e23439428f3f08 /src/declarative/qml
parent34e5b058c81757e58f7356290d7866f37a52be53 (diff)
downloadQt-1c8cf1b080c81c40bedc9d7c342555585fc394b0.zip
Qt-1c8cf1b080c81c40bedc9d7c342555585fc394b0.tar.gz
Qt-1c8cf1b080c81c40bedc9d7c342555585fc394b0.tar.bz2
Basic WorkerScript functionality
Diffstat (limited to 'src/declarative/qml')
-rw-r--r--src/declarative/qml/qml.pri8
-rw-r--r--src/declarative/qml/qmlengine.cpp13
-rw-r--r--src/declarative/qml/qmlengine_p.h4
-rw-r--r--src/declarative/qml/qmlworkerscript.cpp418
-rw-r--r--src/declarative/qml/qmlworkerscript_p.h125
5 files changed, 563 insertions, 5 deletions
diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri
index ca78797..2167ab3 100644
--- a/src/declarative/qml/qml.pri
+++ b/src/declarative/qml/qml.pri
@@ -46,7 +46,8 @@ SOURCES += qml/qmlparser.cpp \
qml/qmlglobalscriptclass.cpp \
qml/qmlvaluetypescriptclass.cpp \
qml/qmltypenamescriptclass.cpp \
- qml/qmllistscriptclass.cpp
+ qml/qmllistscriptclass.cpp \
+ qml/qmlworkerscript.cpp
HEADERS += qml/qmlparser_p.h \
qml/qmlglobal_p.h \
@@ -109,8 +110,9 @@ HEADERS += qml/qmlparser_p.h \
qml/qmlglobalscriptclass_p.h \
qml/qmlvaluetypescriptclass_p.h \
qml/qmltypenamescriptclass_p.h \
- qml/qmllistscriptclass_p.h
-
+ qml/qmllistscriptclass_p.h \
+ qml/qmlworkerscript_p.h
+
QT += sql
include(parser/parser.pri)
diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp
index aa66c17..c0692eb 100644
--- a/src/declarative/qml/qmlengine.cpp
+++ b/src/declarative/qml/qmlengine.cpp
@@ -85,6 +85,7 @@
#include <qmlscriptstring.h>
#include <private/qmlglobal_p.h>
#include <QtCore/qcryptographichash.h>
+#include <private/qmlworkerscript_p.h>
#ifdef Q_OS_WIN // for %APPDATA%
#include "qt_windows.h"
@@ -111,8 +112,8 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e)
: rootContext(0), currentExpression(0),
isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), globalClass(0),
nodeListClass(0), namedNodeMapClass(0), sqlQueryClass(0), cleanup(0), erroredBindings(0),
- inProgressCreations(0), scriptEngine(this), componentAttacheds(0), rootComponent(0),
- networkAccessManager(0), typeManager(e), uniqueId(1)
+ inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttacheds(0),
+ rootComponent(0), networkAccessManager(0), typeManager(e), uniqueId(1)
{
// Note that all documentation for stuff put on the global object goes in
// doc/src/declarative/globalobject.qdoc
@@ -243,6 +244,14 @@ void QmlEnginePrivate::init()
}
}
+QmlWorkerScriptEngine *QmlEnginePrivate::getWorkerScriptEngine()
+{
+ Q_Q(QmlEngine);
+ if (!workerScriptEngine)
+ workerScriptEngine = new QmlWorkerScriptEngine(q);
+ return workerScriptEngine;
+}
+
/*!
\class QmlEngine
\brief The QmlEngine class provides an environment for instantiating QML components.
diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h
index cabd0ad..191be83 100644
--- a/src/declarative/qml/qmlengine_p.h
+++ b/src/declarative/qml/qmlengine_p.h
@@ -99,6 +99,7 @@ class QmlComponentAttached;
class QmlListScriptClass;
class QmlCleanup;
class QmlBindingData;
+class QmlWorkerScriptEngine;
class QmlEnginePrivate : public QObjectPrivate
{
@@ -152,6 +153,9 @@ public:
};
QmlScriptEngine scriptEngine;
+ QmlWorkerScriptEngine *getWorkerScriptEngine();
+ QmlWorkerScriptEngine *workerScriptEngine;
+
QUrl baseUrl;
template<class T>
diff --git a/src/declarative/qml/qmlworkerscript.cpp b/src/declarative/qml/qmlworkerscript.cpp
new file mode 100644
index 0000000..a6428dd
--- /dev/null
+++ b/src/declarative/qml/qmlworkerscript.cpp
@@ -0,0 +1,418 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** 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.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmlworkerscript_p.h"
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtScript/qscriptengine.h>
+#include <private/qmlengine_p.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtScript/qscriptvalueiterator.h>
+#include <QtCore/qfile.h>
+
+class WorkerDataEvent : public QEvent
+{
+public:
+ enum Type { WorkerData = QEvent::User };
+
+ WorkerDataEvent(int workerId, QmlWorkerScriptEngine::Data *data);
+ virtual ~WorkerDataEvent();
+
+ int workerId() const;
+ QmlWorkerScriptEngine::Data *takeData();
+
+private:
+ int m_id;
+ QmlWorkerScriptEngine::Data *m_data;
+};
+
+class WorkerLoadEvent : public QEvent
+{
+public:
+ enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 };
+
+ WorkerLoadEvent(int workerId, const QUrl &url);
+
+ int workerId() const;
+ QUrl url() const;
+
+private:
+ int m_id;
+ QUrl m_url;
+};
+
+class QmlWorkerScriptEnginePrivate : public QObject
+{
+public:
+ QmlWorkerScriptEnginePrivate();
+
+ QScriptEngine *workerEngine;
+
+ QMutex m_lock;
+ QWaitCondition m_wait;
+
+ QScriptValue getWorker(int);
+
+ int m_nextId;
+ QHash<int, QScriptValue> m_workers;
+
+ static QVariant scriptValueToVariant(const QScriptValue &);
+ static QScriptValue variantToScriptValue(const QVariant &, QScriptEngine *);
+
+ static QScriptValue onmessage(QScriptContext *ctxt, QScriptEngine *engine);
+
+protected:
+ virtual bool event(QEvent *);
+
+private:
+ void processMessage(int, QmlWorkerScriptEngine::Data *);
+ void processLoad(int, const QUrl &);
+};
+
+QmlWorkerScriptEnginePrivate::QmlWorkerScriptEnginePrivate()
+: workerEngine(0), m_nextId(0)
+{
+}
+
+QScriptValue QmlWorkerScriptEnginePrivate::onmessage(QScriptContext *ctxt, QScriptEngine *engine)
+{
+ if (ctxt->argumentCount() >= 1)
+ ctxt->thisObject().setData(ctxt->argument(0));
+
+ return ctxt->thisObject().data();
+}
+
+QScriptValue QmlWorkerScriptEnginePrivate::getWorker(int id)
+{
+ QHash<int, QScriptValue>::Iterator iter = m_workers.find(id);
+ if (iter == m_workers.end()) {
+ QScriptValue worker = workerEngine->newObject();
+
+ worker.setProperty(QLatin1String("onmessage"), workerEngine->newFunction(onmessage),
+ QScriptValue::PropertyGetter | QScriptValue::PropertySetter);
+
+ iter = m_workers.insert(id, worker);
+ }
+
+ return *iter;
+}
+
+bool QmlWorkerScriptEnginePrivate::event(QEvent *event)
+{
+ if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
+ WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
+ processMessage(workerEvent->workerId(), workerEvent->takeData());
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) {
+ WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event);
+ processLoad(workerEvent->workerId(), workerEvent->url());
+ return true;
+ } else {
+ return QObject::event(event);
+ }
+}
+
+void QmlWorkerScriptEnginePrivate::processMessage(int id, QmlWorkerScriptEngine::Data *data)
+{
+ QScriptValue worker = getWorker(id);
+ QScriptValue onmessage = worker.data();
+
+ if (onmessage.isFunction()) {
+ QScriptValue args = workerEngine->newArray(1);
+ args.setProperty(0, variantToScriptValue(data->var, workerEngine));
+
+ onmessage.call(worker, args);
+ }
+
+ if (data) delete data;
+}
+
+void QmlWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
+{
+ if (url.isRelative() || url.scheme() != QLatin1String("file"))
+ return;
+
+ QString fileName = url.toLocalFile();
+
+ QFile f(fileName);
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray data = f.readAll();
+ QString script = QString::fromUtf8(data);
+
+ QScriptValue activation = getWorker(id);
+
+ QScriptContext *ctxt = workerEngine->pushContext();
+ ctxt->setActivationObject(activation);
+
+ workerEngine->evaluate(script);
+
+ workerEngine->popContext();
+ }
+}
+
+WorkerDataEvent::WorkerDataEvent(int workerId, QmlWorkerScriptEngine::Data *data)
+: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data)
+{
+}
+
+WorkerDataEvent::~WorkerDataEvent()
+{
+ if (m_data) { delete m_data; m_data = 0; }
+}
+
+int WorkerDataEvent::workerId() const
+{
+ return m_id;
+}
+
+QmlWorkerScriptEngine::Data *WorkerDataEvent::takeData()
+{
+ QmlWorkerScriptEngine::Data *rv = m_data;
+ m_data = 0;
+ return rv;
+}
+
+WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url)
+: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url)
+{
+}
+
+int WorkerLoadEvent::workerId() const
+{
+ return m_id;
+}
+
+QUrl WorkerLoadEvent::url() const
+{
+ return m_url;
+}
+
+QmlWorkerScriptEngine::QmlWorkerScriptEngine(QObject *parent)
+: QThread(parent), d(new QmlWorkerScriptEnginePrivate)
+{
+ d->m_lock.lock();
+ start(QThread::LowPriority);
+ d->m_wait.wait(&d->m_lock);
+ d->moveToThread(this);
+}
+
+QmlWorkerScriptEngine::~QmlWorkerScriptEngine()
+{
+ delete d; d = 0;
+}
+
+QmlWorkerScriptEngine::WorkerScript::WorkerScript()
+: engine(0), id(0)
+{
+}
+
+void QmlWorkerScriptEngine::WorkerScript::executeUrl(const QUrl &url)
+{
+ engine->executeUrl(this, url);
+}
+
+void QmlWorkerScriptEngine::WorkerScript::sendMessage(Data *data)
+{
+ engine->sendMessage(this, data);
+}
+
+void QmlWorkerScriptEngine::executeUrl(WorkerScript *script, const QUrl &data)
+{
+ QCoreApplication::postEvent(d, new WorkerLoadEvent(script->id, data));
+}
+
+/*!
+ Ownership of \a data transfers to QmlWorkerScriptEngine. It can not be modified by
+ the caller.
+*/
+void QmlWorkerScriptEngine::sendMessage(WorkerScript *script, Data *data)
+{
+ QCoreApplication::postEvent(d, new WorkerDataEvent(script->id, data));
+}
+
+QmlWorkerScriptEngine::WorkerScript *QmlWorkerScriptEngine::createWorkerScript()
+{
+ WorkerScript *rv = new WorkerScript;
+ rv->engine = this;
+ rv->id = d->m_nextId++;
+ return rv;
+}
+
+void QmlWorkerScriptEngine::run()
+{
+ d->m_lock.lock();
+
+ d->workerEngine = new QScriptEngine;
+
+ d->m_wait.wakeAll();
+
+ d->m_lock.unlock();
+
+ exec();
+
+ delete d->workerEngine; d->workerEngine = 0;
+}
+
+QmlWorkerScript::QmlWorkerScript(QObject *parent)
+: QObject(parent), m_script(0)
+{
+}
+
+QmlWorkerScript::~QmlWorkerScript()
+{
+ if (m_script) { delete m_script; m_script = 0; }
+}
+
+QVariant QmlWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue &value)
+{
+ if (value.isBool()) {
+ return QVariant(value.toBool());
+ } else if (value.isString()) {
+ return QVariant(value.toString());
+ } else if (value.isNumber()) {
+ return QVariant((qreal)value.toNumber());
+ } else if (value.isArray()) {
+ QVariantList list;
+
+ quint32 length = (quint32)value.property("length").toNumber();
+
+ for (quint32 ii = 0; ii < length; ++ii) {
+ QVariant v = scriptValueToVariant(ii);
+ list << v;
+ }
+
+ return QVariant(list);
+ } else if (value.isObject()) {
+ QVariantHash hash;
+
+ QScriptValueIterator iter(value);
+
+ while (iter.hasNext()) {
+ iter.next();
+ hash.insert(iter.name(), scriptValueToVariant(iter.value()));
+ }
+
+ return QVariant(hash);
+ }
+
+ return QVariant();
+
+}
+
+QScriptValue QmlWorkerScriptEnginePrivate::variantToScriptValue(const QVariant &value, QScriptEngine *engine)
+{
+ if (value.type() == QVariant::Bool) {
+ return QScriptValue(value.toBool());
+ } else if (value.type() == QVariant::String) {
+ return QScriptValue(value.toString());
+ } else if (value.type() == (QVariant::Type)QMetaType::QReal) {
+ return QScriptValue(value.toReal());
+ } else if (value.type() == (QVariant::Type)QMetaType::QVariantList) {
+ QVariantList list = qvariant_cast<QVariantList>(value);
+ QScriptValue rv = engine->newArray(list.count());
+
+ for (quint32 ii = 0; ii < list.count(); ++ii)
+ rv.setProperty(ii, variantToScriptValue(list.at(ii), engine));
+
+ return rv;
+ } else if (value.type() == (QVariant::Type)QMetaType::QVariantHash) {
+
+ QVariantHash hash = qvariant_cast<QVariantHash>(value);
+
+ QScriptValue rv = engine->newObject();
+
+ for (QVariantHash::ConstIterator iter = hash.begin(); iter != hash.end(); ++iter)
+ rv.setProperty(iter.key(), variantToScriptValue(iter.value(), engine));
+
+ return rv;
+ } else {
+ return engine->nullValue();
+ }
+}
+
+QUrl QmlWorkerScript::source() const
+{
+ return m_source;
+}
+
+void QmlWorkerScript::setSource(const QUrl &source)
+{
+ if (m_source == source)
+ return;
+
+ m_source = source;
+
+ if (m_script)
+ m_script->executeUrl(m_source);
+
+ emit sourceChanged();
+}
+
+void QmlWorkerScript::sendMessage(const QScriptValue &message)
+{
+ if (!m_script) {
+ qWarning("QmlWorkerScript: Attempt to send message before WorkerScript establishment");
+ return;
+ }
+
+ QmlWorkerScriptEngine::Data *data = new QmlWorkerScriptEngine::Data;
+ data->var = QmlWorkerScriptEnginePrivate::scriptValueToVariant(message);
+ m_script->sendMessage(data);
+}
+
+void QmlWorkerScript::componentComplete()
+{
+ if (!m_script) {
+ QmlEngine *engine = qmlEngine(this);
+ if (!engine) {
+ qWarning("QmlWorkerScript: componentComplete() called without qmlEngine() set");
+ return;
+ }
+ m_script = QmlEnginePrivate::get(engine)->getWorkerScriptEngine()->createWorkerScript();
+
+ if (m_source.isValid())
+ m_script->executeUrl(m_source);
+ }
+}
+
+QML_DEFINE_TYPE(Qt, 4, 6, WorkerScript, QmlWorkerScript);
diff --git a/src/declarative/qml/qmlworkerscript_p.h b/src/declarative/qml/qmlworkerscript_p.h
new file mode 100644
index 0000000..90861f8
--- /dev/null
+++ b/src/declarative/qml/qmlworkerscript_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** 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.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLWORKERSCRIPT_P_H
+#define QMLWORKERSCRIPT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qthread.h>
+#include <QtDeclarative/qml.h>
+#include <QtDeclarative/qmlparserstatus.h>
+#include <QtScript/qscriptvalue.h>
+#include <QtCore/qurl.h>
+
+class QmlWorkerScriptEnginePrivate;
+class QmlWorkerScriptEngine : public QThread
+{
+Q_OBJECT
+public:
+ QmlWorkerScriptEngine(QObject *parent = 0);
+ virtual ~QmlWorkerScriptEngine();
+
+ class Data;
+ class WorkerScript {
+ public:
+ void sendMessage(Data *);
+ void executeUrl(const QUrl &);
+
+ private:
+ WorkerScript();
+ friend class QmlWorkerScriptEngine;
+ QmlWorkerScriptEngine *engine;
+ int id;
+ };
+ WorkerScript *createWorkerScript();
+
+ struct Data {
+ QVariant var;
+ };
+
+ void executeUrl(WorkerScript *, const QUrl &);
+ void sendMessage(WorkerScript *, Data *);
+
+protected:
+ virtual void run();
+
+private:
+ QmlWorkerScriptEnginePrivate *d;
+};
+
+class QmlWorkerScript : public QObject, public QmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged);
+
+public:
+ QmlWorkerScript(QObject *parent = 0);
+ virtual ~QmlWorkerScript();
+
+ QUrl source() const;
+ void setSource(const QUrl &);
+
+public slots:
+ void sendMessage(const QScriptValue &);
+
+signals:
+ void sourceChanged();
+
+protected:
+ virtual void componentComplete();
+
+private:
+ QmlWorkerScriptEngine::WorkerScript *m_script;
+ QUrl m_source;
+};
+QML_DECLARE_TYPE(QmlWorkerScript);
+
+#endif // QMLWORKERSCRIPT_P_H