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

source code of qtdeclarative/src/qml/qml/qqmlcontext.cpp