1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmlcontextdata_p.h"
5
6#include <QtQml/qqmlengine.h>
7#include <QtQml/private/qqmlcomponentattached_p.h>
8#include <QtQml/private/qqmljavascriptexpression_p.h>
9#include <QtQml/private/qqmlguardedcontextdata_p.h>
10
11QT_BEGIN_NAMESPACE
12
13void QQmlContextData::installContext(QQmlData *ddata, QQmlContextData::QmlObjectKind kind)
14{
15 Q_ASSERT(ddata);
16 if (kind == QQmlContextData::DocumentRoot) {
17 if (ddata->context) {
18 Q_ASSERT(ddata->context != this);
19 Q_ASSERT(ddata->outerContext);
20 Q_ASSERT(ddata->outerContext != this);
21 QQmlRefPointer<QQmlContextData> c = ddata->context;
22 while (QQmlRefPointer<QQmlContextData> linked = c->linkedContext())
23 c = linked;
24 c->setLinkedContext(this);
25 } else {
26 ddata->context = this;
27 }
28 ddata->ownContext.reset(t: ddata->context);
29 } else if (!ddata->context) {
30 ddata->context = this;
31 }
32
33 addOwnedObject(ownedObject: ddata);
34}
35
36QUrl QQmlContextData::resolvedUrl(const QUrl &src) const
37{
38 QUrl resolved;
39 if (src.isRelative() && !src.isEmpty()) {
40 const QUrl ownUrl = url();
41 if (ownUrl.isValid()) {
42 resolved = ownUrl.resolved(relative: src);
43 } else {
44 for (QQmlRefPointer<QQmlContextData> ctxt = parent(); ctxt; ctxt = ctxt->parent()) {
45 const QUrl ctxtUrl = ctxt->url();
46 if (ctxtUrl.isValid()) {
47 resolved = ctxtUrl.resolved(relative: src);
48 break;
49 }
50 }
51
52 if (m_engine && resolved.isEmpty())
53 resolved = m_engine->baseUrl().resolved(relative: src);
54 }
55 } else {
56 resolved = src;
57 }
58
59 if (resolved.isEmpty()) //relative but no ctxt
60 return resolved;
61
62 return m_engine ? m_engine->interceptUrl(url: resolved, type: QQmlAbstractUrlInterceptor::UrlString)
63 : resolved;
64}
65
66void QQmlContextData::emitDestruction()
67{
68 if (!m_hasEmittedDestruction) {
69 m_hasEmittedDestruction = true;
70
71 // Emit the destruction signal - must be emitted before invalidate so that the
72 // context is still valid if bindings or resultant expression evaluation requires it
73 if (m_engine) {
74 while (m_componentAttacheds) {
75 QQmlComponentAttached *attached = m_componentAttacheds;
76 attached->removeFromList();
77 emit attached->destruction();
78 }
79
80 for (QQmlRefPointer<QQmlContextData> child = m_childContexts; !child.isNull(); child = child->m_nextChild)
81 child->emitDestruction();
82 }
83 }
84}
85
86void QQmlContextData::invalidate()
87{
88 emitDestruction();
89
90 while (m_childContexts) {
91 Q_ASSERT(m_childContexts != this);
92 m_childContexts->invalidate();
93 }
94
95 if (m_prevChild) {
96 *m_prevChild = m_nextChild;
97 if (m_nextChild) m_nextChild->m_prevChild = m_prevChild;
98 m_nextChild = nullptr;
99 m_prevChild = nullptr;
100 }
101
102 m_importedScripts.clear();
103
104 m_engine = nullptr;
105 clearParent();
106}
107
108void QQmlContextData::clearContextRecursively()
109{
110 clearContext();
111
112 for (auto ctxIt = m_childContexts; ctxIt; ctxIt = ctxIt->m_nextChild)
113 ctxIt->clearContextRecursively();
114}
115
116void QQmlContextData::clearContext()
117{
118 emitDestruction();
119
120 QQmlJavaScriptExpression *expression = m_expressions;
121 while (expression) {
122 QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression;
123
124 expression->m_prevExpression = nullptr;
125 expression->m_nextExpression = nullptr;
126
127 expression->setContext(nullptr);
128
129 expression = nextExpression;
130 }
131 m_expressions = nullptr;
132}
133
134QQmlContextData::~QQmlContextData()
135{
136 Q_ASSERT(refCount() == 0);
137
138 // avoid recursion
139 addref();
140 if (m_engine)
141 invalidate();
142 m_linkedContext.reset();
143
144 Q_ASSERT(refCount() == 1);
145 clearContext();
146 Q_ASSERT(refCount() == 1);
147
148 while (m_ownedObjects) {
149 QQmlData *co = m_ownedObjects;
150 m_ownedObjects = m_ownedObjects->nextContextObject;
151
152 if (co->context == this)
153 co->context = nullptr;
154 co->outerContext = nullptr;
155 co->nextContextObject = nullptr;
156 co->prevContextObject = nullptr;
157 }
158 Q_ASSERT(refCount() == 1);
159
160 QQmlGuardedContextData *contextGuard = m_contextGuards;
161 while (contextGuard) {
162 // TODO: Is this dead code? Why?
163 QQmlGuardedContextData *next = contextGuard->next();
164 contextGuard->setContextData({});
165 contextGuard = next;
166 }
167 m_contextGuards = nullptr;
168 Q_ASSERT(refCount() == 1);
169
170 delete [] m_idValues;
171 m_idValues = nullptr;
172
173 Q_ASSERT(refCount() == 1);
174 if (m_publicContext)
175 delete m_publicContext;
176
177 Q_ASSERT(refCount() == 1);
178}
179
180void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression)
181{
182 QQmlJavaScriptExpression::DeleteWatcher w(expression);
183
184 if (expression->m_nextExpression)
185 refreshExpressionsRecursive(expression: expression->m_nextExpression);
186
187 if (!w.wasDeleted())
188 expression->refresh();
189}
190
191void QQmlContextData::refreshExpressionsRecursive(bool isGlobal)
192{
193 // For efficiency, we try and minimize the number of guards we have to create
194 if (hasExpressionsToRun(isGlobalRefresh: isGlobal) && (m_nextChild || m_childContexts)) {
195 QQmlGuardedContextData guard(this);
196
197 if (m_childContexts)
198 m_childContexts->refreshExpressionsRecursive(isGlobal);
199
200 if (guard.isNull()) return;
201
202 if (m_nextChild)
203 m_nextChild->refreshExpressionsRecursive(isGlobal);
204
205 if (guard.isNull()) return;
206
207 if (hasExpressionsToRun(isGlobalRefresh: isGlobal))
208 refreshExpressionsRecursive(expression: m_expressions);
209
210 } else if (hasExpressionsToRun(isGlobalRefresh: isGlobal)) {
211 refreshExpressionsRecursive(expression: m_expressions);
212 } else if (m_nextChild && m_childContexts) {
213 QQmlGuardedContextData guard(this);
214 m_childContexts->refreshExpressionsRecursive(isGlobal);
215 if (!guard.isNull() && m_nextChild)
216 m_nextChild->refreshExpressionsRecursive(isGlobal);
217 } else if (m_nextChild) {
218 m_nextChild->refreshExpressionsRecursive(isGlobal);
219 } else if (m_childContexts) {
220 m_childContexts->refreshExpressionsRecursive(isGlobal);
221 }
222}
223
224// Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
225// context-tree dependent caches in the expressions, and should occur every time the context tree
226// *structure* (not values) changes.
227void QQmlContextData::refreshExpressions()
228{
229 bool isGlobal = (m_parent == nullptr);
230
231 // For efficiency, we try and minimize the number of guards we have to create
232 if (hasExpressionsToRun(isGlobalRefresh: isGlobal) && m_childContexts) {
233 QQmlGuardedContextData guard(this);
234 m_childContexts->refreshExpressionsRecursive(isGlobal);
235 if (!guard.isNull() && hasExpressionsToRun(isGlobalRefresh: isGlobal))
236 refreshExpressionsRecursive(expression: m_expressions);
237 } else if (hasExpressionsToRun(isGlobalRefresh: isGlobal)) {
238 refreshExpressionsRecursive(expression: m_expressions);
239 } else if (m_childContexts) {
240 m_childContexts->refreshExpressionsRecursive(isGlobal);
241 }
242}
243
244void QQmlContextData::addOwnedObject(QQmlData *data)
245{
246 if (data->outerContext) {
247 if (data->nextContextObject)
248 data->nextContextObject->prevContextObject = data->prevContextObject;
249 if (data->prevContextObject)
250 *data->prevContextObject = data->nextContextObject;
251 else if (data->outerContext->m_ownedObjects == data)
252 data->outerContext->m_ownedObjects = data->nextContextObject;
253 }
254
255 data->outerContext = this;
256
257 data->nextContextObject = m_ownedObjects;
258 if (data->nextContextObject)
259 data->nextContextObject->prevContextObject = &data->nextContextObject;
260 data->prevContextObject = &m_ownedObjects;
261 m_ownedObjects = data;
262}
263
264void QQmlContextData::setIdValue(int idx, QObject *obj)
265{
266 m_idValues[idx] = obj;
267 m_idValues[idx].setContext(this);
268}
269
270QString QQmlContextData::findObjectId(const QObject *obj) const
271{
272 for (int ii = 0; ii < m_idValueCount; ii++) {
273 if (m_idValues[ii] == obj)
274 return propertyName(index: ii);
275 }
276
277 const QVariant objVariant = QVariant::fromValue(value: obj);
278 if (m_publicContext) {
279 QQmlContextPrivate *p = QQmlContextPrivate::get(context: m_publicContext);
280 for (int ii = 0; ii < p->numPropertyValues(); ++ii)
281 if (p->propertyValue(index: ii) == objVariant)
282 return propertyName(index: ii);
283 }
284
285 if (m_contextObject) {
286 // This is expensive, but nameForObject should really mirror contextProperty()
287 for (const QMetaObject *metaObject = m_contextObject->metaObject();
288 metaObject; metaObject = metaObject->superClass()) {
289 for (int i = metaObject->propertyOffset(), end = metaObject->propertyCount();
290 i != end; ++i) {
291 const QMetaProperty prop = metaObject->property(index: i);
292 if (prop.metaType().flags() & QMetaType::PointerToQObject
293 && prop.read(obj: m_contextObject) == objVariant) {
294 return QString::fromUtf8(utf8: prop.name());
295 }
296 }
297 }
298 }
299
300 return QString();
301}
302
303void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex)
304{
305 m_typeCompilationUnit = unit;
306 m_componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex;
307 Q_ASSERT(!m_idValues);
308 m_idValueCount = m_typeCompilationUnit->objectAt(index: m_componentObjectIndex)
309 ->nNamedObjectsInComponent;
310 m_idValues = new ContextGuard[m_idValueCount];
311}
312
313void QQmlContextData::addComponentAttached(QQmlComponentAttached *attached)
314{
315 attached->insertIntoList(listHead: &m_componentAttacheds);
316}
317
318void QQmlContextData::addExpression(QQmlJavaScriptExpression *expression)
319{
320 expression->insertIntoList(listHead: &m_expressions);
321}
322
323void QQmlContextData::initPropertyNames() const
324{
325 if (m_typeCompilationUnit)
326 m_propertyNameCache = m_typeCompilationUnit->namedObjectsPerComponent(componentObjectIndex: m_componentObjectIndex);
327 else
328 m_propertyNameCache = QV4::IdentifierHash(m_engine->handle());
329 Q_ASSERT(!m_propertyNameCache.isEmpty());
330}
331
332QUrl QQmlContextData::url() const
333{
334 if (m_typeCompilationUnit)
335 return m_typeCompilationUnit->finalUrl();
336 return m_baseUrl;
337}
338
339QString QQmlContextData::urlString() const
340{
341 if (m_typeCompilationUnit)
342 return m_typeCompilationUnit->finalUrlString();
343 return m_baseUrlString;
344}
345
346QT_END_NAMESPACE
347

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