8#include <private/qqmlengine_p.h>
9#include <private/qqmlexpression_p.h>
10#include <private/qjsvalue_p.h>
11#include <private/qqmlscriptblob_p.h>
12#include <private/qqmlscriptdata_p.h>
14#include <QtCore/qcoreevent.h>
15#include <QtCore/qcoreapplication.h>
16#include <QtCore/qdebug.h>
17#include <QtQml/qjsengine.h>
18#include <QtCore/qmutex.h>
19#include <QtCore/qwaitcondition.h>
20#include <QtCore/qfile.h>
21#include <QtCore/qdatetime.h>
22#include <QtQml/qqmlinfo.h>
23#include <QtQml/qqmlfile.h>
24#if QT_CONFIG(qml_network)
25#include <QtNetwork/qnetworkaccessmanager.h>
26#include "qqmlnetworkaccessmanagerfactory.h"
29#include <private/qv4serialize_p.h>
31#include <private/qv4value_p.h>
32#include <private/qv4functionobject_p.h>
33#include <private/qv4script_p.h>
34#include <private/qv4scopedvalue_p.h>
35#include <private/qv4jscall_p.h>
122#if QT_CONFIG(qml_network)
131 QQuickWorkerScript *
owner =
nullptr;
133#if QT_CONFIG(qml_network)
137 void ready(QQmlNotifyingBlob *blob)
final;
177 void processMessage(
int id,
const QByteArray &data);
178 void processLoad(
int id,
const QUrl &url);
182 const QV4::Value *,
const QV4::Value *argv,
int argc)
185 const WorkerScript *script = workerScriptExtension(scope.engine);
188 QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue());
189 QByteArray data = QV4::Serialize::serialize(v, scope.engine);
191 QMutexLocker locker(&script
->p->m_lock);
193 QCoreApplication::postEvent(script->owner,
new WorkerDataEvent(0, data));
195 return QV4::Encode::undefined();
203 processMessage(workerEvent->workerId(), workerEvent->data());
208 processLoad(workerEvent->workerId(), workerEvent->url());
212 QMutexLocker locker(&m_lock);
214 auto itr = workers.constFind(workerEvent->workerId());
215 if (itr != workers.cend()) {
229 return QObject::event(event);
234 QMutexLocker locker(&m_lock);
236 const auto it = workers.find(id);
237 if (it == workers.end())
242 QQuickWorkerScript *owner = it->asT2();
243 auto *engine =
new QV4::ExecutionEngine;
253 QV4::ExecutionEngine *engine = workerEngine(id);
257 QV4::Scope scope(engine);
258 QV4::ScopedString v(scope, engine->newString(QStringLiteral(
"WorkerScript")));
259 QV4::ScopedObject worker(scope, engine->globalObject->get(v));
260 QV4::ScopedFunctionObject onmessage(scope);
262 onmessage = worker->get((v = engine->newString(QStringLiteral(
"onMessage"))));
267 QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, engine));
269 QV4::JSCallArguments jsCallData(scope, 1);
270 *jsCallData.thisObject = engine->global();
271 jsCallData.args[0] = value;
272 onmessage->call(jsCallData);
273 if (scope.hasException()) {
274 QQmlError error = scope.engine->catchExceptionAsQmlError();
276 reportScriptException(script, error);
282 if (url.isRelative())
285 QV4::ExecutionEngine *engine = workerEngine(id);
290 QQmlRefPointer<QQmlScriptBlob> scriptBlob = engine->typeLoader()->getScript(url);
292 if (scriptBlob->isCompleteOrError())
293 script->ready(scriptBlob.data());
295 scriptBlob->registerCallback(script);
300 QMutexLocker locker(&script
->p->m_lock);
302 QCoreApplication::postEvent(script->owner,
new WorkerReadyEvent);
306 const QQmlError &error)
308 QMutexLocker locker(&script
->p->m_lock);
310 QCoreApplication::postEvent(script->owner,
new WorkerErrorEvent(error));
315 , d(
new QQuickWorkerScriptEnginePrivate(QQmlTypeLoader::get(parent)))
317 connect(d, SIGNAL(stopThread()),
this, SLOT(quit()), Qt::DirectConnection);
318 QMutexLocker locker(&d->m_lock);
319 start(QThread::LowestPriority);
320 d->m_wait.wait(&d->m_lock);
321 d->moveToThread(
this);
326 QCoreApplication::postEvent(d,
new WorkerDestroyEvent);
331 while (!isFinished()) {
334 QCoreApplication::processEvents();
335 yieldCurrentThread();
344 engine->initQmlGlobalObject();
346 QV4::Scope scope(engine);
347 QV4::ScopedObject api(scope, engine->newObject());
348 QV4::ScopedString sendMessageName(scope, engine->newString(QStringLiteral(
"sendMessage")));
349 QV4::ScopedFunctionObject sendMessage(
350 scope, QV4::FunctionObject::createBuiltinFunction(
351 engine, sendMessageName,
352 QQuickWorkerScriptEnginePrivate::method_sendMessage, 1));
353 api->put(sendMessageName, sendMessage);
354 QV4::ScopedString workerScriptName(scope, engine->newString(QStringLiteral(
"WorkerScript")));
355 engine->globalObject->put(workerScriptName, api);
357#if QT_CONFIG(qml_network)
358 engine->typeLoader()->setNetworkAccessManagerFactory(
this);
364 if (scriptBlob->isComplete()) {
365 const auto cu = engine->executableCompilationUnit(
366 static_cast<QQmlScriptBlob *>(scriptBlob)->scriptData()->compilationUnit());
367 if (cu->isESModule()) {
368 if (cu->instantiate())
371 QV4::Function *vmFunction = cu->rootFunction();
372 QScopedValueRollback<QV4::Function *> savedGlobal(engine->globalCode, vmFunction);
373 vmFunction->call(engine->globalObject,
nullptr, 0, engine->rootContext());
376 if (engine->hasException)
377 p->reportScriptException(
this, engine->catchExceptionAsQmlError());
380 Q_ASSERT(scriptBlob->isError());
382 const QList<QQmlError> errors = scriptBlob->errors();
383 for (
const QQmlError &error : errors) {
385 engine->throwSyntaxError(
386 error.description(), error.url().toString(), error.line(), error.column());
387 p->reportScriptException(
this, engine->catchExceptionAsQmlError());
394#if QT_CONFIG(qml_network)
395QNetworkAccessManager *WorkerScript::create(QObject *parent)
397 return p->m_typeLoader->createNetworkAccessManager(parent);
405 QMutexLocker locker(&d->m_lock);
406 d->workers.insert(id, owner);
414 QMutexLocker locker(&d->m_lock);
415 const auto it = d->workers.find(id);
416 if (it == d->workers.end())
420 workerScriptExtension(it->asT1())->owner =
nullptr;
422 *it =
static_cast<QQuickWorkerScript *>(
nullptr);
425 QCoreApplication::postEvent(d,
new WorkerRemoveEvent(id));
430 QCoreApplication::postEvent(d,
new WorkerLoadEvent(id, url));
435 QCoreApplication::postEvent(d,
new WorkerDataEvent(id, data));
441 QMutexLocker locker(&d->m_lock);
447 QMutexLocker locker(&d->m_lock);
448 for (
auto it = d->workers.begin(), end = d->workers.end(); it != end; ++it) {
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511QQuickWorkerScript::QQuickWorkerScript(QObject *parent)
516QQuickWorkerScript::~QQuickWorkerScript()
518 if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId);
522
523
524
525
526
527
528
529
530
531QUrl QQuickWorkerScript::source()
const
536void QQuickWorkerScript::setSource(
const QUrl &source)
538 if (m_source == source)
544 const QQmlContext *context = qmlContext(
this);
545 m_engine->executeUrl(m_scriptId, context ? context->resolvedUrl(m_source) : m_source);
553 emit sourceChanged();
557
558
559
560
561
562bool QQuickWorkerScript::ready()
const
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587void QQuickWorkerScript::sendMessage(QQmlV4FunctionPtr args)
590 qWarning(
"QQuickWorkerScript: Attempt to send message before WorkerScript establishment");
594 QV4::Scope scope(args->v4engine());
595 QV4::ScopedValue argument(scope, QV4::Value::undefinedValue());
596 if (args->length() != 0)
597 argument = (*args)[0];
599 m_engine->sendMessage(m_scriptId, QV4::Serialize::serialize(argument, scope.engine));
602void QQuickWorkerScript::classBegin()
604 m_componentComplete =
false;
607QQuickWorkerScriptEngine *QQuickWorkerScript::engine()
609 if (m_engine)
return m_engine;
610 if (m_componentComplete) {
611 const QQmlContext *context = qmlContext(
this);
612 QQmlEngine *engine = context ? context->engine() :
nullptr;
614 qWarning(
"QQuickWorkerScript: engine() called without qmlEngine() set");
618 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
619 if (enginePrivate->workerScriptEngine ==
nullptr)
620 enginePrivate->workerScriptEngine =
new QQuickWorkerScriptEngine(engine);
621 m_engine = qobject_cast<QQuickWorkerScriptEngine *>(enginePrivate->workerScriptEngine);
623 m_scriptId = m_engine->registerWorkerScript(
this);
625 if (m_source.isValid())
626 m_engine->executeUrl(m_scriptId, context->resolvedUrl(m_source));
633void QQuickWorkerScript::componentComplete()
635 m_componentComplete =
true;
640
641
642
643
644
646bool QQuickWorkerScript::event(QEvent *event)
648 switch (WorkerEventType(event->type())) {
649 case WorkerEventType::Data:
650 if (QQmlEngine *engine = qmlEngine(
this)) {
651 QV4::ExecutionEngine *v4 = engine->handle();
652 WorkerDataEvent *workerEvent =
static_cast<WorkerDataEvent *>(event);
653 emit message(QJSValuePrivate::fromReturnedValue(
654 QV4::Serialize::deserialize(workerEvent->data(), v4)));
657 case WorkerEventType::Error: {
658 WorkerErrorEvent *workerEvent =
static_cast<WorkerErrorEvent *>(event);
659 QQmlEnginePrivate::warning(qmlEngine(
this), workerEvent->error());
662 case WorkerEventType::Ready:
671 return QObject::event(event);
676#include <qquickworkerscript.moc>
678#include "moc_qquickworkerscript_p.cpp"
bool event(QEvent *) override
This virtual function receives events to an object and should return true if the event e was recogniz...
static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
void reportScriptException(WorkerScript *, const QQmlError &error)
QV4::ExecutionEngine * workerEngine(int id)
void reportScriptReady(WorkerScript *)
QHash< int, QBiPointer< QV4::ExecutionEngine, QQuickWorkerScript > > workers
void executeUrl(int, const QUrl &)
~QQuickWorkerScriptEngine()
void sendMessage(int, const QByteArray &)
void removeWorkerScript(int)
int registerWorkerScript(QQuickWorkerScript *)
WorkerDataEvent(int workerId, const QByteArray &data)
WorkerErrorEvent(const QQmlError &error)
WorkerIdEvent(int workerId, WorkerEventType type)
WorkerLoadEvent(int workerId, const QUrl &url)
WorkerRemoveEvent(int workerId)
V4_DEFINE_EXTENSION(WorkerScript, workerScriptExtension)
void ready(QQmlNotifyingBlob *blob) final
WorkerScript(QV4::ExecutionEngine *engine)
QQuickWorkerScriptEnginePrivate * p
QQuickWorkerScript * owner
QV4::ExecutionEngine * engine