From f48e8c9711fbeb350ccf70f852ce3732844d4287 Mon Sep 17 00:00:00 2001 From: Kai Dohmen Date: Mon, 11 Apr 2016 19:38:50 +0200 Subject: Make passing objects from website to server possible If you get an object from the server and want to pass it back to the server via a function the id of the object is passed instead of the whole json object. On the server side QMetaObjectPublisher::invokeMethod now looks up the object in QMetaObjectPublisher::wrappedObjects by the passed object-id. Task-number: QTBUG-50075 Change-Id: Id0df2dfaa79bcba12ca48391ae7537ac1a086898 Reviewed-by: Milian Wolff --- src/webchannel/qmetaobjectpublisher.cpp | 63 ++++++++++++++++++++------------ src/webchannel/qmetaobjectpublisher_p.h | 4 ++ src/webchannel/qwebchannel.js | 16 ++++++-- tests/auto/webchannel/tst_webchannel.cpp | 50 +++++++++++++++++++++++++ tests/auto/webchannel/tst_webchannel.h | 16 ++++++++ 5 files changed, 122 insertions(+), 27 deletions(-) diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp index 0fa4ae2..b3fc53d 100644 --- a/src/webchannel/qmetaobjectpublisher.cpp +++ b/src/webchannel/qmetaobjectpublisher.cpp @@ -87,29 +87,6 @@ QJsonObject createResponse(const QJsonValue &id, const QJsonValue &data) /// TODO: what is the proper value here? const int PROPERTY_UPDATE_INTERVAL = 50; - -QVariant toVariant(const QJsonValue &value, int targetType) -{ - if (targetType == QMetaType::QJsonValue) { - return QVariant::fromValue(value); - } else if (targetType == QMetaType::QJsonArray) { - if (!value.isArray()) - qWarning() << "Cannot not convert non-array argument" << value << "to QJsonArray."; - return QVariant::fromValue(value.toArray()); - } else if (targetType == QMetaType::QJsonObject) { - if (!value.isObject()) - qWarning() << "Cannot not convert non-object argument" << value << "to QJsonObject."; - return QVariant::fromValue(value.toObject()); - } - - // this converts QJsonObjects to QVariantMaps, which is not desired when - // we want to get a QJsonObject or QJsonValue (see above) - QVariant variant = value.toVariant(); - if (targetType != QMetaType::QVariant && !variant.convert(targetType)) { - qWarning() << "Could not convert argument" << value << "to target type" << QVariant::typeToName(targetType) << '.'; - } - return variant; -} } QMetaObjectPublisher::QMetaObjectPublisher(QWebChannel *webChannel) @@ -472,6 +449,46 @@ void QMetaObjectPublisher::objectDestroyed(const QObject *object) pendingPropertyUpdates.remove(object); } +QObject *QMetaObjectPublisher::unwrapObject(const QString &objectId) const +{ + if (!objectId.isEmpty()) { + ObjectInfo objectInfo = wrappedObjects.value(objectId); + if (objectInfo.object && !objectInfo.classinfo.isEmpty()) + return objectInfo.object; + } + + qWarning() << "No wrapped object" << objectId; + return Q_NULLPTR; +} + +QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType) const +{ + if (targetType == QMetaType::QJsonValue) { + return QVariant::fromValue(value); + } else if (targetType == QMetaType::QJsonArray) { + if (!value.isArray()) + qWarning() << "Cannot not convert non-array argument" << value << "to QJsonArray."; + return QVariant::fromValue(value.toArray()); + } else if (targetType == QMetaType::QJsonObject) { + if (!value.isObject()) + qWarning() << "Cannot not convert non-object argument" << value << "to QJsonObject."; + return QVariant::fromValue(value.toObject()); + } else if (QMetaType::typeFlags(targetType) & QMetaType::PointerToQObject) { + QObject *unwrappedObject = unwrapObject(value.toObject()[KEY_ID].toString()); + if (unwrappedObject == Q_NULLPTR) + qWarning() << "Cannot not convert non-object argument" << value << "to QObject*."; + return QVariant::fromValue(unwrappedObject); + } + + // this converts QJsonObjects to QVariantMaps, which is not desired when + // we want to get a QJsonObject or QJsonValue (see above) + QVariant variant = value.toVariant(); + if (targetType != QMetaType::QVariant && !variant.convert(targetType)) { + qWarning() << "Could not convert argument" << value << "to target type" << QVariant::typeToName(targetType) << '.'; + } + return variant; +} + // 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 diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h index b0ebd84..048a33c 100644 --- a/src/webchannel/qmetaobjectpublisher_p.h +++ b/src/webchannel/qmetaobjectpublisher_p.h @@ -166,6 +166,10 @@ public: */ void objectDestroyed(const QObject *object); + QObject *unwrapObject(const QString &objectId) const; + + QVariant toVariant(const QJsonValue &value, int targetType) const; + /** * Given a QVariant containing a QObject*, wrap the object and register for property updates * return the objects class information. diff --git a/src/webchannel/qwebchannel.js b/src/webchannel/qwebchannel.js index d8c28bc..148e70d 100644 --- a/src/webchannel/qwebchannel.js +++ b/src/webchannel/qwebchannel.js @@ -324,10 +324,15 @@ function QObject(name, data, webChannel) var args = []; var callback; for (var i = 0; i < arguments.length; ++i) { - if (typeof arguments[i] === "function") - callback = arguments[i]; + 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(arguments[i]); + args.push(argument); } webChannel.exec({ @@ -381,11 +386,14 @@ function QObject(name, data, webChannel) return; } 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__, "property": propertyIndex, - "value": value + "value": valueToSend }); } }); diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp index 93c7aa8..7ae6f78 100644 --- a/tests/auto/webchannel/tst_webchannel.cpp +++ b/tests/auto/webchannel/tst_webchannel.cpp @@ -343,6 +343,12 @@ void TestWebChannel::testInfoForObject() method.append(obj.metaObject()->indexOfMethod("slot2(QString)")); expected.append(method); } + { + QJsonArray method; + method.append(QStringLiteral("setReturnedObject")); + method.append(obj.metaObject()->indexOfMethod("setReturnedObject(TestObject*)")); + expected.append(method); + } { QJsonArray method; method.append(QStringLiteral("setObjectProperty")); @@ -446,6 +452,19 @@ void TestWebChannel::testInfoForObject() property.append(QJsonValue::fromVariant(QVariant::fromValue(obj.objectProperty()))); expected.append(property); } + { + QJsonArray property; + property.append(obj.metaObject()->indexOfProperty("returnedObject")); + property.append(QStringLiteral("returnedObject")); + { + QJsonArray signal; + signal.append(1); + signal.append(obj.metaObject()->indexOfMethod("returnedObjectChanged()")); + property.append(signal); + } + property.append(QJsonValue::fromVariant(QVariant::fromValue(obj.returnedObject()))); + expected.append(property); + } QCOMPARE(info["properties"].toArray(), expected); } } @@ -587,6 +606,36 @@ void TestWebChannel::testWrapRegisteredObject() QCOMPARE(obj.objectName(), returnedId); } +void TestWebChannel::testPassWrappedObjectBack() +{ + QWebChannel channel; + TestObject registeredObj; + TestObject returnedObjMethod; + TestObject returnedObjProperty; + + registeredObj.setObjectName("registeredObject"); + + channel.registerObject(registeredObj.objectName(), ®isteredObj); + channel.connectTo(m_dummyTransport); + channel.d_func()->publisher->initializeClient(m_dummyTransport); + + QMetaObjectPublisher *pub = channel.d_func()->publisher; + QJsonObject returnedObjMethodInfo = pub->wrapResult(QVariant::fromValue(&returnedObjMethod), m_dummyTransport).toObject(); + QJsonObject returnedObjPropertyInfo = pub->wrapResult(QVariant::fromValue(&returnedObjProperty), m_dummyTransport).toObject(); + + QJsonArray argsMethod; + QJsonObject argMethod0; + argMethod0["id"] = returnedObjMethodInfo["id"]; + argsMethod << argMethod0; + QJsonObject argProperty; + argProperty["id"] = returnedObjPropertyInfo["id"]; + + pub->invokeMethod(®isteredObj, registeredObj.metaObject()->indexOfSlot("setReturnedObject(TestObject*)"), argsMethod); + QCOMPARE(registeredObj.mReturnedObject, &returnedObjMethod); + pub->setProperty(®isteredObj, registeredObj.metaObject()->indexOfProperty("returnedObject"), argProperty); + QCOMPARE(registeredObj.mReturnedObject, &returnedObjProperty); +} + void TestWebChannel::testInfiniteRecursion() { QWebChannel channel; @@ -739,6 +788,7 @@ void TestWebChannel::qtbug46548_overriddenProperties() #endif // WEBCHANNEL_TESTS_CAN_USE_JS_ENGINE } + QTEST_MAIN(TestWebChannel) #include "tst_webchannel.moc" diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h index 13294c2..5a9fa11 100644 --- a/tests/auto/webchannel/tst_webchannel.h +++ b/tests/auto/webchannel/tst_webchannel.h @@ -73,11 +73,13 @@ class TestObject : public QObject Q_PROPERTY(int asdf READ asdf NOTIFY asdfChanged) Q_PROPERTY(QString bar READ bar NOTIFY theBarHasChanged) Q_PROPERTY(QObject * objectProperty READ objectProperty WRITE setObjectProperty NOTIFY objectPropertyChanged) + Q_PROPERTY(TestObject * returnedObject READ returnedObject WRITE setReturnedObject NOTIFY returnedObjectChanged) public: explicit TestObject(QObject *parent = 0) : QObject(parent) , mObjectProperty(0) + , mReturnedObject(Q_NULLPTR) { } enum Foo { @@ -94,6 +96,11 @@ public: return mObjectProperty; } + TestObject *returnedObject() const + { + return mReturnedObject; + } + Q_INVOKABLE void method1() {} protected: @@ -108,11 +115,18 @@ signals: void asdfChanged(); void theBarHasChanged(); void objectPropertyChanged(); + void returnedObjectChanged(); public slots: void slot1() {} void slot2(const QString&) {} + void setReturnedObject(TestObject *obj) + { + mReturnedObject = obj; + emit returnedObjectChanged(); + } + void setObjectProperty(QObject *object) { mObjectProperty = object; @@ -127,6 +141,7 @@ private slots: public: QObject *mObjectProperty; + TestObject *mReturnedObject; }; class BenchObject : public QObject @@ -264,6 +279,7 @@ private slots: void testSetPropertyConversion(); void testDisconnect(); void testWrapRegisteredObject(); + void testPassWrappedObjectBack(); void testInfiniteRecursion(); void benchClassInfo(); -- cgit v1.2.1