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#ifndef QQMLCONTEXT_P_H
41#define QQMLCONTEXT_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include "qqmlcontext.h"
55
56#include "qqmldata_p.h"
57#include "qqmltypenamecache_p.h"
58#include "qqmlnotifier_p.h"
59#include "qqmllist.h"
60
61#include <QtCore/qhash.h>
62#include <QtQml/qjsvalue.h>
63#include <QtCore/qset.h>
64
65#include <private/qobject_p.h>
66#include <private/qflagpointer_p.h>
67#include <private/qqmlguard_p.h>
68
69#include <private/qv4executablecompilationunit_p.h>
70#include <private/qv4identifier_p.h>
71
72QT_BEGIN_NAMESPACE
73
74class QQmlContext;
75class QQmlExpression;
76class QQmlEngine;
77class QQmlExpression;
78class QQmlExpressionPrivate;
79class QQmlJavaScriptExpression;
80class QQmlContextData;
81class QQmlGuardedContextData;
82class QQmlIncubatorPrivate;
83
84class QQmlContextPrivate : public QObjectPrivate
85{
86 Q_DECLARE_PUBLIC(QQmlContext)
87public:
88 QQmlContextPrivate();
89
90 QQmlContextData *data;
91
92 QList<QVariant> propertyValues;
93 int notifyIndex;
94
95 static QQmlContextPrivate *get(QQmlContext *context) {
96 return static_cast<QQmlContextPrivate *>(QObjectPrivate::get(context));
97 }
98 static QQmlContext *get(QQmlContextPrivate *context) {
99 return static_cast<QQmlContext *>(context->q_func());
100 }
101
102 // Only used for debugging
103 QList<QPointer<QObject> > instances;
104
105 static int context_count(QQmlListProperty<QObject> *);
106 static QObject *context_at(QQmlListProperty<QObject> *, int);
107
108 void dropDestroyedQObject(const QString &name, QObject *destroyed);
109};
110
111class QQmlComponentAttached;
112
113class Q_QML_PRIVATE_EXPORT QQmlContextData
114{
115public:
116 QQmlContextData();
117 QQmlContextData(QQmlContext *);
118 void emitDestruction();
119 void clearContext();
120 void clearContextRecursively();
121 void invalidate();
122
123 inline bool isValid() const {
124 return engine && (!isInternal || !contextObject || !QObjectPrivate::get(contextObject)->wasDeleted);
125 }
126
127 // My parent context and engine
128 QQmlContextData *parent = nullptr;
129 QQmlEngine *engine;
130
131 void setParent(QQmlContextData *, bool stronglyReferencedByParent = false);
132 void refreshExpressions();
133
134 void addObject(QQmlData *data);
135
136 QUrl resolvedUrl(const QUrl &);
137
138 // My containing QQmlContext. If isInternal is true this owns publicContext.
139 // If internal is false publicContext owns this.
140 QQmlContext *asQQmlContext();
141 QQmlContextPrivate *asQQmlContextPrivate();
142 quint32 refCount = 0;
143 quint32 isInternal:1;
144 quint32 isJSContext:1;
145 quint32 isPragmaLibraryContext:1;
146 quint32 unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name
147 quint32 hasEmittedDestruction:1;
148 quint32 isRootObjectInCreation:1;
149 quint32 stronglyReferencedByParent:1;
150 quint32 dummy:25;
151 QQmlContext *publicContext;
152
153 // The incubator that is constructing this context if any
154 QQmlIncubatorPrivate *incubator;
155
156 // Compilation unit for contexts that belong to a compiled type.
157 QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit;
158
159 // object index in CompiledData::Unit to component that created this context
160 int componentObjectIndex;
161
162 void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex);
163
164 // flag indicates whether the context owns the cache (after mutation) or not.
165 mutable QV4::IdentifierHash propertyNameCache;
166 const QV4::IdentifierHash &propertyNames() const;
167 QV4::IdentifierHash &detachedPropertyNames();
168
169 // Context object
170 QObject *contextObject;
171
172 // Any script blocks that exist on this context
173 QV4::PersistentValue importedScripts; // This is a JS Array
174
175 QUrl baseUrl;
176 QString baseUrlString;
177
178 QUrl url() const;
179 QString urlString() const;
180
181 // List of imports that apply to this context
182 QQmlRefPointer<QQmlTypeNameCache> imports;
183
184 // My children
185 QQmlContextData *childContexts = nullptr;
186
187 // My peers in parent's childContexts list
188 QQmlContextData *nextChild;
189 QQmlContextData **prevChild;
190
191 // Expressions that use this context
192 QQmlJavaScriptExpression *expressions;
193
194 // Doubly-linked list of objects that are owned by this context
195 QQmlData *contextObjects;
196
197 // Doubly-linked list of context guards (XXX merge with contextObjects)
198 QQmlGuardedContextData *contextGuards = nullptr;
199
200 // id guards
201 struct ContextGuard : public QQmlGuard<QObject>
202 {
203 inline ContextGuard();
204 inline ContextGuard &operator=(QObject *obj);
205 inline void objectDestroyed(QObject *) override;
206
207 inline bool wasSet() const;
208
209 QFlagPointer<QQmlContextData> context;
210 QQmlNotifier bindings;
211 };
212 ContextGuard *idValues;
213 int idValueCount;
214 void setIdProperty(int, QObject *);
215
216 // Linked contexts. this owns linkedContext.
217 QQmlContextDataRef linkedContext;
218
219 // Linked list of uses of the Component attached property in this
220 // context
221 QQmlComponentAttached *componentAttached;
222
223 // Return the outermost id for obj, if any.
224 QString findObjectId(const QObject *obj) const;
225
226 static QQmlContextData *get(QQmlContext *context) {
227 return QQmlContextPrivate::get(context)->data;
228 }
229
230private:
231 friend class QQmlContextDataRef;
232 friend class QQmlContext; // needs to do manual refcounting :/
233 void refreshExpressionsRecursive(bool isGlobal);
234 void refreshExpressionsRecursive(QQmlJavaScriptExpression *);
235 ~QQmlContextData();
236 void destroy();
237};
238
239
240class QQmlGuardedContextData
241{
242public:
243 inline QQmlGuardedContextData() = default;
244 inline QQmlGuardedContextData(QQmlContextData *data)
245 { setContextData(data); }
246 inline ~QQmlGuardedContextData()
247 { clear(); }
248
249 inline QQmlContextData *contextData() const
250 { return m_contextData; }
251 inline void setContextData(QQmlContextData *);
252
253 inline bool isNull() const { return !m_contextData; }
254
255 inline operator QQmlContextData*() const { return m_contextData; }
256 inline QQmlContextData* operator->() const { return m_contextData; }
257 inline QQmlGuardedContextData &operator=(QQmlContextData *d) {
258 setContextData(d); return *this;
259 }
260
261private:
262 QQmlGuardedContextData &operator=(const QQmlGuardedContextData &) = delete;
263 QQmlGuardedContextData(const QQmlGuardedContextData &) = delete;
264 friend class QQmlContextData;
265
266 inline void clear();
267
268 QQmlContextData *m_contextData = nullptr;
269 QQmlGuardedContextData *m_next = nullptr;
270 QQmlGuardedContextData **m_prev = nullptr;
271};
272
273
274void QQmlGuardedContextData::setContextData(QQmlContextData *contextData)
275 {
276 if (m_contextData == contextData)
277 return;
278 clear();
279
280 if (contextData) {
281 m_contextData = contextData;
282 m_next = contextData->contextGuards;
283 if (m_next) m_next->m_prev = &m_next;
284 m_prev = &contextData->contextGuards;
285 contextData->contextGuards = this;
286 }
287}
288
289void QQmlGuardedContextData::clear()
290{
291 if (m_prev) {
292 *m_prev = m_next;
293 if (m_next) m_next->m_prev = m_prev;
294 m_contextData = nullptr;
295 m_next = nullptr;
296 m_prev = nullptr;
297 }
298}
299
300QQmlContextDataRef::QQmlContextDataRef()
301 : m_contextData(nullptr)
302{
303}
304
305QQmlContextDataRef::QQmlContextDataRef(const QQmlContextDataRef &other)
306 : m_contextData(other.m_contextData)
307{
308 if (m_contextData)
309 ++m_contextData->refCount;
310}
311
312QQmlContextDataRef::QQmlContextDataRef(QQmlContextData *data)
313 : m_contextData(data)
314{
315 if (m_contextData)
316 ++m_contextData->refCount;
317}
318
319QQmlContextDataRef::~QQmlContextDataRef()
320{
321 clear();
322}
323
324void QQmlContextDataRef::setContextData(QQmlContextData *contextData)
325{
326 if (m_contextData == contextData)
327 return;
328 clear();
329
330 if (contextData) {
331 m_contextData = contextData;
332 ++m_contextData->refCount;
333 }
334}
335
336QQmlContextData *QQmlContextDataRef::contextData() const
337{
338 return m_contextData;
339}
340
341void QQmlContextDataRef::clear()
342{
343 if (m_contextData && !--m_contextData->refCount)
344 m_contextData->destroy();
345 m_contextData = nullptr;
346}
347
348QQmlContextDataRef &
349QQmlContextDataRef::operator=(QQmlContextData *d)
350{
351 setContextData(d);
352 return *this;
353}
354
355QQmlContextDataRef &
356QQmlContextDataRef::operator=(const QQmlContextDataRef &other)
357{
358 setContextData(other.m_contextData);
359 return *this;
360}
361
362QQmlContextData::ContextGuard::ContextGuard()
363: context(nullptr)
364{
365}
366
367QQmlContextData::ContextGuard &QQmlContextData::ContextGuard::operator=(QObject *obj)
368{
369 QQmlGuard<QObject>::operator=(obj);
370 context.setFlag();
371 bindings.notify(); // For alias connections
372 return *this;
373}
374
375void QQmlContextData::ContextGuard::objectDestroyed(QObject *)
376{
377 if (context->contextObject && !QObjectPrivate::get(context->contextObject)->wasDeleted)
378 bindings.notify();
379}
380
381bool QQmlContextData::ContextGuard::wasSet() const
382{
383 return context.flag();
384}
385
386QT_END_NAMESPACE
387
388#endif // QQMLCONTEXT_P_H
389