Intercept qmldir files with the url interceptor
authorAlan Alpert <aalpert@blackberry.com>
Thu, 18 Jul 2013 13:02:37 +0000 (06:02 -0700)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 11 Nov 2013 11:18:15 +0000 (12:18 +0100)
There's another code path which loads qmldir files directly, and it did
not use the interceptor when available.

Note that this, like other interceptors, does not affect baseUrl and so
any other qmldir file still must have paths relative from the initial URL.

Change-Id: I620943c36d488d22fbaf1793514075d31ab76e3e
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/qml/qqmltypeloader.cpp
tests/auto/qml/qqmlengine/data/interception/imports/Test.2/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlengine/data/interception/module/intercepted/Intercepted.qml [new file with mode: 0644]
tests/auto/qml/qqmlengine/data/interception/module/intercepted/comment [new file with mode: 0644]
tests/auto/qml/qqmlengine/data/interception/module/intercepted/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlengine/data/interception/module/urlInterceptor.qml [new file with mode: 0644]
tests/auto/qml/qqmlengine/tst_qqmlengine.cpp

index 4b398fb..85d2fe4 100644 (file)
@@ -1801,9 +1801,25 @@ bool QQmlTypeLoader::directoryExists(const QString &path)
 Return a QmldirContent for absoluteFilePath.  The QmldirContent may be cached.
 
 \a filePath is either a bundle URL, or a local file path.
+
+It can also be a remote path for a remote directory import, but it will have been cached by now in this case.
 */
