From 36ce1490fc541489f9091e9d91234aeac4f9df90 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 22 Sep 2017 15:55:42 +0200 Subject: Don't reject plugin-only qmldir files On QQmlImportsPrivate::updateQmldirContent we need to check if the new module has actually been established after figuring out that it doesn't have any components or scripts. If it has, then we shouldn't fail, as obviously a plugin has been loaded. We don't need to check the component and script versions in that case, as plugins don't have separate versions. Change-Id: Ie328b59038fe65c3f6a2eeecfe969927bba6cd68 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlimport.cpp | 4 +- .../qml/qqmltypeloader/data/test_intercept.qml | 2 +- .../auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp | 51 +++++++++++++++++----- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 18dc8e4b28..c8d17c4b8e 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1577,8 +1577,8 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) { if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) { - // The implicit import qmldir can be empty - if (uri != QLatin1String(".")) { + // The implicit import qmldir can be empty, and plugins have no extra versions + if (uri != QLatin1String(".") && !QQmlMetaType::isModule(uri, vmaj, vmin)) { QQmlError error; if (QQmlMetaType::isAnyModule(uri)) error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri).arg(vmaj).arg(vmin)); diff --git a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml index 091fbe7f49..0d64cb7e28 100644 --- a/tests/auto/qml/qqmltypeloader/data/test_intercept.qml +++ b/tests/auto/qml/qqmltypeloader/data/test_intercept.qml @@ -41,7 +41,7 @@ ListView { width: ListView.view.width height: 100 asynchronous: true - source: "Intercept.qml" + source: index == 0 ? "Intercept.qml" : "GenericView.qml" onLoaded: { test.loaded++ diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 12aeff7591..5ab729042f 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -72,7 +72,7 @@ void tst_QQMLTypeLoader::loadComponentSynchronously() QTest::ignoreMessage(QtWarningMsg, QRegularExpression( QLatin1String(".*nonprotocol::1:1: QtObject is not a type.*"))); QQmlComponent component(&engine, testFileUrl("load_synchronous.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o); } @@ -113,7 +113,7 @@ void tst_QQMLTypeLoader::trimCache() void tst_QQMLTypeLoader::trimCache2() { - QQuickView *window = new QQuickView(); + QScopedPointer window(new QQuickView()); window->setSource(testFileUrl("trim_cache2.qml")); QQmlTypeLoader &loader = QQmlEnginePrivate::get(window->engine())->typeLoader; // in theory if gc has already run this could be false @@ -279,7 +279,7 @@ public: QFile file(filename); if (file.open(QIODevice::ReadOnly)) { emit loaded(filename); - reply->setData(file.readAll()); + reply->setData(transformQmldir(filename, file.readAll())); } else reply->fail(); } @@ -287,6 +287,37 @@ public: return reply; } + QByteArray transformQmldir(const QString &filename, const QByteArray &content) + { + if (!filename.endsWith("/qmldir")) + return content; + + // Make qmldir plugin paths absolute, so that we don't try to load them over the network + QByteArray result; + QByteArray path = filename.toUtf8(); + path.chop(sizeof("qmldir") - 1); + for (QByteArray line : content.split('\n')) { + if (line.isEmpty()) + continue; + QList segments = line.split(' '); + if (segments.startsWith("plugin")) { + if (segments.length() == 2) { + segments.append(path); + } else if (segments.length() == 3) { + if (!segments[2].startsWith('/')) + segments[2] = path + segments[2]; + } else { + // Invalid plugin declaration. Ignore + } + result.append(segments.join(' ')); + } else { + result.append(line); + } + result.append('\n'); + } + return result; + } + signals: void loaded(const QString &filename); }; @@ -315,13 +346,6 @@ public: if (!QQmlFile::isLocalFile(path)) return path; - // Don't rewrite internal Qt paths. We'd hit C++ plugins there. - QString filename = QQmlFile::urlToLocalFileOrQrc(path); - if (filename.startsWith(":/qt-project.org/") - || filename.startsWith(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath))) { - return path; - } - QUrl result = path; QString scheme = result.scheme(); if (!scheme.endsWith("+debug")) @@ -332,8 +356,11 @@ public: void tst_QQMLTypeLoader::intercept() { + qmlClearTypeRegistrations(); + QQmlEngine engine; engine.addImportPath(dataDirectory()); + engine.addImportPath(QT_TESTCASE_BUILDDIR); UrlInterceptor interceptor; NetworkAccessManagerFactory factory; @@ -353,11 +380,13 @@ void tst_QQMLTypeLoader::intercept() QTRY_COMPARE(o->property("created").toInt(), 2); QTRY_COMPARE(o->property("loaded").toInt(), 2); - QCOMPARE(factory.loadedFiles.length(), 4); + QVERIFY(factory.loadedFiles.length() >= 6); QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/test_intercept.qml")); QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Intercept.qml")); QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Fast/qmldir")); QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/Fast/Fast.qml")); + QVERIFY(factory.loadedFiles.contains(dataDirectory() + "/GenericView.qml")); + QVERIFY(factory.loadedFiles.contains(QLatin1String(QT_TESTCASE_BUILDDIR) + "/Slow/qmldir")); } QTEST_MAIN(tst_QQMLTypeLoader) -- cgit v1.2.3