aboutsummaryrefslogtreecommitdiffstats
path: root/src/webchannel
diff options
context:
space:
mode:
Diffstat (limited to 'src/webchannel')
-rw-r--r--src/webchannel/CMakeLists.txt2
-rw-r--r--src/webchannel/doc/qtwebchannel.qdocconf1
-rw-r--r--src/webchannel/doc/src/javascript.qdoc4
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp109
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h9
-rw-r--r--src/webchannel/qqmlwebchannel.cpp23
-rw-r--r--src/webchannel/qqmlwebchannel.h8
-rw-r--r--src/webchannel/qwebchannel_p.h4
-rw-r--r--src/webchannel/qwebchannelabstracttransport.h4
-rw-r--r--src/webchannel/signalhandler_p.h14
-rw-r--r--src/webchannel/variantargument_p.h4
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