diff options
Diffstat (limited to 'src/webchannel')
-rw-r--r-- | src/webchannel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/webchannel/doc/qtwebchannel.qdocconf | 1 | ||||
-rw-r--r-- | src/webchannel/doc/src/javascript.qdoc | 4 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher.cpp | 109 | ||||
-rw-r--r-- | src/webchannel/qmetaobjectpublisher_p.h | 9 | ||||
-rw-r--r-- | src/webchannel/qqmlwebchannel.cpp | 23 | ||||
-rw-r--r-- | src/webchannel/qqmlwebchannel.h | 8 | ||||
-rw-r--r-- | src/webchannel/qwebchannel_p.h | 4 | ||||
-rw-r--r-- | src/webchannel/qwebchannelabstracttransport.h | 4 | ||||
-rw-r--r-- | src/webchannel/signalhandler_p.h | 14 | ||||
-rw-r--r-- | src/webchannel/variantargument_p.h | 4 |
11 files changed, 125 insertions, 57 deletions
diff --git a/src/webchannel/CMakeLists.txt b/src/webchannel/CMakeLists.txt index 45c0373..9fba217 100644 --- a/src/webchannel/CMakeLists.txt +++ b/src/webchannel/CMakeLists.txt @@ -15,6 +15,8 @@ qt_add_module(WebChannel Qt::CorePrivate PUBLIC_LIBRARIES Qt::Core + PRIVATE_MODULE_INTERFACE + Qt::CorePrivate ) # Resources: diff --git a/src/webchannel/doc/qtwebchannel.qdocconf b/src/webchannel/doc/qtwebchannel.qdocconf index 716da64..e7c43d0 100644 --- a/src/webchannel/doc/qtwebchannel.qdocconf +++ b/src/webchannel/doc/qtwebchannel.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtwebchannel.qdocconf) project = QtWebChannel description = Qt WebChannel Reference Documentation diff --git a/src/webchannel/doc/src/javascript.qdoc b/src/webchannel/doc/src/javascript.qdoc index ef44250..92170a0 100644 --- a/src/webchannel/doc/src/javascript.qdoc +++ b/src/webchannel/doc/src/javascript.qdoc @@ -89,8 +89,8 @@ new QWebChannel(yourTransport, function(channel) { // To get notified about remote property changes, // simply connect to the corresponding notify signal: - foo.onMyPropertyChanged.connect(function(newValue) { - console.log(newValue); + foo.myPropertyChanged.connect(function() { + console.log(foo.myProperty); }); // One can also access enums that are marked with Q_ENUM: diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp index 132f8b9..06243df 100644 --- a/src/webchannel/qmetaobjectpublisher.cpp +++ b/src/webchannel/qmetaobjectpublisher.cpp @@ -78,7 +78,7 @@ bool isQFlagsType(uint id) return false; } - QByteArray name = QMetaType::typeName(id); + QByteArray name = type.name(); name = name.mid(name.lastIndexOf(":") + 1); return mo->indexOfEnumerator(name.constData()) > -1; } @@ -123,7 +123,7 @@ int doubleToNumberConversionScore(int userType) break; } - if (QMetaType::typeFlags(userType) & QMetaType::IsEnumeration) + if (QMetaType(userType).flags() & QMetaType::IsEnumeration) return doubleToNumberConversionScore(QMetaType::Int); return IncompatibleScore; @@ -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; @@ -470,7 +471,7 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QMetaMe // Only init variant with return type if its not a variant itself, which would // lead to nested variants which is not what we want. if (method.returnType() != QMetaType::QVariant) - returnValue = QVariant(method.returnType(), 0); + returnValue = QVariant(QMetaType(method.returnType()), nullptr); QGenericReturnArgument returnArgument(method.typeName(), returnValue.data()); method.invoke(object, returnArgument, @@ -496,7 +497,7 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int met QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const QByteArray &methodName, const QJsonArray &args) { - QVector<OverloadResolutionCandidate> candidates; + QList<OverloadResolutionCandidate> candidates; const QMetaObject *mo = object->metaObject(); for (int i = 0; i < mo->methodCount(); ++i) { @@ -611,8 +612,47 @@ 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 QVariant::List: + return unwrapList(value.toList()); + case QVariant::Map: + return unwrapMap(value.toMap()); + default: + break; + } + return value; +} + QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType) const { + QMetaType target(targetType); if (targetType == QMetaType::QJsonValue) { return QVariant::fromValue(value); } else if (targetType == QMetaType::QJsonArray) { @@ -623,34 +663,35 @@ QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType 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) { + } else if (target.flags() & 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); } else if (isQFlagsType(targetType)) { int flagsValue = value.toInt(); - return QVariant(targetType, reinterpret_cast<const void*>(&flagsValue)); + return QVariant(target, reinterpret_cast<const void*>(&flagsValue)); } // 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) << '.'; + QVariant variant = unwrapVariant(value.toVariant()); + if (targetType != QMetaType::QVariant && !variant.convert(target)) { + qWarning() << "Could not convert argument" << value << "to target type" << target.name() << '.'; } return variant; } int QMetaObjectPublisher::conversionScore(const QJsonValue &value, int targetType) const { + QMetaType target(targetType); if (targetType == QMetaType::QJsonValue) { return PerfectMatchScore; } else if (targetType == QMetaType::QJsonArray) { return value.isArray() ? PerfectMatchScore : IncompatibleScore; } else if (targetType == QMetaType::QJsonObject) { return value.isObject() ? PerfectMatchScore : IncompatibleScore; - } else if (QMetaType::typeFlags(targetType) & QMetaType::PointerToQObject) { + } else if (target.flags() & QMetaType::PointerToQObject) { if (value.isNull()) return PerfectMatchScore; if (!value.isObject()) @@ -677,7 +718,7 @@ int QMetaObjectPublisher::conversionScore(const QJsonValue &value, int targetTyp QVariant variant = value.toVariant(); if (variant.userType() == targetType) { return PerfectMatchScore; - } else if (variant.canConvert(targetType)) { + } else if (variant.canConvert(target)) { return GenericConversionScore; } @@ -698,10 +739,10 @@ void QMetaObjectPublisher::transportRemoved(QWebChannelAbstractTransport *transp 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; + QList<QObject *> objectsForDeletion; while (it != transportedWrappedObjects.end() && it.key() == transport) { if (wrappedObjects.contains(it.value())) { - QVector<QWebChannelAbstractTransport*> &transports = wrappedObjects[it.value()].transports; + QList<QWebChannelAbstractTransport *> &transports = wrappedObjects[it.value()].transports; transports.removeOne(transport); if (transports.isEmpty()) objectsForDeletion.append(wrappedObjects[it.value()].object); @@ -739,25 +780,36 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA ObjectInfo oi(object); if (transport) { oi.transports.append(transport); + transportedWrappedObjects.insert(transport, id); } else { // use the transports from the parent object oi.transports = wrappedObjects.value(parentObjectId).transports; // or fallback to all transports if the parent is not wrapped if (oi.transports.isEmpty()) oi.transports = webChannel->d_func()->transports; + + for (auto transport : qAsConst(oi.transports)) { + transportedWrappedObjects.insert(transport, id); + } } wrappedObjects.insert(id, oi); - transportedWrappedObjects.insert(transport, id); initializePropertyUpdates(object, classInfo); - } else if (wrappedObjects.contains(id)) { - Q_ASSERT(object == wrappedObjects.value(id).object); - // check if this transport is already assigned to the object - if (transport && !wrappedObjects.value(id).transports.contains(transport)) { - wrappedObjects[id].transports.append(transport); - transportedWrappedObjects.insert(transport, id); + } else { + auto oi = wrappedObjects.find(id); + if (oi != wrappedObjects.end() && !oi->isBeingWrapped) { + Q_ASSERT(object == oi->object); + // check if this transport is already assigned to the object + if (transport && !oi->transports.contains(transport)) { + oi->transports.append(transport); + transportedWrappedObjects.insert(transport, id); + } + // QTBUG-84007: Block infinite recursion for self-contained objects + // which have already been wrapped + oi->isBeingWrapped = true; + classInfo = classInfoForObject(object, transport); + oi->isBeingWrapped = false; } - classInfo = classInfoForObject(object, transport); } QJsonObject objectInfo; @@ -767,7 +819,7 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA objectInfo[KEY_DATA] = classInfo; return objectInfo; - } else if (QMetaType::typeFlags(result.userType()).testFlag(QMetaType::IsEnumeration)) { + } else if (result.metaType().flags().testFlag(QMetaType::IsEnumeration)) { return result.toInt(); } else if (isQFlagsType(result.userType())) { return *reinterpret_cast<const int*>(result.constData()); @@ -784,10 +836,19 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA // *don't* use result.toList() as that *only* works for QVariantList and QStringList! // Also, don't use QSequentialIterable (yet), since that seems to trigger QTBUG-42016 // in certain cases. - return wrapList(result.value<QVariantList>(), transport); + // additionally, when there's a direct converter to QVariantList, use that one via convert + // but recover when conversion fails and fall back to the .value<QVariantList> conversion + // see also: https://bugreports.qt.io/browse/QTBUG-80751 + auto list = result; + if (!list.convert(QMetaType::fromType<QVariantList>())) + list = result; + return wrapList(list.value<QVariantList>(), transport); } else if (result.canConvert<QVariantMap>()) { // recurse and potentially wrap contents of the map - return wrapMap(result.toMap(), transport); + auto map = result; + if (!map.convert(QMetaType::fromType<QVariantMap>())) + map = result; + return wrapMap(map.value<QVariantMap>(), transport); } return QJsonValue::fromVariant(result); diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h index 6030de2..5e57e8c 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; @@ -292,13 +295,15 @@ private: QHash<const QObject *, QString> registeredObjectIds; // Groups individually wrapped objects with their class information and the transports that have access to it. + // Also tags objects that are in the process of being wrapped to prevent infinite recursion. struct ObjectInfo { ObjectInfo(QObject *o = nullptr) - : object(o) + : object(o), isBeingWrapped(false) {} QObject *object; - QVector<QWebChannelAbstractTransport*> transports; + QList<QWebChannelAbstractTransport *> transports; + bool isBeingWrapped; }; // Map of objects wrapped from invocation returns diff --git a/src/webchannel/qqmlwebchannel.cpp b/src/webchannel/qqmlwebchannel.cpp index 20f6e73..aea6abb 100644 --- a/src/webchannel/qqmlwebchannel.cpp +++ b/src/webchannel/qqmlwebchannel.cpp @@ -93,7 +93,7 @@ class QQmlWebChannelPrivate : public QWebChannelPrivate { Q_DECLARE_PUBLIC(QQmlWebChannel) public: - QVector<QObject*> registeredObjects; + QList<QObject *> registeredObjects; void _q_objectIdChanged(const QString &newId); }; @@ -204,10 +204,8 @@ void QQmlWebChannel::disconnectFrom(QObject *transport) QQmlListProperty<QObject> QQmlWebChannel::registeredObjects() { - return QQmlListProperty<QObject>(this, 0, - registeredObjects_append, - registeredObjects_count, - registeredObjects_at, + return QQmlListProperty<QObject>(this, nullptr, registeredObjects_append, + registeredObjects_count, registeredObjects_at, registeredObjects_clear); } @@ -229,12 +227,12 @@ void QQmlWebChannel::registeredObjects_append(QQmlListProperty<QObject> *prop, Q connect(attached, SIGNAL(idChanged(QString)), channel, SLOT(_q_objectIdChanged(QString))); } -int QQmlWebChannel::registeredObjects_count(QQmlListProperty<QObject> *prop) +qsizetype QQmlWebChannel::registeredObjects_count(QQmlListProperty<QObject> *prop) { return static_cast<QQmlWebChannel*>(prop->object)->d_func()->registeredObjects.size(); } -QObject *QQmlWebChannel::registeredObjects_at(QQmlListProperty<QObject> *prop, int index) +QObject *QQmlWebChannel::registeredObjects_at(QQmlListProperty<QObject> *prop, qsizetype index) { return static_cast<QQmlWebChannel*>(prop->object)->d_func()->registeredObjects.at(index); } @@ -250,11 +248,8 @@ void QQmlWebChannel::registeredObjects_clear(QQmlListProperty<QObject> *prop) QQmlListProperty<QObject> QQmlWebChannel::transports() { - return QQmlListProperty<QObject>(this, 0, - transports_append, - transports_count, - transports_at, - transports_clear); + return QQmlListProperty<QObject>(this, nullptr, transports_append, transports_count, + transports_at, transports_clear); } void QQmlWebChannel::transports_append(QQmlListProperty<QObject> *prop, QObject *transport) @@ -263,12 +258,12 @@ void QQmlWebChannel::transports_append(QQmlListProperty<QObject> *prop, QObject channel->connectTo(transport); } -int QQmlWebChannel::transports_count(QQmlListProperty<QObject> *prop) +qsizetype QQmlWebChannel::transports_count(QQmlListProperty<QObject> *prop) { return static_cast<QQmlWebChannel*>(prop->object)->d_func()->transports.size(); } -QObject *QQmlWebChannel::transports_at(QQmlListProperty<QObject> *prop, int index) +QObject *QQmlWebChannel::transports_at(QQmlListProperty<QObject> *prop, qsizetype index) { QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object); return channel->d_func()->transports.at(index); diff --git a/src/webchannel/qqmlwebchannel.h b/src/webchannel/qqmlwebchannel.h index d6b7bb9..72d067a 100644 --- a/src/webchannel/qqmlwebchannel.h +++ b/src/webchannel/qqmlwebchannel.h @@ -77,13 +77,13 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_objectIdChanged(const QString &newId)) static void registeredObjects_append(QQmlListProperty<QObject> *prop, QObject *item); - static int registeredObjects_count(QQmlListProperty<QObject> *prop); - static QObject *registeredObjects_at(QQmlListProperty<QObject> *prop, int index); + static qsizetype registeredObjects_count(QQmlListProperty<QObject> *prop); + static QObject *registeredObjects_at(QQmlListProperty<QObject> *prop, qsizetype index); static void registeredObjects_clear(QQmlListProperty<QObject> *prop); static void transports_append(QQmlListProperty<QObject> *prop, QObject *item); - static int transports_count(QQmlListProperty<QObject> *prop); - static QObject *transports_at(QQmlListProperty<QObject> *prop, int index); + static qsizetype transports_count(QQmlListProperty<QObject> *prop); + static QObject *transports_at(QQmlListProperty<QObject> *prop, qsizetype index); static void transports_clear(QQmlListProperty<QObject> *prop); }; diff --git a/src/webchannel/qwebchannel_p.h b/src/webchannel/qwebchannel_p.h index 28893ad..a5d1bb3 100644 --- a/src/webchannel/qwebchannel_p.h +++ b/src/webchannel/qwebchannel_p.h @@ -54,7 +54,7 @@ #include "qwebchannelglobal.h" #include <private/qobject_p.h> -#include <QVector> +#include <QList> QT_BEGIN_NAMESPACE @@ -66,7 +66,7 @@ class Q_WEBCHANNEL_EXPORT QWebChannelPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QWebChannel) public: - QVector<QWebChannelAbstractTransport*> transports; + QList<QWebChannelAbstractTransport *> transports; QMetaObjectPublisher *publisher; void init(); 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/signalhandler_p.h b/src/webchannel/signalhandler_p.h index 27afadb..8fdf598 100644 --- a/src/webchannel/signalhandler_p.h +++ b/src/webchannel/signalhandler_p.h @@ -53,7 +53,7 @@ #include <QObject> #include <QHash> -#include <QVector> +#include <QList> #include <QMetaMethod> #include <QDebug> @@ -121,7 +121,7 @@ private: // maps meta object -> signalIndex -> list of arguments // NOTE: This data is "leaked" on disconnect until deletion of the handler, is this a problem? - typedef QVector<int> ArgumentTypeList; + typedef QList<int> ArgumentTypeList; typedef QHash<int, ArgumentTypeList> SignalArgumentHash; QHash<const QMetaObject *, SignalArgumentHash > m_signalArgumentTypes; @@ -202,7 +202,7 @@ void SignalHandler<Receiver>::setupSignalArgumentTypes(const QMetaObject *metaOb return; } // find the type ids of the signal parameters, see also QSignalSpy::initArgs - QVector<int> args; + QList<int> args; args.reserve(signal.parameterCount()); for (int i = 0; i < signal.parameterCount(); ++i) { int tp = signal.parameterType(i); @@ -220,13 +220,13 @@ template<class Receiver> void SignalHandler<Receiver>::dispatch(const QObject *object, const int signalIdx, void **argumentData) { Q_ASSERT(m_signalArgumentTypes.contains(object->metaObject())); - const QHash<int, QVector<int> > &objectSignalArgumentTypes = m_signalArgumentTypes.value(object->metaObject()); - QHash<int, QVector<int> >::const_iterator signalIt = objectSignalArgumentTypes.constFind(signalIdx); + const QHash<int, QList<int>> &objectSignalArgumentTypes = m_signalArgumentTypes.value(object->metaObject()); + QHash<int, QList<int>>::const_iterator signalIt = objectSignalArgumentTypes.constFind(signalIdx); if (signalIt == objectSignalArgumentTypes.constEnd()) { // not connected to this signal, skip return; } - const QVector<int> &argumentTypes = *signalIt; + const QList<int> &argumentTypes = *signalIt; QVariantList arguments; arguments.reserve(argumentTypes.count()); // TODO: basic overload resolution based on number of arguments? @@ -236,7 +236,7 @@ void SignalHandler<Receiver>::dispatch(const QObject *object, const int signalId if (type == QMetaType::QVariant) { arg = *reinterpret_cast<QVariant *>(argumentData[i + 1]); } else { - arg = QVariant(type, argumentData[i + 1]); + arg = QVariant(QMetaType(type), argumentData[i + 1]); } arguments.append(arg); } 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 |