summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/standalone/websockettransport.cpp24
-rw-r--r--examples/standalone/websockettransport.h5
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp140
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h33
-rw-r--r--src/webchannel/qqmlwebchannel.h4
-rw-r--r--src/webchannel/qwebchannel.cpp31
-rw-r--r--src/webchannel/qwebchannel.h2
-rw-r--r--src/webchannel/qwebchannel.js218
-rw-r--r--src/webchannel/qwebchannel_p.h2
-rw-r--r--src/webchannel/qwebchannelabstracttransport.h5
-rw-r--r--tests/auto/qml/Client.qml24
-rw-r--r--tests/auto/qml/testtransport.cpp24
-rw-r--r--tests/auto/qml/testtransport.h6
-rw-r--r--tests/auto/qml/tst_metaobjectpublisher.qml320
-rw-r--r--tests/auto/qml/tst_multiclient.qml13
-rw-r--r--tests/auto/qml/tst_webchannel.qml242
-rw-r--r--tests/auto/webchannel/tst_webchannel.cpp6
-rw-r--r--tests/auto/webchannel/tst_webchannel.h2
18 files changed, 499 insertions, 602 deletions
diff --git a/examples/standalone/websockettransport.cpp b/examples/standalone/websockettransport.cpp
index 5df9f85..f1ecee2 100644
--- a/examples/standalone/websockettransport.cpp
+++ b/examples/standalone/websockettransport.cpp
@@ -41,6 +41,10 @@
#include "websockettransport.h"
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QDebug>
+
#include <QtWebSockets/QWebSocket>
/*!
@@ -66,9 +70,25 @@ WebSocketTransport::~WebSocketTransport()
}
-void WebSocketTransport::sendTextMessage(const QString &message)
+void WebSocketTransport::sendMessage(const QJsonObject &message)
+{
+ QJsonDocument doc(message);
+ m_socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
+}
+
+void WebSocketTransport::textMessageReceived(const QString &messageData)
{
- m_socket->sendTextMessage(message);
+ QJsonParseError error;
+ QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error);
+ if (error.error) {
+ qWarning() << "Failed to parse text message as JSON object:" << messageData
+ << "Error is:" << error.errorString();
+ return;
+ } else if (!message.isObject()) {
+ qWarning() << "Received JSON message that is not an object: " << messageData;
+ return;
+ }
+ emit messageReceived(message.object(), this);
}
QT_END_NAMESPACE
diff --git a/examples/standalone/websockettransport.h b/examples/standalone/websockettransport.h
index 5fab8f6..828ac00 100644
--- a/examples/standalone/websockettransport.h
+++ b/examples/standalone/websockettransport.h
@@ -54,7 +54,10 @@ public:
explicit WebSocketTransport(QWebSocket *socket);
virtual ~WebSocketTransport();
- void sendTextMessage(const QString &message) Q_DECL_OVERRIDE;
+ void sendMessage(const QJsonObject &message) Q_DECL_OVERRIDE;
+
+private Q_SLOTS:
+ void textMessageReceived(const QString &message);
private:
QWebSocket *m_socket;
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index 646398d..d9aa548 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -59,30 +59,11 @@ QT_BEGIN_NAMESPACE
namespace {
-// NOTE: keep in sync with corresponding maps in qwebchannel.js and WebChannelTest.qml
-enum Type {
- TypeInvalid = 0,
-
- TYPES_FIRST_VALUE = 1,
-
- TypeSignal = 1,
- TypePropertyUpdate = 2,
- TypeInit = 3,
- TypeIdle = 4,
- TypeDebug = 5,
- TypeInvokeMethod = 6,
- TypeConnectToSignal = 7,
- TypeDisconnectFromSignal = 8,
- TypeSetProperty = 9,
-
- TYPES_LAST_VALUE = 9
-};
-
-Type toType(const QJsonValue &value)
+MessageType toType(const QJsonValue &value)
{
int i = value.toInt(-1);
if (i >= TYPES_FIRST_VALUE && i <= TYPES_LAST_VALUE) {
- return static_cast<Type>(i);
+ return static_cast<MessageType>(i);
} else {
return TypeInvalid;
}
@@ -99,13 +80,11 @@ const QString KEY_OBJECT = QStringLiteral("object");
const QString KEY_DESTROYED = QStringLiteral("destroyed");
const QString KEY_SIGNAL = QStringLiteral("signal");
const QString KEY_TYPE = QStringLiteral("type");
-const QString KEY_MESSAGE = QStringLiteral("message");
const QString KEY_METHOD = QStringLiteral("method");
const QString KEY_ARGS = QStringLiteral("args");
const QString KEY_PROPERTY = QStringLiteral("property");
const QString KEY_VALUE = QStringLiteral("value");
-
QString objectId(const QObject *object)
{
return QString::number(quintptr(object), 16);
@@ -261,7 +240,10 @@ void QMetaObjectPublisher::initializeClients()
objectInfos[it.key()] = info;
}
}
- webChannel->sendMessage(TypeInit, objectInfos);
+ QJsonObject message;
+ message[KEY_TYPE] = TypeInit;
+ message[KEY_DATA] = objectInfos;
+ broadcastMessage(message);
propertyUpdatesInitialized = true;
pendingInit = false;
}
@@ -334,31 +316,34 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates()
}
pendingPropertyUpdates.clear();
- webChannel->sendMessage(TypePropertyUpdate, data);
+ QJsonObject message;
+ message[KEY_TYPE] = TypePropertyUpdate;
+ message[KEY_DATA] = data;
+ broadcastMessage(message);
setClientIsIdle(false);
}
-QByteArray QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
- const QJsonArray &args, const QJsonValue &id)
+QJsonValue QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
+ const QJsonArray &args)
{
const QMetaMethod &method = object->metaObject()->method(methodIndex);
if (method.name() == QByteArrayLiteral("deleteLater")) {
// invoke `deleteLater` on wrapped QObject indirectly
deleteWrappedObject(object);
- return QByteArray();
+ return QJsonValue();
} else if (!method.isValid()) {
qWarning() << "Cannot invoke unknown method of index" << methodIndex << "on object" << object << '.';
- return QByteArray();
+ return QJsonValue();
} else if (method.access() != QMetaMethod::Public) {
qWarning() << "Cannot invoke non-public method" << method.name() << "on object" << object << '.';
- return QByteArray();
+ return QJsonValue();
} else if (method.methodType() != QMetaMethod::Method && method.methodType() != QMetaMethod::Slot) {
qWarning() << "Cannot invoke non-public method" << method.name() << "on object" << object << '.';
- return QByteArray();
+ return QJsonValue();
} else if (args.size() > 10) {
qWarning() << "Cannot invoke method" << method.name() << "on object" << object << "with more than 10 arguments, as that is not supported by QMetaMethod::invoke.";
- return QByteArray();
+ return QJsonValue();
} else if (args.size() > method.parameterCount()) {
qWarning() << "Ignoring additional arguments while invoking method" << method.name() << "on object" << object << ':'
<< args.size() << "arguments given, but method only takes" << method.parameterCount() << '.';
@@ -389,8 +374,7 @@ QByteArray QMetaObjectPublisher::invokeMethod(QObject *const object, const int m
arguments[0], arguments[1], arguments[2], arguments[3], arguments[4],
arguments[5], arguments[6], arguments[7], arguments[8], arguments[9]);
- // and send the return value to the client
- return generateJSONMessage(id, wrapResult(returnValue), true);
+ return wrapResult(returnValue);
}
void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments)
@@ -399,11 +383,11 @@ void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signal
return;
}
if (!signalToPropertyMap.value(object).contains(signalIndex)) {
- QJsonObject data;
+ QJsonObject message;
const QString &objectName = registeredObjectIds.value(object);
Q_ASSERT(!objectName.isEmpty());
- data[KEY_OBJECT] = objectName;
- data[KEY_SIGNAL] = signalIndex;
+ message[KEY_OBJECT] = objectName;
+ message[KEY_SIGNAL] = signalIndex;
if (!arguments.isEmpty()) {
// TODO: wrap (new) objects on the fly
QJsonArray args;
@@ -419,9 +403,10 @@ void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signal
#else
args = QJsonArray::fromVariantList(arguments);
#endif
- data[KEY_ARGS] = args;
+ message[KEY_ARGS] = args;
}
- webChannel->sendMessage(TypeSignal, data);
+ message[KEY_TYPE] = TypeSignal;
+ broadcastMessage(message);
if (signalIndex == s_destroyedSignalIndex) {
objectDestroyed(object);
@@ -486,78 +471,71 @@ void QMetaObjectPublisher::deleteWrappedObject(QObject *object) const
object->deleteLater();
}
-QByteArray QMetaObjectPublisher::handleRequest(const QJsonObject &message)
+void QMetaObjectPublisher::broadcastMessage(const QJsonObject &message) const
{
- if (!message.contains(KEY_DATA)) {
- return QByteArray();
+ if (webChannel->d_func()->transports.isEmpty()) {
+ qWarning("QWebChannel is not connected to any transports, cannot send messages.");
+ return;
+ }
+
+ foreach (QWebChannelAbstractTransport *transport, webChannel->d_func()->transports) {
+ transport->sendMessage(message);
}
+}
- const QJsonObject &payload = message.value(KEY_DATA).toObject();
- if (!payload.contains(KEY_TYPE)) {
- return QByteArray();
+void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannelAbstractTransport *transport)
+{
+ if (!message.contains(KEY_TYPE)) {
+ qWarning("JSON message object is missing the type property: %s", QJsonDocument(message).toJson().constData());
+ return;
}
- const Type type = toType(payload.value(KEY_TYPE));
+ const MessageType type = toType(message.value(KEY_TYPE));
if (type == TypeIdle) {
setClientIsIdle(true);
- return QByteArray();
} else if (type == TypeInit) {
if (!blockUpdates) {
initializeClients();
} else {
pendingInit = true;
}
- return QByteArray();
} else if (type == TypeDebug) {
static QTextStream out(stdout);
- out << "DEBUG: " << payload.value(KEY_MESSAGE).toString() << endl;
- return QByteArray();
- } else if (payload.contains(KEY_OBJECT)) {
- const QString &objectName = payload.value(KEY_OBJECT).toString();
+ out << "DEBUG: " << message.value(KEY_DATA).toString() << endl;
+ } else if (message.contains(KEY_OBJECT)) {
+ const QString &objectName = message.value(KEY_OBJECT).toString();
QObject *object = registeredObjects.value(objectName);
if (!object) {
qWarning() << "Unknown object encountered" << objectName;
- return QByteArray();
+ return;
}
if (type == TypeInvokeMethod) {
- return invokeMethod(object, payload.value(KEY_METHOD).toInt(-1), payload.value(KEY_ARGS).toArray(), message.value(KEY_ID));
+ if (!message.contains(KEY_ID)) {
+ qWarning("JSON message object is missing the id property: %s",
+ QJsonDocument(message).toJson().constData());
+ return;
+ }
+ QJsonObject response;
+ response[KEY_TYPE] = TypeResponse;
+ response[KEY_ID] = message.value(KEY_ID);
+ response[KEY_DATA] = invokeMethod(object, message.value(KEY_METHOD).toInt(-1), message.value(KEY_ARGS).toArray());
+ transport->sendMessage(response);
} else if (type == TypeConnectToSignal) {
- signalHandler.connectTo(object, payload.value(KEY_SIGNAL).toInt(-1));
- return QByteArray();
+ signalHandler.connectTo(object, message.value(KEY_SIGNAL).toInt(-1));
} else if (type == TypeDisconnectFromSignal) {
- signalHandler.disconnectFrom(object, payload.value(KEY_SIGNAL).toInt(-1));
- return QByteArray();
+ signalHandler.disconnectFrom(object, message.value(KEY_SIGNAL).toInt(-1));
} else if (type == TypeSetProperty) {
- const int propertyIdx = payload.value(KEY_PROPERTY).toInt(-1);
+ const int propertyIdx = message.value(KEY_PROPERTY).toInt(-1);
QMetaProperty property = object->metaObject()->property(propertyIdx);
if (!property.isValid()) {
- qWarning() << "Cannot set unknown property" << payload.value(KEY_PROPERTY) << "of object" << objectName;
- return QByteArray();
- } else if (!object->metaObject()->property(propertyIdx).write(object, payload.value(KEY_VALUE).toVariant())) {
- qWarning() << "Could not write value " << payload.value(KEY_VALUE)
+ qWarning() << "Cannot set unknown property" << message.value(KEY_PROPERTY) << "of object" << objectName;
+ } else if (!object->metaObject()->property(propertyIdx).write(object, message.value(KEY_VALUE).toVariant())) {
+ qWarning() << "Could not write value " << message.value(KEY_VALUE)
<< "to property" << property.name() << "of object" << objectName;
- return QByteArray();
}
- return QByteArray();
}
}
- return QByteArray();
-}
-
-void QMetaObjectPublisher::handleMessage(const QString &message)
-{
- QWebChannelAbstractTransport *transport = qobject_cast<QWebChannelAbstractTransport*>(sender());
- Q_ASSERT(transport);
-
- const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
- if (!doc.isObject()) {
- return;
- }
- const QByteArray &response = handleRequest(doc.object());
- if (!response.isEmpty()) {
- transport->sendTextMessage(QString::fromUtf8(response));
- }
}
void QMetaObjectPublisher::setBlockUpdates(bool block)
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 4da0c26..82afe57 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -54,7 +54,28 @@
QT_BEGIN_NAMESPACE
+// NOTE: keep in sync with corresponding maps in qwebchannel.js and WebChannelTest.qml
+enum MessageType {
+ TypeInvalid = 0,
+
+ TYPES_FIRST_VALUE = 1,
+
+ TypeSignal = 1,
+ TypePropertyUpdate = 2,
+ TypeInit = 3,
+ TypeIdle = 4,
+ TypeDebug = 5,
+ TypeInvokeMethod = 6,
+ TypeConnectToSignal = 7,
+ TypeDisconnectFromSignal = 8,
+ TypeSetProperty = 9,
+ TypeResponse = 10,
+
+ TYPES_LAST_VALUE = 10
+};
+
class QWebChannel;
+class QWebChannelAbstractTransport;
class Q_WEBCHANNEL_EXPORT QMetaObjectPublisher : public QObject
{
Q_OBJECT
@@ -75,11 +96,9 @@ public:
void registerObject(const QString &id, QObject *object);
/**
- * Handle the given WebChannel client request and potentially give a response.
- *
- * @return true if the request was handled, false otherwise.
+ * Send the given message to all known transports.
*/
- QByteArray handleRequest(const QJsonObject &message);
+ void broadcastMessage(const QJsonObject &message) const;
/**
* Serialize the QMetaObject of @p object and return it in JSON form.
@@ -125,7 +144,7 @@ public:
* The return value of the method invocation is then serialized and a response message
* is returned.
*/
- QByteArray invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args, const QJsonValue &id);
+ QJsonValue invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args);
/**
* Callback of the signalHandler which forwards the signal invocation to the webchannel clients.
@@ -164,9 +183,9 @@ Q_SIGNALS:
public Q_SLOTS:
/**
- * Parse the message as JSON and if it succeeds, call handleRequest with the obtained JSON object.
+ * Handle the @p message and if needed send a response to @p transport.
*/
- void handleMessage(const QString &message);
+ void handleMessage(const QJsonObject &message, QWebChannelAbstractTransport *transport);
protected:
void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
diff --git a/src/webchannel/qqmlwebchannel.h b/src/webchannel/qqmlwebchannel.h
index 3bbd2a0..96ffdd3 100644
--- a/src/webchannel/qqmlwebchannel.h
+++ b/src/webchannel/qqmlwebchannel.h
@@ -89,9 +89,9 @@ private:
static void transports_clear(QQmlListProperty<QObject> *prop);
};
+QT_END_NAMESPACE
+
QML_DECLARE_TYPE( QQmlWebChannel )
QML_DECLARE_TYPEINFO( QQmlWebChannel, QML_HAS_ATTACHED_PROPERTIES )
-QT_END_NAMESPACE
-
#endif // QQMLWEBCHANNEL_H
diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp
index d0b968e..3aebb14 100644
--- a/src/webchannel/qwebchannel.cpp
+++ b/src/webchannel/qwebchannel.cpp
@@ -50,20 +50,6 @@
QT_BEGIN_NAMESPACE
-QByteArray generateJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response)
-{
- QJsonObject obj;
- if (response) {
- obj[QStringLiteral("response")] = true;
- }
- obj[QStringLiteral("id")] = id;
- if (!data.isNull()) {
- obj[QStringLiteral("data")] = data;
- }
- QJsonDocument doc(obj);
- return doc.toJson(QJsonDocument::Compact);
-}
-
void QWebChannelPrivate::_q_transportDestroyed(QObject *object)
{
const int idx = transports.indexOf(static_cast<QWebChannelAbstractTransport*>(object));
@@ -144,7 +130,7 @@ void QWebChannel::connectTo(QWebChannelAbstractTransport *transport)
Q_ASSERT(transport);
if (!d->transports.contains(transport)) {
d->transports << transport;
- connect(transport, &QWebChannelAbstractTransport::textMessageReceived,
+ connect(transport, &QWebChannelAbstractTransport::messageReceived,
d->publisher, &QMetaObjectPublisher::handleMessage,
Qt::UniqueConnection);
connect(transport, SIGNAL(destroyed(QObject*)),
@@ -162,21 +148,6 @@ void QWebChannel::disconnectFrom(QWebChannelAbstractTransport *transport)
}
}
-void QWebChannel::sendMessage(const QJsonValue &id, const QJsonValue &data) const
-{
- Q_D(const QWebChannel);
- if (d->transports.isEmpty()) {
- qWarning("QWebChannel is not connected to any transports, cannot send messages.");
- return;
- }
-
- const QByteArray &message = generateJSONMessage(id, data, false);
- const QString &messageText = QString::fromUtf8(message);
- foreach (QWebChannelAbstractTransport *transport, d->transports) {
- transport->sendTextMessage(messageText);
- }
-}
-
QT_END_NAMESPACE
#include "moc_qwebchannel.cpp"
diff --git a/src/webchannel/qwebchannel.h b/src/webchannel/qwebchannel.h
index a65a4ee..46d77a3 100644
--- a/src/webchannel/qwebchannel.h
+++ b/src/webchannel/qwebchannel.h
@@ -97,8 +97,6 @@ public Q_SLOTS:
void connectTo(QWebChannelAbstractTransport *transport);
void disconnectFrom(QWebChannelAbstractTransport *transport);
- void sendMessage(const QJsonValue &id, const QJsonValue &data = QJsonValue()) const;
-
private:
Q_DECLARE_PRIVATE(QWebChannel)
QWebChannel(QWebChannelPrivate &dd, QObject *parent = 0);
diff --git a/src/webchannel/qwebchannel.js b/src/webchannel/qwebchannel.js
index dd6c4b3..3274d65 100644
--- a/src/webchannel/qwebchannel.js
+++ b/src/webchannel/qwebchannel.js
@@ -52,9 +52,12 @@ var QWebChannelMessageTypes = {
connectToSignal: 7,
disconnectFromSignal: 8,
setProperty: 9,
+ response: 10,
};
-var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel)
+// TODO: always expect an initialized transport object with a defined interface
+// to be passed in, remove automagic WebSocket code
+var QWebChannel = function(baseUrlOrSocket, initCallback)
{
var channel = this;
this.send = function(data)
@@ -64,33 +67,115 @@ var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel)
}
channel.socket.send(data);
}
- this.messageReceived = function(message)
+ this.onMessageReceived = function(message)
{
- var jsonData = JSON.parse(message.data);
- if (jsonData.id === undefined) {
- console.error("invalid message received:", message.data);
+ var data = message.data;
+ if (typeof data === "string") {
+ data = JSON.parse(data);
+ }
+ switch (data.type) {
+ case QWebChannelMessageTypes.signal:
+ channel.handleSignal(data);
+ break;
+ case QWebChannelMessageTypes.response:
+ channel.handleResponse(data);
+ break;
+ case QWebChannelMessageTypes.propertyUpdate:
+ channel.handlePropertyUpdate(data);
+ break;
+ case QWebChannelMessageTypes.init:
+ channel.handleInit(data);
+ break;
+ default:
+ console.error("invalid message received:", message.data);
+ break;
+ }
+ }
+
+ this.execCallbacks = {};
+ this.execId = 0;
+ this.exec = function(data, callback)
+ {
+ if (!callback) {
+ // if no callback is given, send directly
+ channel.send(data);
return;
}
- if (jsonData.data === undefined) {
- jsonData.data = {};
+ if (channel.execId === Number.MAX_VALUE) {
+ // wrap
+ channel.execId = Number.MIN_VALUE;
}
- if (jsonData.response) {
- channel.execCallbacks[jsonData.id](jsonData.data);
- delete channel.execCallbacks[jsonData.id];
- } else if (channel.subscriptions[jsonData.id]) {
- channel.subscriptions[jsonData.id].forEach(function(callback) {
- (callback)(jsonData.data); }
- );
+ if (data.hasOwnProperty("id")) {
+ console.error("Cannot exec message with property id: " + JSON.stringify(data));
+ return;
+ }
+ data.id = channel.execId++;
+ channel.execCallbacks[data.id] = callback;
+ channel.send(data);
+ };
+
+ this.objects = {};
+
+ this.handleSignal = function(message)
+ {
+ var object = channel.objects[message.object];
+ if (object) {
+ object.signalEmitted(message.signal, message.args);
+ } else {
+ console.warn("Unhandled signal: " + message.object + "::" + message.signal);
}
}
- this.initialized = function()
+ this.handleResponse = function(message)
{
- if (rawChannel) {
+ if (!message.hasOwnProperty("id")) {
+ console.error("Invalid response message received: ", JSON.stringify(message));
+ return;
+ }
+ channel.execCallbacks[message.id](message.data);
+ delete channel.execCallbacks[message.id];
+ }
+
+ this.handlePropertyUpdate = function(message)
+ {
+ for (var i in message.data) {
+ var data = message.data[i];
+ var object = channel.objects[data.object];
+ if (object) {
+ object.propertyUpdate(data.signals, data.properties);
+ } else {
+ console.warn("Unhandled property update: " + data.object + "::" + data.signal);
+ }
+ }
+ setTimeout(function() { channel.exec({type: QWebChannelMessageTypes.idle}); }, 0);
+ }
+
+ // prevent multiple initialization which might happen with multiple webchannel clients.
+ this.initialized = false;
+ this.handleInit = function(message)
+ {
+ if (channel.initialized) {
+ return;
+ }
+ channel.initialized = true;
+ for (var objectName in message.data) {
+ var data = message.data[objectName];
+ var object = new QObject(objectName, data, channel);
+ }
+ if (initCallback) {
initCallback(channel);
- } else {
- channel.initMetaObjectPublisher(initCallback);
}
+ setTimeout(function() { channel.exec({type: QWebChannelMessageTypes.idle}); }, 0);
+ }
+
+ this.debug = function(message)
+ {
+ channel.send({type: QWebChannelMessageTypes.debug, data: message});
+ };
+
+ this.onSocketReady = function(doneCallback)
+ {
+ channel.exec({type: QWebChannelMessageTypes.init});
}
if (typeof baseUrlOrSocket === 'object') {
@@ -99,13 +184,13 @@ var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel)
{
channel.socket.postMessage(data);
}
- this.socket.onmessage = this.messageReceived
- setTimeout(this.initialized, 0);
+ this.socket.onmessage = this.onMessageReceived
+ this.onSocketReady();
} else {
///TODO: use QWebChannel protocol, once custom protcols are supported by QtWebSocket
this.socket = new WebSocket(baseUrlOrSocket/*, "QWebChannel" */);
- this.socket.onopen = this.initialized
+ this.socket.onopen = this.onSocketReady
this.socket.onclose = function()
{
console.error("web channel closed");
@@ -114,96 +199,7 @@ var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel)
{
console.error("web channel error: " + error);
};
- this.socket.onmessage = this.messageReceived
- }
-
- this.subscriptions = {};
- this.subscribe = function(id, callback)
- {
- if (channel.subscriptions[id]) {
- channel.subscriptions[id].push(callback);
- } else {
- channel.subscriptions[id] = [callback];
- }
- };
-
- this.execCallbacks = {};
- this.execId = 0;
- this.exec = function(data, callback)
- {
- if (!callback) {
- // if no callback is given, send directly
- channel.send({data: data});
- return;
- }
- if (channel.execId === Number.MAX_VALUE) {
- // wrap
- channel.execId = Number.MIN_VALUE;
- }
- var id = channel.execId++;
- channel.execCallbacks[id] = callback;
- channel.send({"id": id, "data": data});
- };
-
- this.objects = {};
-
- this.initMetaObjectPublisher = function(doneCallback)
- {
- // prevent multiple initialization which might happen with multiple webchannel clients.
- var initialized = false;
-
- channel.subscribe(
- QWebChannelMessageTypes.signal,
- function(payload) {
- var object = channel.objects[payload.object];
- if (object) {
- object.signalEmitted(payload.signal, payload.args);
- } else {
- console.warn("Unhandled signal: " + payload.object + "::" + payload.signal);
- }
- }
- );
-
- channel.subscribe(
- QWebChannelMessageTypes.propertyUpdate,
- function(payload) {
- for (var i in payload) {
- var data = payload[i];
- var object = channel.objects[data.object];
- if (object) {
- object.propertyUpdate(data.signals, data.properties);
- } else {
- console.warn("Unhandled property update: " + data.object + "::" + data.signal);
- }
- }
- setTimeout(function() { channel.exec({type: QWebChannelMessageTypes.idle}); }, 0);
- }
- );
-
- channel.subscribe(
- QWebChannelMessageTypes.init,
- function(payload) {
- if (initialized) {
- return;
- }
- initialized = true;
- for (var objectName in payload) {
- var data = payload[objectName];
- var object = new QObject(objectName, data, channel);
- }
- if (doneCallback) {
- doneCallback(channel);
- }
- setTimeout(function() { channel.exec({type: QWebChannelMessageTypes.idle}); }, 0);
- }
- );
-
- channel.debug = function(message)
- {
- channel.send({"data" : {"type" : QWebChannelMessageTypes.debug, "message" : message}});
- };
-
- channel.exec({type: QWebChannelMessageTypes.init});
+ this.socket.onmessage = this.onMessageReceived
}
};
diff --git a/src/webchannel/qwebchannel_p.h b/src/webchannel/qwebchannel_p.h
index 62b2cd5..add55d1 100644
--- a/src/webchannel/qwebchannel_p.h
+++ b/src/webchannel/qwebchannel_p.h
@@ -53,8 +53,6 @@ class QJsonValue;
class QWebChannelAbstractTransport;
class QMetaObjectPublisher;
-Q_WEBCHANNEL_EXPORT QByteArray generateJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response);
-
class Q_WEBCHANNEL_EXPORT QWebChannelPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QWebChannel)
diff --git a/src/webchannel/qwebchannelabstracttransport.h b/src/webchannel/qwebchannelabstracttransport.h
index 0062fab..a13851c 100644
--- a/src/webchannel/qwebchannelabstracttransport.h
+++ b/src/webchannel/qwebchannelabstracttransport.h
@@ -47,6 +47,7 @@
QT_BEGIN_NAMESPACE
+class QJsonObject;
class Q_WEBCHANNEL_EXPORT QWebChannelAbstractTransport : public QObject
{
Q_OBJECT
@@ -58,13 +59,13 @@ public Q_SLOTS:
/**
* Send a text @p message to the remote client.
*/
- virtual void sendTextMessage(const QString &message) = 0;
+ virtual void sendMessage(const QJsonObject &message) = 0;
Q_SIGNALS:
/**
* Emitted when a new text message was received from the remote client.
*/
- void textMessageReceived(const QString &message);
+ void messageReceived(const QJsonObject &message, QWebChannelAbstractTransport *transport);
};
QT_END_NAMESPACE
diff --git a/tests/auto/qml/Client.qml b/tests/auto/qml/Client.qml
index 9904fe8..d15fd58 100644
--- a/tests/auto/qml/Client.qml
+++ b/tests/auto/qml/Client.qml
@@ -68,15 +68,17 @@ Item {
console.log("client posts message: ", message);
}
clientMessages.push(message);
- serverTransport.textMessageReceived(message);
+ serverTransport.receiveMessage(message);
}
Component.onCompleted: {
- serverTransport.sendTextMessageRequested.connect(function(message) {
+ serverTransport.sendMessageRequested.connect(function(message) {
if (debug) {
console.log("client received message: ", message);
}
- onmessage({data:message});
+ if (onmessage) {
+ onmessage({data:message});
+ }
});
}
}
@@ -128,8 +130,11 @@ Item {
function awaitMessage()
{
var msg = awaitRawMessage()
+ if (debug) {
+ console.log("handling message: ", msg);
+ }
if (!msg) {
- return msg;
+ return false;
}
return JSON.parse(msg);
}
@@ -138,17 +143,15 @@ Item {
{
var msg = awaitMessage();
verify(msg);
- verify(msg.data);
- verify(msg.data.type);
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.init);
+ verify(msg.type);
+ compare(msg.type, JSClient.QWebChannelMessageTypes.init);
}
function awaitIdle()
{
var msg = awaitMessage();
verify(msg);
- verify(msg.data);
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.idle);
+ compare(msg.type, JSClient.QWebChannelMessageTypes.idle);
verify(webChannel.clientIsIdle())
}
@@ -158,8 +161,7 @@ Item {
do {
msg = awaitMessage();
verify(msg);
- verify(msg.data);
- } while (msg.data.type === JSClient.QWebChannelMessageTypes.idle);
+ } while (msg.type === JSClient.QWebChannelMessageTypes.idle);
return msg;
}
diff --git a/tests/auto/qml/testtransport.cpp b/tests/auto/qml/testtransport.cpp
index a332955..d73a4b8 100644
--- a/tests/auto/qml/testtransport.cpp
+++ b/tests/auto/qml/testtransport.cpp
@@ -41,6 +41,10 @@
#include "testtransport.h"
+#include <QDebug>
+#include <QJsonDocument>
+#include <QJsonObject>
+
QT_BEGIN_NAMESPACE
TestTransport::TestTransport(QObject *parent)
@@ -49,9 +53,25 @@ TestTransport::TestTransport(QObject *parent)
}
-void TestTransport::sendTextMessage(const QString &message)
+void TestTransport::sendMessage(const QJsonObject &message)
+{
+ emit sendMessageRequested(message);
+}
+
+void TestTransport::receiveMessage(const QString &message)
{
- emit sendTextMessageRequested(message);
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error);
+ if (error.error) {
+ qWarning("Failed to parse JSON message: %s\nError is: %s",
+ qPrintable(message), qPrintable(error.errorString()));
+ return;
+ } else if (!doc.isObject()) {
+ qWarning("Received JSON message that is not an object: %s",
+ qPrintable(message));
+ return;
+ }
+ emit messageReceived(doc.object(), this);
}
QT_END_NAMESPACE
diff --git a/tests/auto/qml/testtransport.h b/tests/auto/qml/testtransport.h
index dd07832..ed7eae9 100644
--- a/tests/auto/qml/testtransport.h
+++ b/tests/auto/qml/testtransport.h
@@ -52,10 +52,12 @@ class TestTransport : public QWebChannelAbstractTransport
public:
explicit TestTransport(QObject *parent = 0);
- virtual void sendTextMessage(const QString &message);
+ virtual void sendMessage(const QJsonObject &message) Q_DECL_OVERRIDE;
+
+ Q_INVOKABLE void receiveMessage(const QString &message);
Q_SIGNALS:
- void sendTextMessageRequested(const QString &message);
+ void sendMessageRequested(const QJsonObject &message);
};
QT_END_NAMESPACE
diff --git a/tests/auto/qml/tst_metaobjectpublisher.qml b/tests/auto/qml/tst_metaobjectpublisher.qml
deleted file mode 100644
index 9b2088c..0000000
--- a/tests/auto/qml/tst_metaobjectpublisher.qml
+++ /dev/null
@@ -1,320 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the QtWebChannel module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.0
-import QtTest 1.0
-
-import QtWebChannel 1.0
-import QtWebChannel.Tests 1.0
-import "qrc:///qtwebchannel/qwebchannel.js" as JSClient
-
-TestCase {
- name: "MetaObjectPublisher"
-
- Client {
- id: client
- }
-
- property var lastMethodArg
-
- QtObject {
- id: myObj
- property int myProperty: 1
-
- signal mySignal(var arg)
-
- function myMethod(arg)
- {
- lastMethodArg = arg;
- }
-
- WebChannel.id: "myObj"
- }
- QtObject {
- id: myOtherObj
- property var foo: 1
- property var bar: 1
- WebChannel.id: "myOtherObj"
- }
- QtObject {
- id: myFactory
- property var lastObj
- function create(id)
- {
- lastObj = component.createObject(myFactory, {objectName: id});
- return lastObj;
- }
- WebChannel.id: "myFactory"
- }
-
- Component {
- id: component
- QtObject {
- property var myProperty : 0
- function myMethod(arg) {
- mySignal(arg, myProperty);
- }
- signal mySignal(var arg1, var arg2)
- }
- }
-
- TestWebChannel {
- id: webChannel
- transports: [client.serverTransport]
- registeredObjects: [myObj, myOtherObj, myFactory]
- }
-
- function init()
- {
- myObj.myProperty = 1
- client.cleanup();
- }
-
- function test_property()
- {
- var channel = client.createChannel(function(channel) {
- channel.exec({label: "init", value: channel.objects.myObj.myProperty});
- channel.objects.myObj.myPropertyChanged.connect(function() {
- channel.exec({label: "changed", value: channel.objects.myObj.myProperty});
- });
- channel.subscribe("setProperty", function(newValue) {
- channel.objects.myObj.myProperty = newValue;
- });
- });
-
- client.awaitInit();
- var msg = client.awaitMessageSkipIdle();
- compare(msg.data.label, "init");
- compare(msg.data.value, 1);
- compare(myObj.myProperty, 1);
-
- // change property, should be propagated to HTML client and a message be send there
- myObj.myProperty = 2;
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.label, "changed");
- compare(msg.data.value, 2);
- compare(myObj.myProperty, 2);
-
- // now trigger a write from the client side
- webChannel.sendMessage("setProperty", 3);
- msg = client.awaitMessageSkipIdle();
- compare(myObj.myProperty, 3);
-
- // the above write is also propagated to the HTML client
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.label, "changed");
- compare(msg.data.value, 3);
-
- client.awaitIdle();
- }
-
- function test_method()
- {
- var channel = client.createChannel(function (channel) {
- channel.subscribe("invokeMethod", function (arg) {
- channel.objects.myObj.myMethod(arg);
- });
- });
-
- client.awaitInit();
- client.awaitIdle();
-
- webChannel.sendMessage("invokeMethod", "test");
-
- var msg = client.awaitMessage();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.invokeMethod);
- compare(msg.data.object, "myObj");
- compare(msg.data.args, ["test"]);
-
- compare(lastMethodArg, "test")
- }
-
- function test_signal()
- {
- var channel = client.createChannel(function(channel) {
- channel.objects.myObj.mySignal.connect(function(arg) {
- channel.exec({label: "signalReceived", value: arg});
- });
- });
- client.awaitInit();
-
- var msg = client.awaitMessage();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.connectToSignal);
- compare(msg.data.object, "myObj");
-
- client.awaitIdle();
-
- myObj.mySignal("test");
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.label, "signalReceived");
- compare(msg.data.value, "test");
- }
-
- function test_grouping()
- {
- var channel = client.createChannel(function(channel) {
- channel.subscribe(JSClient.QWebChannelMessageTypes.propertyUpdate, function() {
- channel.exec({label: "gotPropertyUpdate", values: [channel.objects.myObj.myProperty, channel.objects.myOtherObj.foo, channel.objects.myOtherObj.bar]});
- });
- });
- client.awaitInit();
- client.awaitIdle();
-
- // change properties a lot, we expect this to be grouped into a single update notification
- for (var i = 0; i < 10; ++i) {
- myObj.myProperty = i;
- myOtherObj.foo = i;
- myOtherObj.bar = i;
- }
-
- var msg = client.awaitMessage();
- verify(msg);
- compare(msg.data.label, "gotPropertyUpdate");
- compare(msg.data.values, [myObj.myProperty, myOtherObj.foo, myOtherObj.bar]);
-
- client.awaitIdle();
- }
-
- function test_wrapper()
- {
- var channel = client.createChannel(function(channel) {
- channel.objects.myFactory.create("testObj", function(obj) {
- channel.objects["testObj"] = obj;
- obj.mySignal.connect(function(arg1, arg2) {
- channel.exec({label: "signalReceived", args: [arg1, arg2]});
- });
- obj.myProperty = 42;
- obj.myMethod("foobar");
- });
- channel.subscribe("triggerDelete", function() {
- channel.objects.testObj.deleteLater();
- });
- channel.subscribe("report", function() {
- channel.exec({label:"report", obj: channel.objects.testObj})
- });
- });
- client.awaitInit();
-
- var msg = client.awaitMessageSkipIdle();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.invokeMethod);
- compare(msg.data.object, "myFactory");
- verify(myFactory.lastObj);
- compare(myFactory.lastObj.objectName, "testObj");
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.connectToSignal);
- verify(msg.data.object);
- var objId = msg.data.object;
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.connectToSignal);
- compare(msg.data.object, objId);
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.setProperty);
- compare(msg.data.object, objId);
- compare(myFactory.lastObj.myProperty, 42);
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.invokeMethod);
- compare(msg.data.object, objId);
- compare(msg.data.args, ["foobar"]);
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.label, "signalReceived");
- compare(msg.data.args, ["foobar", 42]);
-
- // pass QObject* on the fly and trigger deleteLater from client side
- webChannel.sendMessage("triggerDelete");
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.invokeMethod);
- compare(msg.data.object, objId);
-
- client.awaitIdle();
-
- webChannel.sendMessage("report");
-
- msg = client.awaitMessageSkipIdle();
- compare(msg.data.label, "report");
- compare(msg.data.obj, {});
- }
-
- function test_disconnect()
- {
- var channel = client.createChannel(function(channel) {
- channel.objects.myObj.mySignal.connect(function(arg) {
- channel.exec({label: "mySignalReceived", args: [arg]});
- channel.objects.myObj.mySignal.disconnect(this);
- });
- channel.subscribe("report", function() {
- channel.exec({label: "report"});
- });
- });
- client.awaitInit();
-
- var msg = client.awaitMessage();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.connectToSignal);
- compare(msg.data.object, "myObj");
-
- client.awaitIdle();
-
- myObj.mySignal(42);
-
- msg = client.awaitMessage();
- compare(msg.data.label, "mySignalReceived");
- compare(msg.data.args, [42]);
-
- msg = client.awaitMessage();
- compare(msg.data.type, JSClient.QWebChannelMessageTypes.disconnectFromSignal);
- compare(msg.data.object, "myObj");
-
- myObj.mySignal(0);
-
- // apparently one cannot expect failure in QML, so trigger another message
- // and verify no mySignalReceived was triggered by the above emission
- webChannel.sendMessage("report");
-
- msg = client.awaitMessage();
- compare(msg.data.label, "report");
- }
-}
diff --git a/tests/auto/qml/tst_multiclient.qml b/tests/auto/qml/tst_multiclient.qml
index bca2a03..66357d1 100644
--- a/tests/auto/qml/tst_multiclient.qml
+++ b/tests/auto/qml/tst_multiclient.qml
@@ -56,9 +56,9 @@ TestCase {
id: client2
}
+ property int bar: 0
QtObject {
id: foo
- property int bar: 0
signal ping()
@@ -86,7 +86,7 @@ TestCase {
{
channel.objects.foo.ping.connect(function() {
channel.objects.foo.pong(function(value) {
- channel.exec({pongAnswer: value});
+ channel.pongAnswer = value;
});
});
}
@@ -108,12 +108,7 @@ TestCase {
client1.awaitMessage();
client2.awaitMessage();
- var msg = client1.awaitMessage();
- compare(msg.data.pongAnswer, 1);
- msg = client2.awaitMessage();
- compare(msg.data.pongAnswer, 2);
-
- client1.awaitIdle();
- client2.awaitIdle();
+ compare(c1.pongAnswer, 1);
+ compare(c2.pongAnswer, 2);
}
}
diff --git a/tests/auto/qml/tst_webchannel.qml b/tests/auto/qml/tst_webchannel.qml
index 53c2b38..8ac59eb 100644
--- a/tests/auto/qml/tst_webchannel.qml
+++ b/tests/auto/qml/tst_webchannel.qml
@@ -44,6 +44,7 @@ import QtTest 1.0
import QtWebChannel 1.0
import QtWebChannel.Tests 1.0
+import "qrc:///qtwebchannel/qwebchannel.js" as JSClient
TestCase {
name: "WebChannel"
@@ -52,35 +53,248 @@ TestCase {
id: client
}
+ property var lastMethodArg
+
+ QtObject {
+ id: myObj
+ property int myProperty: 1
+
+ signal mySignal(var arg)
+
+ function myMethod(arg)
+ {
+ lastMethodArg = arg;
+ }
+
+ WebChannel.id: "myObj"
+ }
+ QtObject {
+ id: myOtherObj
+ property var foo: 1
+ property var bar: 1
+ WebChannel.id: "myOtherObj"
+ }
+ QtObject {
+ id: myFactory
+ property var lastObj
+ function create(id)
+ {
+ lastObj = component.createObject(myFactory, {objectName: id});
+ return lastObj;
+ }
+ WebChannel.id: "myFactory"
+ }
+
+ Component {
+ id: component
+ QtObject {
+ property var myProperty : 0
+ function myMethod(arg) {
+ lastMethodArg = arg;
+ }
+ signal mySignal(var arg1, var arg2)
+ }
+ }
+
TestWebChannel {
id: webChannel
transports: [client.serverTransport]
+ registeredObjects: [myObj, myOtherObj, myFactory]
}
- function cleanup()
+ function init()
{
+ myObj.myProperty = 1
client.cleanup();
}
- function test_receiveRawMessage()
+ function test_property()
{
- var channel = client.createChannel(function (channel) {
- channel.send("foobar");
- }, true /* raw */);
- compare(client.awaitRawMessage(), "foobar");
+ compare(myObj.myProperty, 1);
+
+ var initialValue;
+ var changedValue;
+
+ var channel = client.createChannel(function(channel) {
+ initialValue = channel.objects.myObj.myProperty;
+ channel.objects.myObj.myPropertyChanged.connect(function() {
+ changedValue = channel.objects.myObj.myProperty;
+ });
+ // now trigger a write from the client side
+ channel.objects.myObj.myProperty = 3;
+ });
+
+ client.awaitInit();
+ var msg = client.awaitMessage();
+
+ compare(initialValue, 1);
+ compare(myObj.myProperty, 3);
+
+ client.awaitIdle();
+
+ // change property, should be propagated to HTML client and a message be send there
+ myObj.myProperty = 2;
+ compare(myObj.myProperty, 2);
+ client.awaitIdle();
+ compare(changedValue, 2);
}
- function test_sendMessage()
+ function test_method()
{
var channel = client.createChannel(function (channel) {
- channel.subscribe("myMessage", function(payload) {
- channel.send("myMessagePong:" + payload);
+ channel.objects.myObj.myMethod("test");
+ });
+
+ client.awaitInit();
+
+ var msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.invokeMethod);
+ compare(msg.object, "myObj");
+ compare(msg.args, ["test"]);
+
+ compare(lastMethodArg, "test")
+
+ client.awaitIdle();
+ }
+
+ function test_signal()
+ {
+ var signalReceivedArg;
+ var channel = client.createChannel(function(channel) {
+ channel.objects.myObj.mySignal.connect(function(arg) {
+ signalReceivedArg = arg;
+ });
+ });
+ client.awaitInit();
+
+ var msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.connectToSignal);
+ compare(msg.object, "myObj");
+
+ client.awaitIdle();
+
+ myObj.mySignal("test");
+
+ compare(signalReceivedArg, "test");
+ }
+
+ function test_grouping()
+ {
+ var receivedPropertyUpdates = 0;
+ var properties = 0;
+ var channel = client.createChannel(function(channel) {
+ var originalHandler = channel.handlePropertyUpdate;
+ channel.handlePropertyUpdate = function(message) {
+ originalHandler(message);
+ receivedPropertyUpdates++;
+ properties = [channel.objects.myObj.myProperty, channel.objects.myOtherObj.foo, channel.objects.myOtherObj.bar];
+ };
+ });
+ client.awaitInit();
+ client.awaitIdle();
+
+ // change properties a lot, we expect this to be grouped into a single update notification
+ for (var i = 0; i < 10; ++i) {
+ myObj.myProperty = i;
+ myOtherObj.foo = i;
+ myOtherObj.bar = i;
+ }
+
+ client.awaitIdle();
+ compare(receivedPropertyUpdates, 1);
+ compare(properties, [myObj.myProperty, myOtherObj.foo, myOtherObj.bar]);
+ verify(!client.awaitMessage());
+ }
+
+ function test_wrapper()
+ {
+ var signalArgs;
+ var testObjBeforeDeletion;
+ var testObjAfterDeletion;
+ var channel = client.createChannel(function(channel) {
+ channel.objects.myFactory.create("testObj", function(obj) {
+ channel.objects.testObj = obj;
+ obj.mySignal.connect(function() {
+ signalArgs = arguments;
+ testObjBeforeDeletion = obj;
+ obj.deleteLater();
+ testObjAfterDeletion = obj;
+ });
+ obj.myProperty = 42;
+ obj.myMethod("foobar");
+ });
+ });
+ client.awaitInit();
+
+ var msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.invokeMethod);
+ compare(msg.object, "myFactory");
+ verify(myFactory.lastObj);
+ compare(myFactory.lastObj.objectName, "testObj");
+
+ // deleteLater signal connection
+ msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.connectToSignal);
+ verify(msg.object);
+ var objId = msg.object;
+
+ // mySignal connection
+ msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.connectToSignal);
+ compare(msg.object, objId);
+
+ msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.setProperty);
+ compare(msg.object, objId);
+ compare(myFactory.lastObj.myProperty, 42);
+
+ msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.invokeMethod);
+ compare(msg.object, objId);
+ compare(msg.args, ["foobar"]);
+ compare(lastMethodArg, "foobar");
+
+ myFactory.lastObj.mySignal("foobar", 42);
+
+ // deleteLater call
+ msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.invokeMethod);
+ compare(msg.object, objId);
+
+ compare(signalArgs, {"0": "foobar", "1": 42});
+
+ client.awaitIdle();
+
+ compare(JSON.stringify(testObjBeforeDeletion), JSON.stringify({}));
+ compare(JSON.stringify(testObjAfterDeletion), JSON.stringify({}));
+ compare(JSON.stringify(channel.objects.testObj), JSON.stringify({}));
+ }
+
+ function test_disconnect()
+ {
+ var signalArg;
+ var channel = client.createChannel(function(channel) {
+ channel.objects.myObj.mySignal.connect(function(arg) {
+ signalArg = arg;
+ channel.objects.myObj.mySignal.disconnect(this);
});
- channel.send("initialized");
- }, true /* raw */);
+ });
+ client.awaitInit();
+
+ var msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.connectToSignal);
+ compare(msg.object, "myObj");
+
+ client.awaitIdle();
+
+ myObj.mySignal(42);
+ compare(signalArg, 42);
+
+ msg = client.awaitMessage();
+ compare(msg.type, JSClient.QWebChannelMessageTypes.disconnectFromSignal);
+ compare(msg.object, "myObj");
- compare(client.awaitRawMessage(), "initialized");
- webChannel.sendMessage("myMessage", "foobar");
- compare(client.awaitRawMessage(), "myMessagePong:foobar");
+ myObj.mySignal(0);
+ compare(signalArg, 42);
}
}
diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp
index 632d055..900a6bb 100644
--- a/tests/auto/webchannel/tst_webchannel.cpp
+++ b/tests/auto/webchannel/tst_webchannel.cpp
@@ -227,19 +227,19 @@ void TestWebChannel::testInvokeMethodConversion()
{
int method = metaObject()->indexOfMethod("setInt(int)");
QVERIFY(method != -1);
- QVERIFY(!channel.d_func()->publisher->invokeMethod(this, method, args, QJsonValue()).isEmpty());
+ channel.d_func()->publisher->invokeMethod(this, method, args);
QCOMPARE(m_lastInt, args.at(0).toInt());
}
{
int method = metaObject()->indexOfMethod("setDouble(double)");
QVERIFY(method != -1);
- QVERIFY(!channel.d_func()->publisher->invokeMethod(this, method, args, QJsonValue()).isEmpty());
+ channel.d_func()->publisher->invokeMethod(this, method, args);
QCOMPARE(m_lastDouble, args.at(0).toDouble());
}
{
int method = metaObject()->indexOfMethod("setVariant(QVariant)");
QVERIFY(method != -1);
- QVERIFY(!channel.d_func()->publisher->invokeMethod(this, method, args, QJsonValue()).isEmpty());
+ channel.d_func()->publisher->invokeMethod(this, method, args);
QCOMPARE(m_lastVariant, args.at(0).toVariant());
}
}
diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h
index 4811a5c..bd403dc 100644
--- a/tests/auto/webchannel/tst_webchannel.h
+++ b/tests/auto/webchannel/tst_webchannel.h
@@ -57,7 +57,7 @@ public:
~DummyTransport() {};
public slots:
- void sendTextMessage(const QString &/*message*/) Q_DECL_OVERRIDE
+ void sendMessage(const QJsonObject &/*message*/) Q_DECL_OVERRIDE
{
}
};