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