QML Engine: ArrayBuffer XHR response type support
authorValery Kotov <kotov.valery@gmail.com>
Wed, 4 Mar 2015 19:57:14 +0000 (21:57 +0200)
committerSimon Hausmann <simon.hausmann@theqtcompany.com>
Mon, 9 Mar 2015 19:41:41 +0000 (19:41 +0000)
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 <simon.hausmann@theqtcompany.com>
src/qml/jsruntime/qv4engine.cpp
src/qml/jsruntime/qv4engine_p.h
src/qml/qml/qqmlxmlhttprequest.cpp
tests/auto/qml/qqmlxmlhttprequest/data/qml_logo.png [new file with mode: 0644]
tests/auto/qml/qqmlxmlhttprequest/data/receiveBinaryData.qml [new file with mode: 0644]
tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.expect [new file with mode: 0644]
tests/auto/qml/qqmlxmlhttprequest/data/receive_binary_data.reply [new file with mode: 0644]
tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp

index 2c8a688..54dd597 100644 (file)
@@ -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<ArrayBuffer> object(scope, memoryManager->alloc<ArrayBuffer>(this, array.size()));
+    if (!hasException) {
+        memcpy(object->d()->data->data(), array.data(), array.size());
+    }
+    return object->d();
+}
+
 
 Heap::DateObject *ExecutionEngine::newDateObject(const Value &value)
 {
index 098510e..bcb74ab 100644 (file)
@@ -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);
 
index afa79ee..a2c5f09 100644 (file)
@@ -59,6 +59,7 @@
 
 #include <private/qv4objectproto_p.h>
 #include <private/qv4scopedvalue_p.h>
+#include <private/qv4arraybuffer_p.h>
 
 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<QQmlXMLHttpRequestWrapper> w(scope, ctx->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 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<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>());
+    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<QQmlXMLHttpRequestWrapper> w(scope, ctx->thisObject().as<QQmlXMLHttpRequestWrapper>());
+    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 (file)
index 0000000..681aef8
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 (file)
index 0000000..234d759
--- /dev/null
@@ -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 (file)
index 0000000..79a6138
--- /dev/null
@@ -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 (file)
index 0000000..44ba138
--- /dev/null
@@ -0,0 +1,3 @@
+HTTP/1.1 200 OK
+Connection: close
+Content-Type: image/png
index fe1b8b1..dd70f81 100644 (file)
@@ -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<QObject> 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);