diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | examples/webchannel/chatserver-cpp/chatserver.cpp | 9 | ||||
-rw-r--r-- | examples/webchannel/chatserver-cpp/main.cpp | 12 | ||||
-rw-r--r-- | examples/webchannel/shared/websocketclientwrapper.cpp | 5 | ||||
-rw-r--r-- | src/webchannel/doc/src/index.qdoc | 13 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher.cpp | 24 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher_p.h | 9 | ||||
-rw-r--r-- | src/webchannel/qwebchannel.cpp | 5 | ||||
-rw-r--r-- | tests/auto/webchannel/tst_webchannel.cpp | 53 | ||||
-rw-r--r-- | tests/auto/webchannel/tst_webchannel.h | 2 |
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(); |