diff options
Diffstat (limited to 'src/qml/qml/qqmlimport_p.h')
-rw-r--r-- | src/qml/qml/qqmlimport_p.h | 405 |
1 files changed, 294 insertions, 111 deletions
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 30d7c56a3e..ef9b4b3422 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -1,53 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLIMPORT_P_H #define QQMLIMPORT_P_H #include <QtCore/qurl.h> #include <QtCore/qcoreapplication.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qset.h> #include <QtCore/qstringlist.h> +#include <QtQml/qqmlengine.h> #include <QtQml/qqmlerror.h> +#include <QtQml/qqmlfile.h> #include <private/qqmldirparser_p.h> #include <private/qqmltype_p.h> #include <private/qstringhash_p.h> +#include <private/qfieldlist_p.h> // // W A R N I N G @@ -66,10 +34,12 @@ class QQmlTypeNameCache; class QQmlEngine; class QDir; class QQmlImportNamespace; -class QQmlImportsPrivate; class QQmlImportDatabase; class QQmlTypeLoader; class QQmlTypeLoaderQmldirContent; +class QTypeRevision; + +const QLoggingCategory &lcQmlImport(); namespace QQmlImport { enum RecursionRestriction { PreventRecursion, AllowRecursion }; @@ -77,24 +47,36 @@ namespace QQmlImport { struct QQmlImportInstance { + enum Precedence { + Lowest = std::numeric_limits<quint8>::max(), + Implicit = Lowest / 2, + Highest = 0, + }; + QString uri; // e.g. QtQuick QString url; // the base path of the import - QString localDirectoryPath; // the base path of the import if it's a local file - int majversion; // the major version imported - int minversion; // the minor version imported + QTypeRevision version; // the version imported + bool isLibrary; // true means that this is not a file import + + // not covered by precedence. You can set a component as implicitly imported after the fact. bool implicitlyImported = false; + bool isInlineComponent = false; + + quint8 precedence = 0; + QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); - static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); + static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, + QTypeRevision version); bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, - int *vmajor, int *vminor, QQmlType* type_return, - QString *base = nullptr, bool *typeRecursionDetected = nullptr, + QTypeRevision *version_return, QQmlType* type_return, + const QString *base = nullptr, bool *typeRecursionDetected = nullptr, QQmlType::RegistrationType = QQmlType::AnyRegistrationType, QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion, QList<QQmlError> *errors = nullptr) const; @@ -111,62 +93,85 @@ public: QQmlImportInstance *findImport(const QString &uri) const; bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, - int *vmajor, int *vminor, QQmlType* type_return, - QString *base = nullptr, QList<QQmlError> *errors = nullptr, + QTypeRevision *version_return, QQmlType* type_return, + const QString *base = nullptr, QList<QQmlError> *errors = nullptr, QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType, - QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion); + bool *typeRecursionDeteced = nullptr); // Prefix when used as a qualified import. Otherwise empty. QHashedString prefix; - // Used by QQmlImportsPrivate::qualifiedSets - QQmlImportNamespace *nextNamespace; + // Used by QQmlImports::m_qualifiedSets + // set to this in unqualifiedSet to indicate that the lists of imports needs + // to be sorted when an inline component import was added + // We can't use flag pointer, as that does not work with QFieldList + QQmlImportNamespace *nextNamespace = nullptr; + bool needsSorting() const { return nextNamespace == this; } + void setNeedsSorting(bool needsSorting) + { + Q_ASSERT(nextNamespace == this || nextNamespace == nullptr); + nextNamespace = needsSorting ? this : nullptr; + } }; -class Q_QML_PRIVATE_EXPORT QQmlImports +class Q_QML_EXPORT QQmlImports final : public QQmlRefCounted<QQmlImports> { + Q_DISABLE_COPY_MOVE(QQmlImports) public: enum ImportVersion { FullyVersioned, PartiallyVersioned, Unversioned }; - QQmlImports(QQmlTypeLoader *); - QQmlImports(const QQmlImports &); - ~QQmlImports(); - QQmlImports &operator=(const QQmlImports &); + enum ImportFlag : quint8 { + ImportNoFlag = 0x0, + ImportIncomplete = 0x1, + }; + Q_DECLARE_FLAGS(ImportFlags, ImportFlag) + + QQmlImports() = default; + ~QQmlImports() + { + while (QQmlImportNamespace *ns = m_qualifiedSets.takeFirst()) + delete ns; + } void setBaseUrl(const QUrl &url, const QString &urlString = QString()); - QUrl baseUrl() const; + QUrl baseUrl() const { return m_baseUrl; } - bool resolveType(const QHashedStringRef &type, - QQmlType *type_return, - int *version_major, int *version_minor, - QQmlImportNamespace **ns_return, - QList<QQmlError> *errors = nullptr, - QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType, - QQmlImport::RecursionRestriction recursionRestriction - = QQmlImport::PreventRecursion) const; - bool resolveType(QQmlImportNamespace *, - const QHashedStringRef& type, - QQmlType *type_return, int *version_major, int *version_minor, - QQmlType::RegistrationType registrationType - = QQmlType::AnyRegistrationType) const; + bool resolveType( + QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QQmlType *type_return, + QTypeRevision *version_return, QQmlImportNamespace **ns_return, + QList<QQmlError> *errors = nullptr, + QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType, + bool *typeRecursionDetected = nullptr) const; + + QTypeRevision addImplicitImport( + QQmlTypeLoader *typeLoader, QString *localQmldir, QList<QQmlError> *errors) + { + Q_ASSERT(errors); + qCDebug(lcQmlImport) << "addImplicitImport:" << qPrintable(baseUrl().toString()); - bool addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors); + const ImportFlags flags = + ImportFlags(!isLocal(baseUrl()) ? ImportIncomplete : ImportNoFlag); + return addFileImport( + typeLoader, QLatin1String("."), QString(), QTypeRevision(), flags, + QQmlImportInstance::Implicit, localQmldir, errors); + } - bool addFileImport(QQmlImportDatabase *, - const QString& uri, const QString& prefix, int vmaj, int vmin, bool incomplete, - QList<QQmlError> *errors); + bool addInlineComponentImport( + QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl); - bool addLibraryImport(QQmlImportDatabase *importDb, - const QString &uri, const QString &prefix, int vmaj, int vmin, - const QString &qmldirIdentifier, const QString &qmldirUrl, bool incomplete, QList<QQmlError> *errors); + QTypeRevision addFileImport( + QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, + QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir, + QList<QQmlError> *errors); - bool updateQmldirContent(QQmlImportDatabase *importDb, - const QString &uri, const QString &prefix, - const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors); + QTypeRevision addLibraryImport( + QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, + QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl, + ImportFlags flags, quint16 precedence, QList<QQmlError> *errors); - bool locateQmldir(QQmlImportDatabase *, - const QString &uri, int vmaj, int vmin, - QString *qmldirFilePath, QString *url); + QTypeRevision updateQmldirContent( + QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, + const QString &qmldirIdentifier, const QString &qmldirUrl, QList<QQmlError> *errors); void populateCache(QQmlTypeNameCache *cache) const; @@ -183,83 +188,261 @@ public: { QString typeName; QString prefix; - int majorVersion; - int minorVersion; + QTypeRevision version; }; QList<CompositeSingletonReference> resolvedCompositeSingletons() const; - static QStringList completeQmldirPaths(const QString &uri, const QStringList &basePaths, int vmaj, int vmin); - static QString versionString(int vmaj, int vmin, ImportVersion version); + static QStringList completeQmldirPaths( + const QString &uri, const QStringList &basePaths, QTypeRevision version); + + static QString versionString(QTypeRevision version, ImportVersion importVersion); + + static bool isLocal(const QString &url) + { + return !QQmlFile::urlToLocalFileOrQrc(url).isEmpty(); + } + + static bool isLocal(const QUrl &url) + { + return !QQmlFile::urlToLocalFileOrQrc(url).isEmpty(); + } - static bool isLocal(const QString &url); - static bool isLocal(const QUrl &url); static QUrl urlFromLocalFileOrQrcOrUrl(const QString &); static void setDesignerSupportRequired(bool b); + static QTypeRevision validVersion(QTypeRevision version = QTypeRevision()); + private: friend class QQmlImportDatabase; - QQmlImportsPrivate *d; + + QQmlImportNamespace *importNamespace(const QString &prefix); + + bool resolveType( + QQmlTypeLoader *typeLoader, const QHashedStringRef &type, QTypeRevision *version_return, + QQmlType *type_return, QList<QQmlError> *errors, + QQmlType::RegistrationType registrationType, + bool *typeRecursionDetected = nullptr) const; + + QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const; + + static QTypeRevision matchingQmldirVersion( + const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, + QTypeRevision version, QList<QQmlError> *errors); + + QTypeRevision importExtension( + QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version, + const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors); + + QString redirectQmldirContent(QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir); + + bool getQmldirContent( + QQmlTypeLoader *typeLoader, const QString &qmldirIdentifier, const QString &uri, + QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors); + + QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); + + QUrl m_baseUrl; + QString m_base; + + // storage of data related to imports without a namespace + // TODO: This needs to be mutable because QQmlImportNamespace likes to sort itself on + // resolveType(). Therefore, QQmlImportNamespace::resolveType() is not const. + // There should be a better way to do this. + mutable QQmlImportNamespace m_unqualifiedset; + + // storage of data related to imports with a namespace + QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> m_qualifiedSets; }; -class Q_QML_PRIVATE_EXPORT QQmlImportDatabase +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlImports::ImportFlags) + +class Q_QML_EXPORT QQmlImportDatabase { Q_DECLARE_TR_FUNCTIONS(QQmlImportDatabase) public: enum PathType { Local, Remote, LocalOrRemote }; + enum LocalQmldirSearchLocation { + QmldirFileAndCache, + QmldirCacheOnly, + }; + + enum LocalQmldirResult { + QmldirFound, + QmldirNotFound, + QmldirInterceptedToRemote, + QmldirRejected + }; + QQmlImportDatabase(QQmlEngine *); - ~QQmlImportDatabase(); + ~QQmlImportDatabase() { clearDirCache(); } -#if QT_CONFIG(library) - bool importDynamicPlugin(const QString &filePath, const QString &uri, const QString &importNamespace, int vmaj, QList<QQmlError> *errors); - bool removeDynamicPlugin(const QString &filePath); + bool removeDynamicPlugin(const QString &pluginId); QStringList dynamicPlugins() const; -#endif QStringList importPathList(PathType type = LocalOrRemote) const; void setImportPathList(const QStringList &paths); void addImportPath(const QString& dir); - QStringList pluginPathList() const; + QStringList pluginPathList() const { return filePluginPath; } void setPluginPathList(const QStringList &paths); + void addPluginPath(const QString& path); + template<typename Callback> + LocalQmldirResult locateLocalQmldir( + const QString &uri, QTypeRevision version, LocalQmldirSearchLocation location, + const Callback &callback); + + static QTypeRevision lockModule(const QString &uri, const QString &typeNamespace, + QTypeRevision version, QList<QQmlError> *errors); + private: - friend class QQmlImportsPrivate; - QString resolvePlugin(QQmlTypeLoader *typeLoader, - const QString &qmldirPath, const QString &qmldirPluginPath, - const QString &baseName, const QStringList &suffixes, - const QString &prefix = QString()); - QString resolvePlugin(QQmlTypeLoader *typeLoader, - const QString &qmldirPath, const QString &qmldirPluginPath, - const QString &baseName); - bool importStaticPlugin(QObject *instance, const QString &basePath, const QString &uri, - const QString &typeNamespace, int vmaj, QList<QQmlError> *errors); + friend class QQmlImports; + friend class QQmlPluginImporter; + + QString absoluteFilePath(const QString &path) const; void clearDirCache(); - void finalizePlugin(QObject *instance, const QString &path, const QString &uri); struct QmldirCache { - int versionMajor; - int versionMinor; + QTypeRevision version; QString qmldirFilePath; QString qmldirPathUrl; QmldirCache *next; }; // Maps from an import to a linked list of qmldir info. - // Used in QQmlImportsPrivate::locateQmldir() + // Used in QQmlImports::locateQmldir() QStringHash<QmldirCache *> qmldirCache; // XXX thread QStringList filePluginPath; QStringList fileImportPath; - QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded; + QSet<QString> modulesForWhichPluginsHaveBeenLoaded; QSet<QString> initializedPlugins; QQmlEngine *engine; }; +template<typename Callback> +QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir( + const QString &uri, QTypeRevision version, + QQmlImportDatabase::LocalQmldirSearchLocation location, const Callback &callback) +{ + // Check cache first + + LocalQmldirResult result = QmldirNotFound; + QmldirCache *cacheTail = nullptr; + + QmldirCache **cachePtr = qmldirCache.value(uri); + QmldirCache *cacheHead = cachePtr ? *cachePtr : nullptr; + if (cacheHead) { + cacheTail = cacheHead; + do { + if (cacheTail->version == version) { + if (cacheTail->qmldirFilePath.isEmpty()) { + return cacheTail->qmldirPathUrl.isEmpty() + ? QmldirNotFound + : QmldirInterceptedToRemote; + } + if (callback(cacheTail->qmldirFilePath, cacheTail->qmldirPathUrl)) + return QmldirFound; + result = QmldirRejected; + } + } while (cacheTail->next && (cacheTail = cacheTail->next)); + } + + + // Do not try to construct the cache if it already had any entries for the URI. + // Otherwise we might duplicate cache entries. + if (location == QmldirCacheOnly || result != QmldirNotFound) + return result; + + const bool hasInterceptors = !engine->urlInterceptors().isEmpty(); + + // Interceptor might redirect remote files to local ones. + QStringList localImportPaths = importPathList(hasInterceptors ? LocalOrRemote : Local); + + // Search local import paths for a matching version + const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths( + uri, localImportPaths, version); + + QString qmldirAbsoluteFilePath; + for (QString qmldirPath : qmlDirPaths) { + if (hasInterceptors) { + const QUrl intercepted = engine->interceptUrl( + QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath), + QQmlAbstractUrlInterceptor::QmldirFile); + qmldirPath = QQmlFile::urlToLocalFileOrQrc(intercepted); + if (result != QmldirInterceptedToRemote + && qmldirPath.isEmpty() + && !QQmlFile::isLocalFile(intercepted)) { + result = QmldirInterceptedToRemote; + } + } + + qmldirAbsoluteFilePath = absoluteFilePath(qmldirPath); + if (!qmldirAbsoluteFilePath.isEmpty()) { + QString url; + const QString absolutePath = qmldirAbsoluteFilePath.left( + qmldirAbsoluteFilePath.lastIndexOf(u'/') + 1); + if (absolutePath.at(0) == u':') { + url = QStringLiteral("qrc") + absolutePath; + } else { + url = QUrl::fromLocalFile(absolutePath).toString(); + // This handles the UNC path case as when the path is retrieved from the QUrl it + // will convert the host name from upper case to lower case. So the absoluteFilePath + // is changed at this point to make sure it will match later on in that case. + if (qmldirAbsoluteFilePath.startsWith(QStringLiteral("//"))) { + qmldirAbsoluteFilePath = QUrl::fromLocalFile(qmldirAbsoluteFilePath) + .toString(QUrl::RemoveScheme); + } + } + + QmldirCache *cache = new QmldirCache; + cache->version = version; + cache->qmldirFilePath = qmldirAbsoluteFilePath; + cache->qmldirPathUrl = url; + cache->next = nullptr; + if (cacheTail) + cacheTail->next = cache; + else + qmldirCache.insert(uri, cache); + cacheTail = cache; + + if (result != QmldirFound) + result = callback(qmldirAbsoluteFilePath, url) ? QmldirFound : QmldirRejected; + + // Do not return here. Rather, construct the complete cache for this URI. + } + } + + // Nothing found? Add an empty cache entry to signal that for further requests. + if (result == QmldirNotFound || result == QmldirInterceptedToRemote) { + QmldirCache *cache = new QmldirCache; + cache->version = version; + cache->next = cacheHead; + if (result == QmldirInterceptedToRemote) { + // The actual value doesn't matter as long as it's not empty. + // We only use it to discern QmldirInterceptedToRemote from QmldirNotFound above. + cache->qmldirPathUrl = QStringLiteral("intercepted"); + } + qmldirCache.insert(uri, cache); + + if (result == QmldirNotFound) { + qCDebug(lcQmlImport) + << "locateLocalQmldir:" << qPrintable(uri) << "module's qmldir file not found"; + } + } else { + qCDebug(lcQmlImport) + << "locateLocalQmldir:" << qPrintable(uri) << "module's qmldir found at" + << qmldirAbsoluteFilePath; + } + + return result; +} + void qmlClearEnginePlugins();// For internal use by qmlClearRegisteredProperties QT_END_NAMESPACE |