aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/qml/qqmltypedata.cpp35
-rw-r--r--src/qml/qml/qqmltypeloader.cpp124
-rw-r--r--src/qml/qml/qqmltypeloader_p.h3
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt1
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml3
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp22
-rw-r--r--tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml7
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp61
9 files changed, 182 insertions, 76 deletions
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index c7de010fcd..86b6de63b3 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -798,22 +798,25 @@ void QQmlTypeData::allDependenciesDone()
QList<QQmlError> errors;
auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
for ( ; it != end; ++it) {
- if ((*it)->priority == 0) {
- // This import was not resolved
- for (auto keyIt = m_unresolvedImports.constBegin(),
- keyEnd = m_unresolvedImports.constEnd();
- keyIt != keyEnd; ++keyIt) {
- const PendingImportPtr &import = *keyIt;
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
- error.setUrl(m_importCache->baseUrl());
- error.setLine(qmlConvertSourceCoordinate<quint32, int>(
- import->location.line()));
- error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
- import->location.column()));
- errors.prepend(error);
- }
- }
+ const PendingImportPtr &import = *it;
+ if (import->priority != 0)
+ continue;
+
+ // If the import was potentially remote and all the network requests have failed,
+ // we now know that there is no qmldir. We can register its types.
+ if (registerPendingTypes(import))
+ continue;
+
+ // This import was not resolved
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed")
+ .arg(import->uri));
+ error.setUrl(m_importCache->baseUrl());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.column()));
+ errors.prepend(error);
}
if (errors.size()) {
setError(errors);
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 89199c8652..7420a1654c 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -925,64 +925,57 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
// If there is a qmldir we cannot see, yet, then we have to wait.
// The qmldir might contain import directives.
- if (qmldirResult != QmldirInterceptedToRemote && (
- // Major version of module already registered:
- // We believe that the registration is complete.
- QQmlMetaType::typeModule(import->uri, import->version)
+ // TODO: This should trigger on any potentially remote URLs, not only intercepted ones.
+ // However, fixing this would open the door for follow-up problems while providing
+ // rather limited benefits.
+ if (qmldirResult != QmldirInterceptedToRemote && registerPendingTypes(import)) {
+ if (m_importCache->addLibraryImport(
+ typeLoader(), import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags, import->precedence, errors).isValid()) {
+ return true;
+ }
- // Otherwise, try to register further module types.
- || QQmlMetaType::qmlRegisterModuleTypes(import->uri)
+ return false;
+ }
- // Otherwise, there is no way to register any further types.
- // Try with any module of that name.
- || QQmlMetaType::latestModuleVersion(import->uri).isValid())) {
+ // We haven't yet resolved this import
+ m_unresolvedImports << import;
- if (!m_importCache->addLibraryImport(
- typeLoader(), import->uri, import->qualifier, import->version, QString(),
- QString(), import->flags, import->precedence, errors).isValid()) {
- return false;
- }
- } else {
- // We haven't yet resolved this import
- m_unresolvedImports << import;
-
- const bool hasInterceptors = m_typeLoader->hasUrlInterceptors();
-
- // Query any network import paths for this library.
- // Interceptor might redirect local paths.
- QStringList remotePathList = typeLoader()->importPathList(
- hasInterceptors ? LocalOrRemote : Remote);
- if (!remotePathList.isEmpty()) {
- // Add this library and request the possible locations for it
- const QTypeRevision version = m_importCache->addLibraryImport(
- typeLoader(), import->uri, import->qualifier, import->version, QString(),
- QString(), import->flags | QQmlImports::ImportIncomplete, import->precedence,
- errors);
-
- if (!version.isValid())
- return false;
+ // Add this library and request the possible locations for it
+ const QTypeRevision version = m_importCache->addLibraryImport(
+ typeLoader(), import->uri, import->qualifier, import->version, QString(),
+ QString(), import->flags | QQmlImports::ImportIncomplete, import->precedence,
+ errors);
- // Use more specific version for finding the qmldir if possible
- if (version.hasMajorVersion())
- import->version = version;
-
- // Probe for all possible locations
- int priority = 0;
- const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(
- import->uri, remotePathList, import->version);
- for (const QString &qmldirPath : qmlDirPaths) {
- if (hasInterceptors) {
- QUrl url = m_typeLoader->interceptUrl(
- QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
- QQmlAbstractUrlInterceptor::QmldirFile);
- if (!QQmlFile::isLocalFile(url)
- && !fetchQmldir(url, import, ++priority, errors)) {
- return false;
- }
- } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
+ if (!version.isValid())
+ return false;
+
+ // Use more specific version for finding the qmldir if possible
+ if (version.hasMajorVersion())
+ import->version = version;
+
+ const bool hasInterceptors = m_typeLoader->hasUrlInterceptors();
+
+ // Query any network import paths for this library.
+ // Interceptor might redirect local paths.
+ QStringList remotePathList = typeLoader()->importPathList(
+ hasInterceptors ? LocalOrRemote : Remote);
+ if (!remotePathList.isEmpty()) {
+ // Probe for all possible locations
+ int priority = 0;
+ const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths(
+ import->uri, remotePathList, import->version);
+ for (const QString &qmldirPath : qmlDirPaths) {
+ if (hasInterceptors) {
+ QUrl url = m_typeLoader->interceptUrl(
+ QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
+ QQmlAbstractUrlInterceptor::QmldirFile);
+ if (!QQmlFile::isLocalFile(url)
+ && !fetchQmldir(url, import, ++priority, errors)) {
return false;
}
-
+ } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) {
+ return false;
}
}
}
@@ -990,6 +983,23 @@ bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingI
return true;
}
+bool QQmlTypeLoader::Blob::registerPendingTypes(const PendingImportPtr &import)
+{
+ assertTypeLoaderThread();
+
+ return
+ // Major version of module already registered:
+ // We believe that the registration is complete.
+ QQmlMetaType::typeModule(import->uri, import->version)
+
+ // Otherwise, try to register further module types.
+ || QQmlMetaType::qmlRegisterModuleTypes(import->uri)
+
+ // Otherwise, there is no way to register any further types.
+ // Try with any module of that name.
+ || QQmlMetaType::latestModuleVersion(import->uri).isValid();
+}
+
bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import,
QQmlImports::ImportFlags flags, QList<QQmlError> *errors)
{
@@ -1883,6 +1893,16 @@ QQmlTypeLoader::LocalQmldirResult QQmlTypeLoader::locateLocalQmldir(
QString qmldirAbsoluteFilePath;
for (QString qmldirPath : qmlDirPaths) {
if (hasInterceptors) {
+ // TODO:
+ // 1. This is inexact. It triggers only on the existence of interceptors, not on
+ // actual interception. If the URL was remote to begin with but no interceptor
+ // actually changes it, we still clear the qmldirPath and consider it
+ // QmldirInterceptedToRemote.
+ // 2. This misdiagnosis makes addLibraryImport do the right thing and postpone
+ // the loading of pre-registered types for any QML engine that has interceptors
+ // (even if they don't do anything in this case).
+ // Fixing this would open the door to follow-up problems but wouldn't result in any
+ // significant benefit.
const QUrl intercepted = doInterceptUrl(
QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath),
QQmlAbstractUrlInterceptor::QmldirFile,
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index c59b0069e0..47df026a07 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -114,6 +114,9 @@ public:
QQmlImports::ImportFlags flags, QList<QQmlError> *errors);
protected:
+
+ bool registerPendingTypes(const PendingImportPtr &import);
+
bool loadDependentImports(
const QList<QQmlDirParser::Import> &imports, const QString &qualifier,
QTypeRevision version, quint16 precedence, QQmlImports::ImportFlags flags,
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt
deleted file mode 100644
index de75f47c03..0000000000
--- a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt
+++ /dev/null
@@ -1 +0,0 @@
-1:1:module "org.qtproject.AutoTestQmlNestedPluginType.Nested" is not installed
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml
index 35fff29a69..5148653a53 100644
--- a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml
+++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml
@@ -1,5 +1,8 @@
import org.qtproject.AutoTestQmlNestedPluginType.Nested 1.0
import org.qtproject.AutoTestQmlNestedPluginType 1.0
+import QtQml
MyNestedPluginType {
+ property Conflict conflict: Conflict {}
+ Component.onCompleted: console.log(conflict.value)
}
diff --git a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
index 9bddf0f0c4..ed03894fe3 100644
--- a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp
@@ -33,11 +33,13 @@ public:
{
Q_ASSERT(QLatin1String(uri) == "org.qtproject.AutoTestQmlNestedPluginType");
qmlRegisterType<MyPluginTypeNested>(uri, 1, 0, "MyPluginType");
+ qmlRegisterType<MyPluginTypeNested>(uri, 1, 0, "Conflict");
QString nestedUri(uri);
nestedUri += QLatin1String(".Nested");
qmlRegisterType<MyNestedPluginType>(nestedUri.toLatin1().constData(), 1, 0, "MyNestedPluginType");
+ qmlRegisterType<MyNestedPluginType>(nestedUri.toLatin1().constData(), 1, 0, "Conflict");
}
};
diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
index 832642d22a..35e71612be 100644
--- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
@@ -494,18 +494,22 @@ void tst_qqmlmoduleplugin::importsNested_data()
{
QTest::addColumn<QString>("file");
QTest::addColumn<QString>("errorFile");
-
- // Note: no other test case should import the plugin used for this test, or the
- // wrong order test will pass spuriously
- QTest::newRow("wrongOrder") << "importsNested.1.qml" << "importsNested.1.errors.txt";
- QTest::newRow("missingImport") << "importsNested.3.qml" << "importsNested.3.errors.txt";
- QTest::newRow("invalidVersion") << "importsNested.4.qml" << "importsNested.4.errors.txt";
- QTest::newRow("correctOrder") << "importsNested.2.qml" << QString();
+ QTest::addColumn<bool>("expectGreeting");
+
+ // NB: The order is wrong in the sense that it tries to load the "Nested" URI first, which
+ // is not visible in the file system. However, in the process of loading the non-nested
+ // URI it can discover the types for the nested one and insert them in the right place.
+ // This used to be an error but doesn't have to be.
+ QTest::newRow("wrongOrder") << "importsNested.1.qml" << QString() << true;
+ QTest::newRow("missingImport") << "importsNested.3.qml" << "importsNested.3.errors.txt" << false;
+ QTest::newRow("invalidVersion") << "importsNested.4.qml" << "importsNested.4.errors.txt" << false;
+ QTest::newRow("correctOrder") << "importsNested.2.qml" << QString() << false;
}
void tst_qqmlmoduleplugin::importsNested()
{
QFETCH(QString, file);
QFETCH(QString, errorFile);
+ QFETCH(bool, expectGreeting);
// Note: because imports are cached between test case data rows (and the plugins remain loaded),
// these tests should really be run in new instances of the app...
@@ -522,6 +526,10 @@ void tst_qqmlmoduleplugin::importsNested()
QTest::ignoreMessage(QtWarningMsg, "Module 'org.qtproject.AutoTestQmlNestedPluginType' does not contain a module identifier directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFile(file));
+
+ if (expectGreeting)
+ QTest::ignoreMessage(QtDebugMsg, "Hello");
+
std::unique_ptr<QObject> obj { component.create() };
if (errorFile.isEmpty()) {
diff --git a/tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml b/tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml
new file mode 100644
index 0000000000..d8b380928c
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/qobjectSingletonUser.qml
@@ -0,0 +1,7 @@
+import QtQml
+import Qt.example.qobjectSingleton 1.0
+
+QtObject {
+ property int someValue: MyApi.someProperty
+ property int doneSomething: MyApi.doSomething()
+}
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 0f1ee2caf7..02435f7cdd 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -6,6 +6,7 @@
#include <QtCore/QRandomGenerator>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlapplicationengine.h>
#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
@@ -55,6 +56,7 @@ private slots:
void loadTypeOnShutdown();
void floodTypeLoaderEventQueue();
void retainQmlTypeAcrossEngines();
+ void loadLocalTypesAfterRemoteFails();
private:
void checkSingleton(const QString & dataDirectory);
@@ -924,6 +926,65 @@ void tst_QQMLTypeLoader::retainQmlTypeAcrossEngines()
QCOMPARE(mo1->superClass(), mo3->superClass());
}
+class SingletonTypeExample : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
+
+public:
+ explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {}
+
+ Q_INVOKABLE int doSomething()
+ {
+ setSomeProperty(5);
+ return m_someProperty;
+ }
+
+ int someProperty() const { return m_someProperty; }
+ void setSomeProperty(int val) {
+ if (m_someProperty != val) {
+ m_someProperty = val;
+ emit somePropertyChanged(val);
+ }
+ }
+
+signals:
+ void somePropertyChanged(int newValue);
+
+private:
+ int m_someProperty = 0;
+};
+
+class HttpUrlInterceptor : public QQmlAbstractUrlInterceptor
+{
+public:
+ QUrl intercept(const QUrl &path, DataType type) override
+ {
+ QUrl result = path;
+ if (path.scheme() == "http" && type == QmldirFile)
+ result.setFragment("qmldir");
+ return result;
+ }
+};
+
+void tst_QQMLTypeLoader::loadLocalTypesAfterRemoteFails()
+{
+ std::unique_ptr<SingletonTypeExample> example = std::make_unique<SingletonTypeExample>();
+ qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get());
+
+ HttpUrlInterceptor interceptor;
+ QQmlEngine engine;
+ engine.addUrlInterceptor(&interceptor);
+ engine.addImportPath(QString("http:/127.0.0.1/"));
+
+ QQmlComponent component(&engine, testFileUrl("qobjectSingletonUser.qml"));
+ QTRY_VERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QScopedPointer<QObject> object(component.create());
+ QCOMPARE(object->property("someValue").toInt(), 5);
+ QCOMPARE(object->property("doneSomething").toInt(), 5);
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"