From d988310434b00cc0e159fdf6ff3f586eefc47eed Mon Sep 17 00:00:00 2001 From: Valery Kotov Date: Wed, 4 Mar 2015 21:57:14 +0200 Subject: QML Engine: ArrayBuffer XHR response type support Support for "arraybuffer" response type for QQmlXMLHttpRequest was added. [ChangeLog][QtQml][QQmlXMLHttpRequest] QQmlXMLHttpRequest now supports "arraybuffer" binary response type. Change-Id: I866e543cc7bc6ab037ffff1ef6628057b73daf90 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 10 +++ src/qml/jsruntime/qv4engine_p.h | 2 + src/qml/qml/qqmlxmlhttprequest.cpp | 76 +++++++++++++++++++++ .../auto/qml/qqmlxmlhttprequest/data/qml_logo.png | Bin 0 -> 27151 bytes .../qqmlxmlhttprequest/data/receiveBinaryData.qml | 27 ++++++++ .../data/receive_binary_data.expect | 7 ++ .../data/receive_binary_data.reply | 3 + .../qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp | 19 ++++++ 8 files changed, 144 insertions(+) create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/qml_logo.png create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.reply diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 2c8a68812a..54dd5979cf 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -582,6 +582,16 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *ic, Object *pr return object->d(); } +Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array) +{ + Scope scope(this); + Scoped object(scope, memoryManager->alloc(this, array.size())); + if (!hasException) { + memcpy(object->d()->data->data(), array.data(), array.size()); + } + return object->d(); +} + Heap::DateObject *ExecutionEngine::newDateObject(const Value &value) { diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 098510e91a..bcb74ab694 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -268,6 +268,8 @@ public: Heap::ArrayObject *newArrayObject(const QStringList &list); Heap::ArrayObject *newArrayObject(InternalClass *ic, Object *prototype); + Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array); + Heap::DateObject *newDateObject(const Value &value); Heap::DateObject *newDateObject(const QDateTime &dt); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index afa79ee6b1..a2c5f09061 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -59,6 +59,7 @@ #include #include +#include using namespace QV4; @@ -1022,6 +1023,9 @@ public: QString responseBody(); const QByteArray & rawResponseBody() const; bool receivedXml() const; + + const QString & responseType() const; + void setResponseType(const QString &); private slots: void readyRead(); void error(QNetworkReply::NetworkError); @@ -1070,12 +1074,15 @@ private: QNetworkAccessManager *m_nam; QNetworkAccessManager *networkAccessManager() { return m_nam; } + + QString m_responseType; }; QQmlXMLHttpRequest::QQmlXMLHttpRequest(ExecutionEngine *engine, QNetworkAccessManager *manager) : v4(engine) , m_state(Unsent), m_errorFlag(false), m_sendFlag(false) , m_redirectCount(0), m_gotXml(false), m_textCodec(0), m_network(0), m_nam(manager) + , m_responseType() { } @@ -1461,6 +1468,16 @@ bool QQmlXMLHttpRequest::receivedXml() const return m_gotXml; } +const QString & QQmlXMLHttpRequest::responseType() const +{ + return m_responseType; +} + +void QQmlXMLHttpRequest::setResponseType(const QString &responseType) +{ + m_responseType = responseType; +} + #ifndef QT_NO_TEXTCODEC QTextCodec* QQmlXMLHttpRequest::findTextCodec() const @@ -1640,6 +1657,9 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject static ReturnedValue method_get_statusText(CallContext *ctx); static ReturnedValue method_get_responseText(CallContext *ctx); static ReturnedValue method_get_responseXML(CallContext *ctx); + static ReturnedValue method_get_response(CallContext *ctx); + static ReturnedValue method_get_responseType(CallContext *ctx); + static ReturnedValue method_set_responseType(CallContext *ctx); }; } @@ -1686,6 +1706,10 @@ void QQmlXMLHttpRequestCtor::setupProto() p->defineAccessorProperty(QStringLiteral("statusText"),method_get_statusText, 0); p->defineAccessorProperty(QStringLiteral("responseText"),method_get_responseText, 0); p->defineAccessorProperty(QStringLiteral("responseXML"),method_get_responseXML, 0); + p->defineAccessorProperty(QStringLiteral("response"),method_get_response, 0); + + // Read-write properties + p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType); // State values p->defineReadonlyProperty(QStringLiteral("UNSENT"), Primitive::fromInt32(0)); @@ -1945,6 +1969,58 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(CallContext *ctx) } } +ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(CallContext *ctx) +{ + Scope scope(ctx); + Scoped w(scope, ctx->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 QV4::Encode(scope.engine->newString(QString())); + + const QString& responseType = r->responseType(); + if (responseType.compare(QLatin1String("text"), Qt::CaseInsensitive) == 0) { + return QV4::Encode(scope.engine->newString(r->responseBody())); + } else if (responseType.compare(QLatin1String("arraybuffer"), Qt::CaseInsensitive) == 0) { + return QV4::Encode(scope.engine->newArrayBuffer(r->rawResponseBody())); + } else { + return QV4::Encode(scope.engine->newString(QString())); + } + + return Encode::undefined(); +} + + +ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseType(CallContext *ctx) +{ + Scope scope(ctx); + Scoped w(scope, ctx->thisObject().as()); + if (!w) + V4THROW_REFERENCE("Not an XMLHttpRequest object"); + QQmlXMLHttpRequest *r = w->d()->request; + return QV4::Encode(scope.engine->newString(r->responseType())); +} + +ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(CallContext *ctx) +{ + Scope scope(ctx); + Scoped w(scope, ctx->thisObject().as()); + if (!w) + V4THROW_REFERENCE("Not an XMLHttpRequest object"); + QQmlXMLHttpRequest *r = w->d()->request; + + if (ctx->argc() < 1) + V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count"); + + // Argument 0 - response type + r->setResponseType(ctx->args()[0].toQStringNoThrow()); + + return Encode::undefined(); +} + void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d) { QQmlXMLHttpRequestData *data = (QQmlXMLHttpRequestData *)d; diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/qml_logo.png b/tests/auto/qml/qqmlxmlhttprequest/data/qml_logo.png new file mode 100644 index 0000000000..681aef8aa2 Binary files /dev/null and b/tests/auto/qml/qqmlxmlhttprequest/data/qml_logo.png differ diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml b/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml new file mode 100644 index 0000000000..234d759284 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +QtObject { + property string url + property int readSize: 0 + + Component.onCompleted: { + + var request = new XMLHttpRequest(); + request.open("GET", url); + request.responseType = "arraybuffer"; + + request.onreadystatechange = function() { + if (request.readyState == XMLHttpRequest.DONE) { + var arrayBuffer = request.response; + if (arrayBuffer) { + var byteArray = new Uint8Array(arrayBuffer); + readSize = byteArray.byteLength; + } + } + } + + request.send(null); + + } +} + diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect new file mode 100644 index 0000000000..79a61383e3 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect @@ -0,0 +1,7 @@ +GET /gml_logo.png HTTP/1.1 +Accept-Language: en-US,* +Content-Type: image/png +Connection: Keep-Alive +Accept-Encoding: gzip, deflate +User-Agent: Mozilla/5.0 +Host: 127.0.0.1:14445 diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.reply b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.reply new file mode 100644 index 0000000000..44ba138213 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.reply @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Connection: close +Content-Type: image/png diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index fe1b8b1505..dd70f81d5e 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -87,6 +87,7 @@ private slots: void getAllResponseHeaders_unsent(); void getAllResponseHeaders_sent(); void getAllResponseHeaders_args(); + void getBinaryData(); void status(); void status_data(); void statusText(); @@ -816,6 +817,24 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders_args() QTRY_VERIFY(object->property("exceptionThrown").toBool() == true); } +void tst_qqmlxmlhttprequest::getBinaryData() +{ + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("receive_binary_data.expect"), + testFileUrl("receive_binary_data.reply"), + testFileUrl("qml_logo.png"))); + + QQmlComponent component(&engine, testFileUrl("receiveBinaryData.qml")); + QScopedPointer object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); + object->setProperty("url", "http://127.0.0.1:14445/gml_logo.png"); + component.completeCreate(); + + QFileInfo fileInfo("data/qml_logo.png"); + QTRY_VERIFY(object->property("readSize").toInt() == fileInfo.size()); +} + void tst_qqmlxmlhttprequest::status() { QFETCH(QUrl, replyUrl); -- cgit v1.2.3