From 3ed29bca08dec484003cf573dd428b73627ffa81 Mon Sep 17 00:00:00 2001 From: Bernd Lamecker Date: Tue, 12 Aug 2014 16:46:59 +0200 Subject: 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 --- src/webchannel/qmetaobjectpublisher.cpp | 69 ++++++++++++++++++++++++++------- src/webchannel/qmetaobjectpublisher_p.h | 34 +++++++++------- 2 files changed, 76 insertions(+), 27 deletions(-) (limited to 'src/webchannel') 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 #include #include +#include #include QT_BEGIN_NAMESPACE @@ -272,6 +273,7 @@ void QMetaObjectPublisher::sendPendingPropertyUpdates() } QJsonArray data; + QHash 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::const_iterator suend = specificUpdates.constEnd(); + for (QHash::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()) { 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()) + jsvVariant = result.value().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 #include #include -#include +#include #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 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 transports; + }; + + // Map of objects wrapped from invocation returns + QHash 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 > SignalToPropertyNameMap; @@ -219,15 +236,6 @@ private: typedef QHash PendingPropertyUpdates; PendingPropertyUpdates pendingPropertyUpdates; - // Struct containing the object itself and its ObjectInfo - struct ObjectInfo { - QObject* object; - QJsonValue info; - }; - - // Maps wrapped object to class info - QHash 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. -- cgit v1.2.1