summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--examples/webchannel/chatserver-cpp/chatserver.cpp9
-rw-r--r--examples/webchannel/chatserver-cpp/main.cpp12
-rw-r--r--examples/webchannel/shared/websocketclientwrapper.cpp5
-rw-r--r--src/webchannel/doc/src/index.qdoc13
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp24
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h9
-rw-r--r--src/webchannel/qwebchannel.cpp5
-rw-r--r--tests/auto/webchannel/tst_webchannel.cpp53
-rw-r--r--tests/auto/webchannel/tst_webchannel.h2
10 files changed, 110 insertions, 24 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 40e5787..556f554 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,4 @@
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.7.0
+MODULE_VERSION = 5.8.0
diff --git a/examples/webchannel/chatserver-cpp/chatserver.cpp b/examples/webchannel/chatserver-cpp/chatserver.cpp
index 9bc45ce..74da4c3 100644
--- a/examples/webchannel/chatserver-cpp/chatserver.cpp
+++ b/examples/webchannel/chatserver-cpp/chatserver.cpp
@@ -115,12 +115,14 @@ bool ChatServer::sendMessage(const QString& user, const QString& msg)
}
}
-void ChatServer::sendKeepAlive() {
+void ChatServer::sendKeepAlive()
+{
emit keepAlive();
m_keepAliveCheckTimer->start();
}
-void ChatServer::checkKeepAliveResponses() {
+void ChatServer::checkKeepAliveResponses()
+{
qDebug() << "Keep Alive Check" << m_stillAliveUsers;
m_userList = m_stillAliveUsers;
m_stillAliveUsers.clear();
@@ -128,7 +130,8 @@ void ChatServer::checkKeepAliveResponses() {
emit userListChanged();
}
-void ChatServer::keepAliveResponse(const QString& user) {
+void ChatServer::keepAliveResponse(const QString& user)
+{
m_stillAliveUsers.append(user);
}
diff --git a/examples/webchannel/chatserver-cpp/main.cpp b/examples/webchannel/chatserver-cpp/main.cpp
index 240518c..ea27e87 100644
--- a/examples/webchannel/chatserver-cpp/main.cpp
+++ b/examples/webchannel/chatserver-cpp/main.cpp
@@ -49,17 +49,13 @@
****************************************************************************/
#include "qwebchannel.h"
-
-#include <QCoreApplication>
-#include <QUrl>
-#include <QDebug>
-
-#include <QtWebSockets/QWebSocketServer>
+#include "chatserver.h"
#include "../shared/websocketclientwrapper.h"
#include "../shared/websockettransport.h"
-#include "chatserver.h"
+#include <QtWebSockets/QWebSocketServer>
+#include <QCoreApplication>
int main(int argc, char** argv)
{
@@ -82,7 +78,7 @@ int main(int argc, char** argv)
// setup the dialog and publish it to the QWebChannel
ChatServer* chatserver = new ChatServer(&app);
- channel.registerObject("chatserver", chatserver);
+ channel.registerObject(QStringLiteral("chatserver"), chatserver);
return app.exec();
}
diff --git a/examples/webchannel/shared/websocketclientwrapper.cpp b/examples/webchannel/shared/websocketclientwrapper.cpp
index 0778cee..3743bdd 100644
--- a/examples/webchannel/shared/websocketclientwrapper.cpp
+++ b/examples/webchannel/shared/websocketclientwrapper.cpp
@@ -49,13 +49,12 @@
****************************************************************************/
#include "websocketclientwrapper.h"
+#include "websockettransport.h"
#include <QtWebSockets/QWebSocketServer>
-#include "websockettransport.h"
-
/*!
- \brief Wrapps connected QWebSockets clients in WebSocketTransport objects.
+ \brief Wraps connected QWebSockets clients in WebSocketTransport objects.
This code is all that is required to connect incoming WebSockets to the WebChannel. Any kind
of remote JavaScript client that supports WebSockets can thus receive messages and access the
diff --git a/src/webchannel/doc/src/index.qdoc b/src/webchannel/doc/src/index.qdoc
index 43f4e6f..5529093 100644
--- a/src/webchannel/doc/src/index.qdoc
+++ b/src/webchannel/doc/src/index.qdoc
@@ -31,13 +31,12 @@
\title Qt WebChannel
\brief Bridges the gap between Qt applications and HTML/JavaScript.
- Qt WebChannel enables peer-to-peer communication between the server
- (QML/C++ application) and the client (HTML/JavaScript or QML application). The
- transport mechanism is supported out of the box by \l{Qt WebEngine}. It works on all
- browsers that support \l{Qt WebSockets}, enabling Qt WebChannel applications
- to run in any JavaScript runtime. Additionally, a custom transport
- mechanism can also be implemented using Qt WebSockets to support Qt
- WebChannel-based communication.
+ Qt WebChannel enables peer-to-peer communication between a server (QML/C++
+ application) and a client (HTML/JavaScript or QML application). It is
+ supported out of the box by \l{Qt WebEngine}. In addition it can work on all
+ browsers that support \l{Qt WebSockets}{WebSockets}, enabling Qt WebChannel
+ clients to run in any JavaScript environment (including QML). This requires
+ the implementation of a custom transport based on Qt WebSockets.
The module provides a JavaScript library for seamless integration of C++
and QML applications with HTML/JavaScript and QML clients. The clients must use the
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index e9d93ea..66676f0 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -495,6 +495,29 @@ QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType
return variant;
}
+void QMetaObjectPublisher::transportRemoved(QWebChannelAbstractTransport *transport)
+{
+ auto it = transportedWrappedObjects.find(transport);
+ // It is not allowed to modify a container while iterating over it. So save
+ // objects which should be removed and call objectDestroyed() on them later.
+ QVector<QObject*> objectsForDeletion;
+ while (it != transportedWrappedObjects.end() && it.key() == transport) {
+ if (wrappedObjects.contains(it.value())) {
+ QVector<QWebChannelAbstractTransport*> &transports = wrappedObjects[it.value()].transports;
+ transports.removeOne(transport);
+ if (transports.isEmpty())
+ objectsForDeletion.append(wrappedObjects[it.value()].object);
+ }
+
+ it++;
+ }
+
+ transportedWrappedObjects.remove(transport);
+
+ foreach (QObject *obj, objectsForDeletion)
+ objectDestroyed(obj);
+}
+
// NOTE: transport can be a nullptr
// in such a case, we need to ensure that the property is registered to
// the target transports of the parentObjectId
@@ -526,6 +549,7 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA
oi.transports = webChannel->d_func()->transports;
}
wrappedObjects.insert(id, oi);
+ transportedWrappedObjects.insert(transport, id);
initializePropertyUpdates(object, classInfo);
} else if (wrappedObjects.contains(id)) {
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 29956e0..830e510 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -94,7 +94,7 @@ public:
virtual ~QMetaObjectPublisher();
/**
- * Register @p object nuder the given @p id.
+ * Register @p object under the given @p id.
*
* The properties, signals and public methods of the QObject are
* published to the remote client, where an object with the given identifier
@@ -177,6 +177,11 @@ public:
QVariant toVariant(const QJsonValue &value, int targetType) const;
/**
+ * Remove wrapped objects which last transport relation is with the passed transport object.
+ */
+ void transportRemoved(QWebChannelAbstractTransport *transport);
+
+ /**
* Given a QVariant containing a QObject*, wrap the object and register for property updates
* return the objects class information.
*
@@ -257,6 +262,8 @@ private:
// Map of objects wrapped from invocation returns
QHash<QString, ObjectInfo> wrappedObjects;
+ // Map of transports to wrapped object ids
+ QMultiHash<QWebChannelAbstractTransport*, QString> transportedWrappedObjects;
// 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.
diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp
index c2e0d19..0e9a4c5 100644
--- a/src/webchannel/qwebchannel.cpp
+++ b/src/webchannel/qwebchannel.cpp
@@ -81,9 +81,11 @@ QT_BEGIN_NAMESPACE
*/
void QWebChannelPrivate::_q_transportDestroyed(QObject *object)
{
- const int idx = transports.indexOf(static_cast<QWebChannelAbstractTransport*>(object));
+ QWebChannelAbstractTransport *transport = static_cast<QWebChannelAbstractTransport*>(object);
+ const int idx = transports.indexOf(transport);
if (idx != -1) {
transports.remove(idx);
+ publisher->transportRemoved(transport);
}
}
@@ -252,6 +254,7 @@ void QWebChannel::disconnectFrom(QWebChannelAbstractTransport *transport)
disconnect(transport, 0, this, 0);
disconnect(transport, 0, d->publisher, 0);
d->transports.remove(idx);
+ d->publisher->transportRemoved(transport);
}
}
diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp
index a8a658f..0fe742f 100644
--- a/tests/auto/webchannel/tst_webchannel.cpp
+++ b/tests/auto/webchannel/tst_webchannel.cpp
@@ -659,6 +659,28 @@ void TestWebChannel::testPassWrappedObjectBack()
QCOMPARE(registeredObj.mReturnedObject, &returnedObjProperty);
}
+void TestWebChannel::testRemoveUnusedTransports()
+{
+ QWebChannel channel;
+ DummyTransport *dummyTransport = new DummyTransport(this);
+ TestObject obj;
+
+ channel.connectTo(dummyTransport);
+ channel.d_func()->publisher->initializeClient(dummyTransport);
+
+ QMetaObjectPublisher *pub = channel.d_func()->publisher;
+ pub->wrapResult(QVariant::fromValue(&obj), dummyTransport);
+
+ QCOMPARE(pub->wrappedObjects.size(), 1);
+ QCOMPARE(pub->registeredObjectIds.size(), 1);
+
+ channel.disconnectFrom(dummyTransport);
+ delete dummyTransport;
+
+ QCOMPARE(pub->wrappedObjects.size(), 0);
+ QCOMPARE(pub->registeredObjectIds.size(), 0);
+}
+
void TestWebChannel::testInfiniteRecursion()
{
QWebChannel channel;
@@ -754,6 +776,37 @@ void TestWebChannel::benchRegisterObjects()
channel.registerObjects(objects);
}
}
+
+void TestWebChannel::benchRemoveTransport()
+{
+ QWebChannel channel;
+ QList<DummyTransport*> dummyTransports;
+ for (int i = 500; i > 0; i--)
+ dummyTransports.append(new DummyTransport(this));
+
+ QList<QSharedPointer<TestObject>> objs;
+ QMetaObjectPublisher *pub = channel.d_func()->publisher;
+
+ foreach (DummyTransport *transport, dummyTransports) {
+ channel.connectTo(transport);
+ channel.d_func()->publisher->initializeClient(transport);
+
+ /* 30 objects per transport */
+ for (int i = 30; i > 0; i--) {
+ QSharedPointer<TestObject> obj = QSharedPointer<TestObject>::create();
+ objs.append(obj);
+ pub->wrapResult(QVariant::fromValue(obj.data()), transport);
+ }
+ }
+
+ QBENCHMARK_ONCE {
+ for (auto transport : dummyTransports)
+ pub->transportRemoved(transport);
+ }
+
+ qDeleteAll(dummyTransports);
+}
+
#ifdef WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE
class SubclassedTestObject : public TestObject
diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h
index b46f21b..a587499 100644
--- a/tests/auto/webchannel/tst_webchannel.h
+++ b/tests/auto/webchannel/tst_webchannel.h
@@ -279,12 +279,14 @@ private slots:
void testDisconnect();
void testWrapRegisteredObject();
void testPassWrappedObjectBack();
+ void testRemoveUnusedTransports();
void testInfiniteRecursion();
void benchClassInfo();
void benchInitializeClients();
void benchPropertyUpdates();
void benchRegisterObjects();
+ void benchRemoveTransport();
void qtbug46548_overriddenProperties();