diff options
Diffstat (limited to 'src/qml/types')
-rw-r--r-- | src/qml/types/qqmlbind.cpp | 142 | ||||
-rw-r--r-- | src/qml/types/qqmlbind_p.h | 18 | ||||
-rw-r--r-- | src/qml/types/qqmlconnections.cpp | 6 | ||||
-rw-r--r-- | src/qml/types/qqmlconnections_p.h | 4 | ||||
-rw-r--r-- | src/qml/types/qquickworkerscript.cpp | 669 | ||||
-rw-r--r-- | src/qml/types/qquickworkerscript_p.h | 123 | ||||
-rw-r--r-- | src/qml/types/types.pri | 7 |
7 files changed, 160 insertions, 809 deletions
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 513f7f2997..1ba015f796 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -43,6 +43,8 @@ #include <private/qqmlproperty_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlmetatype_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qv4persistent_p.h> #include <qqmlengine.h> #include <qqmlcontext.h> @@ -60,7 +62,21 @@ QT_BEGIN_NAMESPACE class QQmlBindPrivate : public QObjectPrivate { public: - QQmlBindPrivate() : obj(nullptr), componentComplete(true), delayed(false), pendingEval(false) {} + QQmlBindPrivate() + : obj(nullptr) + , prevBind(QQmlAbstractBinding::Ptr()) + , prevIsVariant(false) + , componentComplete(true) + , delayed(false) + , pendingEval(false) + , restoreBinding(true) +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + , restoreValue(false) + , restoreModeExplicit(false) +#else + , restoreValue(true) +#endif + {} ~QQmlBindPrivate() { } QQmlNullableValue<bool> when; @@ -69,11 +85,20 @@ public: QQmlNullableValue<QVariant> value; QQmlProperty prop; QQmlAbstractBinding::Ptr prevBind; + QV4::PersistentValue v4Value; + QVariant prevValue; + bool prevIsVariant:1; bool componentComplete:1; bool delayed:1; bool pendingEval:1; + bool restoreBinding:1; + bool restoreValue:1; +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + bool restoreModeExplicit:1; +#endif void validate(QObject *binding) const; + void clearPrev(); }; void QQmlBindPrivate::validate(QObject *binding) const @@ -323,6 +348,57 @@ void QQmlBind::setDelayed(bool delayed) eval(); } +/*! + \qmlproperty enumeration QtQml::Binding::restoreMode + \since 5.14 + + This property can be used to describe if and how the original value should + be restored when the binding is disabled. + + The possible values are: + \list + \li Binding.RestoreNone The original value is not restored at all + \li Binding.RestoreBinding The original value is restored if it was another + binding. In that case the old binding is in effect again. + \li Binding.RestoreValue The original value is restored if it was a plain + value rather than a binding. + \li Binding.RestoreBindingOrValue The original value is always restored. + \list + + \warning The default value is Binding.RestoreBinding. This will change in + Qt 5.15 to Binding.RestoreBindingOrValue. + + If you rely on any specific behavior regarding the restoration of plain + values when bindings get disabled you should migrate to explicitly set the + restoreMode. + + Reliance on a restoreMode that doesn't restore the previous binding or value + for a specific property results in a run-time warning. +*/ +QQmlBind::RestorationMode QQmlBind::restoreMode() const +{ + Q_D(const QQmlBind); + unsigned result = RestoreNone; + if (d->restoreValue) + result |= RestoreValue; + if (d->restoreBinding) + result |= RestoreBinding; + return RestorationMode(result); +} + +void QQmlBind::setRestoreMode(RestorationMode newMode) +{ + Q_D(QQmlBind); + if (newMode != restoreMode()) { + d->restoreValue = (newMode & RestoreValue); + d->restoreBinding = (newMode & RestoreBinding); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + d->restoreModeExplicit = true; +#endif + emit restoreModeChanged(); + } +} + void QQmlBind::setTarget(const QQmlProperty &p) { Q_D(QQmlBind); @@ -358,6 +434,14 @@ void QQmlBind::prepareEval() } } +void QQmlBindPrivate::clearPrev() +{ + prevBind = nullptr; + v4Value.clear(); + prevValue.clear(); + prevIsVariant = false; +} + void QQmlBind::eval() { Q_D(QQmlBind); @@ -369,16 +453,64 @@ void QQmlBind::eval() if (!d->when) { //restore any previous binding if (d->prevBind) { - QQmlAbstractBinding::Ptr p = d->prevBind; - d->prevBind = nullptr; - QQmlPropertyPrivate::setBinding(p.data()); + if (d->restoreBinding) { + QQmlAbstractBinding::Ptr p = d->prevBind; + d->clearPrev(); // Do that before setBinding(), as setBinding() may recurse. + QQmlPropertyPrivate::setBinding(p.data()); + } + } else if (!d->v4Value.isEmpty()) { + if (d->restoreValue) { + auto propPriv = QQmlPropertyPrivate::get(d->prop); + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(propPriv->core.coreIndex(), *d->v4Value.valueRef()); + d->clearPrev(); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + } else if (!d->restoreModeExplicit) { + qmlWarning(this) + << "Not restoring previous value because restoreMode has not been set." + << "This behavior is deprecated." + << "In Qt < 5.15 the default is Binding.RestoreBinding." + << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue."; +#endif + } + } else if (d->prevIsVariant) { + if (d->restoreValue) { + d->prop.write(d->prevValue); + d->clearPrev(); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + } else if (!d->restoreModeExplicit) { + qmlWarning(this) + << "Not restoring previous value because restoreMode has not been set." + << "This behavior is deprecated." + << "In Qt < 5.15 the default is Binding.RestoreBinding." + << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue."; +#endif + } } return; } //save any set binding for restoration - if (!d->prevBind) + if (!d->prevBind && d->v4Value.isEmpty() && !d->prevIsVariant) { + // try binding first d->prevBind = QQmlPropertyPrivate::binding(d->prop); + + if (!d->prevBind) { // nope, try a V4 value next + auto propPriv = QQmlPropertyPrivate::get(d->prop); + auto propData = propPriv->core; + if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(propPriv->object); + Q_ASSERT(vmemo); + auto retVal = vmemo->vmeProperty(propData.coreIndex()); + d->v4Value = QV4::PersistentValue(vmemo->engine, retVal); + } else { // nope, use the meta object to get a QVariant + d->prevValue = d->prop.read(); + d->prevIsVariant = true; + } + } + } + QQmlPropertyPrivate::removeBinding(d->prop); } diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h index 5bf9ef85c6..22007a3d25 100644 --- a/src/qml/types/qqmlbind_p.h +++ b/src/qml/types/qqmlbind_p.h @@ -60,6 +60,15 @@ QT_BEGIN_NAMESPACE class QQmlBindPrivate; class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus { +public: + enum RestorationMode { + RestoreNone = 0x0, + RestoreBinding = 0x1, + RestoreValue = 0x2, + RestoreBindingOrValue = RestoreBinding | RestoreValue + }; + +private: Q_OBJECT Q_DECLARE_PRIVATE(QQmlBind) Q_INTERFACES(QQmlParserStatus) @@ -69,6 +78,9 @@ class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSourc Q_PROPERTY(QVariant value READ value WRITE setValue) Q_PROPERTY(bool when READ when WRITE setWhen) Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8) + Q_PROPERTY(RestorationMode restoreMode READ restoreMode WRITE setRestoreMode + NOTIFY restoreModeChanged REVISION 14) + Q_ENUM(RestorationMode) public: QQmlBind(QObject *parent=nullptr); @@ -89,6 +101,12 @@ public: bool delayed() const; void setDelayed(bool); + RestorationMode restoreMode() const; + void setRestoreMode(RestorationMode); + +Q_SIGNALS: + void restoreModeChanged(); + protected: void setTarget(const QQmlProperty &) override; void classBegin() override; diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index f601087690..8ec754a9df 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -66,7 +66,7 @@ public: bool ignoreUnknownSignals; bool componentcomplete; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding *> bindings; }; @@ -231,7 +231,7 @@ void QQmlConnections::setIgnoreUnknownSignals(bool ignore) d->ignoreUnknownSignals = ignore; } -void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) +void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) { for (int ii = 0; ii < props.count(); ++ii) { const QV4::CompiledData::Binding *binding = props.at(ii); @@ -256,7 +256,7 @@ void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledDat } } -void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlConnectionsPrivate *p = static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object)); diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index bd03d7e152..f6ad1eb46c 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -98,8 +98,8 @@ private: class QQmlConnectionsParser : public QQmlCustomParser { public: - void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override; - void applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; + void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override; + void applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; }; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp deleted file mode 100644 index c081c9e7fc..0000000000 --- a/src/qml/types/qquickworkerscript.cpp +++ /dev/null @@ -1,669 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtqmlglobal_p.h" -#include "qquickworkerscript_p.h" -#include <private/qqmlengine_p.h> -#include <private/qqmlexpression_p.h> - -#include <QtCore/qcoreevent.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtQml/qjsengine.h> -#include <QtCore/qmutex.h> -#include <QtCore/qwaitcondition.h> -#include <QtCore/qfile.h> -#include <QtCore/qdatetime.h> -#include <QtQml/qqmlinfo.h> -#include <QtQml/qqmlfile.h> -#if QT_CONFIG(qml_network) -#include <QtNetwork/qnetworkaccessmanager.h> -#include "qqmlnetworkaccessmanagerfactory.h" -#endif - -#include <private/qv8engine_p.h> -#include <private/qv4serialize_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> -#include <private/qv4script_p.h> -#include <private/qv4scopedvalue_p.h> -#include <private/qv4jscall_p.h> - -QT_BEGIN_NAMESPACE - -class WorkerDataEvent : public QEvent -{ -public: - enum Type { WorkerData = QEvent::User }; - - WorkerDataEvent(int workerId, const QByteArray &data); - virtual ~WorkerDataEvent(); - - int workerId() const; - QByteArray data() const; - -private: - int m_id; - QByteArray 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 WorkerRemoveEvent : public QEvent -{ -public: - enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; - - WorkerRemoveEvent(int workerId); - - int workerId() const; - -private: - int m_id; -}; - -class WorkerErrorEvent : public QEvent -{ -public: - enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; - - WorkerErrorEvent(const QQmlError &error); - - QQmlError error() const; - -private: - QQmlError m_error; -}; - -class QQuickWorkerScriptEnginePrivate : public QObject -{ - Q_OBJECT -public: - enum WorkerEventTypes { - WorkerDestroyEvent = QEvent::User + 100 - }; - - QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); - - QQmlEngine *qmlengine; - - QMutex m_lock; - QWaitCondition m_wait; - - struct WorkerScript : public QV8Engine { - WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent); - ~WorkerScript() override; - -#if QT_CONFIG(qml_network) - QNetworkAccessManager *networkAccessManager() override; -#endif - - QQuickWorkerScriptEnginePrivate *p = nullptr; - QUrl source; - QQuickWorkerScript *owner = nullptr; -#if QT_CONFIG(qml_network) - QScopedPointer<QNetworkAccessManager> accessManager; -#endif - int id = -1; - }; - - QHash<int, WorkerScript *> workers; - QV4::ReturnedValue getWorker(WorkerScript *); - - int m_nextId; - - static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - -signals: - void stopThread(); - -protected: - bool event(QEvent *) override; - -private: - void processMessage(int, const QByteArray &); - void processLoad(int, const QUrl &); - void reportScriptException(WorkerScript *, const QQmlError &error); -}; - -QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) -: qmlengine(engine), m_nextId(0) -{ -} - -QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::FunctionObject *b, - const QV4::Value *, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - WorkerScript *script = static_cast<WorkerScript *>(scope.engine->v8Engine); - - QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue()); - QByteArray data = QV4::Serialize::serialize(v, scope.engine); - - QMutexLocker locker(&script->p->m_lock); - if (script && script->owner) - QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - - return QV4::Encode::undefined(); -} - -bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - processMessage(workerEvent->workerId(), workerEvent->data()); - return true; - } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { - WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event); - processLoad(workerEvent->workerId(), workerEvent->url()); - return true; - } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { - emit stopThread(); - return true; - } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { - QMutexLocker locker(&m_lock); - WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event); - QHash<int, WorkerScript *>::iterator itr = workers.find(workerEvent->workerId()); - if (itr != workers.end()) { - delete itr.value(); - workers.erase(itr); - } - return true; - } else { - return QObject::event(event); - } -} - -void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) -{ - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - QV4::Scope scope(v4); - QV4::ScopedString v(scope); - QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript"))))); - QV4::ScopedFunctionObject onmessage(scope); - if (worker) - onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage")))); - - if (!onmessage) - return; - - QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4)); - - QV4::JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = v4->global(); - jsCallData->args[0] = value; - onmessage->call(jsCallData); - if (scope.hasException()) { - QQmlError error = scope.engine->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) -{ - if (url.isRelative()) - return; - - QString fileName = QQmlFile::urlToLocalFileOrQrc(url); - - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - - script->source = url; - - if (fileName.endsWith(QLatin1String(".mjs"))) { - auto moduleUnit = v4->loadModule(url); - if (moduleUnit) { - if (moduleUnit->instantiate(v4)) - moduleUnit->evaluate(); - } else { - v4->throwError(QStringLiteral("Could not load module file")); - } - } else { - QString error; - QV4::Scope scope(v4); - QScopedPointer<QV4::Script> program; - program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error)); - if (program.isNull()) { - if (!error.isEmpty()) - qWarning().nospace() << error; - return; - } - - if (!v4->hasException) - program->run(); - } - - if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, - const QQmlError &error) -{ - QMutexLocker locker(&script->p->m_lock); - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); -} - -WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) -: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) -{ -} - -WorkerDataEvent::~WorkerDataEvent() -{ -} - -int WorkerDataEvent::workerId() const -{ - return m_id; -} - -QByteArray WorkerDataEvent::data() const -{ - return m_data; -} - -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; -} - -WorkerRemoveEvent::WorkerRemoveEvent(int workerId) -: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) -{ -} - -int WorkerRemoveEvent::workerId() const -{ - return m_id; -} - -WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) -: QEvent((QEvent::Type)WorkerError), m_error(error) -{ -} - -QQmlError WorkerErrorEvent::error() const -{ - return m_error; -} - -QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) -: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) -{ - d->m_lock.lock(); - connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); - start(QThread::LowestPriority); - d->m_wait.wait(&d->m_lock); - d->moveToThread(this); - d->m_lock.unlock(); -} - -QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() -{ - d->m_lock.lock(); - QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); - d->m_lock.unlock(); - - //We have to force to cleanup the main thread's event queue here - //to make sure the main GUI release all pending locks/wait conditions which - //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). - while (!isFinished()) { - // We can't simply wait here, because the worker thread will not terminate - // until the main thread processes the last data event it generates - QCoreApplication::processEvents(); - yieldCurrentThread(); - } - - d->deleteLater(); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent) - : QV8Engine(new QV4::ExecutionEngine) - , p(parent) - , id(id) -{ - m_v4Engine->v8Engine = this; - - initQmlGlobalObject(); - - QV4::Scope scope(m_v4Engine); - QV4::ScopedObject api(scope, scope.engine->newObject()); - QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); - QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1)); - api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage); - m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() -{ - delete m_v4Engine; -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager() -{ - if (!accessManager) { - if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { - accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p)); - } else { - accessManager.reset(new QNetworkAccessManager(p)); - } - } - return accessManager.data(); -} -#endif - -int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) -{ - typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; - WorkerScript *script = new WorkerScript(d->m_nextId++, d); - - script->owner = owner; - - d->m_lock.lock(); - d->workers.insert(script->id, script); - d->m_lock.unlock(); - - return script->id; -} - -void QQuickWorkerScriptEngine::removeWorkerScript(int id) -{ - QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); - if (script) { - script->owner = nullptr; - QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); - } -} - -void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) -{ - QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); -} - -void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) -{ - QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); -} - -void QQuickWorkerScriptEngine::run() -{ - d->m_lock.lock(); - - d->m_wait.wakeAll(); - - d->m_lock.unlock(); - - exec(); - - qDeleteAll(d->workers); - d->workers.clear(); -} - - -/*! - \qmltype WorkerScript - \instantiates QQuickWorkerScript - \ingroup qtquick-threading - \inqmlmodule QtQml - \brief Enables the use of threads in a Qt Quick application. - - Use WorkerScript to run operations in a new thread. - This is useful for running operations in the background so - that the main GUI thread is not blocked. - - Messages can be passed between the new thread and the parent thread - using \l sendMessage() and the \c onMessage() handler. - - An example: - - \snippet qml/workerscript/workerscript.qml 0 - - The above worker script specifies a JavaScript file, "script.mjs", that handles - the operations to be performed in the new thread. Here is \c script.mjs: - - \quotefile qml/workerscript/script.mjs - - When the user clicks anywhere within the rectangle, \c sendMessage() is - called, triggering the \tt WorkerScript.onMessage() handler in - \tt script.mjs. This in turn sends a reply message that is then received - by the \tt onMessage() handler of \tt myWorker. - - The example uses a script that is an ECMAScript module, because it has the ".mjs" extension. - It can use import statements to access functionality from other modules and it is run in JavaScript - strict mode. - - If a worker script has the extension ".js" instead, then it is considered to contain plain JavaScript - statements and it is run in non-strict mode. - - \note Each WorkerScript element will instantiate a separate JavaScript engine to ensure perfect - isolation and thread-safety. If the impact of that results in a memory consumption that is too - high for your environment, then consider sharing a WorkerScript element. - - \section3 Restrictions - - Since the \c WorkerScript.onMessage() function is run in a separate thread, the - JavaScript file is evaluated in a context separate from the main QML engine. This means - that unlike an ordinary JavaScript file that is imported into QML, the \c script.mjs - in the above example cannot access the properties, methods or other attributes - of the QML item, nor can it access any context properties set on the QML object - through QQmlContext. - - Additionally, there are restrictions on the types of values that can be passed to and - from the worker script. See the sendMessage() documentation for details. - - Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax. - Scripts that are ECMAScript modules can freely use import and export statements. - - \sa {Qt Quick Examples - Threading}, - {Threaded ListModel Example} -*/ -QQuickWorkerScript::QQuickWorkerScript(QObject *parent) -: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true) -{ -} - -QQuickWorkerScript::~QQuickWorkerScript() -{ - if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); -} - -/*! - \qmlproperty url WorkerScript::source - - This holds the url of the JavaScript file that implements the - \tt WorkerScript.onMessage() handler for threaded operations. - - If the file name component of the url ends with ".mjs", then the script - is parsed as an ECMAScript module and run in strict mode. Otherwise it is considered to be - plain script. -*/ -QUrl QQuickWorkerScript::source() const -{ - return m_source; -} - -void QQuickWorkerScript::setSource(const QUrl &source) -{ - if (m_source == source) - return; - - m_source = source; - - if (engine()) - m_engine->executeUrl(m_scriptId, m_source); - - emit sourceChanged(); -} - -/*! - \qmlmethod WorkerScript::sendMessage(jsobject message) - - Sends the given \a message to a worker script handler in another - thread. The other worker script handler can receive this message - through the onMessage() handler. - - The \c message object may only contain values of the following - types: - - \list - \li boolean, number, string - \li JavaScript objects and arrays - \li ListModel objects (any other type of QObject* is not allowed) - \endlist - - All objects and arrays are copied to the \c message. With the exception - of ListModel objects, any modifications by the other thread to an object - passed in \c message will not be reflected in the original object. -*/ -void QQuickWorkerScript::sendMessage(QQmlV4Function *args) -{ - if (!engine()) { - qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); - return; - } - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue argument(scope, QV4::Value::undefinedValue()); - if (args->length() != 0) - argument = (*args)[0]; - - m_engine->sendMessage(m_scriptId, QV4::Serialize::serialize(argument, scope.engine)); -} - -void QQuickWorkerScript::classBegin() -{ - m_componentComplete = false; -} - -QQuickWorkerScriptEngine *QQuickWorkerScript::engine() -{ - if (m_engine) return m_engine; - if (m_componentComplete) { - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); - return nullptr; - } - - m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); - m_scriptId = m_engine->registerWorkerScript(this); - - if (m_source.isValid()) - m_engine->executeUrl(m_scriptId, m_source); - - return m_engine; - } - return nullptr; -} - -void QQuickWorkerScript::componentComplete() -{ - m_componentComplete = true; - engine(); // Get it started now. -} - -/*! - \qmlsignal WorkerScript::message(jsobject msg) - - This signal is emitted when a message \a msg is received from a worker - script in another thread through a call to sendMessage(). - - The corresponding handler is \c onMessage. -*/ - -bool QQuickWorkerScript::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - if (QQmlEngine *engine = qmlEngine(this)) { - QV4::ExecutionEngine *v4 = engine->handle(); - WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - emit message(QJSValue(v4, QV4::Serialize::deserialize(workerEvent->data(), v4))); - } - return true; - } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { - WorkerErrorEvent *workerEvent = static_cast<WorkerErrorEvent *>(event); - QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); - return true; - } else { - return QObject::event(event); - } -} - -QT_END_NAMESPACE - -#include <qquickworkerscript.moc> - -#include "moc_qquickworkerscript_p.cpp" diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h deleted file mode 100644 index 87cf2e9754..0000000000 --- a/src/qml/types/qquickworkerscript_p.h +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKWORKERSCRIPT_P_H -#define QQUICKWORKERSCRIPT_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 <qqml.h> - -#include <QtQml/qqmlparserstatus.h> -#include <QtCore/qthread.h> -#include <QtQml/qjsvalue.h> -#include <QtCore/qurl.h> - -QT_BEGIN_NAMESPACE - - -class QQuickWorkerScript; -class QQuickWorkerScriptEnginePrivate; -class QQuickWorkerScriptEngine : public QThread -{ -Q_OBJECT -public: - QQuickWorkerScriptEngine(QQmlEngine *parent = nullptr); - ~QQuickWorkerScriptEngine(); - - int registerWorkerScript(QQuickWorkerScript *); - void removeWorkerScript(int); - void executeUrl(int, const QUrl &); - void sendMessage(int, const QByteArray &); - -protected: - void run() override; - -private: - QQuickWorkerScriptEnginePrivate *d; -}; - -class QQmlV4Function; -class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - - Q_INTERFACES(QQmlParserStatus) -public: - QQuickWorkerScript(QObject *parent = nullptr); - ~QQuickWorkerScript(); - - QUrl source() const; - void setSource(const QUrl &); - -public Q_SLOTS: - void sendMessage(QQmlV4Function*); - -Q_SIGNALS: - void sourceChanged(); - void message(const QJSValue &messageObject); - -protected: - void classBegin() override; - void componentComplete() override; - bool event(QEvent *) override; - -private: - QQuickWorkerScriptEngine *engine(); - QQuickWorkerScriptEngine *m_engine; - int m_scriptId; - QUrl m_source; - bool m_componentComplete; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickWorkerScript) - -#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index ba11271d66..c50273071b 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -8,13 +8,6 @@ HEADERS += \ $$PWD/qqmlconnections_p.h \ $$PWD/qqmlmodelindexvaluetype_p.h -qtConfig(qml-worker-script) { - SOURCES += \ - $$PWD/qquickworkerscript.cpp - HEADERS += \ - $$PWD/qquickworkerscript_p.h -} - qtConfig(qml-animation) { SOURCES += \ $$PWD/qqmltimer.cpp |