diff options
Diffstat (limited to 'src/qml/qml/qqmlcontextdata.cpp')
-rw-r--r-- | src/qml/qml/qqmlcontextdata.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlcontextdata.cpp b/src/qml/qml/qqmlcontextdata.cpp new file mode 100644 index 0000000000..33be23b14d --- /dev/null +++ b/src/qml/qml/qqmlcontextdata.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcontextdata_p.h" + +#include <QtQml/qqmlengine.h> +#include <QtQml/private/qqmlcomponentattached_p.h> +#include <QtQml/private/qqmljavascriptexpression_p.h> +#include <QtQml/private/qqmlguardedcontextdata_p.h> + +QT_BEGIN_NAMESPACE + +QUrl QQmlContextData::resolvedUrl(const QUrl &src) +{ + QUrl resolved; + if (src.isRelative() && !src.isEmpty()) { + QQmlRefPointer<QQmlContextData> ctxt = this; + do { + if (ctxt->url().isValid()) + break; + else + ctxt = ctxt->parent(); + } while (ctxt); + + if (ctxt) + resolved = ctxt->url().resolved(src); + else if (m_engine) + resolved = m_engine->baseUrl().resolved(src); + } else { + resolved = src; + } + + if (resolved.isEmpty()) //relative but no ctxt + return resolved; + + return m_engine ? m_engine->interceptUrl(resolved, QQmlAbstractUrlInterceptor::UrlString) + : resolved; +} + +void QQmlContextData::emitDestruction() +{ + if (!m_hasEmittedDestruction) { + m_hasEmittedDestruction = true; + + // Emit the destruction signal - must be emitted before invalidate so that the + // context is still valid if bindings or resultant expression evaluation requires it + if (m_engine) { + while (m_componentAttacheds) { + QQmlComponentAttached *attached = m_componentAttacheds; + attached->removeFromList(); + emit attached->destruction(); + } + + for (QQmlContextData *child = m_childContexts; child; child = child->m_nextChild) + child->emitDestruction(); + } + } +} + +void QQmlContextData::invalidate() +{ + emitDestruction(); + + while (m_childContexts) { + Q_ASSERT(m_childContexts != this); + m_childContexts->invalidate(); + } + + if (m_prevChild) { + *m_prevChild = m_nextChild; + if (m_nextChild) m_nextChild->m_prevChild = m_prevChild; + m_nextChild = nullptr; + m_prevChild = nullptr; + } + + m_importedScripts.clear(); + + m_engine = nullptr; + clearParent(); +} + +void QQmlContextData::clearContextRecursively() +{ + clearContext(); + + for (auto ctxIt = m_childContexts; ctxIt; ctxIt = ctxIt->m_nextChild) + ctxIt->clearContextRecursively(); +} + +void QQmlContextData::clearContext() +{ + emitDestruction(); + + QQmlJavaScriptExpression *expression = m_expressions; + while (expression) { + QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression; + + expression->m_prevExpression = nullptr; + expression->m_nextExpression = nullptr; + + expression->setContext(nullptr); + + expression = nextExpression; + } + m_expressions = nullptr; +} + +QQmlContextData::~QQmlContextData() +{ + Q_ASSERT(refCount() == 0); + m_linkedContext = nullptr; + + // avoid recursion + addref(); + if (m_engine) + invalidate(); + + Q_ASSERT(refCount() == 1); + clearContext(); + Q_ASSERT(refCount() == 1); + + while (m_ownedObjects) { + QQmlData *co = m_ownedObjects; + m_ownedObjects = m_ownedObjects->nextContextObject; + + if (co->context == this) + co->context = nullptr; + co->outerContext = nullptr; + co->nextContextObject = nullptr; + co->prevContextObject = nullptr; + } + Q_ASSERT(refCount() == 1); + + QQmlGuardedContextData *contextGuard = m_contextGuards; + while (contextGuard) { + QQmlGuardedContextData *next = contextGuard->next(); + next->reset(); + contextGuard = next; + } + m_contextGuards = nullptr; + Q_ASSERT(refCount() == 1); + + delete [] m_idValues; + m_idValues = nullptr; + + Q_ASSERT(refCount() == 1); + if (m_publicContext) + delete m_publicContext; + + Q_ASSERT(refCount() == 1); +} + +void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression) +{ + QQmlJavaScriptExpression::DeleteWatcher w(expression); + + if (expression->m_nextExpression) + refreshExpressionsRecursive(expression->m_nextExpression); + + if (!w.wasDeleted()) + expression->refresh(); +} + +void QQmlContextData::refreshExpressionsRecursive(bool isGlobal) +{ + // For efficiency, we try and minimize the number of guards we have to create + if (hasExpressionsToRun(isGlobal) && (m_nextChild || m_childContexts)) { + QQmlGuardedContextData guard(this); + + if (m_childContexts) + m_childContexts->refreshExpressionsRecursive(isGlobal); + + if (guard.isNull()) return; + + if (m_nextChild) + m_nextChild->refreshExpressionsRecursive(isGlobal); + + if (guard.isNull()) return; + + if (hasExpressionsToRun(isGlobal)) + refreshExpressionsRecursive(m_expressions); + + } else if (hasExpressionsToRun(isGlobal)) { + refreshExpressionsRecursive(m_expressions); + } else if (m_nextChild && m_childContexts) { + QQmlGuardedContextData guard(this); + m_childContexts->refreshExpressionsRecursive(isGlobal); + if (!guard.isNull() && m_nextChild) + m_nextChild->refreshExpressionsRecursive(isGlobal); + } else if (m_nextChild) { + m_nextChild->refreshExpressionsRecursive(isGlobal); + } else if (m_childContexts) { + m_childContexts->refreshExpressionsRecursive(isGlobal); + } +} + +// Refreshes all expressions that could possibly depend on this context. Refreshing flushes all +// context-tree dependent caches in the expressions, and should occur every time the context tree +// *structure* (not values) changes. +void QQmlContextData::refreshExpressions() +{ + bool isGlobal = (m_parent == nullptr); + + // For efficiency, we try and minimize the number of guards we have to create + if (hasExpressionsToRun(isGlobal) && m_childContexts) { + QQmlGuardedContextData guard(this); + m_childContexts->refreshExpressionsRecursive(isGlobal); + if (!guard.isNull() && hasExpressionsToRun(isGlobal)) + refreshExpressionsRecursive(m_expressions); + } else if (hasExpressionsToRun(isGlobal)) { + refreshExpressionsRecursive(m_expressions); + } else if (m_childContexts) { + m_childContexts->refreshExpressionsRecursive(isGlobal); + } +} + +void QQmlContextData::addOwnedObject(QQmlData *data) +{ + if (data->outerContext) { + if (data->nextContextObject) + data->nextContextObject->prevContextObject = data->prevContextObject; + if (data->prevContextObject) + *data->prevContextObject = data->nextContextObject; + else if (data->outerContext->m_ownedObjects == data) + data->outerContext->m_ownedObjects = data->nextContextObject; + } + + data->outerContext = this; + + data->nextContextObject = m_ownedObjects; + if (data->nextContextObject) + data->nextContextObject->prevContextObject = &data->nextContextObject; + data->prevContextObject = &m_ownedObjects; + m_ownedObjects = data; +} + +void QQmlContextData::setIdValue(int idx, QObject *obj) +{ + m_idValues[idx] = obj; + m_idValues[idx].setContext(this); +} + +QString QQmlContextData::findObjectId(const QObject *obj) const +{ + const QV4::IdentifierHash &properties = propertyNames(); + if (m_propertyNameCache.isEmpty()) + return QString(); + + for (int ii = 0; ii < m_idValueCount; ii++) { + if (m_idValues[ii] == obj) + return properties.findId(ii); + } + + if (m_publicContext) { + QQmlContextPrivate *p = QQmlContextPrivate::get(m_publicContext); + for (int ii = 0; ii < p->numPropertyValues(); ++ii) + if (p->propertyValue(ii) == QVariant::fromValue(const_cast<QObject *>(obj))) + return properties.findId(ii); + } + + if (m_linkedContext) + return m_linkedContext->findObjectId(obj); + return QString(); +} + +QQmlContext *QQmlContextData::asQQmlContext() +{ + if (!m_publicContext) + m_publicContext = new QQmlContext(*new QQmlContextPrivate(this)); + return m_publicContext; +} + +QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() +{ + return QQmlContextPrivate::get(asQQmlContext()); +} + +void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex) +{ + m_typeCompilationUnit = unit; + m_componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex; + Q_ASSERT(!m_idValues); + m_idValueCount = m_typeCompilationUnit->objectAt(m_componentObjectIndex) + ->nNamedObjectsInComponent; + m_idValues = new ContextGuard[m_idValueCount]; +} + +void QQmlContextData::addComponentAttached(QQmlComponentAttached *attached) +{ + attached->insertIntoList(&m_componentAttacheds); +} + +void QQmlContextData::addExpression(QQmlJavaScriptExpression *expression) +{ + expression->insertIntoList(&m_expressions); +} + +QV4::IdentifierHash QQmlContextData::propertyNames() const +{ + if (m_propertyNameCache.isEmpty()) { + if (m_typeCompilationUnit) + m_propertyNameCache = m_typeCompilationUnit->namedObjectsPerComponent(m_componentObjectIndex); + else + m_propertyNameCache = QV4::IdentifierHash(m_engine->handle()); + } + return m_propertyNameCache; +} + +QV4::IdentifierHash *QQmlContextData::detachedPropertyNames() +{ + propertyNames(); + m_propertyNameCache.detach(); + return &m_propertyNameCache; +} + +QUrl QQmlContextData::url() const +{ + if (m_typeCompilationUnit) + return m_typeCompilationUnit->finalUrl(); + return m_baseUrl; +} + +QString QQmlContextData::urlString() const +{ + if (m_typeCompilationUnit) + return m_typeCompilationUnit->finalUrlString(); + return m_baseUrlString; +} + +QT_END_NAMESPACE |