diff options
author | Tasuku Suzuki <stasuku@gmail.com> | 2014-11-30 01:40:42 +0900 |
---|---|---|
committer | Tasuku Suzuki <stasuku@gmail.com> | 2014-12-26 04:39:46 +0100 |
commit | f8beab1d7390095114e1d5f02097751dd1b07345 (patch) | |
tree | 87e668f21ac1f010f7a635c826b928df183c1814 | |
parent | d641008d04475bfb1c1996e408b1408618845b6f (diff) |
support XMLHttpRequest sync mode
[ChangeLog][QtQml][XMLHttpRequest] Supported synchronous requests
Change-Id: Ia38fcf97f212a14657bb519240d8406368a72390
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
-rw-r--r-- | src/qml/doc/src/javascript/qmlglobalobject.qdoc | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlxmlhttprequest.cpp | 41 | ||||
-rw-r--r-- | tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml | 14 | ||||
-rw-r--r-- | tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp | 39 |
4 files changed, 72 insertions, 23 deletions
diff --git a/src/qml/doc/src/javascript/qmlglobalobject.qdoc b/src/qml/doc/src/javascript/qmlglobalobject.qdoc index 62ec296178..fd702cd879 100644 --- a/src/qml/doc/src/javascript/qmlglobalobject.qdoc +++ b/src/qml/doc/src/javascript/qmlglobalobject.qdoc @@ -57,7 +57,6 @@ The XMLHttpRequest API implements the same \l {http://www.w3.org/TR/XMLHttpReque as many popular web browsers with following exceptions: \list \li QML's XMLHttpRequest does not enforce the same origin policy. -\li QML's XMLHttpRequest does not support \e synchronous requests. \endlist Additionally, the \c responseXML XML DOM tree currently supported by QML is a reduced subset diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 517f5a8b59..0117432e31 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1040,6 +1040,10 @@ class QQmlXMLHttpRequest : public QObject { Q_OBJECT public: + enum LoadType { + AsynchronousLoad, + SynchronousLoad + }; enum State { Unsent = 0, Opened = 1, HeadersReceived = 2, Loading = 3, Done = 4 }; @@ -1053,7 +1057,7 @@ public: int replyStatus() const; QString replyStatusText() const; - ReturnedValue open(const ValueRef me, const QString &, const QUrl &); + ReturnedValue open(const ValueRef me, const QString &, const QUrl &, LoadType); ReturnedValue send(const ValueRef me, const QByteArray &); ReturnedValue abort(const ValueRef me); @@ -1152,7 +1156,7 @@ QString QQmlXMLHttpRequest::replyStatusText() const return m_statusText; } -ReturnedValue QQmlXMLHttpRequest::open(const ValueRef me, const QString &method, const QUrl &url) +ReturnedValue QQmlXMLHttpRequest::open(const ValueRef me, const QString &method, const QUrl &url, LoadType loadType) { destroyNetwork(); m_sendFlag = false; @@ -1160,6 +1164,7 @@ ReturnedValue QQmlXMLHttpRequest::open(const ValueRef me, const QString &method, m_responseEntityBody = QByteArray(); m_method = method; m_url = url; + m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, loadType == SynchronousLoad); m_state = Opened; m_addedHeaders.clear(); dispatchCallback(me); @@ -1276,12 +1281,24 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) buffer->setParent(m_network); } - QObject::connect(m_network, SIGNAL(readyRead()), - this, SLOT(readyRead())); - QObject::connect(m_network, SIGNAL(error(QNetworkReply::NetworkError)), - this, SLOT(error(QNetworkReply::NetworkError))); - QObject::connect(m_network, SIGNAL(finished()), - this, SLOT(finished())); + if (m_request.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) { + if (m_network->bytesAvailable() > 0) + readyRead(); + + QNetworkReply::NetworkError networkError = m_network->error(); + if (networkError != QNetworkReply::NoError) { + error(networkError); + } else { + finished(); + } + } else { + QObject::connect(m_network, SIGNAL(readyRead()), + this, SLOT(readyRead())); + QObject::connect(m_network, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(error(QNetworkReply::NetworkError))); + QObject::connect(m_network, SIGNAL(finished()), + this, SLOT(finished())); + } } ReturnedValue QQmlXMLHttpRequest::send(const ValueRef me, const QByteArray &data) @@ -1759,9 +1776,11 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) if (url.isRelative()) url = engine->callingContext()->resolvedUrl(url); + bool async = true; // Argument 2 - async (optional) - if (ctx->d()->callData->argc > 2 && !ctx->d()->callData->args[2].booleanValue()) - V4THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Synchronous XMLHttpRequest calls are not supported"); + if (ctx->d()->callData->argc > 2) { + async = ctx->d()->callData->args[2].booleanValue(); + } // Argument 3/4 - user/pass (optional) QString username, password; @@ -1778,7 +1797,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) if (!password.isNull()) url.setPassword(password); ScopedValue meObject(scope, constructMeObject(ctx->d()->callData->thisObject, engine)); - return r->open(meObject, method, url); + return r->open(meObject, method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); } ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(CallContext *ctx) diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml b/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml index 0f31c966fa..3c73141954 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml +++ b/tests/auto/qml/qqmlxmlhttprequest/data/open_sync.qml @@ -1,17 +1,15 @@ import QtQuick 2.0 QtObject { - property bool exceptionThrown: false + property url url + property string responseText Component.onCompleted: { var x = new XMLHttpRequest; - - try { - x.open("GET", "http://www.qt-project.org", false); - } catch (e) { - if (e.code == DOMException.NOT_SUPPORTED_ERR) - exceptionThrown = true; - } + x.open("GET", url, false); + x.setRequestHeader("Accept-Language", "en-US"); + x.send(); + responseText = x.responseText; } } diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 441893a853..0d70e47b3d 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -37,6 +37,7 @@ #include <QDebug> #include <QScopedPointer> #include <QNetworkCookieJar> +#include <QThread> #include "testhttpserver.h" #include "../../shared/util.h" @@ -282,14 +283,46 @@ void tst_qqmlxmlhttprequest::open_invalid_method() QCOMPARE(object->property("exceptionThrown").toBool(), true); } -// Test that calling XMLHttpRequest.open() with sync raises an exception +class TestThreadedHTTPServer : public QObject +{ + Q_OBJECT +public: + TestThreadedHTTPServer(const QUrl &expectUrl, const QUrl &replyUrl, const QUrl &bodyUrl) + : m_server(Q_NULLPTR) { + moveToThread(&m_thread); + m_thread.start(); + QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection, Q_ARG(QUrl, expectUrl), Q_ARG(QUrl, replyUrl), Q_ARG(QUrl, bodyUrl)); + } + ~TestThreadedHTTPServer() { + m_server->deleteLater(); + m_thread.exit(); + m_thread.wait(); + } + +private slots: + void start(const QUrl &expectUrl, const QUrl &replyUrl, const QUrl &bodyUrl) { + m_server = new TestHTTPServer; + QVERIFY2(m_server->listen(SERVER_PORT), qPrintable(m_server->errorString())); + QVERIFY(m_server->wait(expectUrl, replyUrl, bodyUrl)); + } + +private: + TestHTTPServer *m_server; + QThread m_thread; +}; + +// Test that calling XMLHttpRequest.open() with sync 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.create()); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); QVERIFY(!object.isNull()); + object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); + component.completeCreate(); - QCOMPARE(object->property("exceptionThrown").toBool(), true); + QCOMPARE(object->property("responseText").toString(), QStringLiteral("QML Rocks!\n")); } // Calling with incorrect arg count raises an exception |