/**************************************************************************** ** ** 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 #include #include #include QT_BEGIN_NAMESPACE QUrl QQmlContextData::resolvedUrl(const QUrl &src) const { QUrl resolved; if (src.isRelative() && !src.isEmpty()) { const QUrl ownUrl = url(); if (ownUrl.isValid()) { resolved = ownUrl.resolved(src); } else { for (QQmlRefPointer ctxt = parent(); ctxt; ctxt = ctxt->parent()) { const QUrl ctxtUrl = ctxt->url(); if (ctxtUrl.isValid()) { resolved = ctxtUrl.resolved(src); break; } } if (m_engine && resolved.isEmpty()) 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 (QQmlRefPointer child = m_childContexts; !child.isNull(); 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); // avoid recursion addref(); if (m_engine) invalidate(); m_linkedContext = nullptr; 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(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 &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