diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | dependencies.yaml | 4 | ||||
-rw-r--r-- | examples/webchannel/shared/qwebchannel.js | 26 | ||||
-rw-r--r-- | src/imports/webchannel/plugin.cpp | 5 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher.cpp | 41 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher_p.h | 3 | ||||
-rw-r--r-- | src/webchannel/qwebchannelabstracttransport.h | 4 | ||||
-rw-r--r-- | src/webchannel/variantargument_p.h | 4 | ||||
-rw-r--r-- | tests/auto/qml/testobject.cpp | 13 | ||||
-rw-r--r-- | tests/auto/qml/testobject.h | 2 | ||||
-rw-r--r-- | tests/auto/qml/tst_webchannel.qml | 125 |
11 files changed, 210 insertions, 19 deletions
diff --git a/.qmake.conf b/.qmake.conf index 748ca99..05001fb 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,4 @@ load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.15.0 +MODULE_VERSION = 6.0.0 diff --git a/dependencies.yaml b/dependencies.yaml new file mode 100644 index 0000000..df75d3f --- /dev/null +++ b/dependencies.yaml @@ -0,0 +1,4 @@ +dependencies: + ../qtwebsockets: + ref: 0c488149eafadc550ee4034a49c1bacdc1e955ec + required: false diff --git a/examples/webchannel/shared/qwebchannel.js b/examples/webchannel/shared/qwebchannel.js index 32ad51e..3cfcd62 100644 --- a/examples/webchannel/shared/qwebchannel.js +++ b/examples/webchannel/shared/qwebchannel.js @@ -273,13 +273,11 @@ function QObject(name, data, webChannel) console.error("Bad callback given to disconnect from signal " + signalName); return; } - object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; - var idx = object.__objectSignals__[signalIndex].indexOf(callback); - if (idx === -1) { - console.error("Cannot find connection of signal " + signalName + " to " + callback.name); - return; - } - object.__objectSignals__[signalIndex].splice(idx, 1); + // This makes a new list. This is important because it won't interfere with + // signal processing if a disconnection happens while emittig a signal + object.__objectSignals__[signalIndex] = (object.__objectSignals__[signalIndex] || []).filter(function(c) { + return c != callback; + }); if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) { // only required for "pure" signals, handled separately for properties in propertyUpdate webChannel.exec({ @@ -341,10 +339,6 @@ function QObject(name, data, webChannel) var argument = arguments[i]; if (typeof argument === "function") callback = argument; - else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined) - args.push({ - "id": argument.__id__ - }); else args.push(argument); } @@ -416,8 +410,6 @@ function QObject(name, data, webChannel) } object.__propertyCache__[propertyIndex] = value; var valueToSend = value; - if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined) - valueToSend = { "id": valueToSend.__id__ }; webChannel.exec({ "type": QWebChannelMessageTypes.setProperty, "object": object.__id__, @@ -440,6 +432,14 @@ function QObject(name, data, webChannel) Object.assign(object, data.enums); } +QObject.prototype.toJSON = function() { + if (this.__id__ === undefined) return {}; + return { + id: this.__id__, + "__QObject*__": true + }; +}; + //required for use with nodejs if (typeof module === 'object') { module.exports = { diff --git a/src/imports/webchannel/plugin.cpp b/src/imports/webchannel/plugin.cpp index b6d4018..45d83b1 100644 --- a/src/imports/webchannel/plugin.cpp +++ b/src/imports/webchannel/plugin.cpp @@ -61,8 +61,9 @@ void QWebChannelPlugin::registerTypes(const char *uri) int minor = 0; qmlRegisterType<QQmlWebChannel>(uri, major, minor, "WebChannel"); - // Auto-increment the import to stay in sync with ALL future QtQuick minor versions - qmlRegisterModule(uri, major, QT_VERSION_MINOR); + // The minor version used to be the current Qt 5 minor. For compatibility it is the last + // Qt 5 release. + qmlRegisterModule(uri, major, 15); } QT_END_NAMESPACE diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp index 677f79c..f6110ed 100644 --- a/src/webchannel/qmetaobjectpublisher.cpp +++ b/src/webchannel/qmetaobjectpublisher.cpp @@ -457,6 +457,7 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMe VariantArgument arguments[10]; for (int i = 0; i < qMin(args.size(), method.parameterCount()); ++i) { arguments[i].value = toVariant(args.at(i), method.parameterType(i)); + arguments[i].type = method.parameterType(i); } // construct QGenericReturnArgument QVariant returnValue; @@ -611,6 +612,44 @@ QObject *QMetaObjectPublisher::unwrapObject(const QString &objectId) const return Q_NULLPTR; } +QVariant QMetaObjectPublisher::unwrapMap(QVariantMap map) const +{ + const auto qobj = map.value(KEY_QOBJECT).toBool(); + const auto id = qobj ? map.value(KEY_ID).toString() : QString(); + + if (!id.isEmpty()) // it's probably a QObject + return QVariant::fromValue(unwrapObject(id)); + + // it's probably just a normal JS object, continue searching for objects + // that look like QObject* + for (auto &value : map) + value = unwrapVariant(value); + + return map; +} + +QVariant QMetaObjectPublisher::unwrapList(QVariantList list) const +{ + for (auto &value : list) + value = unwrapVariant(value); + + return list; +} + +QVariant QMetaObjectPublisher::unwrapVariant(const QVariant &value) const +{ + switch (value.type()) + { + case QMetaType::QVariantList: + return unwrapList(value.toList()); + case QMetaType::QVariantMap: + return unwrapMap(value.toMap()); + default: + break; + } + return value; +} + QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType) const { if (targetType == QMetaType::QJsonValue) { @@ -635,7 +674,7 @@ QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType // this converts QJsonObjects to QVariantMaps, which is not desired when // we want to get a QJsonObject or QJsonValue (see above) - QVariant variant = value.toVariant(); + QVariant variant = unwrapVariant(value.toVariant()); if (targetType != QMetaType::QVariant && !variant.convert(targetType)) { qWarning() << "Could not convert argument" << value << "to target type" << QVariant::typeToName(targetType) << '.'; } diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h index 6030de2..2ffeca3 100644 --- a/src/webchannel/qmetaobjectpublisher_p.h +++ b/src/webchannel/qmetaobjectpublisher_p.h @@ -191,6 +191,9 @@ public: void objectDestroyed(const QObject *object); QObject *unwrapObject(const QString &objectId) const; + QVariant unwrapMap(QVariantMap map) const; + QVariant unwrapList(QVariantList list) const; + QVariant unwrapVariant(const QVariant &value) const; QVariant toVariant(const QJsonValue &value, int targetType) const; diff --git a/src/webchannel/qwebchannelabstracttransport.h b/src/webchannel/qwebchannelabstracttransport.h index ecac588..e3747c7 100644 --- a/src/webchannel/qwebchannelabstracttransport.h +++ b/src/webchannel/qwebchannelabstracttransport.h @@ -50,8 +50,8 @@ class Q_WEBCHANNEL_EXPORT QWebChannelAbstractTransport : public QObject { Q_OBJECT public: - explicit QWebChannelAbstractTransport(QObject *parent = Q_NULLPTR); - virtual ~QWebChannelAbstractTransport(); + explicit QWebChannelAbstractTransport(QObject *parent = nullptr); + ~QWebChannelAbstractTransport() override; public Q_SLOTS: virtual void sendMessage(const QJsonObject &message) = 0; diff --git a/src/webchannel/variantargument_p.h b/src/webchannel/variantargument_p.h index 263a742..afac507 100644 --- a/src/webchannel/variantargument_p.h +++ b/src/webchannel/variantargument_p.h @@ -62,6 +62,9 @@ struct VariantArgument { operator QGenericArgument() const { + if (type == QMetaType::QVariant) { + return Q_ARG(QVariant, value); + } if (!value.isValid()) { return QGenericArgument(); } @@ -69,6 +72,7 @@ struct VariantArgument } QVariant value; + int type; }; QT_END_NAMESPACE diff --git a/tests/auto/qml/testobject.cpp b/tests/auto/qml/testobject.cpp index ad302e7..bd9db04 100644 --- a/tests/auto/qml/testobject.cpp +++ b/tests/auto/qml/testobject.cpp @@ -77,4 +77,17 @@ QString TestObject::testOverload(const QString &str, int i) return str.toUpper() + QString::number(i + 1); } +int TestObject::testVariantType(const QVariant &val) +{ + return val.type(); +} + +bool TestObject::testEmbeddedObjects(const QVariantList &list) +{ + return list.size() == 2 && + QMetaType::Type(list[0].type()) == QMetaType::QObjectStar && + QMetaType::Type(list[1].type()) == QMetaType::QVariantMap && + QMetaType::Type(list[1].toMap()["obj"].type()) == QMetaType::QObjectStar; +} + QT_END_NAMESPACE diff --git a/tests/auto/qml/testobject.h b/tests/auto/qml/testobject.h index b9c5ecc..9889523 100644 --- a/tests/auto/qml/testobject.h +++ b/tests/auto/qml/testobject.h @@ -51,6 +51,8 @@ public slots: int testOverload(int i); QString testOverload(const QString &str); QString testOverload(const QString &str, int i); + int testVariantType(const QVariant &val); + bool testEmbeddedObjects(const QVariantList &list); signals: void testSignalBool(bool testBool); diff --git a/tests/auto/qml/tst_webchannel.qml b/tests/auto/qml/tst_webchannel.qml index ed1c4a1..0255d43 100644 --- a/tests/auto/qml/tst_webchannel.qml +++ b/tests/auto/qml/tst_webchannel.qml @@ -497,6 +497,67 @@ TestCase { compare(signalArgs, [42, 42, 1, 1, 0, 0]); } + function test_connectDuringEmit() + { + var cb1 = 0; + var cb2 = 0; + var channel = client.createChannel(function(channel) { + var myObj = channel.objects.myObj; + myObj.mySignal.connect(function() { + cb1++; + myObj.mySignal.connect(function() { + cb2++; + }); + }); + }); + client.awaitInit(); + + var msg = client.awaitMessage(); + compare(msg.type, JSClient.QWebChannelMessageTypes.connectToSignal); + compare(msg.object, "myObj"); + + client.awaitIdle(); + + myObj.mySignal(42, myObj); + + compare(cb1, 1); + compare(cb2, 0); + } + + function test_disconnectDuringEmit() + { + var cb1 = 0; + var cb2 = 0; + var cb3 = 0; + var channel = client.createChannel(function(channel) { + var myObj = channel.objects.myObj; + var cb1impl = function() { + cb1++; + }; + myObj.mySignal.connect(cb1impl); + myObj.mySignal.connect(function() { + cb2++; + myObj.mySignal.disconnect(cb1impl); + }); + myObj.mySignal.connect(function() { + cb3++; + }); + }); + client.awaitInit(); + + var msg = client.awaitMessage(); + compare(msg.type, JSClient.QWebChannelMessageTypes.connectToSignal); + compare(msg.object, "myObj"); + + client.awaitIdle(); + + myObj.mySignal(42, myObj); + + compare(cb1, 1); + compare(cb2, 1); + compare(cb3, 1); + } + function test_overloading() { var signalArgs_implicit = []; @@ -556,4 +617,68 @@ TestCase { compare(signalArgs_explicit3, [["the answer is ", 41]]); compare(returnValues, [100, 42, "HELLO WORLD", "THE ANSWER IS 42"]); } + + function test_variantType() + { + var returnValues = []; + function logReturnValue(value) { + returnValues.push(value); + } + var channel = client.createChannel(function(channel) { + var testObject = channel.objects.testObject; + testObject.testVariantType(0.25, logReturnValue); + testObject.testVariantType("0", logReturnValue); + testObject.testVariantType(null, logReturnValue); + testObject.testVariantType(testObject, logReturnValue); + }); + client.awaitInit(); + + function awaitMessage(type) + { + var msg = client.awaitMessage(); + compare(msg.type, type); + compare(msg.object, "testObject"); + } + + console.log("double arg"); + awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod); + console.log("string arg"); + awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod); + console.log("null arg"); + awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod); + console.log("QObject arg"); + awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod); + + client.awaitIdle(); + + // QMetaType::Double: 6, QMetaType::QString: 10, QMetaType::Nullptr: 51, + // QMetaType::QObjectStar: 39 + compare(returnValues, [6, 10, 51, 39]); + } + + function test_embeddedQObject() + { + var success = false; + function logReturnValue(value) { + success = value; + } + var channel = client.createChannel(function(channel) { + var testObject = channel.objects.testObject; + testObject.testEmbeddedObjects([testObject, { obj: testObject }], logReturnValue); + }); + client.awaitInit(); + + function awaitMessage(type) + { + var msg = client.awaitMessage(); + compare(msg.type, type); + compare(msg.object, "testObject"); + } + + awaitMessage(JSClient.QWebChannelMessageTypes.invokeMethod); + + client.awaitIdle(); + + compare(success, true); + } } |