1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qqmlcontext.h" |
41 | #include "qqmlcontext_p.h" |
42 | #include "qqmlcomponentattached_p.h" |
43 | |
44 | #include "qqmlcomponent_p.h" |
45 | #include "qqmlexpression_p.h" |
46 | #include "qqmlengine_p.h" |
47 | #include "qqmlengine.h" |
48 | #include "qqmlinfo.h" |
49 | #include "qqmlabstracturlinterceptor.h" |
50 | |
51 | #include <qjsengine.h> |
52 | #include <QtCore/qvarlengtharray.h> |
53 | #include <private/qmetaobject_p.h> |
54 | #include <QtCore/qdebug.h> |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | QQmlContextPrivate::QQmlContextPrivate() |
59 | : data(nullptr), notifyIndex(-1) |
60 | { |
61 | } |
62 | |
63 | /*! |
64 | \class QQmlContext |
65 | \brief The QQmlContext class defines a context within a QML engine. |
66 | \inmodule QtQml |
67 | |
68 | Contexts allow data to be exposed to the QML components instantiated by the |
69 | QML engine. |
70 | |
71 | Each QQmlContext contains a set of properties, distinct from its QObject |
72 | properties, that allow data to be explicitly bound to a context by name. The |
73 | context properties are defined and updated by calling |
74 | QQmlContext::setContextProperty(). The following example shows a Qt model |
75 | being bound to a context and then accessed from a QML file. |
76 | |
77 | \code |
78 | QQmlEngine engine; |
79 | QStringListModel modelData; |
80 | QQmlContext *context = new QQmlContext(engine.rootContext()); |
81 | context->setContextProperty("myModel", &modelData); |
82 | |
83 | QQmlComponent component(&engine); |
84 | component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl()); |
85 | QObject *window = component.create(context); |
86 | \endcode |
87 | |
88 | Note it is the responsibility of the creator to delete any QQmlContext it |
89 | constructs. If the \c context object in the example is no longer needed when the |
90 | \c window component instance is destroyed, the \c context must be destroyed explicitly. |
91 | The simplest way to ensure this is to set \c window as the parent of \c context. |
92 | |
93 | To simplify binding and maintaining larger data sets, a context object can be set |
94 | on a QQmlContext. All the properties of the context object are available |
95 | by name in the context, as though they were all individually added through calls |
96 | to QQmlContext::setContextProperty(). Changes to the property's values are |
97 | detected through the property's notify signal. Setting a context object is both |
98 | faster and easier than manually adding and maintaining context property values. |
99 | |
100 | The following example has the same effect as the previous one, but it uses a context |
101 | object. |
102 | |
103 | \code |
104 | class MyDataSet : ... { |
105 | ... |
106 | Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged) |
107 | ... |
108 | }; |
109 | |
110 | MyDataSet myDataSet; |
111 | QQmlEngine engine; |
112 | QQmlContext *context = new QQmlContext(engine.rootContext()); |
113 | context->setContextObject(&myDataSet); |
114 | |
115 | QQmlComponent component(&engine); |
116 | component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl()); |
117 | component.create(context); |
118 | \endcode |
119 | |
120 | All properties added explicitly by QQmlContext::setContextProperty() take |
121 | precedence over the context object's properties. |
122 | |
123 | \section2 The Context Hierarchy |
124 | |
125 | Contexts form a hierarchy. The root of this hierarchy is the QML engine's |
126 | \l {QQmlEngine::rootContext()}{root context}. Child contexts inherit |
127 | the context properties of their parents; if a child context sets a context property |
128 | that already exists in its parent, the new context property overrides that of the |
129 | parent. |
130 | |
131 | The following example defines two contexts - \c context1 and \c context2. The |
132 | second context overrides the "b" context property inherited from the first with a |
133 | new value. |
134 | |
135 | \code |
136 | QQmlEngine engine; |
137 | QQmlContext *context1 = new QQmlContext(engine.rootContext()); |
138 | QQmlContext *context2 = new QQmlContext(context1); |
139 | |
140 | context1->setContextProperty("a", 12); |
141 | context1->setContextProperty("b", 12); |
142 | |
143 | context2->setContextProperty("b", 15); |
144 | \endcode |
145 | |
146 | While QML objects instantiated in a context are not strictly owned by that |
147 | context, their bindings are. If a context is destroyed, the property bindings of |
148 | outstanding QML objects will stop evaluating. |
149 | |
150 | \warning Setting the context object or adding new context properties after an object |
151 | has been created in that context is an expensive operation (essentially forcing all bindings |
152 | to reevaluate). Thus whenever possible you should complete "setup" of the context |
153 | before using it to create any objects. |
154 | |
155 | \sa {qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Types to QML} |
156 | */ |
157 | |
158 | /*! \internal */ |
159 | QQmlContext::QQmlContext(QQmlEngine *e, bool) |
160 | : QObject(*(new QQmlContextPrivate)) |
161 | { |
162 | Q_D(QQmlContext); |
163 | d->data = new QQmlContextData(this); |
164 | ++d->data->refCount; |
165 | |
166 | d->data->engine = e; |
167 | } |
168 | |
169 | /*! |
170 | Create a new QQmlContext as a child of \a engine's root context, and the |
171 | QObject \a parent. |
172 | */ |
173 | QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent) |
174 | : QObject(*(new QQmlContextPrivate), parent) |
175 | { |
176 | Q_D(QQmlContext); |
177 | d->data = new QQmlContextData(this); |
178 | ++d->data->refCount; |
179 | |
180 | d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):nullptr); |
181 | } |
182 | |
183 | /*! |
184 | Create a new QQmlContext with the given \a parentContext, and the |
185 | QObject \a parent. |
186 | */ |
187 | QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent) |
188 | : QObject(*(new QQmlContextPrivate), parent) |
189 | { |
190 | Q_D(QQmlContext); |
191 | d->data = new QQmlContextData(this); |
192 | ++d->data->refCount; |
193 | |
194 | d->data->setParent(parentContext?QQmlContextData::get(parentContext):nullptr); |
195 | } |
196 | |
197 | /*! |
198 | \internal |
199 | */ |
200 | QQmlContext::QQmlContext(QQmlContextData *data) |
201 | : QObject(*(new QQmlContextPrivate), nullptr) |
202 | { |
203 | Q_D(QQmlContext); |
204 | d->data = data; |
205 | // don't add a refcount here, as the data owns this context |
206 | } |
207 | |
208 | /*! |
209 | Destroys the QQmlContext. |
210 | |
211 | Any expressions, or sub-contexts dependent on this context will be |
212 | invalidated, but not destroyed (unless they are parented to the QQmlContext |
213 | object). |
214 | */ |
215 | QQmlContext::~QQmlContext() |
216 | { |
217 | Q_D(QQmlContext); |
218 | |
219 | d->data->publicContext = nullptr; |
220 | if (!--d->data->refCount) |
221 | d->data->destroy(); |
222 | } |
223 | |
224 | /*! |
225 | Returns whether the context is valid. |
226 | |
227 | To be valid, a context must have a engine, and it's contextObject(), if any, |
228 | must not have been deleted. |
229 | */ |
230 | bool QQmlContext::isValid() const |
231 | { |
232 | Q_D(const QQmlContext); |
233 | return d->data && d->data->isValid(); |
234 | } |
235 | |
236 | /*! |
237 | Return the context's QQmlEngine, or 0 if the context has no QQmlEngine or the |
238 | QQmlEngine was destroyed. |
239 | */ |
240 | QQmlEngine *QQmlContext::engine() const |
241 | { |
242 | Q_D(const QQmlContext); |
243 | return d->data->engine; |
244 | } |
245 | |
246 | /*! |
247 | Return the context's parent QQmlContext, or 0 if this context has no |
248 | parent or if the parent has been destroyed. |
249 | */ |
250 | QQmlContext *QQmlContext::parentContext() const |
251 | { |
252 | Q_D(const QQmlContext); |
253 | return d->data->parent?d->data->parent->asQQmlContext():nullptr; |
254 | } |
255 | |
256 | /*! |
257 | Return the context object, or 0 if there is no context object. |
258 | */ |
259 | QObject *QQmlContext::contextObject() const |
260 | { |
261 | Q_D(const QQmlContext); |
262 | return d->data->contextObject; |
263 | } |
264 | |
265 | /*! |
266 | Set the context \a object. |
267 | */ |
268 | void QQmlContext::setContextObject(QObject *object) |
269 | { |
270 | Q_D(QQmlContext); |
271 | |
272 | QQmlContextData *data = d->data; |
273 | |
274 | if (data->isInternal) { |
275 | qWarning("QQmlContext: Cannot set context object for internal context." ); |
276 | return; |
277 | } |
278 | |
279 | if (!isValid()) { |
280 | qWarning("QQmlContext: Cannot set context object on invalid context." ); |
281 | return; |
282 | } |
283 | |
284 | data->contextObject = object; |
285 | data->refreshExpressions(); |
286 | } |
287 | |
288 | /*! |
289 | Set a the \a value of the \a name property on this context. |
290 | */ |
291 | void QQmlContext::setContextProperty(const QString &name, const QVariant &value) |
292 | { |
293 | Q_D(QQmlContext); |
294 | if (d->notifyIndex == -1) |
295 | d->notifyIndex = QMetaObjectPrivate::absoluteSignalCount(&QQmlContext::staticMetaObject); |
296 | |
297 | QQmlContextData *data = d->data; |
298 | |
299 | if (data->isInternal) { |
300 | qWarning("QQmlContext: Cannot set property on internal context." ); |
301 | return; |
302 | } |
303 | |
304 | if (!isValid()) { |
305 | qWarning("QQmlContext: Cannot set property on invalid context." ); |
306 | return; |
307 | } |
308 | |
309 | QV4::IdentifierHash &properties = data->detachedPropertyNames(); |
310 | int idx = properties.value(name); |
311 | if (idx == -1) { |
312 | properties.add(name, data->idValueCount + d->propertyValues.count()); |
313 | d->propertyValues.append(value); |
314 | |
315 | data->refreshExpressions(); |
316 | } else { |
317 | d->propertyValues[idx] = value; |
318 | QMetaObject::activate(this, d->notifyIndex, idx, nullptr); |
319 | } |
320 | |
321 | if (auto *obj = qvariant_cast<QObject *>(value)) { |
322 | connect(obj, &QObject::destroyed, this, [d, name](QObject *destroyed) { |
323 | d->dropDestroyedQObject(name, destroyed); |
324 | }); |
325 | } |
326 | } |
327 | |
328 | /*! |
329 | Set the \a value of the \a name property on this context. |
330 | |
331 | QQmlContext does \b not take ownership of \a value. |
332 | */ |
333 | void QQmlContext::setContextProperty(const QString &name, QObject *value) |
334 | { |
335 | setContextProperty(name, QVariant::fromValue(value)); |
336 | } |
337 | |
338 | /*! |
339 | \since 5.11 |
340 | |
341 | Set a batch of \a properties on this context. |
342 | |
343 | Setting all properties in one batch avoids unnecessary |
344 | refreshing expressions, and is therefore recommended |
345 | instead of calling \l setContextProperty() for each individual property. |
346 | |
347 | \sa QQmlContext::setContextProperty() |
348 | */ |
349 | void QQmlContext::setContextProperties(const QVector<PropertyPair> &properties) |
350 | { |
351 | Q_D(const QQmlContext); |
352 | |
353 | QQmlContextData *data = d->data; |
354 | |
355 | QQmlJavaScriptExpression *expressions = data->expressions; |
356 | QQmlContextData *childContexts = data->childContexts; |
357 | |
358 | data->expressions = nullptr; |
359 | data->childContexts = nullptr; |
360 | |
361 | for (auto property : properties) |
362 | setContextProperty(property.name, property.value); |
363 | |
364 | data->expressions = expressions; |
365 | data->childContexts = childContexts; |
366 | |
367 | data->refreshExpressions(); |
368 | } |
369 | |
370 | /*! |
371 | \since 5.11 |
372 | |
373 | \class QQmlContext::PropertyPair |
374 | \inmodule QtQml |
375 | |
376 | This struct contains a property name and a property value. |
377 | It is used as a parameter for the \c setContextProperties function. |
378 | |
379 | \sa QQmlContext::setContextProperties() |
380 | */ |
381 | |
382 | /*! |
383 | Returns the value of the \a name property for this context |
384 | as a QVariant. |
385 | */ |
386 | QVariant QQmlContext::contextProperty(const QString &name) const |
387 | { |
388 | Q_D(const QQmlContext); |
389 | QVariant value; |
390 | int idx = -1; |
391 | |
392 | QQmlContextData *data = d->data; |
393 | |
394 | const QV4::IdentifierHash &properties = data->propertyNames(); |
395 | if (properties.count()) |
396 | idx = properties.value(name); |
397 | |
398 | if (idx == -1) { |
399 | if (data->contextObject) { |
400 | QObject *obj = data->contextObject; |
401 | QQmlPropertyData local; |
402 | QQmlPropertyData *property = |
403 | QQmlPropertyCache::property(data->engine, obj, name, data, local); |
404 | |
405 | if (property) value = obj->metaObject()->property(property->coreIndex()).read(obj); |
406 | } |
407 | if (!value.isValid() && parentContext()) |
408 | value = parentContext()->contextProperty(name); |
409 | } else { |
410 | if (idx >= d->propertyValues.count()) |
411 | value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data()); |
412 | else |
413 | value = d->propertyValues[idx]; |
414 | } |
415 | |
416 | return value; |
417 | } |
418 | |
419 | /*! |
420 | Returns the name of \a object in this context, or an empty string if \a object |
421 | is not named in the context. Objects are named by setContextProperty(), or by ids in |
422 | the case of QML created contexts. |
423 | |
424 | If the object has multiple names, the first is returned. |
425 | */ |
426 | QString QQmlContext::nameForObject(QObject *object) const |
427 | { |
428 | Q_D(const QQmlContext); |
429 | |
430 | return d->data->findObjectId(object); |
431 | } |
432 | |
433 | /*! |
434 | Resolves the URL \a src relative to the URL of the |
435 | containing component. |
436 | |
437 | \sa QQmlEngine::baseUrl(), setBaseUrl() |
438 | */ |
439 | QUrl QQmlContext::resolvedUrl(const QUrl &src) |
440 | { |
441 | Q_D(QQmlContext); |
442 | return d->data->resolvedUrl(src); |
443 | } |
444 | |
445 | QUrl QQmlContextData::resolvedUrl(const QUrl &src) |
446 | { |
447 | QUrl resolved; |
448 | if (src.isRelative() && !src.isEmpty()) { |
449 | QQmlContextData *ctxt = this; |
450 | do { |
451 | if (ctxt->url().isValid()) |
452 | break; |
453 | else |
454 | ctxt = ctxt->parent; |
455 | } while (ctxt); |
456 | |
457 | if (ctxt) |
458 | resolved = ctxt->url().resolved(src); |
459 | else if (engine) |
460 | resolved = engine->baseUrl().resolved(src); |
461 | } else { |
462 | resolved = src; |
463 | } |
464 | |
465 | if (resolved.isEmpty()) //relative but no ctxt |
466 | return resolved; |
467 | |
468 | if (engine && engine->urlInterceptor()) |
469 | resolved = engine->urlInterceptor()->intercept(resolved, QQmlAbstractUrlInterceptor::UrlString); |
470 | return resolved; |
471 | } |
472 | |
473 | |
474 | /*! |
475 | Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl. |
476 | |
477 | Calling this function will override the url of the containing |
478 | component used by default. |
479 | |
480 | \sa resolvedUrl() |
481 | */ |
482 | void QQmlContext::setBaseUrl(const QUrl &baseUrl) |
483 | { |
484 | Q_D(QQmlContext); |
485 | |
486 | d->data->baseUrl = baseUrl; |
487 | d->data->baseUrlString = baseUrl.toString(); |
488 | } |
489 | |
490 | /*! |
491 | Returns the base url of the component, or the containing component |
492 | if none is set. |
493 | */ |
494 | QUrl QQmlContext::baseUrl() const |
495 | { |
496 | Q_D(const QQmlContext); |
497 | const QQmlContextData* data = d->data; |
498 | while (data && data->url().isEmpty()) |
499 | data = data->parent; |
500 | |
501 | if (data) |
502 | return data->url(); |
503 | else |
504 | return QUrl(); |
505 | } |
506 | |
507 | int QQmlContextPrivate::context_count(QQmlListProperty<QObject> *prop) |
508 | { |
509 | QQmlContext *context = static_cast<QQmlContext*>(prop->object); |
510 | QQmlContextPrivate *d = QQmlContextPrivate::get(context); |
511 | int contextProperty = (int)(quintptr)prop->data; |
512 | |
513 | if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) { |
514 | return 0; |
515 | } else { |
516 | return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count(); |
517 | } |
518 | } |
519 | |
520 | QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int index) |
521 | { |
522 | QQmlContext *context = static_cast<QQmlContext*>(prop->object); |
523 | QQmlContextPrivate *d = QQmlContextPrivate::get(context); |
524 | int contextProperty = (int)(quintptr)prop->data; |
525 | |
526 | if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) { |
527 | return nullptr; |
528 | } else { |
529 | return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index); |
530 | } |
531 | } |
532 | |
533 | void QQmlContextPrivate::dropDestroyedQObject(const QString &name, QObject *destroyed) |
534 | { |
535 | const int idx = data->propertyNames().value(name); |
536 | Q_ASSERT(idx >= 0); |
537 | if (qvariant_cast<QObject *>(propertyValues[idx]) != destroyed) |
538 | return; |
539 | |
540 | propertyValues[idx] = QVariant::fromValue<QObject *>(nullptr); |
541 | QMetaObject::activate(q_func(), notifyIndex, idx, nullptr); |
542 | } |
543 | |
544 | |
545 | QQmlContextData::QQmlContextData() |
546 | : QQmlContextData(nullptr) |
547 | { |
548 | } |
549 | |
550 | QQmlContextData::QQmlContextData(QQmlContext *ctxt) |
551 | : engine(nullptr), isInternal(false), isJSContext(false), |
552 | isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), |
553 | stronglyReferencedByParent(false), publicContext(ctxt), incubator(nullptr), componentObjectIndex(-1), |
554 | contextObject(nullptr), nextChild(nullptr), prevChild(nullptr), |
555 | expressions(nullptr), contextObjects(nullptr), idValues(nullptr), idValueCount(0), |
556 | componentAttached(nullptr) |
557 | { |
558 | } |
559 | |
560 | void QQmlContextData::emitDestruction() |
561 | { |
562 | if (!hasEmittedDestruction) { |
563 | hasEmittedDestruction = true; |
564 | |
565 | // Emit the destruction signal - must be emitted before invalidate so that the |
566 | // context is still valid if bindings or resultant expression evaluation requires it |
567 | if (engine) { |
568 | while (componentAttached) { |
569 | QQmlComponentAttached *a = componentAttached; |
570 | componentAttached = a->next; |
571 | if (componentAttached) componentAttached->prev = &componentAttached; |
572 | |
573 | a->next = nullptr; |
574 | a->prev = nullptr; |
575 | |
576 | emit a->destruction(); |
577 | } |
578 | |
579 | QQmlContextData * child = childContexts; |
580 | while (child) { |
581 | child->emitDestruction(); |
582 | child = child->nextChild; |
583 | } |
584 | } |
585 | } |
586 | } |
587 | |
588 | void QQmlContextData::invalidate() |
589 | { |
590 | emitDestruction(); |
591 | |
592 | while (childContexts) { |
593 | Q_ASSERT(childContexts != this); |
594 | if (childContexts->stronglyReferencedByParent && !--childContexts->refCount) |
595 | childContexts->destroy(); |
596 | else |
597 | childContexts->invalidate(); |
598 | } |
599 | |
600 | if (prevChild) { |
601 | *prevChild = nextChild; |
602 | if (nextChild) nextChild->prevChild = prevChild; |
603 | nextChild = nullptr; |
604 | prevChild = nullptr; |
605 | } |
606 | |
607 | importedScripts.clear(); |
608 | |
609 | engine = nullptr; |
610 | parent = nullptr; |
611 | } |
612 | |
613 | void QQmlContextData::clearContextRecursively() |
614 | { |
615 | clearContext(); |
616 | |
617 | for (auto ctxIt = childContexts; ctxIt; ctxIt = ctxIt->nextChild) |
618 | ctxIt->clearContextRecursively(); |
619 | } |
620 | |
621 | void QQmlContextData::clearContext() |
622 | { |
623 | emitDestruction(); |
624 | |
625 | QQmlJavaScriptExpression *expression = expressions; |
626 | while (expression) { |
627 | QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression; |
628 | |
629 | expression->m_prevExpression = nullptr; |
630 | expression->m_nextExpression = nullptr; |
631 | |
632 | expression->setContext(nullptr); |
633 | |
634 | expression = nextExpression; |
635 | } |
636 | expressions = nullptr; |
637 | } |
638 | |
639 | void QQmlContextData::destroy() |
640 | { |
641 | Q_ASSERT(refCount == 0); |
642 | linkedContext = nullptr; |
643 | |
644 | // avoid recursion |
645 | ++refCount; |
646 | if (engine) |
647 | invalidate(); |
648 | |
649 | Q_ASSERT(refCount == 1); |
650 | clearContext(); |
651 | Q_ASSERT(refCount == 1); |
652 | |
653 | while (contextObjects) { |
654 | QQmlData *co = contextObjects; |
655 | contextObjects = contextObjects->nextContextObject; |
656 | |
657 | if (co->context == this) |
658 | co->context = nullptr; |
659 | co->outerContext = nullptr; |
660 | co->nextContextObject = nullptr; |
661 | co->prevContextObject = nullptr; |
662 | } |
663 | Q_ASSERT(refCount == 1); |
664 | |
665 | QQmlGuardedContextData *contextGuard = contextGuards; |
666 | while (contextGuard) { |
667 | QQmlGuardedContextData *next = contextGuard->m_next; |
668 | contextGuard->m_next = nullptr; |
669 | contextGuard->m_prev = nullptr; |
670 | contextGuard->m_contextData = nullptr; |
671 | contextGuard = next; |
672 | } |
673 | contextGuards = nullptr; |
674 | Q_ASSERT(refCount == 1); |
675 | |
676 | delete [] idValues; |
677 | idValues = nullptr; |
678 | |
679 | Q_ASSERT(refCount == 1); |
680 | if (publicContext) { |
681 | // the QQmlContext destructor will remove one ref again |
682 | ++refCount; |
683 | delete publicContext; |
684 | } |
685 | |
686 | Q_ASSERT(refCount == 1); |
687 | --refCount; |
688 | Q_ASSERT(refCount == 0); |
689 | |
690 | delete this; |
691 | } |
692 | |
693 | void QQmlContextData::setParent(QQmlContextData *p, bool stronglyReferencedByParent) |
694 | { |
695 | if (p == parent) |
696 | return; |
697 | if (p) { |
698 | Q_ASSERT(!parent); |
699 | parent = p; |
700 | this->stronglyReferencedByParent = stronglyReferencedByParent; |
701 | if (stronglyReferencedByParent) |
702 | ++refCount; // balanced in QQmlContextData::invalidate() |
703 | engine = p->engine; |
704 | nextChild = p->childContexts; |
705 | if (nextChild) nextChild->prevChild = &nextChild; |
706 | prevChild = &p->childContexts; |
707 | p->childContexts = this; |
708 | } |
709 | } |
710 | |
711 | void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression) |
712 | { |
713 | QQmlJavaScriptExpression::DeleteWatcher w(expression); |
714 | |
715 | if (expression->m_nextExpression) |
716 | refreshExpressionsRecursive(expression->m_nextExpression); |
717 | |
718 | if (!w.wasDeleted()) |
719 | expression->refresh(); |
720 | } |
721 | |
722 | QQmlContextData::~QQmlContextData() |
723 | { |
724 | } |
725 | |
726 | static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh) |
727 | { |
728 | return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames); |
729 | } |
730 | |
731 | void QQmlContextData::refreshExpressionsRecursive(bool isGlobal) |
732 | { |
733 | // For efficiency, we try and minimize the number of guards we have to create |
734 | if (expressions_to_run(this, isGlobal) && (nextChild || childContexts)) { |
735 | QQmlGuardedContextData guard(this); |
736 | |
737 | if (childContexts) |
738 | childContexts->refreshExpressionsRecursive(isGlobal); |
739 | |
740 | if (guard.isNull()) return; |
741 | |
742 | if (nextChild) |
743 | nextChild->refreshExpressionsRecursive(isGlobal); |
744 | |
745 | if (guard.isNull()) return; |
746 | |
747 | if (expressions_to_run(this, isGlobal)) |
748 | refreshExpressionsRecursive(expressions); |
749 | |
750 | } else if (expressions_to_run(this, isGlobal)) { |
751 | |
752 | refreshExpressionsRecursive(expressions); |
753 | |
754 | } else if (nextChild && childContexts) { |
755 | |
756 | QQmlGuardedContextData guard(this); |
757 | |
758 | childContexts->refreshExpressionsRecursive(isGlobal); |
759 | |
760 | if (!guard.isNull() && nextChild) |
761 | nextChild->refreshExpressionsRecursive(isGlobal); |
762 | |
763 | } else if (nextChild) { |
764 | |
765 | nextChild->refreshExpressionsRecursive(isGlobal); |
766 | |
767 | } else if (childContexts) { |
768 | |
769 | childContexts->refreshExpressionsRecursive(isGlobal); |
770 | |
771 | } |
772 | } |
773 | |
774 | // Refreshes all expressions that could possibly depend on this context. Refreshing flushes all |
775 | // context-tree dependent caches in the expressions, and should occur every time the context tree |
776 | // *structure* (not values) changes. |
777 | void QQmlContextData::refreshExpressions() |
778 | { |
779 | bool isGlobal = (parent == nullptr); |
780 | |
781 | // For efficiency, we try and minimize the number of guards we have to create |
782 | if (expressions_to_run(this, isGlobal) && childContexts) { |
783 | QQmlGuardedContextData guard(this); |
784 | |
785 | childContexts->refreshExpressionsRecursive(isGlobal); |
786 | |
787 | if (!guard.isNull() && expressions_to_run(this, isGlobal)) |
788 | refreshExpressionsRecursive(expressions); |
789 | |
790 | } else if (expressions_to_run(this, isGlobal)) { |
791 | |
792 | refreshExpressionsRecursive(expressions); |
793 | |
794 | } else if (childContexts) { |
795 | |
796 | childContexts->refreshExpressionsRecursive(isGlobal); |
797 | |
798 | } |
799 | } |
800 | |
801 | void QQmlContextData::addObject(QQmlData *data) |
802 | { |
803 | if (data->outerContext) { |
804 | if (data->nextContextObject) |
805 | data->nextContextObject->prevContextObject = data->prevContextObject; |
806 | if (data->prevContextObject) |
807 | *data->prevContextObject = data->nextContextObject; |
808 | else if (data->outerContext->contextObjects == data) |
809 | data->outerContext->contextObjects = data->nextContextObject; |
810 | } |
811 | |
812 | data->outerContext = this; |
813 | |
814 | data->nextContextObject = contextObjects; |
815 | if (data->nextContextObject) |
816 | data->nextContextObject->prevContextObject = &data->nextContextObject; |
817 | data->prevContextObject = &contextObjects; |
818 | contextObjects = data; |
819 | } |
820 | |
821 | void QQmlContextData::setIdProperty(int idx, QObject *obj) |
822 | { |
823 | idValues[idx] = obj; |
824 | idValues[idx].context = this; |
825 | } |
826 | |
827 | QString QQmlContextData::findObjectId(const QObject *obj) const |
828 | { |
829 | const QV4::IdentifierHash &properties = propertyNames(); |
830 | if (propertyNameCache.isEmpty()) |
831 | return QString(); |
832 | |
833 | for (int ii = 0; ii < idValueCount; ii++) { |
834 | if (idValues[ii] == obj) |
835 | return properties.findId(ii); |
836 | } |
837 | |
838 | if (publicContext) { |
839 | QQmlContextPrivate *p = QQmlContextPrivate::get(publicContext); |
840 | for (int ii = 0; ii < p->propertyValues.count(); ++ii) |
841 | if (p->propertyValues.at(ii) == QVariant::fromValue(const_cast<QObject *>(obj))) |
842 | return properties.findId(ii); |
843 | } |
844 | |
845 | if (linkedContext) |
846 | return linkedContext->findObjectId(obj); |
847 | return QString(); |
848 | } |
849 | |
850 | QQmlContext *QQmlContextData::asQQmlContext() |
851 | { |
852 | if (!publicContext) |
853 | publicContext = new QQmlContext(this); |
854 | return publicContext; |
855 | } |
856 | |
857 | QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() |
858 | { |
859 | return QQmlContextPrivate::get(asQQmlContext()); |
860 | } |
861 | |
862 | void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex) |
863 | { |
864 | typeCompilationUnit = unit; |
865 | componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex; |
866 | Q_ASSERT(!idValues); |
867 | idValueCount = typeCompilationUnit->objectAt(componentObjectIndex)->nNamedObjectsInComponent; |
868 | idValues = new ContextGuard[idValueCount]; |
869 | } |
870 | |
871 | const QV4::IdentifierHash &QQmlContextData::propertyNames() const |
872 | { |
873 | if (propertyNameCache.isEmpty()) { |
874 | if (typeCompilationUnit) |
875 | propertyNameCache = typeCompilationUnit->namedObjectsPerComponent(componentObjectIndex); |
876 | else |
877 | propertyNameCache = QV4::IdentifierHash(engine->handle()); |
878 | } |
879 | return propertyNameCache; |
880 | } |
881 | |
882 | QV4::IdentifierHash &QQmlContextData::detachedPropertyNames() |
883 | { |
884 | propertyNames(); |
885 | propertyNameCache.detach(); |
886 | return propertyNameCache; |
887 | } |
888 | |
889 | QUrl QQmlContextData::url() const |
890 | { |
891 | if (typeCompilationUnit) |
892 | return typeCompilationUnit->finalUrl(); |
893 | return baseUrl; |
894 | } |
895 | |
896 | QString QQmlContextData::urlString() const |
897 | { |
898 | if (typeCompilationUnit) |
899 | return typeCompilationUnit->finalUrlString(); |
900 | return baseUrlString; |
901 | } |
902 | |
903 | QT_END_NAMESPACE |
904 | |
905 | #include "moc_qqmlcontext.cpp" |
906 | |