1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation (qt-info@nokia.com)
|
---|
6 | **
|
---|
7 | ** This file is part of the QtDeclarative module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Lesser General Public License Usage
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
18 | ** General Public License version 2.1 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
20 | ** packaging of this file. Please review the following information to
|
---|
21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
23 | **
|
---|
24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at qt-info@nokia.com.
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "private/qdeclarativeworkerscript_p.h"
|
---|
43 | #include "private/qdeclarativelistmodel_p.h"
|
---|
44 | #include "private/qdeclarativelistmodelworkeragent_p.h"
|
---|
45 | #include "private/qdeclarativeengine_p.h"
|
---|
46 |
|
---|
47 | #include <QtCore/qcoreevent.h>
|
---|
48 | #include <QtCore/qcoreapplication.h>
|
---|
49 | #include <QtCore/qdebug.h>
|
---|
50 | #include <QtScript/qscriptengine.h>
|
---|
51 | #include <QtCore/qmutex.h>
|
---|
52 | #include <QtCore/qwaitcondition.h>
|
---|
53 | #include <QtScript/qscriptvalueiterator.h>
|
---|
54 | #include <QtCore/qfile.h>
|
---|
55 | #include <QtCore/qdatetime.h>
|
---|
56 | #include <QtNetwork/qnetworkaccessmanager.h>
|
---|
57 | #include <QtDeclarative/qdeclarativeinfo.h>
|
---|
58 | #include "qdeclarativenetworkaccessmanagerfactory.h"
|
---|
59 |
|
---|
60 |
|
---|
61 | QT_BEGIN_NAMESPACE
|
---|
62 |
|
---|
63 | class WorkerDataEvent : public QEvent
|
---|
64 | {
|
---|
65 | public:
|
---|
66 | enum Type { WorkerData = QEvent::User };
|
---|
67 |
|
---|
68 | WorkerDataEvent(int workerId, const QVariant &data);
|
---|
69 | virtual ~WorkerDataEvent();
|
---|
70 |
|
---|
71 | int workerId() const;
|
---|
72 | QVariant data() const;
|
---|
73 |
|
---|
74 | private:
|
---|
75 | int m_id;
|
---|
76 | QVariant m_data;
|
---|
77 | };
|
---|
78 |
|
---|
79 | class WorkerLoadEvent : public QEvent
|
---|
80 | {
|
---|
81 | public:
|
---|
82 | enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 };
|
---|
83 |
|
---|
84 | WorkerLoadEvent(int workerId, const QUrl &url);
|
---|
85 |
|
---|
86 | int workerId() const;
|
---|
87 | QUrl url() const;
|
---|
88 |
|
---|
89 | private:
|
---|
90 | int m_id;
|
---|
91 | QUrl m_url;
|
---|
92 | };
|
---|
93 |
|
---|
94 | class WorkerRemoveEvent : public QEvent
|
---|
95 | {
|
---|
96 | public:
|
---|
97 | enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 };
|
---|
98 |
|
---|
99 | WorkerRemoveEvent(int workerId);
|
---|
100 |
|
---|
101 | int workerId() const;
|
---|
102 |
|
---|
103 | private:
|
---|
104 | int m_id;
|
---|
105 | };
|
---|
106 |
|
---|
107 | class QDeclarativeWorkerScriptEnginePrivate : public QObject
|
---|
108 | {
|
---|
109 | Q_OBJECT
|
---|
110 | public:
|
---|
111 | enum WorkerEventTypes {
|
---|
112 | WorkerDestroyEvent = QEvent::User + 100
|
---|
113 | };
|
---|
114 |
|
---|
115 | QDeclarativeWorkerScriptEnginePrivate(QDeclarativeEngine *eng);
|
---|
116 |
|
---|
117 | struct ScriptEngine : public QDeclarativeScriptEngine
|
---|
118 | {
|
---|
119 | ScriptEngine(QDeclarativeWorkerScriptEnginePrivate *parent) : QDeclarativeScriptEngine(0), p(parent), accessManager(0) {}
|
---|
120 | ~ScriptEngine() { delete accessManager; }
|
---|
121 | QDeclarativeWorkerScriptEnginePrivate *p;
|
---|
122 | QNetworkAccessManager *accessManager;
|
---|
123 |
|
---|
124 | virtual QNetworkAccessManager *networkAccessManager() {
|
---|
125 | if (!accessManager) {
|
---|
126 | if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) {
|
---|
127 | accessManager = p->qmlengine->networkAccessManagerFactory()->create(this);
|
---|
128 | } else {
|
---|
129 | accessManager = new QNetworkAccessManager(this);
|
---|
130 | }
|
---|
131 | }
|
---|
132 | return accessManager;
|
---|
133 | }
|
---|
134 | };
|
---|
135 | ScriptEngine *workerEngine;
|
---|
136 | static QDeclarativeWorkerScriptEnginePrivate *get(QScriptEngine *e) {
|
---|
137 | return static_cast<ScriptEngine *>(e)->p;
|
---|
138 | }
|
---|
139 |
|
---|
140 | QDeclarativeEngine *qmlengine;
|
---|
141 |
|
---|
142 | QMutex m_lock;
|
---|
143 | QWaitCondition m_wait;
|
---|
144 |
|
---|
145 | struct WorkerScript {
|
---|
146 | WorkerScript();
|
---|
147 |
|
---|
148 | int id;
|
---|
149 | bool initialized;
|
---|
150 | QDeclarativeWorkerScript *owner;
|
---|
151 | QScriptValue object;
|
---|
152 |
|
---|
153 | QScriptValue callback;
|
---|
154 | };
|
---|
155 |
|
---|
156 | QHash<int, WorkerScript *> workers;
|
---|
157 | QScriptValue getWorker(int);
|
---|
158 |
|
---|
159 | int m_nextId;
|
---|
160 |
|
---|
161 | static QVariant scriptValueToVariant(const QScriptValue &);
|
---|
162 | static QScriptValue variantToScriptValue(const QVariant &, QScriptEngine *);
|
---|
163 |
|
---|
164 | static QScriptValue onMessage(QScriptContext *ctxt, QScriptEngine *engine);
|
---|
165 | static QScriptValue sendMessage(QScriptContext *ctxt, QScriptEngine *engine);
|
---|
166 |
|
---|
167 | signals:
|
---|
168 | void stopThread();
|
---|
169 |
|
---|
170 | protected:
|
---|
171 | virtual bool event(QEvent *);
|
---|
172 |
|
---|
173 | private:
|
---|
174 | void processMessage(int, const QVariant &);
|
---|
175 | void processLoad(int, const QUrl &);
|
---|
176 | };
|
---|
177 |
|
---|
178 | QDeclarativeWorkerScriptEnginePrivate::QDeclarativeWorkerScriptEnginePrivate(QDeclarativeEngine *engine)
|
---|
179 | : workerEngine(0), qmlengine(engine), m_nextId(0)
|
---|
180 | {
|
---|
181 | }
|
---|
182 |
|
---|
183 | QScriptValue QDeclarativeWorkerScriptEnginePrivate::onMessage(QScriptContext *ctxt, QScriptEngine *engine)
|
---|
184 | {
|
---|
185 | QDeclarativeWorkerScriptEnginePrivate *p = QDeclarativeWorkerScriptEnginePrivate::get(engine);
|
---|
186 |
|
---|
187 | int id = ctxt->thisObject().data().toVariant().toInt();
|
---|
188 |
|
---|
189 | WorkerScript *script = p->workers.value(id);
|
---|
190 | if (!script)
|
---|
191 | return engine->undefinedValue();
|
---|
192 |
|
---|
193 | if (ctxt->argumentCount() >= 1)
|
---|
194 | script->callback = ctxt->argument(0);
|
---|
195 |
|
---|
196 | return script->callback;
|
---|
197 | }
|
---|
198 |
|
---|
199 | QScriptValue QDeclarativeWorkerScriptEnginePrivate::sendMessage(QScriptContext *ctxt, QScriptEngine *engine)
|
---|
200 | {
|
---|
201 | if (!ctxt->argumentCount())
|
---|
202 | return engine->undefinedValue();
|
---|
203 |
|
---|
204 | QDeclarativeWorkerScriptEnginePrivate *p = QDeclarativeWorkerScriptEnginePrivate::get(engine);
|
---|
205 |
|
---|
206 | int id = ctxt->thisObject().data().toVariant().toInt();
|
---|
207 |
|
---|
208 | WorkerScript *script = p->workers.value(id);
|
---|
209 | if (!script)
|
---|
210 | return engine->undefinedValue();
|
---|
211 |
|
---|
212 | QMutexLocker(&p->m_lock);
|
---|
213 |
|
---|
214 | if (script->owner)
|
---|
215 | QCoreApplication::postEvent(script->owner,
|
---|
216 | new WorkerDataEvent(0, scriptValueToVariant(ctxt->argument(0))));
|
---|
217 |
|
---|
218 | return engine->undefinedValue();
|
---|
219 | }
|
---|
220 |
|
---|
221 | QScriptValue QDeclarativeWorkerScriptEnginePrivate::getWorker(int id)
|
---|
222 | {
|
---|
223 | QHash<int, WorkerScript *>::ConstIterator iter = workers.find(id);
|
---|
224 |
|
---|
225 | if (iter == workers.end())
|
---|
226 | return workerEngine->nullValue();
|
---|
227 |
|
---|
228 | WorkerScript *script = *iter;
|
---|
229 | if (!script->initialized) {
|
---|
230 |
|
---|
231 | script->initialized = true;
|
---|
232 | script->object = workerEngine->newObject();
|
---|
233 |
|
---|
234 | QScriptValue api = workerEngine->newObject();
|
---|
235 | api.setData(script->id);
|
---|
236 |
|
---|
237 | api.setProperty(QLatin1String("onMessage"), workerEngine->newFunction(onMessage),
|
---|
238 | QScriptValue::PropertyGetter | QScriptValue::PropertySetter);
|
---|
239 | api.setProperty(QLatin1String("sendMessage"), workerEngine->newFunction(sendMessage));
|
---|
240 |
|
---|
241 | script->object.setProperty(QLatin1String("WorkerScript"), api);
|
---|
242 | }
|
---|
243 |
|
---|
244 | return script->object;
|
---|
245 | }
|
---|
246 |
|
---|
247 | bool QDeclarativeWorkerScriptEnginePrivate::event(QEvent *event)
|
---|
248 | {
|
---|
249 | if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
|
---|
250 | WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
|
---|
251 | processMessage(workerEvent->workerId(), workerEvent->data());
|
---|
252 | return true;
|
---|
253 | } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) {
|
---|
254 | WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event);
|
---|
255 | processLoad(workerEvent->workerId(), workerEvent->url());
|
---|
256 | return true;
|
---|
257 | } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) {
|
---|
258 | emit stopThread();
|
---|
259 | return true;
|
---|
260 | } else {
|
---|
261 | return QObject::event(event);
|
---|
262 | }
|
---|
263 | }
|
---|
264 |
|
---|
265 | void QDeclarativeWorkerScriptEnginePrivate::processMessage(int id, const QVariant &data)
|
---|
266 | {
|
---|
267 | WorkerScript *script = workers.value(id);
|
---|
268 | if (!script)
|
---|
269 | return;
|
---|
270 |
|
---|
271 | if (script->callback.isFunction()) {
|
---|
272 | QScriptValue args = workerEngine->newArray(1);
|
---|
273 | args.setProperty(0, variantToScriptValue(data, workerEngine));
|
---|
274 |
|
---|
275 | script->callback.call(script->object, args);
|
---|
276 | }
|
---|
277 | }
|
---|
278 |
|
---|
279 | void QDeclarativeWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
|
---|
280 | {
|
---|
281 | if (url.isRelative())
|
---|
282 | return;
|
---|
283 |
|
---|
284 | QString fileName = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
|
---|
285 |
|
---|
286 | QFile f(fileName);
|
---|
287 | if (f.open(QIODevice::ReadOnly)) {
|
---|
288 | QByteArray data = f.readAll();
|
---|
289 | QString script = QString::fromUtf8(data);
|
---|
290 |
|
---|
291 | QScriptValue activation = getWorker(id);
|
---|
292 |
|
---|
293 | QScriptContext *ctxt = QScriptDeclarativeClass::pushCleanContext(workerEngine);
|
---|
294 | QScriptValue urlContext = workerEngine->newObject();
|
---|
295 | urlContext.setData(QScriptValue(workerEngine, fileName));
|
---|
296 | ctxt->pushScope(urlContext);
|
---|
297 | ctxt->pushScope(activation);
|
---|
298 | ctxt->setActivationObject(activation);
|
---|
299 | QDeclarativeScriptParser::extractPragmas(script);
|
---|
300 |
|
---|
301 | workerEngine->baseUrl = url;
|
---|
302 | workerEngine->evaluate(script);
|
---|
303 |
|
---|
304 | workerEngine->popContext();
|
---|
305 | } else {
|
---|
306 | qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString();
|
---|
307 | }
|
---|
308 | }
|
---|
309 |
|
---|
310 | QVariant QDeclarativeWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue &value)
|
---|
311 | {
|
---|
312 | if (value.isBool()) {
|
---|
313 | return QVariant(value.toBool());
|
---|
314 | } else if (value.isString()) {
|
---|
315 | return QVariant(value.toString());
|
---|
316 | } else if (value.isNumber()) {
|
---|
317 | return QVariant((qreal)value.toNumber());
|
---|
318 | } else if (value.isDate()) {
|
---|
319 | return QVariant(value.toDateTime());
|
---|
320 | #ifndef QT_NO_REGEXP
|
---|
321 | } else if (value.isRegExp()) {
|
---|
322 | return QVariant(value.toRegExp());
|
---|
323 | #endif
|
---|
324 | } else if (value.isArray()) {
|
---|
325 | QVariantList list;
|
---|
326 |
|
---|
327 | quint32 length = (quint32)value.property(QLatin1String("length")).toNumber();
|
---|
328 |
|
---|
329 | for (quint32 ii = 0; ii < length; ++ii) {
|
---|
330 | QVariant v = scriptValueToVariant(value.property(ii));
|
---|
331 | list << v;
|
---|
332 | }
|
---|
333 |
|
---|
334 | return QVariant(list);
|
---|
335 | } else if (value.isQObject()) {
|
---|
336 | QDeclarativeListModel *lm = qobject_cast<QDeclarativeListModel *>(value.toQObject());
|
---|
337 | if (lm) {
|
---|
338 | QDeclarativeListModelWorkerAgent *agent = lm->agent();
|
---|
339 | if (agent) {
|
---|
340 | QDeclarativeListModelWorkerAgent::VariantRef v(agent);
|
---|
341 | return qVariantFromValue(v);
|
---|
342 | } else {
|
---|
343 | return QVariant();
|
---|
344 | }
|
---|
345 | } else {
|
---|
346 | // No other QObject's are allowed to be sent
|
---|
347 | return QVariant();
|
---|
348 | }
|
---|
349 | } else if (value.isObject()) {
|
---|
350 | QVariantHash hash;
|
---|
351 |
|
---|
352 | QScriptValueIterator iter(value);
|
---|
353 |
|
---|
354 | while (iter.hasNext()) {
|
---|
355 | iter.next();
|
---|
356 | hash.insert(iter.name(), scriptValueToVariant(iter.value()));
|
---|
357 | }
|
---|
358 |
|
---|
359 | return QVariant(hash);
|
---|
360 | }
|
---|
361 |
|
---|
362 | return QVariant();
|
---|
363 |
|
---|
364 | }
|
---|
365 |
|
---|
366 | QScriptValue QDeclarativeWorkerScriptEnginePrivate::variantToScriptValue(const QVariant &value, QScriptEngine *engine)
|
---|
367 | {
|
---|
368 | if (value.userType() == QVariant::Bool) {
|
---|
369 | return QScriptValue(value.toBool());
|
---|
370 | } else if (value.userType() == QVariant::String) {
|
---|
371 | return QScriptValue(value.toString());
|
---|
372 | } else if (value.userType() == QMetaType::QReal) {
|
---|
373 | return QScriptValue(value.toReal());
|
---|
374 | } else if (value.userType() == QVariant::DateTime) {
|
---|
375 | return engine->newDate(value.toDateTime());
|
---|
376 | #ifndef QT_NO_REGEXP
|
---|
377 | } else if (value.userType() == QVariant::RegExp) {
|
---|
378 | return engine->newRegExp(value.toRegExp());
|
---|
379 | #endif
|
---|
380 | } else if (value.userType() == qMetaTypeId<QDeclarativeListModelWorkerAgent::VariantRef>()) {
|
---|
381 | QDeclarativeListModelWorkerAgent::VariantRef vr = qvariant_cast<QDeclarativeListModelWorkerAgent::VariantRef>(value);
|
---|
382 | if (vr.a->scriptEngine() == 0)
|
---|
383 | vr.a->setScriptEngine(engine);
|
---|
384 | else if (vr.a->scriptEngine() != engine)
|
---|
385 | return engine->nullValue();
|
---|
386 | QScriptValue o = engine->newQObject(vr.a);
|
---|
387 | o.setData(engine->newVariant(value)); // Keeps the agent ref so that it is cleaned up on gc
|
---|
388 | return o;
|
---|
389 | } else if (value.userType() == QMetaType::QVariantList) {
|
---|
390 | QVariantList list = qvariant_cast<QVariantList>(value);
|
---|
391 | QScriptValue rv = engine->newArray(list.count());
|
---|
392 |
|
---|
393 | for (quint32 ii = 0; ii < quint32(list.count()); ++ii)
|
---|
394 | rv.setProperty(ii, variantToScriptValue(list.at(ii), engine));
|
---|
395 |
|
---|
396 | return rv;
|
---|
397 | } else if (value.userType() == QMetaType::QVariantHash) {
|
---|
398 |
|
---|
399 | QVariantHash hash = qvariant_cast<QVariantHash>(value);
|
---|
400 |
|
---|
401 | QScriptValue rv = engine->newObject();
|
---|
402 |
|
---|
403 | for (QVariantHash::ConstIterator iter = hash.begin(); iter != hash.end(); ++iter)
|
---|
404 | rv.setProperty(iter.key(), variantToScriptValue(iter.value(), engine));
|
---|
405 |
|
---|
406 | return rv;
|
---|
407 | } else {
|
---|
408 | return engine->nullValue();
|
---|
409 | }
|
---|
410 | }
|
---|
411 |
|
---|
412 | WorkerDataEvent::WorkerDataEvent(int workerId, const QVariant &data)
|
---|
413 | : QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data)
|
---|
414 | {
|
---|
415 | }
|
---|
416 |
|
---|
417 | WorkerDataEvent::~WorkerDataEvent()
|
---|
418 | {
|
---|
419 | }
|
---|
420 |
|
---|
421 | int WorkerDataEvent::workerId() const
|
---|
422 | {
|
---|
423 | return m_id;
|
---|
424 | }
|
---|
425 |
|
---|
426 | QVariant WorkerDataEvent::data() const
|
---|
427 | {
|
---|
428 | return m_data;
|
---|
429 | }
|
---|
430 |
|
---|
431 | WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url)
|
---|
432 | : QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url)
|
---|
433 | {
|
---|
434 | }
|
---|
435 |
|
---|
436 | int WorkerLoadEvent::workerId() const
|
---|
437 | {
|
---|
438 | return m_id;
|
---|
439 | }
|
---|
440 |
|
---|
441 | QUrl WorkerLoadEvent::url() const
|
---|
442 | {
|
---|
443 | return m_url;
|
---|
444 | }
|
---|
445 |
|
---|
446 | WorkerRemoveEvent::WorkerRemoveEvent(int workerId)
|
---|
447 | : QEvent((QEvent::Type)WorkerRemove), m_id(workerId)
|
---|
448 | {
|
---|
449 | }
|
---|
450 |
|
---|
451 | int WorkerRemoveEvent::workerId() const
|
---|
452 | {
|
---|
453 | return m_id;
|
---|
454 | }
|
---|
455 |
|
---|
456 | QDeclarativeWorkerScriptEngine::QDeclarativeWorkerScriptEngine(QDeclarativeEngine *parent)
|
---|
457 | : QThread(parent), d(new QDeclarativeWorkerScriptEnginePrivate(parent))
|
---|
458 | {
|
---|
459 | d->m_lock.lock();
|
---|
460 | connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection);
|
---|
461 | start(QThread::IdlePriority);
|
---|
462 | d->m_wait.wait(&d->m_lock);
|
---|
463 | d->moveToThread(this);
|
---|
464 | d->m_lock.unlock();
|
---|
465 | }
|
---|
466 |
|
---|
467 | QDeclarativeWorkerScriptEngine::~QDeclarativeWorkerScriptEngine()
|
---|
468 | {
|
---|
469 | d->m_lock.lock();
|
---|
470 | qDeleteAll(d->workers);
|
---|
471 | d->workers.clear();
|
---|
472 | QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QDeclarativeWorkerScriptEnginePrivate::WorkerDestroyEvent));
|
---|
473 | d->m_lock.unlock();
|
---|
474 |
|
---|
475 | wait();
|
---|
476 | d->deleteLater();
|
---|
477 | }
|
---|
478 |
|
---|
479 | QDeclarativeWorkerScriptEnginePrivate::WorkerScript::WorkerScript()
|
---|
480 | : id(-1), initialized(false), owner(0)
|
---|
481 | {
|
---|
482 | }
|
---|
483 |
|
---|
484 | int QDeclarativeWorkerScriptEngine::registerWorkerScript(QDeclarativeWorkerScript *owner)
|
---|
485 | {
|
---|
486 | QDeclarativeWorkerScriptEnginePrivate::WorkerScript *script = new QDeclarativeWorkerScriptEnginePrivate::WorkerScript;
|
---|
487 | script->id = d->m_nextId++;
|
---|
488 | script->owner = owner;
|
---|
489 |
|
---|
490 | d->m_lock.lock();
|
---|
491 | d->workers.insert(script->id, script);
|
---|
492 | d->m_lock.unlock();
|
---|
493 |
|
---|
494 | return script->id;
|
---|
495 | }
|
---|
496 |
|
---|
497 | void QDeclarativeWorkerScriptEngine::removeWorkerScript(int id)
|
---|
498 | {
|
---|
499 | QCoreApplication::postEvent(d, new WorkerRemoveEvent(id));
|
---|
500 | }
|
---|
501 |
|
---|
502 | void QDeclarativeWorkerScriptEngine::executeUrl(int id, const QUrl &url)
|
---|
503 | {
|
---|
504 | QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url));
|
---|
505 | }
|
---|
506 |
|
---|
507 | void QDeclarativeWorkerScriptEngine::sendMessage(int id, const QVariant &data)
|
---|
508 | {
|
---|
509 | QCoreApplication::postEvent(d, new WorkerDataEvent(id, data));
|
---|
510 | }
|
---|
511 |
|
---|
512 | void QDeclarativeWorkerScriptEngine::run()
|
---|
513 | {
|
---|
514 | d->m_lock.lock();
|
---|
515 |
|
---|
516 | d->workerEngine = new QDeclarativeWorkerScriptEnginePrivate::ScriptEngine(d);
|
---|
517 |
|
---|
518 | d->m_wait.wakeAll();
|
---|
519 |
|
---|
520 | d->m_lock.unlock();
|
---|
521 |
|
---|
522 | exec();
|
---|
523 |
|
---|
524 | delete d->workerEngine; d->workerEngine = 0;
|
---|
525 | }
|
---|
526 |
|
---|
527 |
|
---|
528 | /*!
|
---|
529 | \qmlclass WorkerScript QDeclarativeWorkerScript
|
---|
530 | \ingroup qml-utility-elements
|
---|
531 | \brief The WorkerScript element enables the use of threads in QML.
|
---|
532 |
|
---|
533 | Use WorkerScript to run operations in a new thread.
|
---|
534 | This is useful for running operations in the background so
|
---|
535 | that the main GUI thread is not blocked.
|
---|
536 |
|
---|
537 | Messages can be passed between the new thread and the parent thread
|
---|
538 | using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler.
|
---|
539 |
|
---|
540 | An example:
|
---|
541 |
|
---|
542 | \snippet doc/src/snippets/declarative/workerscript.qml 0
|
---|
543 |
|
---|
544 | The above worker script specifies a JavaScript file, "script.js", that handles
|
---|
545 | the operations to be performed in the new thread. Here is \c script.js:
|
---|
546 |
|
---|
547 | \qml
|
---|
548 | WorkerScript.onMessage = function(message) {
|
---|
549 | // ... long-running operations and calculations are done here
|
---|
550 | WorkerScript.sendMessage({ 'reply': 'Mouse is at ' + message.x + ',' + message.y })
|
---|
551 | }
|
---|
552 | \endqml
|
---|
553 |
|
---|
554 | When the user clicks anywhere within the rectangle, \c sendMessage() is
|
---|
555 | called, triggering the \tt WorkerScript.onMessage() handler in
|
---|
556 | \tt script.js. This in turn sends a reply message that is then received
|
---|
557 | by the \tt onMessage() handler of \tt myWorker.
|
---|
558 |
|
---|
559 |
|
---|
560 | \section3 Restrictions
|
---|
561 |
|
---|
562 | Since the \c WorkerScript.onMessage() function is run in a separate thread, the
|
---|
563 | JavaScript file is evaluated in a context separate from the main QML engine. This means
|
---|
564 | that unlike an ordinary JavaScript file that is imported into QML, the \c script.js
|
---|
565 | in the above example cannot access the properties, methods or other attributes
|
---|
566 | of the QML item, nor can it access any context properties set on the QML object
|
---|
567 | through QDeclarativeContext.
|
---|
568 |
|
---|
569 | Additionally, there are restrictions on the types of values that can be passed to and
|
---|
570 | from the worker script. See the sendMessage() documentation for details.
|
---|
571 |
|
---|
572 | \sa {declarative/threading/workerscript}{WorkerScript example},
|
---|
573 | {declarative/threading/threadedlistmodel}{Threaded ListModel example}
|
---|
574 | */
|
---|
575 | QDeclarativeWorkerScript::QDeclarativeWorkerScript(QObject *parent)
|
---|
576 | : QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true)
|
---|
577 | {
|
---|
578 | }
|
---|
579 |
|
---|
580 | QDeclarativeWorkerScript::~QDeclarativeWorkerScript()
|
---|
581 | {
|
---|
582 | if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId);
|
---|
583 | }
|
---|
584 |
|
---|
585 | /*!
|
---|
586 | \qmlproperty url WorkerScript::source
|
---|
587 |
|
---|
588 | This holds the url of the JavaScript file that implements the
|
---|
589 | \tt WorkerScript.onMessage() handler for threaded operations.
|
---|
590 | */
|
---|
591 | QUrl QDeclarativeWorkerScript::source() const
|
---|
592 | {
|
---|
593 | return m_source;
|
---|
594 | }
|
---|
595 |
|
---|
596 | void QDeclarativeWorkerScript::setSource(const QUrl &source)
|
---|
597 | {
|
---|
598 | if (m_source == source)
|
---|
599 | return;
|
---|
600 |
|
---|
601 | m_source = source;
|
---|
602 |
|
---|
603 | if (engine())
|
---|
604 | m_engine->executeUrl(m_scriptId, m_source);
|
---|
605 |
|
---|
606 | emit sourceChanged();
|
---|
607 | }
|
---|
608 |
|
---|
609 | /*!
|
---|
610 | \qmlmethod WorkerScript::sendMessage(jsobject message)
|
---|
611 |
|
---|
612 | Sends the given \a message to a worker script handler in another
|
---|
613 | thread. The other worker script handler can receive this message
|
---|
614 | through the onMessage() handler.
|
---|
615 |
|
---|
616 | The \c message object may only contain values of the following
|
---|
617 | types:
|
---|
618 |
|
---|
619 | \list
|
---|
620 | \o boolean, number, string
|
---|
621 | \o JavaScript objects and arrays
|
---|
622 | \o ListModel objects (any other type of QObject* is not allowed)
|
---|
623 | \endlist
|
---|
624 |
|
---|
625 | All objects and arrays are copied to the \c message. With the exception
|
---|
626 | of ListModel objects, any modifications by the other thread to an object
|
---|
627 | passed in \c message will not be reflected in the original object.
|
---|
628 | */
|
---|
629 | void QDeclarativeWorkerScript::sendMessage(const QScriptValue &message)
|
---|
630 | {
|
---|
631 | if (!engine()) {
|
---|
632 | qWarning("QDeclarativeWorkerScript: Attempt to send message before WorkerScript establishment");
|
---|
633 | return;
|
---|
634 | }
|
---|
635 |
|
---|
636 | m_engine->sendMessage(m_scriptId, QDeclarativeWorkerScriptEnginePrivate::scriptValueToVariant(message));
|
---|
637 | }
|
---|
638 |
|
---|
639 | void QDeclarativeWorkerScript::classBegin()
|
---|
640 | {
|
---|
641 | m_componentComplete = false;
|
---|
642 | }
|
---|
643 |
|
---|
644 | QDeclarativeWorkerScriptEngine *QDeclarativeWorkerScript::engine()
|
---|
645 | {
|
---|
646 | if (m_engine) return m_engine;
|
---|
647 | if (m_componentComplete) {
|
---|
648 | QDeclarativeEngine *engine = qmlEngine(this);
|
---|
649 | if (!engine) {
|
---|
650 | qWarning("QDeclarativeWorkerScript: engine() called without qmlEngine() set");
|
---|
651 | return 0;
|
---|
652 | }
|
---|
653 |
|
---|
654 | m_engine = QDeclarativeEnginePrivate::get(engine)->getWorkerScriptEngine();
|
---|
655 | m_scriptId = m_engine->registerWorkerScript(this);
|
---|
656 |
|
---|
657 | if (m_source.isValid())
|
---|
658 | m_engine->executeUrl(m_scriptId, m_source);
|
---|
659 |
|
---|
660 | return m_engine;
|
---|
661 | }
|
---|
662 | return 0;
|
---|
663 | }
|
---|
664 |
|
---|
665 | void QDeclarativeWorkerScript::componentComplete()
|
---|
666 | {
|
---|
667 | m_componentComplete = true;
|
---|
668 | engine(); // Get it started now.
|
---|
669 | }
|
---|
670 |
|
---|
671 | /*!
|
---|
672 | \qmlsignal WorkerScript::onMessage(jsobject msg)
|
---|
673 |
|
---|
674 | This handler is called when a message \a msg is received from a worker
|
---|
675 | script in another thread through a call to sendMessage().
|
---|
676 | */
|
---|
677 |
|
---|
678 | bool QDeclarativeWorkerScript::event(QEvent *event)
|
---|
679 | {
|
---|
680 | if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
|
---|
681 | QDeclarativeEngine *engine = qmlEngine(this);
|
---|
682 | if (engine) {
|
---|
683 | QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
|
---|
684 | WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
|
---|
685 | QScriptValue value =
|
---|
686 | QDeclarativeWorkerScriptEnginePrivate::variantToScriptValue(workerEvent->data(), scriptEngine);
|
---|
687 | emit message(value);
|
---|
688 | }
|
---|
689 | return true;
|
---|
690 | } else {
|
---|
691 | return QObject::event(event);
|
---|
692 | }
|
---|
693 | }
|
---|
694 |
|
---|
695 | QT_END_NAMESPACE
|
---|
696 |
|
---|
697 | #include <qdeclarativeworkerscript.moc>
|
---|
698 |
|
---|