diff options
18 files changed, 578 insertions, 96 deletions
diff --git a/examples/qml/qmlextensionplugins/plugins.qmlproject b/examples/qml/qmlextensionplugins/plugins.qmlproject index 771ab45b66..c2de5f5703 100644 --- a/examples/qml/qmlextensionplugins/plugins.qmlproject +++ b/examples/qml/qmlextensionplugins/plugins.qmlproject @@ -13,4 +13,6 @@ Project { ImageFiles { directory: "." } + + importPaths: [ "imports" ] } diff --git a/examples/qml/qmlextensionplugins/qmlextensionplugins.pro b/examples/qml/qmlextensionplugins/qmlextensionplugins.pro index 40c2b396a3..c074b8d671 100644 --- a/examples/qml/qmlextensionplugins/qmlextensionplugins.pro +++ b/examples/qml/qmlextensionplugins/qmlextensionplugins.pro @@ -7,6 +7,7 @@ QML_IMPORT_MAJOR_VERSION = 1 DESTDIR = imports/$$QML_IMPORT_NAME TARGET = qmlqtimeexampleplugin +QMLTYPES_FILENAME = $$DESTDIR/plugins.qmltypes SOURCES += \ plugin.cpp \ @@ -23,14 +24,22 @@ PLUGINFILES = \ imports/$$QML_IMPORT_NAME/hour.png \ imports/$$QML_IMPORT_NAME/minute.png -pluginfiles.files += $$PLUGINFILES +target.path = $$[QT_INSTALL_EXAMPLES]/qml/qmlextensionplugins/imports/$$QML_IMPORT_NAME -qml.files = plugins.qml -qml.path += $$[QT_INSTALL_EXAMPLES]/qml/qmlextensionplugins -target.path += $$[QT_INSTALL_EXAMPLES]/qml/qmlextensionplugins/imports/$$QML_IMPORT_NAME -pluginfiles.path += $$[QT_INSTALL_EXAMPLES]/qml/qmlextensionplugins/imports/$$QML_IMPORT_NAME +pluginfiles_copy.files = $$PLUGINFILES +pluginfiles_copy.path = $$DESTDIR -INSTALLS += target qml pluginfiles +pluginfiles_install.files = $$PLUGINFILES $$OUT_PWD/$$DESTDIR/plugins.qmltypes +pluginfiles_install.path = $$[QT_INSTALL_EXAMPLES]/qml/qmlextensionplugins/imports/$$QML_IMPORT_NAME + +qml_copy.files = plugins.qml plugins.qmlproject +qml_copy.path = $$OUT_PWD + +qml_install.files = plugins.qml plugins.qmlproject +qml_install.path = $$[QT_INSTALL_EXAMPLES]/qml/qmlextensionplugins + +INSTALLS += target qml_install pluginfiles_install +COPIES += qml_copy pluginfiles_copy OTHER_FILES += $$PLUGINFILES plugins.qml diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 82436bcd5d..ee42342bf2 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -304,8 +304,10 @@ public: return false; } - if (d()->isReadOnly) + if (d()->isReadOnly) { + engine()->throwTypeError(QLatin1String("Cannot insert into a readonly container")); return false; + } if (d()->isReference) { if (!d()->object) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 84f56eb051..6cdb012e26 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -985,8 +985,6 @@ QQmlEngine::~QQmlEngine() Q_D(QQmlEngine); QJSEnginePrivate::removeFromDebugServer(this); - d->typeLoader.invalidate(); - // Emit onDestruction signals for the root context before // we destroy the contexts, engine, Singleton Types etc. that // may be required to handle the destruction signal. @@ -1002,6 +1000,8 @@ QQmlEngine::~QQmlEngine() delete d->rootContext; d->rootContext = nullptr; + + d->typeLoader.invalidate(); } /*! \fn void QQmlEngine::quit() diff --git a/src/qml/qml/qqmltypemodule.cpp b/src/qml/qml/qqmltypemodule.cpp index 9c9bf3e48f..9d6f269030 100644 --- a/src/qml/qml/qqmltypemodule.cpp +++ b/src/qml/qml/qqmltypemodule.cpp @@ -97,10 +97,14 @@ void QQmlTypeModule::add(QQmlTypePrivate *type) QList<QQmlTypePrivate *> &list = d->typeHash[type->elementName]; for (int ii = 0; ii < list.count(); ++ii) { - Q_ASSERT(list.at(ii)); - if (list.at(ii)->version_min < type->version_min) { + QQmlTypePrivate *in_list = list.at(ii); + Q_ASSERT(in_list); + if (in_list->version_min < type->version_min) { list.insert(ii, type); return; + } else if (in_list->version_min == type->version_min) { + list[ii] = type; + return; } } list.append(type); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index c2e7be73e7..2afbdb616b 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -54,6 +54,7 @@ #include <QtCore/qobject.h> #include <QtQml/qjsvalue.h> #include <QtQml/qjsengine.h> +#include <QtQml/qqmlfile.h> #include <QtNetwork/qnetworkreply.h> #include <QtCore/qtextcodec.h> #include <QtCore/qxmlstream.h> @@ -77,6 +78,8 @@ using namespace QV4; QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP); +DEFINE_BOOL_CONFIG_OPTION(xhrFileWrite, QML_XHR_ALLOW_FILE_WRITE); +DEFINE_BOOL_CONFIG_OPTION(xhrFileRead, QML_XHR_ALLOW_FILE_READ); struct QQmlXMLHttpRequestData { QQmlXMLHttpRequestData(); @@ -1195,6 +1198,37 @@ void QQmlXMLHttpRequest::fillHeadersList() void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) { QNetworkRequest request = m_request; + + if (QQmlFile::isLocalFile(url)) { + if (m_method == QLatin1String("PUT")) + { + if (!xhrFileWrite()) { + if (qEnvironmentVariableIsSet("QML_XHR_ALLOW_FILE_WRITE")) { + qWarning("XMLHttpRequest: Tried to use PUT on a local file despite being disabled."); + return; + } else { + qWarning("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."); + } + } + } else if (m_method == QLatin1String("GET")) { + if (!xhrFileRead()) { + if (qEnvironmentVariableIsSet("QML_XHR_ALLOW_FILE_READ")) { + qWarning("XMLHttpRequest: Tried to use GET on a local file despite being disabled."); + return; + } else { + qWarning("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."); + } + } + } else { + qWarning("XMLHttpRequest: Unsupported method used on a local file"); + return; + } + } + request.setUrl(url); if(m_method == QLatin1String("POST") || m_method == QLatin1String("PUT")) { @@ -1389,7 +1423,7 @@ void QQmlXMLHttpRequest::finished() QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute); if (redirect.isValid()) { QUrl url = m_network->url().resolved(redirect.toUrl()); - if (url.scheme() != QLatin1String("file")) { + if (!QQmlFile::isLocalFile(url)) { // See http://www.ietf.org/rfc/rfc2616.txt, section 10.3.4 "303 See Other": // Result of 303 redirection should be a new "GET" request. const QVariant code = m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute); diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h index 480f69035b..c0751aa5c5 100644 --- a/src/quick/handlers/qquickmultipointhandler_p.h +++ b/src/quick/handlers/qquickmultipointhandler_p.h @@ -81,7 +81,6 @@ public: signals: void minimumPointCountChanged(); void maximumPointCountChanged(); - void marginChanged(); void centroidChanged(); protected: diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp index aef2e8ebfb..16f38af962 100644 --- a/src/quick/handlers/qquickwheelhandler.cpp +++ b/src/quick/handlers/qquickwheelhandler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -500,6 +500,13 @@ void QQuickWheelHandler::timerEvent(QTimerEvent *event) } } +/*! + \qmlsignal QtQuick::WheelHandler::wheel(PointerScrollEvent event) + + This signal is emitted every time this handler receives a \l QWheelEvent: + that is, every time the wheel is moved or the scrolling gesture is updated. +*/ + QQuickWheelHandlerPrivate::QQuickWheelHandlerPrivate() : QQuickSinglePointHandlerPrivate() { diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 89340dd992..03280e4c1f 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -448,7 +448,7 @@ Item { */ /*! - \qmlproperty int QtQuick::WheelEvent::inverted + \qmlproperty bool QtQuick::WheelEvent::inverted Returns whether the delta values delivered with the event are inverted. @@ -1431,6 +1431,135 @@ QQuickEventPoint *QQuickSinglePointEvent::point(int i) const return nullptr; } + +/*! + \qmltype PointerScrollEvent + \instantiates QQuickPointerScrollEvent + \inqmlmodule QtQuick + \ingroup qtquick-input-events + \brief Provides information about a scrolling event, such as from a mouse wheel. + + \sa WheelHandler +*/ + +/*! + \internal + \class QQuickPointerScrollEvent +*/ + +/*! + \readonly + \qmlproperty PointerDevice QtQuick::PointerScrollEvent::device + + This property holds the device that generated the event. +*/ + +/*! + \qmlproperty int QtQuick::PointerScrollEvent::buttons + + This property holds the mouse buttons pressed when the wheel event was generated. + + It contains a bitwise combination of: + \list + \li \l {Qt::LeftButton} {Qt.LeftButton} + \li \l {Qt::RightButton} {Qt.RightButton} + \li \l {Qt::MiddleButton} {Qt.MiddleButton} + \endlist +*/ + +/*! + \readonly + \qmlproperty int QtQuick::PointerScrollEvent::modifiers + + This property holds the \l {Qt::KeyboardModifier}{keyboard modifier} keys + that were pressed immediately before the event occurred. + + It contains a bitwise combination of the following flags: + \value Qt.NoModifier + No modifier key is pressed. + \value Qt.ShiftModifier + A Shift key on the keyboard is pressed. + \value Qt.ControlModifier + A Ctrl key on the keyboard is pressed. + \value Qt.AltModifier + An Alt key on the keyboard is pressed. + \value Qt.MetaModifier + A Meta key on the keyboard is pressed. + \value Qt.KeypadModifier + A keypad button is pressed. + + For example, to react to a Shift key + Left mouse button click: + \qml + Item { + TapHandler { + onTapped: { + if ((event.button == Qt.LeftButton) && (event.modifiers & Qt.ShiftModifier)) + doSomething(); + } + } + } + \endqml +*/ + +/*! + \qmlproperty point QtQuick::PointerScrollEvent::angleDelta + + This property holds the distance that the wheel is rotated in wheel degrees. + The x and y cordinate of this property holds the delta in horizontal and + vertical orientation. + + A positive value indicates that the wheel was rotated up/right; + a negative value indicates that the wheel was rotated down/left. + + Most mouse types work in steps of 15 degrees, in which case the delta value is a + multiple of 120; i.e., 120 units * 1/8 = 15 degrees. +*/ + +/*! + \qmlproperty point QtQuick::PointerScrollEvent::pixelDelta + + This property holds the delta in screen pixels and is available in platforms that + have high-resolution trackpads, such as \macos. + The x and y coordinates of this property hold the delta in horizontal and + vertical orientation. The value should be used directly to scroll content on screen. + + For platforms without high-resolution touchpad support, pixelDelta will + always be (0,0), and angleDelta should be used instead. +*/ + +/*! + \qmlproperty bool QtQuick::PointerScrollEvent::hasAngleDelta + + Returns whether the \l angleDelta property has a non-null value. +*/ + +/*! + \qmlproperty bool QtQuick::PointerScrollEvent::hasPixelDelta + + Returns whether the \l pixelDelta property has a non-null value. +*/ + +/*! + \qmlproperty bool QtQuick::PointerScrollEvent::inverted + + Returns whether the delta values delivered with the event are inverted. + + Normally, a vertical wheel will produce a PointerScrollEvent with positive delta + values if the top of the wheel is rotating away from the hand operating it. + Similarly, a horizontal wheel movement will produce a PointerScrollEvent with + positive delta values if the top of the wheel is moved to the left. + + However, on some platforms this is configurable, so that the same + operations described above will produce negative delta values (but with the + same magnitude). In a QML component (such as a tumbler or a slider) where + it is appropriate to synchronize the movement or rotation of an item with + the direction of the wheel, regardless of the system settings, the wheel + event handler can use the inverted property to decide whether to negate the + \l angleDelta or \l pixelDelta values. + + \note Many platforms provide no such information. On such platforms, + \c inverted always returns false. +*/ QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event) { m_event = static_cast<QInputEvent*>(event); diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index f9b0a61b81..bd6b9d741e 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. 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 2a3b945509..d782df3e7f 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 () @@ -1144,6 +1145,36 @@ void tst_qqmlengine::cachedGetterLookup_qtbug_75335() QVERIFY(object != nullptr); } +class EvilSingleton : public QObject +{ + Q_OBJECT +public: + QPointer<QQmlEngine> 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<QObject> obj(component.create()); + QVERIFY(obj); + }); + } +}; + +void tst_qqmlengine::createComponentOnSingletonDestruction() +{ + qmlRegisterSingletonType<EvilSingleton>("foo.foo", 1, 0, "Singleton", + [](QQmlEngine *engine, QJSEngine *) { + return new EvilSingleton(engine); + }); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("evilSingletonInstantiation.qml")); + QVERIFY(component.isReady()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" 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<DeferredProperties>("Test", 1, 0, "DeferredProperties"); qmlRegisterTypesAndRevisions<Extended, Foreign, ForeignExtended>("Test", 1); + qmlRegisterTypesAndRevisions<BareSingleton>("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<QObject> 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" 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 <QThread> #include <QMutex> #include <QWaitCondition> +#include <QTemporaryFile> + +#if QT_CONFIG(process) +#include <QProcess> +#include <QProcessEnvironment> +#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<void(QObject *component, QTemporaryFile &writeFile)> verifyFunction); + + QScopedPointer<QQmlEngine> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("callbackException.qml")); + QScopedPointer<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -248,8 +279,8 @@ void tst_qqmlxmlhttprequest::open() url = server.urlString(url); } - QQmlComponent component(&engine, qmlFile); - QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), qmlFile); + QScopedPointer<QObject> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("open_sync.qml")); + QScopedPointer<QObject> 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<QObject> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_caseInsensitive.qml")); + QScopedPointer<QObject> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_illegalName.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("setRequestHeader_sent.qml")); + QScopedPointer<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_ignoreData.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(file_qml)); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(file_qml)); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("send_patch.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort_unsent.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort_opened.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("abort.qml")); + QScopedPointer<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("receiveBinaryData.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("receiveJsonData.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("status.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("statusText.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("responseText.qml")); + QScopedPointer<QObject> 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<QObject> object(component.create()); QVERIFY(!object.isNull()); @@ -1053,6 +1084,163 @@ void tst_qqmlxmlhttprequest::nonUtf8_data() QTest::newRow("responseXML") << "utf16.xml" << "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone='yes'?>\n<root>\n" + uc + "\n</root>\n" << QString('\n' + uc + '\n'); } +static const QString testString = QStringLiteral("Test-String"); + +void tst_qqmlxmlhttprequest::doFileRequest(std::function<void(QObject *component, QTemporaryFile &writeFile)> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl(prefix + qml)); + QScopedPointer<QObject> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirects.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirectError.qml")); + QScopedPointer<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("redirectRecur.qml")); + QScopedPointer<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> 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<QObject> object(component.beginCreate(engine.rootContext())); + QQmlComponent component(engine.get(), testFileUrl("stateChangeCallingContext.qml")); + QScopedPointer<QObject> object(component.beginCreate(engine.get()->rootContext())); QVERIFY(!object.isNull()); object->setProperty("serverBaseUrl", server.baseUrl().toString()); component.completeCreate(); 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] |