Decode directory separators in source URLs
authorMatthew Vogt <matthew.vogt@nokia.com>
Tue, 19 Jun 2012 00:24:01 +0000 (10:24 +1000)
committerQt by Nokia <qt-info@nokia.com>
Sun, 24 Jun 2012 23:50:37 +0000 (01:50 +0200)
URLs with encoded directory-separator characters are not correctly
processed by QUrl.

Task-number: QTBUG-25981
Change-Id: I78173ef44c4850774b56753335bea34db04c0735
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
src/qml/doc/src/typesystem/basictypes.qdoc
src/qml/qml/qqmlcompiler.cpp
src/qml/qml/qqmlproperty.cpp
src/qml/qml/v4/qv4bindings.cpp
tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
tests/auto/quick/qquickloader/data/subdir/Test.qml [new file with mode: 0644]
tests/auto/quick/qquickloader/tst_qquickloader.cpp

index 3c1b849..c24787b 100644 (file)
@@ -167,6 +167,21 @@ See \l {QML Basic Types} for the list of basic types that are supported by the Q
     Usually you will only have to do this once, because relative URLs resolved from
     that file will use the same protocol.
 
+    URLs may contain encoded characters using the 'percent-encoding' scheme
+    specified by \l {http://tools.ietf.org/html/rfc3986}{RFC 3986}.  These characters
+    will be preserved within properties of type \c url, to allow QML code to
+    construct precise URL values. An exception to this rule is the preemptive
+    decoding of directory-separator characters (\c '/') - these characters are decoded
+    to allow the URL to be correctly classified.
+
+    For example, a local file containing a '#' character, which would normally be
+    interpreted as the beginning of the URL 'fragment' element, can be accessed by
+    encoding the characters of the file name:
+
+    \qml
+    Image { source: encodeURIComponent("/tmp/test#1.png") }
+    \endqml
+
     \sa {QML Basic Types}
 */
 
index 2bb99dd..13ff9e8 100644 (file)
@@ -515,6 +515,8 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop,
             {
             Instruction::StoreUrl instr;
             QString string = v->value.asString();
+            // Encoded dir-separators defeat QUrl processing - decode them first
+            string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
             QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string));
             instr.propertyIndex = prop->index;
             instr.value = output->indexForUrl(u);
index 33f860f..d0c2761 100644 (file)
@@ -1319,10 +1319,16 @@ bool QQmlPropertyPrivate::write(QObject *object,
             u = value.toUrl();
             found = true;
         } else if (variantType == QVariant::ByteArray) {
-            u = QUrl(QString::fromUtf8(value.toByteArray()));
+            QString input(QString::fromUtf8(value.toByteArray()));
+            // Encoded dir-separators defeat QUrl processing - decode them first
+            input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
+            u = QUrl(input);
             found = true;
         } else if (variantType == QVariant::String) {
-            u = QUrl(value.toString());
+            QString input(value.toString());
+            // Encoded dir-separators defeat QUrl processing - decode them first
+            input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
+            u = QUrl(input);
             found = true;
         }
 
index ff16f3b..7d42b47 100644 (file)
@@ -1296,7 +1296,9 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks,
         if (src.isUndefined()) {
             output.setUndefined();
         } else {
-            const QString tmp(*src.getstringptr());
+            QString tmp(*src.getstringptr());
+            // Encoded dir-separators defeat QUrl processing - decode them first
+            tmp.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
             if (instr->unaryop.src == instr->unaryop.output) {
                 output.cleanupString();
                 MARK_CLEAN_REGISTER(instr->unaryop.output);
@@ -1326,7 +1328,7 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks,
             COLOR_REGISTER(instr->unaryop.output);
         }
     }
-    QML_V4_END_INSTR(ConvertStringToUrl, unaryop)
+    QML_V4_END_INSTR(ConvertStringToColor, unaryop)
 
     QML_V4_BEGIN_INSTR(ConvertStringToVariant, unaryop)
     {
index 248cc0d..658874f 100644 (file)
@@ -1557,11 +1557,12 @@ void tst_qqmlproperty::urlHandling_data()
         << QString("/main.qml")
         << QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now%20working?");
 
+    // Although 'text%2Fqml' is pre-encoded, it will be decoded to allow correct QUrl classification
     QTest::newRow("preencodedQuery")
         << QByteArray("http://www.example.com/main.qml?type=text%2Fqml&comment=now working%3F")
         << QString("http")
         << QString("/main.qml")
-        << QByteArray("http://www.example.com/main.qml?type=text%2Fqml&comment=now%20working%3F");
+        << QByteArray("http://www.example.com/main.qml?type=text/qml&comment=now%20working%3F");
 
     QTest::newRow("encodableFragment")
         << QByteArray("http://www.example.com/main.qml?type=text/qml#start+30000|volume+50%")
diff --git a/tests/auto/quick/qquickloader/data/subdir/Test.qml b/tests/auto/quick/qquickloader/data/subdir/Test.qml
new file mode 100644 (file)
index 0000000..3abbd89
--- /dev/null
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+    id: test
+}
index dfe02c6..dc21af8 100644 (file)
@@ -201,6 +201,10 @@ void tst_QQuickLoader::sourceOrComponent_data()
     QTest::addColumn<QString>("errorString");
 
     QTest::newRow("source") << "source" << "source: 'Rect120x60.qml'\n" << testFileUrl("Rect120x60.qml") << "";
+    QTest::newRow("source with subdir") << "source" << "source: 'subdir/Test.qml'\n" << testFileUrl("subdir/Test.qml") << "";
+    QTest::newRow("source with encoded subdir literal") << "source" << "source: 'subdir%2fTest.qml'\n" << testFileUrl("subdir/Test.qml") << "";
+    QTest::newRow("source with encoded subdir optimized binding") << "source" << "source: 'subdir' + '%2fTest.qml'\n" << testFileUrl("subdir/Test.qml") << "";
+    QTest::newRow("source with encoded subdir binding") << "source" << "source: encodeURIComponent('subdir/Test.qml')\n" << testFileUrl("subdir/Test.qml") << "";
     QTest::newRow("sourceComponent") << "component" << "Component { id: comp; Rectangle { width: 100; height: 50 } }\n sourceComponent: comp\n" << QUrl() << "";
     QTest::newRow("invalid source") << "source" << "source: 'IDontExist.qml'\n" << testFileUrl("IDontExist.qml")
             << QString(testFileUrl("IDontExist.qml").toString() + ": File not found");