-const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePath, const QString &uriHint)
-{
+const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn, const QString &uriHint)
+{
+    QUrl url(filePathIn); //May already contain bundle or http scheme
+    if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https"))
+        return *(m_importQmlDirCache.value(filePathIn)); //Can't load the remote here, but should be cached
+    else if (!QQmlFile::isBundle(filePathIn))
+        url = QUrl::fromLocalFile(filePathIn);
+    if (engine() && engine()->urlInterceptor())
+        url = engine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::QmldirFile);
+    Q_ASSERT(url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("bundle"));
+    QString filePath;
+    if (url.scheme() == QLatin1String("file"))
+        filePath = url.toLocalFile();
+    else
+        filePath = url.path();
+
     QmldirContent *qmldir;
     QmldirContent **val = m_importQmlDirCache.value(filePath);
     if (!val) {
@@ -1813,13 +1829,10 @@ const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString
 #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable"))
 #define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\""))
 
-        if (QQmlFile::isBundle(filePath)) {
-
-            QUrl url(filePath);
-
+        if (QQmlFile::isBundle(url.toString())) {
             QQmlFile file(engine(), url);
             if (file.isError()) {
-                ERROR(NOT_READABLE_ERROR.arg(filePath));
+                ERROR(NOT_READABLE_ERROR.arg(url.toString()));
             } else {
                 QString content(QString::fromUtf8(file.data(), file.size()));
                 qmldir->setContent(filePath, content);
diff --git a/tests/auto/qml/qqmlengine/data/interception/imports/Test.2/qmldir b/tests/auto/qml/qqmlengine/data/interception/imports/Test.2/qmldir
new file mode 100644 (file)
index 0000000..971812c
--- /dev/null
@@ -0,0 +1 @@
+This qmldir file is intentionally invalid, as the URL interception should prevent it being accessed.
diff --git a/tests/auto/qml/qqmlengine/data/interception/module/intercepted/Intercepted.qml b/tests/auto/qml/qqmlengine/data/interception/module/intercepted/Intercepted.qml
new file mode 100644 (file)
index 0000000..5416ef5
--- /dev/null
@@ -0,0 +1,9 @@
+import QtQml 2.0
+
+QtObject {
+    property url filePath: "doesNotExist.file"
+    property url resolvedUrl: Qt.resolvedUrl("doesNotExist.file");
+    property url absoluteUrl: Qt.resolvedUrl("file:///doesNotExist.file");
+    property string childString: "intercepted"
+    property string scriptString: "intercepted"
+}
diff --git a/tests/auto/qml/qqmlengine/data/interception/module/intercepted/comment b/tests/auto/qml/qqmlengine/data/interception/module/intercepted/comment
new file mode 100644 (file)
index 0000000..b10233e
--- /dev/null
@@ -0,0 +1 @@
+Note that the paths are relative the the qmldir file in the actual module path, not the intercepted qmldir file
diff --git a/tests/auto/qml/qqmlengine/data/interception/module/intercepted/qmldir b/tests/auto/qml/qqmlengine/data/interception/module/intercepted/qmldir
new file mode 100644 (file)
index 0000000..c5457b2
--- /dev/null
@@ -0,0 +1 @@
+TestObject 2.0 ../../module/intercepted/Intercepted.qml
diff --git a/tests/auto/qml/qqmlengine/data/interception/module/urlInterceptor.qml b/tests/auto/qml/qqmlengine/data/interception/module/urlInterceptor.qml
new file mode 100644 (file)
index 0000000..b79a783
--- /dev/null
@@ -0,0 +1,3 @@
+import Test 2.0 //This import will be intercepted to make this file valid
+
+TestObject {}
index 42e17d5..004514d 100644 (file)
@@ -680,6 +680,7 @@ void tst_qqmlengine::qtqmlModule()
 class CustomSelector : public QQmlAbstractUrlInterceptor
 {
 public:
+    CustomSelector(const QUrl &base):m_base(base){}
     virtual QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType d)
     {
         if (url.scheme() != QStringLiteral("file"))
@@ -687,6 +688,9 @@ public:
         if (!m_interceptionPoints.contains(d))
             return url;
 
+        if (url.path().endsWith("Test.2/qmldir"))//Special case
+            return QUrl::fromLocalFile(m_base.path() + "interception/module/intercepted/qmldir");
+
         QString alteredPath = url.path();
         int a = alteredPath.lastIndexOf('/');
         if (a < 0)
@@ -698,6 +702,7 @@ public:
         return ret;
     }
     QList<QQmlAbstractUrlInterceptor::DataType> m_interceptionPoints;
+    QUrl m_base;
 };
 
 Q_DECLARE_METATYPE(QList<QQmlAbstractUrlInterceptor::DataType>);
@@ -729,6 +734,15 @@ void tst_qqmlengine::urlInterceptor_data()
         << testFileUrl("interception/qmldir/intercepted/doesNotExist.file").toString()
         << QStringLiteral("file:///intercepted/doesNotExist.file");
 
+    QTest::newRow("InterceptModule")//just a Test{}, needs to intercept the module import for it to work
+        << testFileUrl("interception/module/urlInterceptor.qml")
+        << (QList<QQmlAbstractUrlInterceptor::DataType>() << QQmlAbstractUrlInterceptor::QmldirFile )
+        << testFileUrl("interception/module/intercepted/doesNotExist.file").toString()
+        << QStringLiteral("intercepted")
+        << QStringLiteral("intercepted")
+        << testFileUrl("interception/module/intercepted/doesNotExist.file").toString()
+        << QStringLiteral("file:///doesNotExist.file");
+
     QTest::newRow("InterceptStrings")
         << testFileUrl("interception/strings/urlInterceptor.qml")
         << (QList<QQmlAbstractUrlInterceptor::DataType>() << QQmlAbstractUrlInterceptor::UrlString)
@@ -751,7 +765,8 @@ void tst_qqmlengine::urlInterceptor()
     QFETCH(QString, expectedAbsoluteUrl);
 
     QQmlEngine e;
-    CustomSelector cs;
+    e.setImportPathList(QStringList() << testFileUrl("interception/imports").toLocalFile());
+    CustomSelector cs(testFileUrl(""));
     cs.m_interceptionPoints = interceptionPoint;
     e.setUrlInterceptor(&cs);
     QQmlComponent c(&e, testFile); //Note that this can get intercepted too