summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSumedha Widyadharma <sumedha.widyadharma@basyskom.com>2014-08-07 16:05:05 +0200
committerMilian Wolff <milian.wolff@kdab.com>2014-12-15 15:15:38 +0100
commit9fdce8e443030ab99d31e42fffc977cf284c36c4 (patch)
tree692dae756b25c5ee433550a209e5f950d27b244e
parent0a43a43a166d2e2b551f543de61090637bd8b387 (diff)
downloadqtwebchannel-9fdce8e443030ab99d31e42fffc977cf284c36c4.tar.gz
Separate registered and autoregistered QObjects
Currently, a new client gets a list of _all_ registered QObjects, whether they were explicitly registered or not. This leaks internal information which the clients cannot use right away anyway. Change-Id: I4b25a731e9bc2d646f903057b409aecd34dc7f11 Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp53
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h10
-rw-r--r--tests/auto/qml/Client.qml109
-rw-r--r--tests/auto/qml/qml.pro4
-rw-r--r--tests/auto/qml/tst_multiclient.qml143
5 files changed, 254 insertions, 65 deletions
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index 527b3df..3e78ebc 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -398,38 +398,42 @@ void QMetaObjectPublisher::objectDestroyed(const QObject *object)
{
const QString &id = registeredObjectIds.take(object);
Q_ASSERT(!id.isEmpty());
- bool removed = registeredObjects.remove(id);
+ bool removed = registeredObjects.remove(id)
+ || wrappedObjects.remove(id);
Q_ASSERT(removed);
Q_UNUSED(removed);
signalToPropertyMap.remove(object);
pendingPropertyUpdates.remove(object);
- wrappedObjects.remove(object);
}
QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result)
{
if (QObject *object = result.value<QObject *>()) {
- QJsonObject &objectInfo = wrappedObjects[object];
- if (!objectInfo.isEmpty()) {
- // already registered, use cached information
- Q_ASSERT(registeredObjectIds.contains(object));
- return objectInfo;
- } // else the object is not yet wrapped, do it now
-
- const QString &id = QUuid::createUuid().toString();
- Q_ASSERT(!registeredObjectIds.contains(object));
-
- QJsonObject info = classInfoForObject(object);
- objectInfo[KEY_QOBJECT] = true;
- objectInfo[KEY_ID] = id;
- objectInfo[KEY_DATA] = info;
-
- registeredObjectIds[object] = id;
- registeredObjects[id] = object;
- wrappedObjects.insert(object, objectInfo);
-
- initializePropertyUpdates(object, info);
+ QString id = registeredObjectIds.value(object);
+
+ QJsonObject objectInfo;
+
+ if (!id.isEmpty() && wrappedObjects.contains(id)) {
+ Q_ASSERT(object == wrappedObjects.value(id).object);
+ return wrappedObjects.value(id).info;
+ } else {
+ id = QUuid::createUuid().toString();
+
+ QJsonObject info = classInfoForObject(object);
+ objectInfo[KEY_QOBJECT] = true;
+ objectInfo[KEY_ID] = id;
+ objectInfo[KEY_DATA] = info;
+
+ if (!registeredObjects.contains(id)) {
+ registeredObjectIds[object] = id;
+ ObjectInfo oi = { object, objectInfo };
+ wrappedObjects.insert(id, oi);
+
+ initializePropertyUpdates(object, info);
+ }
+ }
+
return objectInfo;
}
@@ -439,7 +443,7 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result)
void QMetaObjectPublisher::deleteWrappedObject(QObject *object) const
{
- if (!wrappedObjects.contains(object)) {
+ if (!wrappedObjects.contains(registeredObjectIds.value(object))) {
qWarning() << "Not deleting non-wrapped object" << object;
return;
}
@@ -486,6 +490,9 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel
} else if (message.contains(KEY_OBJECT)) {
const QString &objectName = message.value(KEY_OBJECT).toString();
QObject *object = registeredObjects.value(objectName);
+ if (!object)
+ object = wrappedObjects.value(objectName).object;
+
if (!object) {
qWarning() << "Unknown object encountered" << objectName;
return;
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index eda17d9..6ba5ee7 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -40,6 +40,8 @@
#include <QStringList>
#include <QMetaObject>
#include <QBasicTimer>
+#include <QPointer>
+#include <QJsonValue>
#include "qwebchannelglobal.h"
@@ -217,8 +219,14 @@ 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<const QObject *, QJsonObject> wrappedObjects;
+ 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
diff --git a/tests/auto/qml/Client.qml b/tests/auto/qml/Client.qml
index 20da8f6..6e12993 100644
--- a/tests/auto/qml/Client.qml
+++ b/tests/auto/qml/Client.qml
@@ -39,12 +39,15 @@ import QtWebChannel.Tests 1.0
import "qrc:///qtwebchannel/qwebchannel.js" as JSClient
Item {
+ id: root
+
TestTransport {
id: serverTransport
}
readonly property var serverTransport: serverTransport
property var clientMessages: []
+ property var serverMessages: []
property bool debug: false
@@ -55,24 +58,21 @@ Item {
function send(message)
{
- if (debug) {
- console.log("client posts message: ", message, "is idle:", webChannel.clientIsIdle());
- }
- clientMessages.push(message);
+ if (debug)
+ console.log("client", (root.objectName ? "(" + root.objectName + ")" : ""), "posts message: ", message, "is idle:", webChannel.clientIsIdle());
+ clientMessages.push(JSON.parse(message));
serverTransport.receiveMessage(message);
- if (message && message.type && message.type === JSClient.QWebChannelMessageTypes.idle) {
+ if (message && message.type && message.type === JSClient.QWebChannelMessageTypes.idle)
verify(webChannel.clientIsIdle());
- }
}
Component.onCompleted: {
- serverTransport.sendMessageRequested.connect(function(message) {
- if (debug) {
- console.log("client received message: ", JSON.stringify(message));
- }
- if (onmessage) {
+ serverTransport.sendMessageRequested.connect(function receive(message) {
+ if (debug)
+ console.log("client", (root.objectName ? "(" + root.objectName + ")" : ""), "received message:", JSON.stringify(message));
+ serverMessages.push(message);
+ if (onmessage)
onmessage({data:message});
- }
});
}
}
@@ -86,51 +86,82 @@ Item {
function cleanup()
{
clientMessages = [];
+ serverMessages = [];
}
- function awaitRawMessage()
+ function awaitRawMessage(from)
{
- for (var i = 0; i < 10 && !clientMessages.length; ++i) {
+ if (!from || typeof from !== "string")
+ from = "clientMessages";
+ else
+ from += "Messages";
+
+ for (var i = 0; i < 10 && !root[from].length; ++i)
wait(10);
- }
- return clientMessages.shift();
+ return root[from].shift();
}
- function awaitMessage()
+ function awaitMessage(from)
{
- var msg = awaitRawMessage()
- if (debug) {
- console.log("handling message: ", msg);
- }
- if (!msg) {
+ var msg = awaitRawMessage(from)
+ if (debug)
+ console.log((root.objectName ? "(" + root.objectName + ")" : ""), "handling message: ", JSON.stringify(msg));
+ if (!msg)
return false;
+ return msg;
+ }
+
+ function await(type, from, skip) {
+ var msg;
+ do {
+ msg = awaitMessage();
+ verify(msg);
+ } while (skip && (msg.type === JSClient.QWebChannelMessageTypes.idle));
+ if (type !== null) {
+ verify(msg);
+ verify(msg.type);
+ compare(msg.type, type);
}
- return JSON.parse(msg);
+ return msg;
+ }
+
+ function awaitInit() {
+ return await(JSClient.QWebChannelMessageTypes.init);
+ }
+
+ function awaitIdle() {
+ return await(JSClient.QWebChannelMessageTypes.idle);
+ }
+
+ function awaitMessageSkipIdle() {
+ return awaitFunc(null, null, true);
+ }
+
+ function awaitServerInit() {
+ return await(JSClient.QWebChannelMessageTypes.init, "server");
}
- function awaitInit()
+ function awaitSignal()
{
- var msg = awaitMessage();
- verify(msg);
- verify(msg.type);
- compare(msg.type, JSClient.QWebChannelMessageTypes.init);
+ return await(JSClient.QWebChannelMessageTypes.signal, "server");
}
- function awaitIdle()
+ function awaitPropertyUpdate()
{
- var msg = awaitMessage();
- verify(msg);
- compare(msg.type, JSClient.QWebChannelMessageTypes.idle);
+ return await(JSClient.QWebChannelMessageTypes.propertyUpdate, "server");
}
- function awaitMessageSkipIdle()
+ function awaitResponse()
{
- var msg;
- do {
- msg = awaitMessage();
- verify(msg);
- } while (msg.type === JSClient.QWebChannelMessageTypes.idle);
- return msg;
+ return await(JSClient.QWebChannelMessageTypes.response, "server");
}
+ function skipToMessage(type, from, max) {
+ do {
+ var msg = awaitMessage(from);
+ if (msg && msg.type === type)
+ return msg
+ } while (--max > 0);
+ return false;
+ }
}
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index b0c52b5..89c5f56 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -17,9 +17,11 @@ HEADERS += \
testwebchannel.h
OTHER_FILES += \
+ Client.qml \
WebChannelTest.qml \
tst_webchannel.qml \
tst_metaobjectpublisher.qml \
- tst_bench.qml
+ tst_bench.qml \
+ tst_multiclient.qml \
TESTDATA = data/*
diff --git a/tests/auto/qml/tst_multiclient.qml b/tests/auto/qml/tst_multiclient.qml
index b696391..115857d 100644
--- a/tests/auto/qml/tst_multiclient.qml
+++ b/tests/auto/qml/tst_multiclient.qml
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Copyright (C) 2014 basysKom GmbH, info@basyskom.com, author Lutz Schönemann <lutz.schoenemann@basyskom.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtWebChannel module of the Qt Toolkit.
@@ -37,6 +38,8 @@ import QtTest 1.0
import QtWebChannel 1.0
import QtWebChannel.Tests 1.0
+import "qrc:///qtwebchannel/qwebchannel.js" as JSClient
+
TestCase {
name: "MultiClient"
@@ -62,18 +65,85 @@ TestCase {
WebChannel.id: "foo"
}
+ 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
+ property var createdObjects: []
+
+ function cleanup() {
+ while (createdObjects.length) {
+ var obj = createdObjects.shift();
+ if (obj) {
+ obj.destroy();
+ }
+ }
+ }
+
+ function create(id)
+ {
+ lastObj = component.createObject(myFactory, {objectName: id});
+ createdObjects.push(lastObj);
+ 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: [client1.serverTransport, client2.serverTransport]
- registeredObjects: [foo]
+ registeredObjects: [foo, myObj, myOtherObj, myFactory]
}
function init()
{
+ myObj.myProperty = 1
client1.cleanup();
client2.cleanup();
}
+ function cleanup() {
+ client1.debug = false;
+ client2.debug = false;
+ // delete all created objects
+ myFactory.cleanup();
+ myFactory.lastObj = undefined;
+ // reschedule current task to end of event loop
+ wait(1);
+ }
+
function clientInitCallback(channel)
{
channel.objects.foo.ping.connect(function() {
@@ -103,4 +173,75 @@ TestCase {
compare(c1.pongAnswer, 1);
compare(c2.pongAnswer, 2);
}
+
+ function test_autowrappedObjectsNotInInit() {
+ var testObj1;
+ var testObj1Id;
+
+ var channel1 = client1.createChannel(function (channel1) {
+ channel1.objects.myFactory.create("testObj1", function (obj1) {
+ testObj1 = myFactory.lastObj;
+ testObj1Id = obj1.__id__;
+
+ // create second channel after factory has created first
+ // object to make sure that a dynamically created object
+ // exists but does not get exposed to new channels
+ createSecondChannel();
+ });
+ });
+ var channel2;
+ function createSecondChannel() {
+ // dismiss all messges received before channel creation
+ client2.cleanup();
+
+ channel2 = client2.createChannel(function (channel2) {
+ });
+ }
+
+ client1.awaitInit();
+ var msg1 = client1.awaitMessage();
+ compare(msg1.type, JSClient.QWebChannelMessageTypes.invokeMethod); // create
+
+ client1.awaitIdle();
+
+ client2.awaitInit();
+ client2.awaitIdle();
+
+ compare(typeof channel2.objects[testObj1Id], "undefined")
+ }
+
+ function test_autowrappedObjectsNotBroadcasted() {
+ var testObj2;
+ var testObj2Id;
+
+ var channel1 = client1.createChannel(function (channel1) {
+ // create second channel after first channel to make sure
+ // that a dynamically created object do not get exposed to
+ // existing channels
+ createSecondChannel();
+ });
+ var channel2;
+ function createSecondChannel() {
+ // dismiss all messges received before channel creation
+ client2.cleanup();
+
+ channel2 = client2.createChannel(function (channel2) {
+ channel2.objects.myFactory.create("testObj2", function (obj2) {
+ testObj2 = myFactory.lastObj;
+ testObj2Id = obj2.__id__;
+ });
+ });
+ }
+
+ client1.awaitInit();
+ client1.awaitIdle();
+
+ client2.awaitInit();
+ var msg2 = client2.awaitMessage();
+ compare(msg2.type, JSClient.QWebChannelMessageTypes.invokeMethod); // create
+
+ client2.awaitIdle();
+
+ compare(typeof channel1.objects[testObj2Id], "undefined")
+ }
}