diff --git a/src/qml/doc/src/javascript/xmlhttprequest.qdoc b/src/qml/doc/src/javascript/xmlhttprequest.qdoc index 61613adb74..1ed15a008f 100644 --- a/src/qml/doc/src/javascript/xmlhttprequest.qdoc +++ b/src/qml/doc/src/javascript/xmlhttprequest.qdoc @@ -223,6 +223,13 @@ \sa {XMLHttpRequest::onreadystatechange}{onreadystatechange} */ +/*! + \qmlproperty string XMLHttpRequest::responseURL + \readonly + + Returns the url that was used to retrieve the response data, after any redirects have occurred. +*/ + /*! \qmlproperty string XMLHttpRequest::responseText \readonly diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 4d8c035971..634f22759f 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -998,6 +998,7 @@ public: QString responseBody(); const QByteArray & rawResponseBody() const; bool receivedXml() const; + QUrl url() const; const QString & responseType() const; void setResponseType(const QString &); @@ -1162,6 +1163,7 @@ void QQmlXMLHttpRequest::fillHeadersList() void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) { + m_url = url; QNetworkRequest request = m_request; if (QQmlFile::isLocalFile(url)) { @@ -1462,6 +1464,11 @@ bool QQmlXMLHttpRequest::receivedXml() const return m_gotXml; } +QUrl QQmlXMLHttpRequest::url() const +{ + return m_url; +} + const QString & QQmlXMLHttpRequest::responseType() const { return m_responseType; @@ -1666,6 +1673,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject static ReturnedValue method_get_response(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); }; } @@ -1714,6 +1722,7 @@ void QQmlXMLHttpRequestCtor::setupProto() p->defineAccessorProperty(QStringLiteral("responseText"),method_get_responseText, nullptr); p->defineAccessorProperty(QStringLiteral("responseXML"),method_get_responseXML, nullptr); p->defineAccessorProperty(QStringLiteral("response"),method_get_response, nullptr); + p->defineAccessorProperty(QStringLiteral("responseURL"),method_get_responseURL, nullptr); // Read-write properties p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType); @@ -2039,6 +2048,24 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const FunctionObje return Encode::undefined(); } +ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped w(scope, thisObject->as()); + if (!w) + V4THROW_REFERENCE("Not an XMLHttpRequest object"); + QQmlXMLHttpRequest *r = w->d()->request; + + if (r->readyState() != QQmlXMLHttpRequest::Loading && + r->readyState() != QQmlXMLHttpRequest::Done) { + return Encode(scope.engine->newString(QString())); + } else { + QUrl url = r->url(); + url.setFragment(QString()); + return Encode(scope.engine->newString(url.toString())); + } +} + void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d) { QQmlXMLHttpRequestData *data = (QQmlXMLHttpRequestData *)d; diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml b/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml new file mode 100644 index 0000000000..437727abd5 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/responseURL.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 + +QtObject { + property string url + property string expectedURL + + property bool dataOK: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + + x.open("GET", url); + x.setRequestHeader("Accept-Language", "en-US"); + + // Test to the end + x.onreadystatechange = function() { + if (x.readyState === XMLHttpRequest.DONE) { + dataOK = (x.responseURL === expectedURL); + } + } + + x.send() + } +} + diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 4730d7233c..defbe3cf01 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -75,6 +75,7 @@ private slots: void statusText_data(); void responseText(); void responseText_data(); + void responseURL(); void responseXML_invalid(); void invalidMethodUsage(); void redirects(); @@ -1025,6 +1026,64 @@ void tst_qqmlxmlhttprequest::responseText_data() QTest::newRow("Internal server error") << testFileUrl("status.500.reply") << testFileUrl("testdocument.html") << "QML Rocks!\n"; } + +void tst_qqmlxmlhttprequest::responseURL() +{ + // 200 OK + { + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("status.expect"), + testFileUrl("status.200.reply"), + testFileUrl("testdocument.html"))); + + QQmlComponent component(engine.get(), testFileUrl("responseURL.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/testdocument.html")); + object->setProperty("expectedURL", server.urlString("/testdocument.html")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + } + + // 200 OK with the exclude fragment flag set + { + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("status.expect"), + testFileUrl("status.200.reply"), + testFileUrl("testdocument.html"))); + + QQmlComponent component(engine.get(), testFileUrl("responseURL.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/testdocument.html#fragment")); + object->setProperty("expectedURL", server.urlString("/testdocument.html")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + } + + // 302 Found + { + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + server.addRedirect("redirect.html", server.urlString("/redirecttarget.html")); + server.serveDirectory(dataDirectory()); + + QQmlComponent component(engine.get(), testFileUrl("responseURL.qml")); + QScopedPointer object(component.beginCreate(engine.get()->rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", server.urlString("/redirect.html")); + object->setProperty("expectedURL", server.urlString("/redirecttarget.html")); + component.completeCreate(); + + QTRY_VERIFY(object->property("dataOK").toBool()); + } +} + + void tst_qqmlxmlhttprequest::nonUtf8() { QFETCH(QString, fileName);