summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp176
-rw-r--r--src/webchannel/qmetaobjectpublisher.h5
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h176
-rw-r--r--src/webchannel/signalhandler_p.h2
-rw-r--r--tests/qml/tst_bench.qml35
-rw-r--r--tests/webchannel/tst_webchannel.cpp96
-rw-r--r--tests/webchannel/tst_webchannel.h97
7 files changed, 388 insertions, 199 deletions
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index fcf809a..375cf1b 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -41,20 +41,14 @@
****************************************************************************/
#include "qmetaobjectpublisher.h"
+#include "qmetaobjectpublisher_p.h"
#include "qwebchannel.h"
-#include "variantargument_p.h"
-#include "signalhandler_p.h"
-
-#include <QStringList>
-#include <QMetaObject>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QBasicTimer>
-#include <QDebug>
-#include <QPointer>
#include <QEvent>
#include <QJsonDocument>
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonArray>
namespace {
const QString KEY_SIGNALS = QStringLiteral("signals");
@@ -95,130 +89,15 @@ const int s_destroyedSignalIndex = QObject::staticMetaObject.indexOfMethod("dest
const int PROPERTY_UPDATE_INTERVAL = 50;
}
-struct QMetaObjectPublisherPrivate
+QMetaObjectPublisherPrivate::QMetaObjectPublisherPrivate(QMetaObjectPublisher *q)
+ : q(q)
+ , signalHandler(this)
+ , clientIsIdle(false)
+ , blockUpdates(false)
+ , pendingInit(false)
+ , propertyUpdatesInitialized(false)
{
- QMetaObjectPublisherPrivate(QMetaObjectPublisher *q)
- : q(q)
- , signalHandler(this)
- , clientIsIdle(false)
- , blockUpdates(false)
- , pendingInit(false)
- , propertyUpdatesInitialized(false)
- {
- }
-
- /**
- * Set the client to idle or busy, based on the value of @p isIdle.
- *
- * When the value changed, start/stop the property update timer accordingly.
- */
- void setClientIsIdle(bool isIdle);
-
- /**
- * Initialize clients by sending them the class information of the registered objects.
- *
- * Furthermore, if that was not done already, connect to their property notify signals.
- */
- void initializeClients();
-
- /**
- * Go through all properties of the given object and connect to their notify signal.
- *
- * When receiving a notify signal, it will store the information in pendingPropertyUpdates which
- * gets send via a Qt.propertyUpdate message to the server when the grouping timer timeouts.
- */
- void initializePropertyUpdates(const QObject *const object, const QVariantMap &objectInfo);
-
- /**
- * Send the clients the new property values since the last time this function was invoked.
- *
- * This is a grouped batch of all properties for which their notify signal was emitted.
- * The list of signals as well as the arguments they contained, are also transmitted to
- * the remote clients.
- *
- * @sa timer, initializePropertyUpdates
- */
- void sendPendingPropertyUpdates();
-
- /**
- * Invoke the method of index @p methodIndex on @p object with the arguments @p args.
- *
- * The return value of the method invocation is then transmitted to the calling client
- * via a webchannel response to the message identified by @p id.
- */
- bool invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args, const QJsonValue &id);
-
- /**
- * Callback of the signalHandler which forwards the signal invocation to the webchannel clients.
- */
- void signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments);
-
- /**
- * Callback for registered or wrapped objects which erases all data related to @p object.
- *
- * @sa signalEmitted
- */
- void objectDestroyed(const QObject *object);
-
- /**
- * Given a QVariant containing a QObject*, wrap the object and register for property updates
- * return the objects class information.
- *
- * All other input types are returned as-is.
- *
- * TODO: support wrapping of initially-registered objects
- */
- QVariant wrapResult(const QVariant &result);
-
- /**
- * Invoke delete later on @p object.
- */
- void deleteWrappedObject(QObject *object) const;
-
- QMetaObjectPublisher *q;
- QPointer<QWebChannel> webChannel;
- SignalHandler<QMetaObjectPublisherPrivate> signalHandler;
-
- // true when the client is idle, false otherwise
- bool clientIsIdle;
-
- // true when no property updates should be sent, false otherwise
- bool blockUpdates;
-
- // true when at least one client needs to be initialized,
- // i.e. when a Qt.init came in which was not handled yet.
- bool pendingInit;
-
- // true when at least one client was initialized and thus
- // the property updates have been initialized and the
- // object info map set.
- bool propertyUpdatesInitialized;
-
- // Map of registered objects indexed by their id.
- QHash<QString, QObject *> registeredObjects;
-
- // Map the registered objects to their id.
- QHash<const QObject *, QString> registeredObjectIds;
-
- // Map of object names to maps of signal indices to a set of all their properties.
- // The last value is a set as a signal can be the notify signal of multiple properties.
- typedef QHash<int, QSet<QString> > SignalToPropertyNameMap;
- QHash<const QObject *, SignalToPropertyNameMap> signalToPropertyMap;
-
- // Objects that changed their properties and are waiting for idle client.
- // map of object name to map of signal index to arguments
- typedef QHash<int, QVariantList> SignalToArgumentsMap;
- typedef QHash<const QObject *, SignalToArgumentsMap> PendingPropertyUpdates;
- PendingPropertyUpdates pendingPropertyUpdates;
-
- // Maps wrapped object to class info
- QHash<const QObject *, QVariantMap> 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.
- QBasicTimer timer;
-};
+}
void QMetaObjectPublisherPrivate::setClientIsIdle(bool isIdle)
{
@@ -235,6 +114,10 @@ void QMetaObjectPublisherPrivate::setClientIsIdle(bool isIdle)
void QMetaObjectPublisherPrivate::initializeClients()
{
+ if (!webChannel) {
+ return;
+ }
+
QJsonObject objectInfos;
{
const QHash<QString, QObject *>::const_iterator end = registeredObjects.constEnd();
@@ -700,33 +583,6 @@ void QMetaObjectPublisher::setBlockUpdates(bool block)
emit blockUpdatesChanged(block);
}
-void QMetaObjectPublisher::bench_ensureUpdatesInitialized()
-{
- if (!d->propertyUpdatesInitialized) {
- d->initializeClients();
- }
-}
-
-void QMetaObjectPublisher::bench_sendPendingPropertyUpdates()
-{
- d->clientIsIdle = true;
- d->sendPendingPropertyUpdates();
-}
-
-void QMetaObjectPublisher::bench_initializeClients()
-{
- d->propertyUpdatesInitialized = false;
- d->signalToPropertyMap.clear();
- d->signalHandler.clear();
- d->initializeClients();
-}
-
-void QMetaObjectPublisher::bench_registerObjects(const QVariantMap &objects)
-{
- d->propertyUpdatesInitialized = false;
- registerObjects(objects);
-}
-
bool QMetaObjectPublisher::test_clientIsIdle() const
{
return d->clientIsIdle;
diff --git a/src/webchannel/qmetaobjectpublisher.h b/src/webchannel/qmetaobjectpublisher.h
index 684be1d..f4763a7 100644
--- a/src/webchannel/qmetaobjectpublisher.h
+++ b/src/webchannel/qmetaobjectpublisher.h
@@ -93,10 +93,6 @@ public:
void setBlockUpdates(bool block);
/// TODO: cleanup: rewrite tests in C++ and access PIMPL data from there
- Q_INVOKABLE void bench_ensureUpdatesInitialized();
- Q_INVOKABLE void bench_sendPendingPropertyUpdates();
- Q_INVOKABLE void bench_registerObjects(const QVariantMap &objects);
- Q_INVOKABLE void bench_initializeClients();
Q_INVOKABLE bool test_clientIsIdle() const;
signals:
@@ -118,6 +114,7 @@ protected:
private:
QScopedPointer<QMetaObjectPublisherPrivate> d;
friend struct QMetaObjectPublisherPrivate;
+ friend class TestWebChannel;
};
#endif // QMETAOBJECTPUBLISHER_H
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
new file mode 100644
index 0000000..e364510
--- /dev/null
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+
+#ifndef QMETAOBJECTPUBLISHER_P_H
+#define QMETAOBJECTPUBLISHER_P_H
+
+#include "variantargument_p.h"
+#include "signalhandler_p.h"
+
+#include <QStringList>
+#include <QMetaObject>
+#include <QBasicTimer>
+#include <QPointer>
+
+class QMetaObjectPublisher;
+class QWebChannel;
+
+#include "qwebchannelglobal.h"
+
+struct Q_WEBCHANNEL_EXPORT QMetaObjectPublisherPrivate
+{
+ QMetaObjectPublisherPrivate(QMetaObjectPublisher *q);
+
+ /**
+ * Set the client to idle or busy, based on the value of @p isIdle.
+ *
+ * When the value changed, start/stop the property update timer accordingly.
+ */
+ void setClientIsIdle(bool isIdle);
+
+ /**
+ * Initialize clients by sending them the class information of the registered objects.
+ *
+ * Furthermore, if that was not done already, connect to their property notify signals.
+ */
+ void initializeClients();
+
+ /**
+ * Go through all properties of the given object and connect to their notify signal.
+ *
+ * When receiving a notify signal, it will store the information in pendingPropertyUpdates which
+ * gets send via a Qt.propertyUpdate message to the server when the grouping timer timeouts.
+ */
+ void initializePropertyUpdates(const QObject *const object, const QVariantMap &objectInfo);
+
+ /**
+ * Send the clients the new property values since the last time this function was invoked.
+ *
+ * This is a grouped batch of all properties for which their notify signal was emitted.
+ * The list of signals as well as the arguments they contained, are also transmitted to
+ * the remote clients.
+ *
+ * @sa timer, initializePropertyUpdates
+ */
+ void sendPendingPropertyUpdates();
+
+ /**
+ * Invoke the method of index @p methodIndex on @p object with the arguments @p args.
+ *
+ * The return value of the method invocation is then transmitted to the calling client
+ * via a webchannel response to the message identified by @p id.
+ */
+ bool invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args, const QJsonValue &id);
+
+ /**
+ * Callback of the signalHandler which forwards the signal invocation to the webchannel clients.
+ */
+ void signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments);
+
+ /**
+ * Callback for registered or wrapped objects which erases all data related to @p object.
+ *
+ * @sa signalEmitted
+ */
+ void objectDestroyed(const QObject *object);
+
+ /**
+ * Given a QVariant containing a QObject*, wrap the object and register for property updates
+ * return the objects class information.
+ *
+ * All other input types are returned as-is.
+ *
+ * TODO: support wrapping of initially-registered objects
+ */
+ QVariant wrapResult(const QVariant &result);
+
+ /**
+ * Invoke delete later on @p object.
+ */
+ void deleteWrappedObject(QObject *object) const;
+
+ QMetaObjectPublisher *q;
+ QPointer<QWebChannel> webChannel;
+ SignalHandler<QMetaObjectPublisherPrivate> signalHandler;
+
+ // true when the client is idle, false otherwise
+ bool clientIsIdle;
+
+ // true when no property updates should be sent, false otherwise
+ bool blockUpdates;
+
+ // true when at least one client needs to be initialized,
+ // i.e. when a Qt.init came in which was not handled yet.
+ bool pendingInit;
+
+ // true when at least one client was initialized and thus
+ // the property updates have been initialized and the
+ // object info map set.
+ bool propertyUpdatesInitialized;
+
+ // Map of registered objects indexed by their id.
+ QHash<QString, QObject *> registeredObjects;
+
+ // Map the registered objects to their id.
+ QHash<const QObject *, QString> registeredObjectIds;
+
+ // Map of object names to maps of signal indices to a set of all their properties.
+ // The last value is a set as a signal can be the notify signal of multiple properties.
+ typedef QHash<int, QSet<QString> > SignalToPropertyNameMap;
+ QHash<const QObject *, SignalToPropertyNameMap> signalToPropertyMap;
+
+ // Objects that changed their properties and are waiting for idle client.
+ // map of object name to map of signal index to arguments
+ typedef QHash<int, QVariantList> SignalToArgumentsMap;
+ typedef QHash<const QObject *, SignalToArgumentsMap> PendingPropertyUpdates;
+ PendingPropertyUpdates pendingPropertyUpdates;
+
+ // Maps wrapped object to class info
+ QHash<const QObject *, QVariantMap> 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.
+ QBasicTimer timer;
+};
+
+#endif // QMETAOBJECTPUBLISHER_P_H
diff --git a/src/webchannel/signalhandler_p.h b/src/webchannel/signalhandler_p.h
index 2613f92..5bc1f4d 100644
--- a/src/webchannel/signalhandler_p.h
+++ b/src/webchannel/signalhandler_p.h
@@ -272,7 +272,9 @@ void SignalHandler<Receiver>::clear()
}
}
m_connectionsCounter.clear();
+ const SignalArgumentHash keep = m_signalArgumentTypes.take(&QObject::staticMetaObject);
m_signalArgumentTypes.clear();
+ m_signalArgumentTypes[&QObject::staticMetaObject] = keep;
}
#endif // SIGNALHANDLER_H
diff --git a/tests/qml/tst_bench.qml b/tests/qml/tst_bench.qml
index 6893a00..46eb6be 100644
--- a/tests/qml/tst_bench.qml
+++ b/tests/qml/tst_bench.qml
@@ -100,41 +100,6 @@ WebChannelTest {
publisher.registerObjects(objects);
}
- function benchmark_classInfo()
- {
- publisher.classInfoForObjects(objects);
- }
-
- function benchmark_initializeClients()
- {
- publisher.bench_initializeClients();
- }
-
- function benchmark_propertyUpdates()
- {
- // required to make the benchmark work standalone
- publisher.bench_ensureUpdatesInitialized();
-
- for (var o in objects) {
- objects[o].p0++;
- objects[o].p1++;
- objects[o].p2++;
- objects[o].p3++;
- objects[o].p4++;
- objects[o].p5++;
- objects[o].p6++;
- objects[o].p7++;
- objects[o].p8++;
- objects[o].p9++;
- }
- publisher.bench_sendPendingPropertyUpdates();
- }
-
- function benchmark_registerObjects()
- {
- publisher.bench_registerObjects(objects);
- }
-
function benchmark_init_baseline()
{
loadUrl("bench_init.html");
diff --git a/tests/webchannel/tst_webchannel.cpp b/tests/webchannel/tst_webchannel.cpp
index c1f23d1..e997bd7 100644
--- a/tests/webchannel/tst_webchannel.cpp
+++ b/tests/webchannel/tst_webchannel.cpp
@@ -44,6 +44,7 @@
#include <qwebchannel.h>
#include <qmetaobjectpublisher.h>
+#include <qmetaobjectpublisher_p.h>
#include <QtTest>
@@ -145,4 +146,99 @@ void TestWebChannel::testInfoForObject()
}
}
+static QVariantMap createObjects(QObject *parent)
+{
+ const int num = 100;
+ QVariantMap objects;
+ for (int i = 0; i < num; ++i) {
+ objects[QStringLiteral("obj%1").arg(i)] = QVariant::fromValue(new BenchObject(parent));
+ }
+ return objects;
+}
+
+void TestWebChannel::benchClassInfo()
+{
+ QWebChannel channel;
+ QSignalSpy initSpy(&channel, SIGNAL(initialized()));
+ QVERIFY(initSpy.wait());
+
+ QMetaObjectPublisher publisher;
+ publisher.setWebChannel(&channel);
+
+ QObject parent;
+ const QVariantMap objects = createObjects(&parent);
+
+ QBENCHMARK {
+ publisher.classInfoForObjects(objects);
+ }
+}
+
+void TestWebChannel::benchInitializeClients()
+{
+ QWebChannel channel;
+ QSignalSpy initSpy(&channel, SIGNAL(initialized()));
+ QVERIFY(initSpy.wait());
+
+ QMetaObjectPublisher publisher;
+ publisher.setWebChannel(&channel);
+
+ QObject parent;
+ const QVariantMap objects = createObjects(&parent);
+ publisher.registerObjects(objects);
+
+ QBENCHMARK {
+ publisher.d->initializeClients();
+
+ publisher.d->propertyUpdatesInitialized = false;
+ publisher.d->signalToPropertyMap.clear();
+ publisher.d->signalHandler.clear();
+ }
+}
+
+void TestWebChannel::benchPropertyUpdates()
+{
+ QWebChannel channel;
+ QSignalSpy initSpy(&channel, SIGNAL(initialized()));
+ QVERIFY(initSpy.wait());
+
+ QMetaObjectPublisher publisher;
+ publisher.setWebChannel(&channel);
+
+ QObject parent;
+ const QVariantMap objects = createObjects(&parent);
+ QVector<BenchObject*> objectList;
+ foreach (const QVariant &var, objects) {
+ objectList << var.value<BenchObject*>();
+ }
+
+ publisher.registerObjects(objects);
+ publisher.d->initializeClients();
+
+ QBENCHMARK {
+ foreach (BenchObject *obj, objectList) {
+ obj->change();
+ }
+
+ publisher.d->clientIsIdle = true;
+ publisher.d->sendPendingPropertyUpdates();
+ }
+}
+
+void TestWebChannel::benchRegisterObjects()
+{
+ QWebChannel channel;
+ QSignalSpy initSpy(&channel, SIGNAL(initialized()));
+ QVERIFY(initSpy.wait());
+
+ QMetaObjectPublisher publisher;
+ publisher.setWebChannel(&channel);
+
+ QObject parent;
+ const QVariantMap objects = createObjects(&parent);
+
+ QBENCHMARK {
+ publisher.registerObjects(objects);
+ }
+}
+
QTEST_MAIN(TestWebChannel)
diff --git a/tests/webchannel/tst_webchannel.h b/tests/webchannel/tst_webchannel.h
index 5ea7a45..173921b 100644
--- a/tests/webchannel/tst_webchannel.h
+++ b/tests/webchannel/tst_webchannel.h
@@ -92,6 +92,98 @@ private slots:
void slot4() {}
};
+class BenchObject : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int p0 MEMBER m_p0 NOTIFY p0Changed)
+ Q_PROPERTY(int p1 MEMBER m_p1 NOTIFY p1Changed)
+ Q_PROPERTY(int p2 MEMBER m_p2 NOTIFY p2Changed)
+ Q_PROPERTY(int p3 MEMBER m_p3 NOTIFY p3Changed)
+ Q_PROPERTY(int p4 MEMBER m_p4 NOTIFY p4Changed)
+ Q_PROPERTY(int p5 MEMBER m_p5 NOTIFY p5Changed)
+ Q_PROPERTY(int p6 MEMBER m_p6 NOTIFY p6Changed)
+ Q_PROPERTY(int p7 MEMBER m_p7 NOTIFY p7Changed)
+ Q_PROPERTY(int p8 MEMBER m_p8 NOTIFY p8Changed)
+ Q_PROPERTY(int p9 MEMBER m_p9 NOTIFY p9Changed)
+public:
+ explicit BenchObject(QObject *parent = 0)
+ : QObject(parent)
+ , m_p0(0)
+ , m_p1(0)
+ , m_p2(0)
+ , m_p3(0)
+ , m_p4(0)
+ , m_p5(0)
+ , m_p6(0)
+ , m_p7(0)
+ , m_p8(0)
+ , m_p9(0)
+ { }
+
+ void change()
+ {
+ m_p0++;
+ m_p1++;
+ m_p2++;
+ m_p3++;
+ m_p4++;
+ m_p5++;
+ m_p6++;
+ m_p7++;
+ m_p8++;
+ m_p9++;
+ emit p0Changed(m_p0);
+ emit p1Changed(m_p1);
+ emit p2Changed(m_p2);
+ emit p3Changed(m_p3);
+ emit p4Changed(m_p4);
+ emit p5Changed(m_p5);
+ emit p6Changed(m_p6);
+ emit p7Changed(m_p7);
+ emit p8Changed(m_p8);
+ emit p9Changed(m_p9);
+ }
+
+signals:
+ void s0();
+ void s1();
+ void s2();
+ void s3();
+ void s4();
+ void s5();
+ void s6();
+ void s7();
+ void s8();
+ void s9();
+
+ void p0Changed(int);
+ void p1Changed(int);
+ void p2Changed(int);
+ void p3Changed(int);
+ void p4Changed(int);
+ void p5Changed(int);
+ void p6Changed(int);
+ void p7Changed(int);
+ void p8Changed(int);
+ void p9Changed(int);
+
+public slots:
+ void m0(){};
+ void m1(){};
+ void m2(){};
+ void m3(){};
+ void m4(){};
+ void m5(){};
+ void m6(){};
+ void m7(){};
+ void m8(){};
+ void m9(){};
+
+private:
+ int m_p0, m_p1, m_p2, m_p3, m_p4, m_p5, m_p6, m_p7, m_p8, m_p9;
+};
+
class TestWebChannel : public QObject
{
Q_OBJECT
@@ -104,6 +196,11 @@ private slots:
void testInitChannel();
void testRegisterObjects();
void testInfoForObject();
+
+ void benchClassInfo();
+ void benchInitializeClients();
+ void benchPropertyUpdates();
+ void benchRegisterObjects();
};
#endif // TST_WEBCHANNEL_H