aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/valuetypes.qdoc27
-rw-r--r--src/qml/qml/qqmltypeloader.cpp6
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp25
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp12
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");