summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBernd Lamecker <bernd.lamecker@basyskom.com>2014-08-12 16:46:59 +0200
committerMilian Wolff <milian.wolff@kdab.com>2014-12-19 11:39:52 +0100
commit3ed29bca08dec484003cf573dd428b73627ffa81 (patch)
tree8e688a00c409cbd4a37412342faa2185b7b3869d /src
parent9fdce8e443030ab99d31e42fffc977cf284c36c4 (diff)
downloadqtwebchannel-3ed29bca08dec484003cf573dd428b73627ffa81.tar.gz
Do not broadcast signals or property changes of wrapped qobjects
Signals and property changes caused by dynamically created qobjects (qobjects returned from a method call at runtime) should not be broadcasted to any client but only sent to clients which know these qobjects. Also added testcases for the changes. Change-Id: I9aacfa9e7e9df9314b44c6ba8e7339a2069e3c37 Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
Diffstat (limited to 'src')
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp69
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h34
2 files changed, 76 insertions, 27 deletions
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index 3e78ebc..a141035 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -41,6 +41,7 @@
#include <QDebug>
#include <QJsonObject>
#include <QJsonArray>
+#include <QJSValue>
#include <QUuid>
QT_BEGIN_NAMESPACE
@@ -272,6 +273,7 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates()
}
QJsonArray data;
+ QHash<QWebChannelAbstractTransport*, QJsonArray> specificUpdates;
// convert pending property updates to JSON data
const PendingPropertyUpdates::const_iterator end = pendingPropertyUpdates.constEnd();
@@ -294,21 +296,41 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates()
sigs[QString::number(sigIt.key())] = QJsonArray::fromVariantList(sigIt.value());
}
QJsonObject obj;
- obj[KEY_OBJECT] = registeredObjectIds.value(object);
+ const QString objectId = registeredObjectIds.value(object);
+ obj[KEY_OBJECT] = objectId;
obj[KEY_SIGNALS] = sigs;
obj[KEY_PROPERTIES] = properties;
- data.push_back(obj);
+
+ // if the object is auto registered, just send the update only to clients which know this object
+ if (wrappedObjects.contains(objectId)) {
+ foreach (QWebChannelAbstractTransport *transport, wrappedObjects.value(objectId).transports) {
+ QJsonArray &arr = specificUpdates[transport];
+ arr.push_back(obj);
+ }
+ } else {
+ data.push_back(obj);
+ }
}
pendingPropertyUpdates.clear();
QJsonObject message;
message[KEY_TYPE] = TypePropertyUpdate;
- message[KEY_DATA] = data;
+ message[KEY_DATA] = data; // data does not contain specific updates
+
setClientIsIdle(false);
+
broadcastMessage(message);
+
+ // send every property update which is not supposed to be broadcasted
+ const QHash<QWebChannelAbstractTransport*, QJsonArray>::const_iterator suend = specificUpdates.constEnd();
+ for (QHash<QWebChannelAbstractTransport*, QJsonArray>::const_iterator it = specificUpdates.constBegin(); it != suend; ++it) {
+ message[KEY_TYPE] = TypePropertyUpdate;
+ message[KEY_DATA] = it.value();
+ it.key()->sendMessage(message);
+ }
}
-QJsonValue QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
+QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
const QJsonArray &args)
{
const QMetaMethod &method = object->metaObject()->method(methodIndex);
@@ -359,7 +381,7 @@ QJsonValue 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]);
- return wrapResult(returnValue);
+ return returnValue;
}
void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments)
@@ -381,7 +403,15 @@ void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signal
message[KEY_ARGS] = args;
}
message[KEY_TYPE] = TypeSignal;
- broadcastMessage(message);
+
+ // if the object is wrapped, just send the response to clients which know this object
+ if (wrappedObjects.contains(objectName)) {
+ foreach (QWebChannelAbstractTransport *transport, wrappedObjects.value(objectName).transports) {
+ transport->sendMessage(message);
+ }
+ } else {
+ broadcastMessage(message);
+ }
if (signalIndex == s_destroyedSignalIndex) {
objectDestroyed(object);
@@ -407,7 +437,7 @@ void QMetaObjectPublisher::objectDestroyed(const QObject *object)
pendingPropertyUpdates.remove(object);
}
-QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result)
+QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelAbstractTransport *transport)
{
if (QObject *object = result.value<QObject *>()) {
QString id = registeredObjectIds.value(object);
@@ -416,7 +446,10 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result)
if (!id.isEmpty() && wrappedObjects.contains(id)) {
Q_ASSERT(object == wrappedObjects.value(id).object);
- return wrappedObjects.value(id).info;
+ // check if this transport is already assigned to the object
+ if (!wrappedObjects.value(id).transports.contains(transport))
+ wrappedObjects[id].transports.append(transport);
+ return wrappedObjects.value(id).classinfo;
} else {
id = QUuid::createUuid().toString();
@@ -427,18 +460,25 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result)
if (!registeredObjects.contains(id)) {
registeredObjectIds[object] = id;
- ObjectInfo oi = { object, objectInfo };
+ ObjectInfo oi(object, objectInfo);
+ oi.transports.append(transport);
wrappedObjects.insert(id, oi);
initializePropertyUpdates(object, info);
}
}
-
return objectInfo;
}
- // no need to wrap this
- return QJsonValue::fromVariant(result);
+ // Workaround for keeping QJSValues from QVariant.
+ // Calling QJSValue::toVariant() converts JS-objects/arrays to QVariantMap/List
+ // instead of stashing a QJSValue itself into a variant.
+ // TODO: Improve QJSValue-QJsonValue conversion in Qt.
+ QVariant jsvVariant = result;
+ if (result.canConvert<QJSValue>())
+ jsvVariant = result.value<QJSValue>().toVariant();
+
+ return QJsonValue::fromVariant(jsvVariant);
}
void QMetaObjectPublisher::deleteWrappedObject(QObject *object) const
@@ -504,9 +544,10 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel
QJsonDocument(message).toJson().constData());
return;
}
+
transport->sendMessage(createResponse(message.value(KEY_ID),
- invokeMethod(object, message.value(KEY_METHOD).toInt(-1),
- message.value(KEY_ARGS).toArray())));
+ wrapResult(invokeMethod(object, message.value(KEY_METHOD).toInt(-1),
+ message.value(KEY_ARGS).toArray()), transport)));
} else if (type == TypeConnectToSignal) {
signalHandler.connectTo(object, message.value(KEY_SIGNAL).toInt(-1));
} else if (type == TypeDisconnectFromSignal) {
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 6ba5ee7..1d269f1 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -41,7 +41,7 @@
#include <QMetaObject>
#include <QBasicTimer>
#include <QPointer>
-#include <QJsonValue>
+#include <QJsonObject>
#include "qwebchannelglobal.h"
@@ -72,7 +72,6 @@ class QWebChannelAbstractTransport;
class Q_WEBCHANNEL_EXPORT QMetaObjectPublisher : public QObject
{
Q_OBJECT
-
public:
explicit QMetaObjectPublisher(QWebChannel *webChannel);
virtual ~QMetaObjectPublisher();
@@ -137,7 +136,7 @@ public:
* The return value of the method invocation is then serialized and a response message
* is returned.
*/
- QJsonValue invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args);
+ QVariant invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args);
/**
* Callback of the signalHandler which forwards the signal invocation to the webchannel clients.
@@ -159,7 +158,7 @@ public:
*
* TODO: support wrapping of initially-registered objects
*/
- QJsonValue wrapResult(const QVariant &result);
+ QJsonValue wrapResult(const QVariant &result, QWebChannelAbstractTransport *transport);
/**
* Invoke delete later on @p object.
@@ -208,6 +207,24 @@ private:
// Map the registered objects to their id.
QHash<const QObject *, QString> registeredObjectIds;
+ // Groups individually wrapped objects with their class information and the transports that have access to it.
+ struct ObjectInfo
+ {
+ ObjectInfo()
+ : object(Q_NULLPTR)
+ {}
+ ObjectInfo(QObject *o, const QJsonObject &i)
+ : object(o)
+ , classinfo(i)
+ {}
+ QObject *object;
+ QJsonObject classinfo;
+ QVector<QWebChannelAbstractTransport*> transports;
+ };
+
+ // Map of objects wrapped from invocation returns
+ QHash<QString, ObjectInfo> wrappedObjects;
+
// Map of objects to maps of signal indices to a set of all their property indices.
// The last value is a set as a signal can be the notify signal of multiple properties.
typedef QHash<int, QSet<int> > SignalToPropertyNameMap;
@@ -219,15 +236,6 @@ private:
typedef QHash<const QObject *, SignalToArgumentsMap> PendingPropertyUpdates;
PendingPropertyUpdates pendingPropertyUpdates;
- // Struct containing the object itself and its ObjectInfo
- struct ObjectInfo {
- QObject* object;
- QJsonValue info;
- };
-
- // Maps wrapped object to class info
- QHash<QString, ObjectInfo> wrappedObjects;
-
// Aggregate property updates since we get multiple Qt.idle message when we have multiple
// clients. They all share the same QWebProcess though so we must take special care to
// prevent message flooding.