Remove relative directory elements in import paths
authorMatthew Vogt <matthew.vogt@nokia.com>
Fri, 20 Apr 2012 04:50:03 +0000 (14:50 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 26 Apr 2012 01:33:13 +0000 (03:33 +0200)
Avoid unnecessary conversions to/from QUrl.

Change-Id: If52e78cfdaf4fe344f34d961e300b21dd4a11fb2
Reviewed-by: Michael Brasser <michael.brasser@nokia.com>
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
src/qml/qml/qqmlimport.cpp
tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.4.qml [new file with mode: 0644]
tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.5.qml [new file with mode: 0644]
tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp

index 90228f1..a8a3ed0 100644 (file)
@@ -63,9 +63,6 @@ static bool greaterThan(const QString &s1, const QString &s2)
 
 QString resolveLocalUrl(const QString &url, const QString &relative)
 {
-    return QUrl(url).resolved(QUrl(relative)).toString();
-
-    //XXX Find out why this broke with new QUrl.
     if (relative.contains(QLatin1Char(':'))) {
         // contains a host name
         return QUrl(url).resolved(QUrl(relative)).toString();
@@ -74,11 +71,41 @@ QString resolveLocalUrl(const QString &url, const QString &relative)
     } else if (relative.at(0) == QLatin1Char('/') || !url.contains(QLatin1Char('/'))) {
         return relative;
     } else {
+        QString base(url.left(url.lastIndexOf(QLatin1Char('/')) + 1));
+
         if (relative == QLatin1String("."))
-            return url.left(url.lastIndexOf(QLatin1Char('/')) + 1);
-        else if (relative.startsWith(QLatin1String("./")))
-            return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative.mid(2);
-        return url.left(url.lastIndexOf(QLatin1Char('/')) + 1) + relative;
+            return base;
+
+        base.append(relative);
+
+        // Remove any relative directory elements in the path
+        const QLatin1Char dot('.');
+        const QLatin1Char slash('/');
+
+        int length = base.length();
+        int index = 0;
+        while ((index = base.indexOf(QLatin1String("/."), index)) != -1) {
+            if ((length > (index + 2)) && (base.at(index + 2) == dot) &&
+                (length == (index + 3) || (base.at(index + 3) == slash))) {
+                // Either "/../" or "/..<END>"
+                int previous = base.lastIndexOf(slash, index - 1);
+                if (previous == -1)
+                    break;
+
+                int removeLength = (index - previous) + 3;
+                base.remove(previous + 1, removeLength);
+                length -= removeLength;
+                index = previous;
+            } else if ((length == (index + 2)) || (base.at(index + 2) == slash)) {
+                // Either "/./" or "/.<END>"
+                base.remove(index, 2);
+                length -= 2;
+            } else {
+                ++index;
+            }
+        }
+
+        return base;
     }
 }
 
diff --git a/tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.4.qml b/tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.4.qml
new file mode 100644 (file)
index 0000000..cddaf64
--- /dev/null
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+import "SpecificComponent"
+import "./SpecificComponent"
+import "././SpecificComponent"
+import "./././SpecificComponent"
+import "SpecificComponent/."
+import "SpecificComponent/./"
+import "SpecificComponent/./."
+import "SpecificComponent/././"
+import "SpecificComponent/././."
+import "SpecificComponent/./././"
+import "../data/SpecificComponent"
+import "./../data/SpecificComponent"
+import ".././data/SpecificComponent"
+import "SpecificComponent/nonexistent/../."
+import "SpecificComponent/nonexistent/.././"
+import "SpecificComponent/nonexistent/./.."
+import "SpecificComponent/nonexistent/./../"
+import "SpecificComponent/nonexistent/nonexistent/../.."
+import "SpecificComponent/nonexistent/nonexistent/../../"
+import "SpecificComponent/nonexistent/nonexistent/nonexistent/../../.."
+import "SpecificComponent/nonexistent/nonexistent/nonexistent/../../../"
+import "SpecificComponent/.././SpecificComponent"
+import "SpecificComponent/./../SpecificComponent"
+
+Item {
+    property bool success : true
+}
diff --git a/tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.5.qml b/tests/auto/qml/qqmlcomponent/data/componentUrlCanonicalization.5.qml
new file mode 100644 (file)
index 0000000..66993ab
--- /dev/null
@@ -0,0 +1,4 @@
+import "../../../../../../Invalid"
+
+Item {
+}
index eebb558..b534d0c 100644 (file)
@@ -323,10 +323,9 @@ void tst_qqmlcomponent::componentUrlCanonicalization()
         // load components via import
         QQmlEngine engine;
         QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.qml"));
-        QObject *object = component.create();
+        QScopedPointer<QObject> object(component.create());
         QVERIFY(object != 0);
         QVERIFY(object->property("success").toBool());
-        delete object;
     }
 
     {
@@ -334,20 +333,36 @@ void tst_qqmlcomponent::componentUrlCanonicalization()
         // import of the other if it were not already loaded.
         QQmlEngine engine;
         QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.2.qml"));
-        QObject *object = component.create();
+        QScopedPointer<QObject> object(component.create());
         QVERIFY(object != 0);
         QVERIFY(object->property("success").toBool());
-        delete object;
     }
 
     {
         // load components with more deeply nested imports
         QQmlEngine engine;
         QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.3.qml"));
-        QObject *object = component.create();
+        QScopedPointer<QObject> object(component.create());
         QVERIFY(object != 0);
         QVERIFY(object->property("success").toBool());
-        delete object;
+    }
+
+    {
+        // load components with unusually specified import paths
+        QQmlEngine engine;
+        QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.4.qml"));
+        QScopedPointer<QObject> object(component.create());
+        QVERIFY(object != 0);
+        QVERIFY(object->property("success").toBool());
+    }
+
+    {
+        // Do not crash with various nonsense import paths
+        QQmlEngine engine;
+        QQmlComponent component(&engine, testFileUrl("componentUrlCanonicalization.5.qml"));
+        QTest::ignoreMessage(QtWarningMsg, QLatin1String("QQmlComponent: Component is not ready").data());
+        QScopedPointer<QObject> object(component.create());
+        QVERIFY(object == 0);
     }
 }