XHR: Add responseURL

https://xhr.spec.whatwg.org/#the-responseurl-attribute
the attribute was introduced around 2014.

[ChangeLog][Qml][XMLHttpRequest] Added missing responseURL property. This returns the url that was used to retrieve the response data, after any redirects have occurred.

Change-Id: Ice70520913bb306885a10dfd7a3a89da31bcfdeb
Task-number: QTBUG-111217
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
This commit is contained in:
Tasuku Suzuki 2023-04-04 21:47:38 +09:00
parent 3991a2e8ca
commit efc037191a
4 changed files with 118 additions and 0 deletions

View File

@ -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

View File

@ -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<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
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;

View File

@ -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()
}
}

View File

@ -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<QObject> 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<QObject> 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<QObject> 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);