diff options
| -rw-r--r-- | src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc | 27 | ||||
| -rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 6 | ||||
| -rw-r--r-- | tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp | 25 | ||||
| -rw-r--r-- | tests/auto/quick/qquickloader/tst_qquickloader.cpp | 12 |
4 files changed, 60 insertions, 10 deletions
diff --git a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc index f26a4d9870..9ff4065ea1 100644 --- a/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc @@ -316,22 +316,35 @@ property is only invoked when the property is reassigned to a different object v The individual elements that use URLs have to resolve them themselves. \note When referring to files stored with the \l{resources.html}{Qt Resource System} - from within QML, you should use "qrc:///" instead of ":/" as QML requires URL paths. - Relative URLs resolved from within that file will use the same protocol. + from within QML, you should use "qrc:///" instead of ":/" as QML requires + URLs rather than paths. Relative URLs resolved from within that file will + use the same scheme. - Additionally, URLs may contain encoded characters using the 'percent-encoding' scheme - specified by \l {https://datatracker.ietf.org/doc/html/rfc3986}{RFC 3986}. These characters - will be preserved within properties of type \c url, to allow QML code to - construct precise URL values. + \note Additionally, URLs may contain encoded characters using the + 'percent-encoding' scheme specified by + \l {https://datatracker.ietf.org/doc/html/rfc3986}{RFC 3986}. These + characters will be preserved within properties of type \c url, to allow QML + code to construct precise URL values. 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") } + Image { source: "file:///tmp/" + encodeURIComponent("test#1.png") } \endqml + However, this also holds for slashes, backslashes, and \c nul characters. + If you do \c{encodeURIComponent("/tmp/test#1.png")} you get + \c{%2Ftmp%2Ftest%231.png}. This is generally not what you want and in + particular it is \e not a local file \e path. The encoded slashes are now + part of the file \e name and resolving this URL in the context of, say, + \c{qrc:/qt/qml/MyModule/Main.qml} results in the following URL: + \c{qrc:/qt/qml/MyModule/%2Ftmp%2Ftest%231.png}. + + If you need more control over the construction of URLs, you can use a JavaScript + \l{https://developer.mozilla.org/en-US/docs/Web/API/URL}{URL} object. + This value type is provided by the QML language. \sa {QML Value Types} diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 3596cc8f7d..fd42e3c5cb 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -303,6 +303,12 @@ void QQmlTypeLoader::loadThread(const QQmlDataBlob::Ptr &blob) if (QQmlFile::isSynchronous(blob->m_url)) { const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url); + if (fileName.isEmpty()) { + blob->setError(QLatin1String("URL cannot be represented as a local file: " + "unexpected NUL or percent-encoded directory separator " + "(%2F, or %5C on Windows).")); + return; + } const bool exists = fileExists(fileName); if (!exists && !isResource(fileName) && QFileInfo::exists(fileName)) { diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 1631088d0f..e4799a1e38 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -68,6 +68,8 @@ private slots: void removeFromCache(); void addImportPathDuringAsyncLoad(); void addImportPathFromStatusChanged(); + void urlNotRepresentableAsLocalFile_data(); + void urlNotRepresentableAsLocalFile(); private: void checkSingleton(const QString & dataDirectory); @@ -1120,6 +1122,29 @@ void tst_QQMLTypeLoader::addImportPathFromStatusChanged() QVERIFY(engine.importPathList().contains(extraPath)); } +void tst_QQMLTypeLoader::urlNotRepresentableAsLocalFile_data() +{ + QTest::addColumn<QUrl>("url"); + + QTest::newRow("encoded slash") << dataDirectoryUrl().resolved(QUrl("subdir%2FSimple.qml")); + QTest::newRow("encoded NUL at end") << dataDirectoryUrl().resolved(QUrl("Simple.qml%00")); + QTest::newRow("encoded NUL within") << dataDirectoryUrl().resolved(QUrl("Embed%00ded.qml")); +} + +void tst_QQMLTypeLoader::urlNotRepresentableAsLocalFile() +{ + QFETCH(QUrl, url); + + QQmlEngine engine; + QQmlComponent component(&engine, url); + QVERIFY(component.isError()); + QCOMPARE(component.errors().size(), 1); + QCOMPARE(component.errors().first().description(), + QLatin1String("URL cannot be represented as a local file: " + "unexpected NUL or percent-encoded directory separator " + "(%2F, or %5C on Windows).")); +} + QTEST_MAIN(tst_QQMLTypeLoader) #include "tst_qqmltypeloader.moc" diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index 73c6838a86..146ebbfdf5 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -208,9 +208,15 @@ void tst_QQuickLoader::sourceOrComponent_data() QTest::newRow("source") << "source" << "source: 'Rect120x60.qml'\n" << QUrl("Rect120x60.qml") << ""; QTest::newRow("source with subdir") << "source" << "source: 'subdir/Test.qml'\n" << QUrl("subdir/Test.qml") << ""; - QTest::newRow("source with encoded subdir literal") << "source" << "source: 'subdir%2fTest.qml'\n" << QUrl("subdir%2FTest.qml") << ""; - QTest::newRow("source with encoded subdir optimized binding") << "source" << "source: 'subdir' + '%2fTest.qml'\n" << QUrl("subdir%2FTest.qml") << ""; - QTest::newRow("source with encoded subdir binding") << "source" << "source: encodeURIComponent('subdir/Test.qml')\n" << QUrl("subdir%2FTest.qml") << ""; + + // An encoded slash is not a directory separator and can't be part of a local file name. + const QString encodedSubdirError = dataDirectoryUrl().resolved(QUrl("subdir%2FTest.qml")).toString() + + QLatin1String(": URL cannot be represented as a local file: " + "unexpected NUL or percent-encoded directory separator " + "(%2F, or %5C on Windows)."); + QTest::newRow("source with encoded subdir literal") << "source" << "source: 'subdir%2fTest.qml'\n" << QUrl("subdir%2FTest.qml") << encodedSubdirError; + QTest::newRow("source with encoded subdir optimized binding") << "source" << "source: 'subdir' + '%2fTest.qml'\n" << QUrl("subdir%2FTest.qml") << encodedSubdirError; + QTest::newRow("source with encoded subdir binding") << "source" << "source: encodeURIComponent('subdir/Test.qml')\n" << QUrl("subdir%2FTest.qml") << encodedSubdirError; 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" << QUrl("IDontExist.qml") << QString(testFileUrl("IDontExist.qml").toString() + ": No such file or directory"); |
