From 3c23f5371a19991771bd29c27d377c6672e46cd1 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 10 Jan 2020 10:01:36 +0100 Subject: On QQmlEngine destruction drop singletons before type loader The singleton destruction can trigger additional types to be loaded. Fixes: QTBUG-80840 Change-Id: Ifa406b2a1cfd3b9e9b36e8005dfc0808eebf15cf Reviewed-by: Fabian Kosmale --- .../qqmlengine/data/evilSingletonInstantiation.qml | 6 +++++ tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 31 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/auto/qml/qqmlengine/data/evilSingletonInstantiation.qml (limited to 'tests') diff --git a/tests/auto/qml/qqmlengine/data/evilSingletonInstantiation.qml b/tests/auto/qml/qqmlengine/data/evilSingletonInstantiation.qml new file mode 100644 index 0000000000..757b0c90bb --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/evilSingletonInstantiation.qml @@ -0,0 +1,6 @@ +import QtQml 2.12 +import foo.foo 1.0 + +QtObject { + objectName: Singleton.objectName +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 64f167b47e..613616ab71 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -83,6 +83,7 @@ private slots: void singletonInstance(); void aggressiveGc(); void cachedGetterLookup_qtbug_75335(); + void createComponentOnSingletonDestruction(); public slots: QObject *createAQObjectForOwnershipTest () @@ -1141,6 +1142,36 @@ void tst_qqmlengine::cachedGetterLookup_qtbug_75335() QVERIFY(object != nullptr); } +class EvilSingleton : public QObject +{ + Q_OBJECT +public: + QPointer m_engine; + EvilSingleton(QQmlEngine *engine) : m_engine(engine) { + connect(this, &QObject::destroyed, this, [this]() { + QQmlComponent component(m_engine); + component.setData("import QtQml 2.0\nQtObject {}", QUrl("file://Stuff.qml")); + QVERIFY(component.isReady()); + QScopedPointer obj(component.create()); + QVERIFY(obj); + }); + } +}; + +void tst_qqmlengine::createComponentOnSingletonDestruction() +{ + qmlRegisterSingletonType("foo.foo", 1, 0, "Singleton", + [](QQmlEngine *engine, QJSEngine *) { + return new EvilSingleton(engine); + }); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("evilSingletonInstantiation.qml")); + QVERIFY(component.isReady()); + QScopedPointer obj(component.create()); + QVERIFY(obj); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" -- cgit v1.2.3 From 08e0742699286b47d7ea9a1cbc49b9f91130135a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 10 Jan 2020 12:33:23 +0100 Subject: Blacklist touchAndDragHandlerOnFlickable() on OpenSuse 15.0 Task-number: QTBUG-81290 Change-Id: I58f64f5eabdb72f2f01bf72e988941521a89d331 Reviewed-by: Simon Hausmann Reviewed-by: Fabian Kosmale --- tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST index 20f989fc50..92903955ac 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST +++ b/tests/auto/quick/pointerhandlers/flickableinterop/BLACKLIST @@ -1,5 +1,6 @@ [touchAndDragHandlerOnFlickable] windows gcc +opensuse-leap [touchDragFlickableBehindSlider] windows gcc [touchDragFlickableBehindButton] -- cgit v1.2.3 From a07251df7c1e6158cb323e60cb08e687ead15b19 Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Wed, 8 Jan 2020 13:17:34 +0100 Subject: qqmlxmlhttprequest: Add ability to disable file:// requests Introduces two new flags: QML_XHR_ALLOW_FILE_READ: Controls whether GET can be used with file://. QML_XHR_ALLOW_FILE_WRITE: Controls whether PUT can be used with file://. In Qt 6 these will be off by default. At the moment having these unset while using either GET or PUT on file:// will just result in a warning. Change-Id: I2d85e88f1ddba8153ccbbd833ec7de5c1d0d8b5b Reviewed-by: Fabian Kosmale --- .../qml/qqmlxmlhttprequest/data/file_request.qml | 32 ++ .../qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp | 346 ++++++++++++++++----- 2 files changed, 299 insertions(+), 79 deletions(-) create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/file_request.qml (limited to 'tests') diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/file_request.qml b/tests/auto/qml/qqmlxmlhttprequest/data/file_request.qml new file mode 100644 index 0000000000..51020c185e --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/file_request.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 + +QtObject { + // Inputs + + id: root + + property string writeURL + property string readURL + // Outputs + property bool writeDone: false + property variant readResult + + Component.onCompleted: { + // PUT + var xhrWrite = new XMLHttpRequest; + xhrWrite.open("PUT", writeURL); + xhrWrite.onreadystatechange = function() { + if (xhrWrite.readyState === XMLHttpRequest.DONE) + writeDone = true; + }; + xhrWrite.send("Test-String"); + // GET + var xhrRead = new XMLHttpRequest; + xhrRead.open("GET", readURL); + xhrRead.onreadystatechange = function() { + if (xhrRead.readyState === XMLHttpRequest.DONE) + readResult = xhrRead.responseText; + }; + xhrRead.send(); + } +} diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 6cf80ccfdb..ae794e76a9 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -35,6 +35,13 @@ #include #include #include +#include + +#if QT_CONFIG(process) +#include +#include +#endif + #include "testhttpserver.h" #include "../../shared/util.h" @@ -45,6 +52,8 @@ public: tst_qqmlxmlhttprequest() {} private slots: + void initTestCase(); + void domExceptionCodes(); void callbackException(); void callbackException_data(); @@ -97,6 +106,14 @@ private slots: void nonUtf8(); void nonUtf8_data(); + void sendFileRequest(); + +#if QT_CONFIG(process) + void sendFileRequestNotSet(); + void sendFileRequestNoWrite(); + void sendFileRequestNoRead(); +#endif + // WebDAV void sendPropfind(); void sendPropfind_data(); @@ -119,13 +136,27 @@ private slots: void stateChangeCallingContext(); private: - QQmlEngine engine; + void doFileRequest(std::function verifyFunction); + + QScopedPointer engine; }; +void tst_qqmlxmlhttprequest::initTestCase() +{ + QQmlDataTest::initTestCase(); + + if (!qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + qputenv("QML_XHR_ALLOW_FILE_READ", "1"); + qputenv("QML_XHR_ALLOW_FILE_WRITE", "1"); + } + + engine.reset(new QQmlEngine); +} + // Test that the dom exception codes are correct void tst_qqmlxmlhttprequest::domExceptionCodes() { - QQmlComponent component(&engine, testFileUrl("domExceptionCodes.qml")); + QQmlComponent component(engine.get(), testFileUrl("domExceptionCodes.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -168,8 +199,8 @@ void tst_qqmlxmlhttprequest::callbackException() QString expect = testFileUrl("callbackException.qml").toString() + ":"+QString::number(line)+": Error: Exception from Callback"; QTest::ignoreMessage(QtWarningMsg, expect.toLatin1()); - QQmlComponent component(&engine, testFileUrl("callbackException.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("callbackException.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); object->setProperty("which", which); @@ -182,7 +213,7 @@ void tst_qqmlxmlhttprequest::callbackException() // ### WebKit does not do this, but it seems to fit the standard and QML better void tst_qqmlxmlhttprequest::staticStateValues() { - QQmlComponent component(&engine, testFileUrl("staticStateValues.qml")); + QQmlComponent component(engine.get(), testFileUrl("staticStateValues.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -196,7 +227,7 @@ void tst_qqmlxmlhttprequest::staticStateValues() // Test that the state value properties on instances have the correct values. void tst_qqmlxmlhttprequest::instanceStateValues() { - QQmlComponent component(&engine, testFileUrl("instanceStateValues.qml")); + QQmlComponent component(engine.get(), testFileUrl("instanceStateValues.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -210,7 +241,7 @@ void tst_qqmlxmlhttprequest::instanceStateValues() // Test calling constructor void tst_qqmlxmlhttprequest::constructor() { - QQmlComponent component(&engine, testFileUrl("constructor.qml")); + QQmlComponent component(engine.get(), testFileUrl("constructor.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -221,7 +252,7 @@ void tst_qqmlxmlhttprequest::constructor() // Test that all the properties are set correctly before any request is sent void tst_qqmlxmlhttprequest::defaultState() { - QQmlComponent component(&engine, testFileUrl("defaultState.qml")); + QQmlComponent component(engine.get(), testFileUrl("defaultState.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -248,8 +279,8 @@ void tst_qqmlxmlhttprequest::open() url = server.urlString(url); } - QQmlComponent component(&engine, qmlFile); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), qmlFile); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", url); component.completeCreate(); @@ -281,7 +312,7 @@ void tst_qqmlxmlhttprequest::open_data() // Test that calling XMLHttpRequest.open() with an invalid method raises an exception void tst_qqmlxmlhttprequest::open_invalid_method() { - QQmlComponent component(&engine, testFileUrl("open_invalid_method.qml")); + QQmlComponent component(engine.get(), testFileUrl("open_invalid_method.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -330,8 +361,8 @@ void tst_qqmlxmlhttprequest::open_sync() { TestThreadedHTTPServer server(testFileUrl("open_network.expect"), testFileUrl("open_network.reply"), testFileUrl("testdocument.html")); - QQmlComponent component(&engine, testFileUrl("open_sync.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("open_sync.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.serverBaseUrl.resolved(QStringLiteral("/testdocument.html")).toString()); component.completeCreate(); @@ -343,7 +374,7 @@ void tst_qqmlxmlhttprequest::open_sync() void tst_qqmlxmlhttprequest::open_arg_count() { { - QQmlComponent component(&engine, testFileUrl("open_arg_count.1.qml")); + QQmlComponent component(engine.get(), testFileUrl("open_arg_count.1.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -351,7 +382,7 @@ void tst_qqmlxmlhttprequest::open_arg_count() } { - QQmlComponent component(&engine, testFileUrl("open_arg_count.2.qml")); + QQmlComponent component(engine.get(), testFileUrl("open_arg_count.2.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -368,8 +399,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader() testFileUrl("setRequestHeader.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -386,8 +417,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_caseInsensitive() testFileUrl("setRequestHeader.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader_caseInsensitive.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_caseInsensitive.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -397,7 +428,7 @@ void tst_qqmlxmlhttprequest::setRequestHeader_caseInsensitive() // Test setting headers before open() throws exception void tst_qqmlxmlhttprequest::setRequestHeader_unsent() { - QQmlComponent component(&engine, testFileUrl("setRequestHeader_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_unsent.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -443,8 +474,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_illegalName() testFileUrl("open_network.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader_illegalName.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_illegalName.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("header", name); @@ -469,8 +500,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_sent() testFileUrl("open_network.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("setRequestHeader_sent.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_sent.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -483,7 +514,7 @@ void tst_qqmlxmlhttprequest::setRequestHeader_sent() // Invalid arg count throws exception void tst_qqmlxmlhttprequest::setRequestHeader_args() { - QQmlComponent component(&engine, testFileUrl("setRequestHeader_args.qml")); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_args.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -493,7 +524,7 @@ void tst_qqmlxmlhttprequest::setRequestHeader_args() // Test that calling send() in UNSENT state throws an exception void tst_qqmlxmlhttprequest::send_unsent() { - QQmlComponent component(&engine, testFileUrl("send_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("send_unsent.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -503,7 +534,7 @@ void tst_qqmlxmlhttprequest::send_unsent() // Test attempting to resend a sent request throws an exception void tst_qqmlxmlhttprequest::send_alreadySent() { - QQmlComponent component(&engine, testFileUrl("send_alreadySent.qml")); + QQmlComponent component(engine.get(), testFileUrl("send_alreadySent.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -521,8 +552,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() testFileUrl("send_ignoreData.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("reqType", "GET"); object->setProperty("url", server.urlString("/testdocument.html")); @@ -538,8 +569,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() testFileUrl("send_ignoreData.reply"), QUrl())); - QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("reqType", "HEAD"); object->setProperty("url", server.urlString("/testdocument.html")); @@ -555,8 +586,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() testFileUrl("send_ignoreData.reply"), QUrl())); - QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("reqType", "DELETE"); object->setProperty("url", server.urlString("/testdocument.html")); @@ -578,8 +609,8 @@ void tst_qqmlxmlhttprequest::send_withdata() testFileUrl("send_data.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl(file_qml)); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(file_qml)); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); component.completeCreate(); @@ -615,8 +646,8 @@ void tst_qqmlxmlhttprequest::send_options() testFileUrl(file_reply), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl(file_qml)); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(file_qml)); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); QString url = server.baseUrl().toString(); if (url_suffix != "/") @@ -652,8 +683,8 @@ void tst_qqmlxmlhttprequest::send_patch() // the content of response file will be ignored due to 204 status code testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("send_patch.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_patch.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/qqmlxmlhttprequest.cpp")); component.completeCreate(); @@ -666,8 +697,8 @@ void tst_qqmlxmlhttprequest::send_patch() // Test abort() has no effect in unsent state void tst_qqmlxmlhttprequest::abort_unsent() { - QQmlComponent component(&engine, testFileUrl("abort_unsent.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort_unsent.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); component.completeCreate(); @@ -685,8 +716,8 @@ void tst_qqmlxmlhttprequest::abort_unsent() // Test abort() cancels an open (but unsent) request void tst_qqmlxmlhttprequest::abort_opened() { - QQmlComponent component(&engine, testFileUrl("abort_opened.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort_opened.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); component.completeCreate(); @@ -710,8 +741,8 @@ void tst_qqmlxmlhttprequest::abort() testFileUrl("abort.reply"), testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("abort.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); const QUrl url = server.url("/testdocument.html"); QUrl dummyUrl = url; @@ -767,7 +798,7 @@ void tst_qqmlxmlhttprequest::getResponseHeader() // Test getResponseHeader throws an exception in an invalid state void tst_qqmlxmlhttprequest::getResponseHeader_unsent() { - QQmlComponent component(&engine, testFileUrl("getResponseHeader_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getResponseHeader_unsent.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -777,7 +808,7 @@ void tst_qqmlxmlhttprequest::getResponseHeader_unsent() // Test getResponseHeader throws an exception in an invalid state void tst_qqmlxmlhttprequest::getResponseHeader_sent() { - QQmlComponent component(&engine, testFileUrl("getResponseHeader_sent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getResponseHeader_sent.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -787,7 +818,7 @@ void tst_qqmlxmlhttprequest::getResponseHeader_sent() // Invalid arg count throws exception void tst_qqmlxmlhttprequest::getResponseHeader_args() { - QQmlComponent component(&engine, testFileUrl("getResponseHeader_args.qml")); + QQmlComponent component(engine.get(), testFileUrl("getResponseHeader_args.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -827,7 +858,7 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders() // Test getAllResponseHeaders throws an exception in an invalid state void tst_qqmlxmlhttprequest::getAllResponseHeaders_unsent() { - QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_unsent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getAllResponseHeaders_unsent.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -837,7 +868,7 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders_unsent() // Test getAllResponseHeaders throws an exception in an invalid state void tst_qqmlxmlhttprequest::getAllResponseHeaders_sent() { - QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_sent.qml")); + QQmlComponent component(engine.get(), testFileUrl("getAllResponseHeaders_sent.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -847,7 +878,7 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders_sent() // Invalid arg count throws exception void tst_qqmlxmlhttprequest::getAllResponseHeaders_args() { - QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_args.qml")); + QQmlComponent component(engine.get(), testFileUrl("getAllResponseHeaders_args.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -862,8 +893,8 @@ void tst_qqmlxmlhttprequest::getBinaryData() testFileUrl("receive_binary_data.reply"), testFileUrl("qml_logo.png"))); - QQmlComponent component(&engine, testFileUrl("receiveBinaryData.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("receiveBinaryData.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/gml_logo.png")); component.completeCreate(); @@ -881,8 +912,8 @@ void tst_qqmlxmlhttprequest::getJsonData() testFileUrl("receive_binary_data.reply"), testFileUrl("json.data"))); - QQmlComponent component(&engine, testFileUrl("receiveJsonData.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("receiveJsonData.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/json.data")); component.completeCreate(); @@ -901,8 +932,8 @@ void tst_qqmlxmlhttprequest::status() replyUrl, testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("status.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("status.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("expectedStatus", status); @@ -943,8 +974,8 @@ void tst_qqmlxmlhttprequest::statusText() replyUrl, testFileUrl("testdocument.html"))); - QQmlComponent component(&engine, testFileUrl("statusText.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("statusText.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("expectedStatus", statusText); @@ -983,8 +1014,8 @@ void tst_qqmlxmlhttprequest::responseText() replyUrl, bodyUrl)); - QQmlComponent component(&engine, testFileUrl("responseText.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("responseText.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/testdocument.html")); object->setProperty("expectedText", responseText); @@ -1020,7 +1051,7 @@ void tst_qqmlxmlhttprequest::nonUtf8() QFETCH(QString, responseText); QFETCH(QString, xmlRootNodeValue); - QQmlComponent component(&engine, testFileUrl("utf16.qml")); + QQmlComponent component(engine.get(), testFileUrl("utf16.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1053,6 +1084,163 @@ void tst_qqmlxmlhttprequest::nonUtf8_data() QTest::newRow("responseXML") << "utf16.xml" << "\n\n" + uc + "\n\n" << QString('\n' + uc + '\n'); } +static const QString testString = QStringLiteral("Test-String"); + +void tst_qqmlxmlhttprequest::doFileRequest(std::function verifyFunction) +{ + // Create test files + QTemporaryFile writeFile; + QTemporaryFile readFile; + + writeFile.open(); + writeFile.close(); + + QVERIFY(readFile.open()); + readFile.write(testString.toUtf8()); + readFile.close(); + + // Avoid cached environment variables + QQmlEngine engine; + + QQmlComponent component(&engine, testFileUrl("file_request.qml")); + + const QVariantMap properties = { + {"writeURL", QUrl::fromLocalFile(writeFile.fileName()).toString()}, + {"readURL", QUrl::fromLocalFile(readFile.fileName()).toString()} + }; + + QScopedPointer object(component.createWithInitialProperties(properties, engine.rootContext())); + QVERIFY(!object.isNull()); + + verifyFunction(object.get(), writeFile); +} + +// Test file:// requests +void tst_qqmlxmlhttprequest::sendFileRequest() +{ + // Test with both writing and reading allowed + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + QTRY_COMPARE(object->property("readResult").toString(), testString); + + QTRY_VERIFY(object->property("writeDone").toBool()); + + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), testString); + writeFile.close(); + }); +} + +#if QT_CONFIG(process) +void tst_qqmlxmlhttprequest::sendFileRequestNotSet() { + if (qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + // Test with no settings + // Should just result in warnings in Qt 5 + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + QTRY_COMPARE(object->property("readResult").toString(), testString); + + QTRY_VERIFY(object->property("writeDone").toBool()); + + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), testString); + writeFile.close(); + }); + return; + } + + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(QLatin1String("sendFileRequestNotSet"))); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("TEST_CUSTOM_PERMISSIONS"), QLatin1String("1")); + env.remove("QML_XHR_ALLOW_FILE_WRITE"); + env.remove("QML_XHR_ALLOW_FILE_READ"); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + + // Check exit code + QCOMPARE(child.exitCode(), 0); + + // Check if all warnings were printed + QString output = QString::fromUtf8(child.readAllStandardOutput()); + + + const QString readingWarning = QLatin1String( + "XMLHttpRequest: Using GET on a local file is dangerous " + "and will be disabled by default in a future Qt version." + "Set QML_XHR_ALLOW_FILE_READ to 1 if you wish to continue using this feature."); + + const QString writingWarning = QLatin1String( + "XMLHttpRequest: Using PUT on a local file is dangerous " + "and will be disabled by default in a future Qt version." + "Set QML_XHR_ALLOW_FILE_WRITE to 1 if you wish to continue using this feature."); + + QVERIFY(output.contains(readingWarning)); + QVERIFY(output.contains(writingWarning)); +} +#endif + +#if QT_CONFIG(process) +void tst_qqmlxmlhttprequest::sendFileRequestNoWrite() { + if (qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + // Test with no writing enabled + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + QTRY_COMPARE(object->property("readResult").toString(), testString); + + // Check that the file stays empty + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), ""); + writeFile.close(); + }); + return; + } + + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(QLatin1String("sendFileRequestNoWrite"))); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("TEST_CUSTOM_PERMISSIONS"), QLatin1String("1")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_WRITE"), QLatin1String("0")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_READ"), QLatin1String("1")); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + QCOMPARE(child.exitCode(), 0); +} +#endif + +#if QT_CONFIG(process) +void tst_qqmlxmlhttprequest::sendFileRequestNoRead() { + if (qEnvironmentVariableIsSet("TEST_CUSTOM_PERMISSIONS")) { + // Test with no reading enabled + doFileRequest([](QObject* object, QTemporaryFile &writeFile) { + // Check that the write happens + QTRY_VERIFY(object->property("writeDone").toBool()); + + QVERIFY(writeFile.open()); + QCOMPARE(QString::fromUtf8(writeFile.readAll()), testString); + writeFile.close(); + + // Verify that the read has not yielded any value + QVERIFY(object->property("readResult").isNull()); + }); + return; + } + + QProcess child; + child.setProgram(QCoreApplication::applicationFilePath()); + child.setArguments(QStringList(QLatin1String("sendFileRequestNoRead"))); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("TEST_CUSTOM_PERMISSIONS"), QLatin1String("1")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_WRITE"), QLatin1String("1")); + env.insert(QLatin1String("QML_XHR_ALLOW_FILE_READ"), QLatin1String("0")); + child.setProcessEnvironment(env); + child.start(); + QVERIFY(child.waitForFinished()); + QCOMPARE(child.exitCode(), 0); +} +#endif + void tst_qqmlxmlhttprequest::sendPropfind() { const QString prefix = "WebDAV//"; @@ -1070,8 +1258,8 @@ void tst_qqmlxmlhttprequest::sendPropfind() testFileUrl(prefix + replyHeader), testFileUrl(prefix + replyBody))); - QQmlComponent component(&engine, testFileUrl(prefix + qml)); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(prefix + qml)); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString(resource)); component.completeCreate(); @@ -1097,7 +1285,7 @@ void tst_qqmlxmlhttprequest::sendPropfind_data() // throws an exception void tst_qqmlxmlhttprequest::invalidMethodUsage() { - QQmlComponent component(&engine, testFileUrl("invalidMethodUsage.qml")); + QQmlComponent component(engine.get(), testFileUrl("invalidMethodUsage.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1124,8 +1312,8 @@ void tst_qqmlxmlhttprequest::redirects() server.addRedirect("redirect.html", server.urlString("/redirecttarget.html")); server.serveDirectory(dataDirectory()); - QQmlComponent component(&engine, testFileUrl("redirects.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirects.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); object->setProperty("expectedText", ""); @@ -1141,8 +1329,8 @@ void tst_qqmlxmlhttprequest::redirects() server.addRedirect("redirect.html", server.urlString("/redirectmissing.html")); server.serveDirectory(dataDirectory()); - QQmlComponent component(&engine, testFileUrl("redirectError.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirectError.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); object->setProperty("expectedText", ""); @@ -1158,8 +1346,8 @@ void tst_qqmlxmlhttprequest::redirects() server.addRedirect("redirect.html", server.urlString("/redirect.html")); server.serveDirectory(dataDirectory()); - QQmlComponent component(&engine, testFileUrl("redirectRecur.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirectRecur.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("url", server.urlString("/redirect.html")); object->setProperty("expectedText", ""); @@ -1177,7 +1365,7 @@ void tst_qqmlxmlhttprequest::redirects() void tst_qqmlxmlhttprequest::responseXML_invalid() { - QQmlComponent component(&engine, testFileUrl("responseXML_invalid.qml")); + QQmlComponent component(engine.get(), testFileUrl("responseXML_invalid.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1189,7 +1377,7 @@ void tst_qqmlxmlhttprequest::responseXML_invalid() // Test the Document DOM element void tst_qqmlxmlhttprequest::document() { - QQmlComponent component(&engine, testFileUrl("document.qml")); + QQmlComponent component(engine.get(), testFileUrl("document.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1201,7 +1389,7 @@ void tst_qqmlxmlhttprequest::document() // Test the Element DOM element void tst_qqmlxmlhttprequest::element() { - QQmlComponent component(&engine, testFileUrl("element.qml")); + QQmlComponent component(engine.get(), testFileUrl("element.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1213,7 +1401,7 @@ void tst_qqmlxmlhttprequest::element() // Test the Attr DOM element void tst_qqmlxmlhttprequest::attr() { - QQmlComponent component(&engine, testFileUrl("attr.qml")); + QQmlComponent component(engine.get(), testFileUrl("attr.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1225,7 +1413,7 @@ void tst_qqmlxmlhttprequest::attr() // Test the Text DOM element void tst_qqmlxmlhttprequest::text() { - QQmlComponent component(&engine, testFileUrl("text.qml")); + QQmlComponent component(engine.get(), testFileUrl("text.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1238,7 +1426,7 @@ void tst_qqmlxmlhttprequest::text() // Test the CDataSection DOM element void tst_qqmlxmlhttprequest::cdata() { - QQmlComponent component(&engine, testFileUrl("cdata.qml")); + QQmlComponent component(engine.get(), testFileUrl("cdata.qml")); QScopedPointer object(component.create()); QVERIFY(!object.isNull()); @@ -1285,8 +1473,8 @@ void tst_qqmlxmlhttprequest::stateChangeCallingContext() QVERIFY2(server.listen(), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); - QQmlComponent component(&engine, testFileUrl("stateChangeCallingContext.qml")); - QScopedPointer object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("stateChangeCallingContext.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("serverBaseUrl", server.baseUrl().toString()); component.completeCreate(); -- cgit v1.2.3 From c5a8dc153b3d3218360184837b098ba09018d982 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 14 Jan 2020 17:40:50 +0100 Subject: Allow overriding type registrations Previously if the same revision of the same type was registered multiple times, the first registration would dominate. Now, the last one does. [ChangeLog] If you register the same revision of the same type multiple times, the last registration is the one QML will use. This allows you to override qmlRegisterSingleton*() calls, for example in order to register a singleton instance for a type tagged with QML_SINGLETON. Task-number: QTBUG-79331 Change-Id: I32a2acabc89af6faa89d8ccb6380a473bc8b8ddd Reviewed-by: Fabian Kosmale --- tests/auto/qml/qqmllanguage/testtypes.cpp | 1 + tests/auto/qml/qqmllanguage/testtypes.h | 13 +++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+) (limited to 'tests') diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 462745eb93..0ddb1b1491 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -120,6 +120,7 @@ void registerTypes() qmlRegisterType("Test", 1, 0, "DeferredProperties"); qmlRegisterTypesAndRevisions("Test", 1); + qmlRegisterTypesAndRevisions("Test", 1); } QVariant myCustomVariantTypeConverter(const QString &data) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index bfbd3e66f5..a7410e190b 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1455,6 +1455,19 @@ class ForeignExtended QML_EXTENDED(Extension) }; +class BareSingleton : public QObject +{ + Q_OBJECT + QML_SINGLETON + QML_ELEMENT + +public: + BareSingleton(QObject *parent = nullptr) : QObject(parent) + { + setObjectName("statically registered"); + } +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index fca398d7b2..7b4662a5cd 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -310,6 +310,7 @@ private slots: void selfReferencingSingleton(); void listContainingDeletedObject(); + void overrideSingleton(); private: QQmlEngine engine; @@ -5375,6 +5376,29 @@ void tst_qqmllanguage::listContainingDeletedObject() } +void tst_qqmllanguage::overrideSingleton() +{ + auto check = [](const QString &name) { + const QByteArray testQml = "import Test 1.0\n" + "import QtQml 2.0\n" + "QtObject { objectName: BareSingleton.objectName }"; + QQmlEngine engine; + QQmlComponent component(&engine, nullptr); + component.setData(testQml, QUrl()); + QVERIFY(component.isReady()); + QScopedPointer obj(component.create()); + QCOMPARE(obj->objectName(), name); + }; + + check("statically registered"); + + BareSingleton singleton; + singleton.setObjectName("dynamically registered"); + qmlRegisterSingletonInstance("Test", 1, 0, "BareSingleton", &singleton); + + check("dynamically registered"); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3