Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlcontextdata.cpp
Go to the documentation of this file.
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
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
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(ddata->context);
29 } else if (!ddata->context) {
30 ddata->context = this;
31 }
32
33 addOwnedObject(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(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(src);
48 break;
49 }
50 }
51
52 if (m_engine && resolved.isEmpty())
53 resolved = m_engine->baseUrl().resolved(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(resolved, 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 clearChildrenAndSiblings();
91 clearImportedScripts();
92
93 m_engine = nullptr;
94 clearParent();
95}
96
97void QQmlContextData::clearContextRecursively()
98{
99 emitDestruction();
100 clearExpressions();
101
102 for (auto ctxIt = m_childContexts; ctxIt; ctxIt = ctxIt->m_nextChild)
103 ctxIt->clearContextRecursively();
104
105 m_engine = nullptr;
106}
107
108void QQmlContextData::clearChildrenAndSiblings()
109{
110 while (m_childContexts) {
111 Q_ASSERT(m_childContexts != this);
112 m_childContexts->invalidate();
113 }
114
115 if (m_prevChild) {
116 *m_prevChild = m_nextChild;
117 if (m_nextChild) m_nextChild->m_prevChild = m_prevChild;
118 m_nextChild = nullptr;
119 m_prevChild = nullptr;
120 }
121}
122
123void QQmlContextData::clearImportedScripts()
124{
125 if (!m_hasWeakImportedScripts) { // might be called multiple times
126 if (m_engine && !m_importedScripts.isNullOrUndefined()) {
127 QV4::Scope scope(m_engine->handle());
128 QV4::ScopedValue val(scope, m_importedScripts.value());
129 m_importedScripts.~PersistentValue();
130 new (&m_weakImportedScripts) QV4::WeakValue();
131 m_weakImportedScripts.set(m_engine->handle(), val);
132 m_hasWeakImportedScripts = true;
133 } else {
134 // clear even if the value is null/undefined, in case it was set to explicit null/undefined
135 m_importedScripts.clear();
136 }
137 }
138}
139
140void QQmlContextData::clearOwnedObjects()
141{
142 while (m_ownedObjects) {
143 QQmlData *co = m_ownedObjects;
144 m_ownedObjects = m_ownedObjects->nextContextObject;
145
146 if (co->context == this)
147 co->context = nullptr;
148 co->outerContext = nullptr;
149 co->nextContextObject = nullptr;
150 co->prevContextObject = nullptr;
151 }
152}
153
154void QQmlContextData::clearContextGuards()
155{
156 for (QQmlGuardedContextData *contextGuard = m_contextGuards; contextGuard;) {
157 // TODO: Is this dead code? Why?
158 QQmlGuardedContextData *next = contextGuard->next();
159 contextGuard->setContextData({});
160 contextGuard = next;
161 }
162 m_contextGuards = nullptr;
163}
164
165void QQmlContextData::clearIdValues()
166{
167 delete[] std::exchange(m_idValues, nullptr);
168 m_idValueCount = 0;
169}
170
171void QQmlContextData::clearExpressions()
172{
173 QQmlJavaScriptExpression *expression = m_expressions;
174 while (expression) {
175 QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression;
176
177 expression->m_prevExpression = nullptr;
178 expression->m_nextExpression = nullptr;
179
180 expression->setContext(nullptr);
181
182 expression = nextExpression;
183 }
184 m_expressions = nullptr;
185}
186
187QQmlContextData::~QQmlContextData()
188{
189 Q_ASSERT(refCount() == 0);
190
191 // avoid recursion
192 addref();
193 if (!m_hasWeakImportedScripts) {
194 // avoid busy work in invalidate – we don't want to construct a weak value
195 // just to throw it away afterwards
196 m_importedScripts.clear();
197 }
198 invalidate();
199 if (m_hasWeakImportedScripts)
200 m_weakImportedScripts.~WeakValue();
201 else
202 m_importedScripts.~PersistentValue();
203 m_linkedContext.reset();
204
205 Q_ASSERT(refCount() == 1);
206 emitDestruction();
207 clearExpressions();
208 Q_ASSERT(refCount() == 1);
209
210 clearOwnedObjects();
211 Q_ASSERT(refCount() == 1);
212
213 clearContextGuards();
214 Q_ASSERT(refCount() == 1);
215
216 clearIdValues();
217
218 Q_ASSERT(refCount() == 1);
219 if (m_publicContext)
220 delete m_publicContext;
221
222 Q_ASSERT(refCount() == 1);
223}
224
225void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression)
226{
227 QQmlJavaScriptExpression::DeleteWatcher w(expression);
228
229 if (expression->m_nextExpression)
230 refreshExpressionsRecursive(expression->m_nextExpression);
231
232 if (!w.wasDeleted())
233 expression->refresh();
234}
235
236void QQmlContextData::refreshExpressionsRecursive(bool isGlobal)
237{
238 // For efficiency, we try and minimize the number of guards we have to create
239 if (hasExpressionsToRun(isGlobal) && (m_nextChild || m_childContexts)) {
240 QQmlGuardedContextData guard(this);
241
242 if (m_childContexts)
243 m_childContexts->refreshExpressionsRecursive(isGlobal);
244
245 if (guard.isNull()) return;
246
247 if (m_nextChild)
248 m_nextChild->refreshExpressionsRecursive(isGlobal);
249
250 if (guard.isNull()) return;
251
252 if (hasExpressionsToRun(isGlobal))
253 refreshExpressionsRecursive(m_expressions);
254
255 } else if (hasExpressionsToRun(isGlobal)) {
256 refreshExpressionsRecursive(m_expressions);
257 } else if (m_nextChild && m_childContexts) {
258 QQmlGuardedContextData guard(this);
259 m_childContexts->refreshExpressionsRecursive(isGlobal);
260 if (!guard.isNull() && m_nextChild)
261 m_nextChild->refreshExpressionsRecursive(isGlobal);
262 } else if (m_nextChild) {
263 m_nextChild->refreshExpressionsRecursive(isGlobal);
264 } else if (m_childContexts) {
265 m_childContexts->refreshExpressionsRecursive(isGlobal);
266 }
267}
268
269// Refreshes all expressions that could possibly depend on this context. Refreshing flushes all
270// context-tree dependent caches in the expressions, and should occur every time the context tree
271// *structure* (not values) changes.
272void QQmlContextData::refreshExpressions()
273{
274 bool isGlobal = (m_parent == nullptr);
275
276 // For efficiency, we try and minimize the number of guards we have to create
277 if (hasExpressionsToRun(isGlobal) && m_childContexts) {
278 QQmlGuardedContextData guard(this);
279 m_childContexts->refreshExpressionsRecursive(isGlobal);
280 if (!guard.isNull() && hasExpressionsToRun(isGlobal))
281 refreshExpressionsRecursive(m_expressions);
282 } else if (hasExpressionsToRun(isGlobal)) {
283 refreshExpressionsRecursive(m_expressions);
284 } else if (m_childContexts) {
285 m_childContexts->refreshExpressionsRecursive(isGlobal);
286 }
287}
288
289void QQmlContextData::addOwnedObject(QQmlData *data)
290{
291 if (data->outerContext) {
292 if (data->nextContextObject)
293 data->nextContextObject->prevContextObject = data->prevContextObject;
294 if (data->prevContextObject)
295 *data->prevContextObject = data->nextContextObject;
296 else if (data->outerContext->m_ownedObjects == data)
297 data->outerContext->m_ownedObjects = data->nextContextObject;
298 }
299
300 data->outerContext = this;
301
302 data->nextContextObject = m_ownedObjects;
303 if (data->nextContextObject)
304 data->nextContextObject->prevContextObject = &data->nextContextObject;
305 data->prevContextObject = &m_ownedObjects;
306 m_ownedObjects = data;
307}
308
309void QQmlContextData::setIdValue(int idx, QObject *obj)
310{
311 m_idValues[idx] = obj;
312 m_idValues[idx].setContext(this);
313}
314
315QString QQmlContextData::findObjectId(const QObject *obj) const
316{
317 for (int ii = 0; ii < m_idValueCount; ii++) {
318 if (m_idValues[ii] == obj)
319 return propertyName(ii);
320 }
321
322 const QVariant objVariant = QVariant::fromValue(obj);
323 if (m_publicContext) {
324 QQmlContextPrivate *p = QQmlContextPrivate::get(m_publicContext);
325 for (int ii = 0; ii < p->numPropertyValues(); ++ii)
326 if (p->propertyValue(ii) == objVariant)
327 return propertyName(ii);
328 }
329
330 if (m_contextObject) {
331 // This is expensive, but nameForObject should really mirror contextProperty()
332 for (const QMetaObject *metaObject = m_contextObject->metaObject();
333 metaObject; metaObject = metaObject->superClass()) {
334 for (int i = metaObject->propertyOffset(), end = metaObject->propertyCount();
335 i != end; ++i) {
336 const QMetaProperty prop = metaObject->property(i);
337 if (prop.metaType().flags() & QMetaType::PointerToQObject
338 && prop.read(m_contextObject) == objVariant) {
339 return QString::fromUtf8(prop.name());
340 }
341 }
342 }
343 }
344
345 return QString();
346}
347
348void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex)
349{
350 m_typeCompilationUnit = unit;
351 m_componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex;
352 Q_ASSERT(!m_idValues);
353 m_idValueCount = m_typeCompilationUnit->objectAt(m_componentObjectIndex)
354 ->nNamedObjectsInComponent;
355 if (m_idValueCount > 0)
356 m_idValues = new ContextGuard[m_idValueCount];
357}
358
359void QQmlContextData::addComponentAttached(QQmlComponentAttached *attached)
360{
361 attached->insertIntoList(&m_componentAttacheds);
362}
363
364void QQmlContextData::addExpression(QQmlJavaScriptExpression *expression)
365{
366 expression->insertIntoList(&m_expressions);
367}
368
369void QQmlContextData::initPropertyNames() const
370{
371 if (m_typeCompilationUnit)
372 m_propertyNameCache = m_typeCompilationUnit->namedObjectsPerComponent(m_componentObjectIndex);
373 else
374 m_propertyNameCache = QV4::IdentifierHash(m_engine->handle());
375 Q_ASSERT(!m_propertyNameCache.isEmpty());
376}
377
378QUrl QQmlContextData::url() const
379{
380 if (m_typeCompilationUnit)
381 return m_typeCompilationUnit->finalUrl();
382 return m_baseUrl;
383}
384
385QString QQmlContextData::urlString() const
386{
387 if (m_typeCompilationUnit)
388 return m_typeCompilationUnit->finalUrlString();
389 return m_baseUrlString;
390}
391
392QT_END_NAMESPACE
Combined button and popup list for selecting options.