summaryrefslogtreecommitdiff
path: root/src/webchannel/qmetaobjectpublisher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/webchannel/qmetaobjectpublisher.cpp')
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp344
1 files changed, 142 insertions, 202 deletions
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index 27c7d70..d3659bd 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -40,7 +40,6 @@
**
****************************************************************************/
-#include "qmetaobjectpublisher.h"
#include "qmetaobjectpublisher_p.h"
#include "qwebchannel.h"
@@ -87,8 +86,9 @@ QString objectId(const QObject *object)
const int PROPERTY_UPDATE_INTERVAL = 50;
}
-QMetaObjectPublisherPrivate::QMetaObjectPublisherPrivate(QMetaObjectPublisher *q)
- : q(q)
+QMetaObjectPublisher::QMetaObjectPublisher(QWebChannel *webChannel)
+ : QObject(webChannel)
+ , webChannel(webChannel)
, signalHandler(this)
, clientIsIdle(false)
, blockUpdates(false)
@@ -97,7 +97,112 @@ QMetaObjectPublisherPrivate::QMetaObjectPublisherPrivate(QMetaObjectPublisher *q
{
}
-void QMetaObjectPublisherPrivate::setClientIsIdle(bool isIdle)
+QMetaObjectPublisher::~QMetaObjectPublisher()
+{
+
+}
+
+void QMetaObjectPublisher::registerObject(const QString &id, QObject *object)
+{
+ if (propertyUpdatesInitialized) {
+ qWarning("Registered new object after initialization. This does not work!");
+ return;
+ }
+ registeredObjects[id] = object;
+ registeredObjectIds[object] = id;
+}
+
+QJsonObject QMetaObjectPublisher::classInfoForObject(const QObject *object) const
+{
+ QJsonObject data;
+ if (!object) {
+ qWarning("null object given to MetaObjectPublisher - bad API usage?");
+ return data;
+ }
+
+ QJsonArray qtSignals;
+ QJsonArray qtMethods;
+ QJsonArray qtProperties;
+ QJsonObject qtEnums;
+
+ const QMetaObject *metaObject = object->metaObject();
+ QSet<int> notifySignals;
+ QSet<QString> identifiers;
+ for (int i = 0; i < metaObject->propertyCount(); ++i) {
+ const QMetaProperty &prop = metaObject->property(i);
+ QJsonArray propertyInfo;
+ const QString &propertyName = QString::fromLatin1(prop.name());
+ propertyInfo.append(i);
+ propertyInfo.append(propertyName);
+ identifiers << propertyName;
+ QJsonArray signalInfo;
+ if (prop.hasNotifySignal()) {
+ notifySignals << prop.notifySignalIndex();
+ const int numParams = prop.notifySignal().parameterCount();
+ if (numParams > 1) {
+ qWarning("Notify signal for property '%s' has %d parameters, expected zero or one.",
+ prop.name(), numParams);
+ }
+ // optimize: compress the common propertyChanged notification names, just send a 1
+ const QByteArray &notifySignal = prop.notifySignal().name();
+ static const QByteArray changedSuffix = QByteArrayLiteral("Changed");
+ if (notifySignal.length() == changedSuffix.length() + propertyName.length() &&
+ notifySignal.endsWith(changedSuffix) && notifySignal.startsWith(prop.name()))
+ {
+ signalInfo.append(1);
+ } else {
+ signalInfo.append(QString::fromLatin1(notifySignal));
+ }
+ signalInfo.append(prop.notifySignalIndex());
+ } else if (!prop.isConstant()) {
+ qWarning("Property '%s'' of object '%s' has no notify signal and is not constant, "
+ "value updates in HTML will be broken!",
+ prop.name(), object->metaObject()->className());
+ }
+ propertyInfo.append(signalInfo);
+ propertyInfo.append(QJsonValue::fromVariant(prop.read(object)));
+ qtProperties.append(propertyInfo);
+ }
+ for (int i = 0; i < metaObject->methodCount(); ++i) {
+ if (notifySignals.contains(i)) {
+ continue;
+ }
+ const QMetaMethod &method = metaObject->method(i);
+ //NOTE: this must be a string, otherwise it will be converted to '{}' in QML
+ const QString &name = QString::fromLatin1(method.name());
+ // optimize: skip overloaded methods/signals or property getters, on the JS side we can only
+ // call one of them anyways
+ // TODO: basic support for overloaded signals, methods
+ if (identifiers.contains(name)) {
+ continue;
+ }
+ identifiers << name;
+ // send data as array to client with format: [name, index]
+ QJsonArray data;
+ data.append(name);
+ data.append(i);
+ if (method.methodType() == QMetaMethod::Signal) {
+ qtSignals.append(data);
+ } else if (method.access() == QMetaMethod::Public) {
+ qtMethods.append(data);
+ }
+ }
+ for (int i = 0; i < metaObject->enumeratorCount(); ++i) {
+ QMetaEnum enumerator = metaObject->enumerator(i);
+ QJsonObject values;
+ for (int k = 0; k < enumerator.keyCount(); ++k) {
+ values[QString::fromLatin1(enumerator.key(k))] = enumerator.value(k);
+ }
+ qtEnums[QString::fromLatin1(enumerator.name())] = values;
+ }
+ data[KEY_SIGNALS] = qtSignals;
+ data[KEY_METHODS] = qtMethods;
+ data[KEY_PROPERTIES] = qtProperties;
+ data[KEY_ENUMS] = qtEnums;
+ return data;
+}
+
+void QMetaObjectPublisher::setClientIsIdle(bool isIdle)
{
if (clientIsIdle == isIdle) {
return;
@@ -106,11 +211,11 @@ void QMetaObjectPublisherPrivate::setClientIsIdle(bool isIdle)
if (!isIdle && timer.isActive()) {
timer.stop();
} else if (isIdle && !timer.isActive()) {
- timer.start(PROPERTY_UPDATE_INTERVAL, q);
+ timer.start(PROPERTY_UPDATE_INTERVAL, this);
}
}
-void QMetaObjectPublisherPrivate::initializeClients()
+void QMetaObjectPublisher::initializeClients()
{
if (!webChannel) {
return;
@@ -120,7 +225,7 @@ void QMetaObjectPublisherPrivate::initializeClients()
{
const QHash<QString, QObject *>::const_iterator end = registeredObjects.constEnd();
for (QHash<QString, QObject *>::const_iterator it = registeredObjects.constBegin(); it != end; ++it) {
- const QJsonObject &info = q->classInfoForObject(it.value());
+ const QJsonObject &info = classInfoForObject(it.value());
if (!propertyUpdatesInitialized) {
initializePropertyUpdates(it.value(), info);
}
@@ -132,7 +237,7 @@ void QMetaObjectPublisherPrivate::initializeClients()
pendingInit = false;
}
-void QMetaObjectPublisherPrivate::initializePropertyUpdates(const QObject *const object, const QJsonObject &objectInfo)
+void QMetaObjectPublisher::initializePropertyUpdates(const QObject *const object, const QJsonObject &objectInfo)
{
foreach (const QJsonValue &propertyInfoVar, objectInfo[KEY_PROPERTIES].toArray()) {
const QJsonArray &propertyInfo = propertyInfoVar.toArray();
@@ -164,7 +269,7 @@ void QMetaObjectPublisherPrivate::initializePropertyUpdates(const QObject *const
signalHandler.connectTo(object, s_destroyedSignalIndex);
}
-void QMetaObjectPublisherPrivate::sendPendingPropertyUpdates()
+void QMetaObjectPublisher::sendPendingPropertyUpdates()
{
if (blockUpdates || !clientIsIdle || pendingPropertyUpdates.isEmpty()) {
return;
@@ -204,7 +309,7 @@ void QMetaObjectPublisherPrivate::sendPendingPropertyUpdates()
setClientIsIdle(false);
}
-bool QMetaObjectPublisherPrivate::invokeMethod(QObject *const object, const int methodIndex,
+bool QMetaObjectPublisher::invokeMethod(QObject *const object, const int methodIndex,
const QJsonArray &args, const QJsonValue &id)
{
const QMetaMethod &method = object->metaObject()->method(methodIndex);
@@ -257,7 +362,7 @@ bool QMetaObjectPublisherPrivate::invokeMethod(QObject *const object, const int
return true;
}
-void QMetaObjectPublisherPrivate::signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments)
+void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments)
{
if (!webChannel) {
return;
@@ -280,12 +385,12 @@ void QMetaObjectPublisherPrivate::signalEmitted(const QObject *object, const int
} else {
pendingPropertyUpdates[object][signalIndex] = arguments;
if (clientIsIdle && !blockUpdates && !timer.isActive()) {
- timer.start(PROPERTY_UPDATE_INTERVAL, q);
+ timer.start(PROPERTY_UPDATE_INTERVAL, this);
}
}
}
-void QMetaObjectPublisherPrivate::objectDestroyed(const QObject *object)
+void QMetaObjectPublisher::objectDestroyed(const QObject *object)
{
const QString &id = registeredObjectIds.take(object);
Q_ASSERT(!id.isEmpty());
@@ -298,7 +403,7 @@ void QMetaObjectPublisherPrivate::objectDestroyed(const QObject *object)
wrappedObjects.remove(object);
}
-QJsonValue QMetaObjectPublisherPrivate::wrapResult(const QVariant &result)
+QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result)
{
if (QObject *object = result.value<QObject *>()) {
QJsonObject &objectInfo = wrappedObjects[object];
@@ -314,7 +419,7 @@ QJsonValue QMetaObjectPublisherPrivate::wrapResult(const QVariant &result)
objectInfo[KEY_QOBJECT] = true;
objectInfo[KEY_ID] = id;
- objectInfo[KEY_DATA] = q->classInfoForObject(object);
+ objectInfo[KEY_DATA] = classInfoForObject(object);
registeredObjectIds[object] = id;
registeredObjects[id] = object;
@@ -328,7 +433,7 @@ QJsonValue QMetaObjectPublisherPrivate::wrapResult(const QVariant &result)
return QJsonValue::fromVariant(result);
}
-void QMetaObjectPublisherPrivate::deleteWrappedObject(QObject *object) const
+void QMetaObjectPublisher::deleteWrappedObject(QObject *object) const
{
if (!wrappedObjects.contains(object)) {
qWarning() << "Not deleting non-wrapped object" << object;
@@ -337,142 +442,6 @@ void QMetaObjectPublisherPrivate::deleteWrappedObject(QObject *object) const
object->deleteLater();
}
-QMetaObjectPublisher::QMetaObjectPublisher(QObject *parent)
- : QObject(parent)
- , d(new QMetaObjectPublisherPrivate(this))
-{
-}
-
-QMetaObjectPublisher::~QMetaObjectPublisher()
-{
-
-}
-
-QJsonObject QMetaObjectPublisher::classInfoForObjects(const QVariantMap &objectMap) const
-{
- QJsonObject ret;
- QMap<QString, QVariant>::const_iterator it = objectMap.constBegin();
- while (it != objectMap.constEnd()) {
- QObject *object = it.value().value<QObject *>();
- if (object) {
- const QJsonObject &info = classInfoForObject(object);
- if (!info.isEmpty()) {
- ret[it.key()] = info;
- }
- }
- ++it;
- }
- return ret;
-}
-
-QJsonObject QMetaObjectPublisher::classInfoForObject(QObject *object) const
-{
- QJsonObject data;
- if (!object) {
- qWarning("null object given to MetaObjectPublisher - bad API usage?");
- return data;
- }
-
- QJsonArray qtSignals;
- QJsonArray qtMethods;
- QJsonArray qtProperties;
- QJsonObject qtEnums;
-
- const QMetaObject *metaObject = object->metaObject();
- QSet<int> notifySignals;
- QSet<QString> identifiers;
- for (int i = 0; i < metaObject->propertyCount(); ++i) {
- const QMetaProperty &prop = metaObject->property(i);
- QJsonArray propertyInfo;
- const QString &propertyName = QString::fromLatin1(prop.name());
- propertyInfo.append(i);
- propertyInfo.append(propertyName);
- identifiers << propertyName;
- QJsonArray signalInfo;
- if (prop.hasNotifySignal()) {
- notifySignals << prop.notifySignalIndex();
- const int numParams = prop.notifySignal().parameterCount();
- if (numParams > 1) {
- qWarning("Notify signal for property '%s' has %d parameters, expected zero or one.",
- prop.name(), numParams);
- }
- // optimize: compress the common propertyChanged notification names, just send a 1
- const QByteArray &notifySignal = prop.notifySignal().name();
- static const QByteArray changedSuffix = QByteArrayLiteral("Changed");
- if (notifySignal.length() == changedSuffix.length() + propertyName.length() &&
- notifySignal.endsWith(changedSuffix) && notifySignal.startsWith(prop.name()))
- {
- signalInfo.append(1);
- } else {
- signalInfo.append(QString::fromLatin1(notifySignal));
- }
- signalInfo.append(prop.notifySignalIndex());
- } else if (!prop.isConstant()) {
- qWarning("Property '%s'' of object '%s' has no notify signal and is not constant, "
- "value updates in HTML will be broken!",
- prop.name(), object->metaObject()->className());
- }
- propertyInfo.append(signalInfo);
- propertyInfo.append(QJsonValue::fromVariant(prop.read(object)));
- qtProperties.append(propertyInfo);
- }
- for (int i = 0; i < metaObject->methodCount(); ++i) {
- if (notifySignals.contains(i)) {
- continue;
- }
- const QMetaMethod &method = metaObject->method(i);
- //NOTE: this must be a string, otherwise it will be converted to '{}' in QML
- const QString &name = QString::fromLatin1(method.name());
- // optimize: skip overloaded methods/signals or property getters, on the JS side we can only
- // call one of them anyways
- // TODO: basic support for overloaded signals, methods
- if (identifiers.contains(name)) {
- continue;
- }
- identifiers << name;
- // send data as array to client with format: [name, index]
- QJsonArray data;
- data.append(name);
- data.append(i);
- if (method.methodType() == QMetaMethod::Signal) {
- qtSignals.append(data);
- } else if (method.access() == QMetaMethod::Public) {
- qtMethods.append(data);
- }
- }
- for (int i = 0; i < metaObject->enumeratorCount(); ++i) {
- QMetaEnum enumerator = metaObject->enumerator(i);
- QJsonObject values;
- for (int k = 0; k < enumerator.keyCount(); ++k) {
- values[QString::fromLatin1(enumerator.key(k))] = enumerator.value(k);
- }
- qtEnums[QString::fromLatin1(enumerator.name())] = values;
- }
- data[KEY_SIGNALS] = qtSignals;
- data[KEY_METHODS] = qtMethods;
- data[KEY_PROPERTIES] = qtProperties;
- data[KEY_ENUMS] = qtEnums;
- return data;
-}
-
-void QMetaObjectPublisher::registerObjects(const QVariantMap &objects)
-{
- if (d->propertyUpdatesInitialized) {
- qWarning("Registered new object after initialization. This does not work!");
- return;
- }
- const QMap<QString, QVariant>::const_iterator end = objects.end();
- for (QMap<QString, QVariant>::const_iterator it = objects.begin(); it != end; ++it) {
- QObject *object = it.value().value<QObject *>();
- if (!object) {
- qWarning("Invalid QObject given to register under name %s", qPrintable(it.key()));
- continue;
- }
- d->registeredObjects[it.key()] = object;
- d->registeredObjectIds[object] = it.key();
- }
-}
-
bool QMetaObjectPublisher::handleRequest(const QJsonObject &message)
{
if (!message.contains(KEY_DATA)) {
@@ -486,13 +455,13 @@ bool QMetaObjectPublisher::handleRequest(const QJsonObject &message)
const QString &type = payload.value(KEY_TYPE).toString();
if (type == TYPE_IDLE) {
- d->setClientIsIdle(true);
+ setClientIsIdle(true);
return true;
} else if (type == TYPE_INIT) {
- if (!d->blockUpdates) {
- d->initializeClients();
+ if (!blockUpdates) {
+ initializeClients();
} else {
- d->pendingInit = true;
+ pendingInit = true;
}
return true;
} else if (type == TYPE_DEBUG) {
@@ -501,19 +470,19 @@ bool QMetaObjectPublisher::handleRequest(const QJsonObject &message)
return true;
} else if (payload.contains(KEY_OBJECT)) {
const QString &objectName = payload.value(KEY_OBJECT).toString();
- QObject *object = d->registeredObjects.value(objectName);
+ QObject *object = registeredObjects.value(objectName);
if (!object) {
qWarning() << "Unknown object encountered" << objectName;
return false;
}
if (type == TYPE_INVOKE_METHOD) {
- return d->invokeMethod(object, payload.value(KEY_METHOD).toInt(-1), payload.value(KEY_ARGS).toArray(), message.value(KEY_ID));
+ return invokeMethod(object, payload.value(KEY_METHOD).toInt(-1), payload.value(KEY_ARGS).toArray(), message.value(KEY_ID));
} else if (type == TYPE_CONNECT_TO_SIGNAL) {
- d->signalHandler.connectTo(object, payload.value(KEY_SIGNAL).toInt(-1));
+ signalHandler.connectTo(object, payload.value(KEY_SIGNAL).toInt(-1));
return true;
} else if (type == TYPE_DISCONNECT_FROM_SIGNAL) {
- d->signalHandler.disconnectFrom(object, payload.value(KEY_SIGNAL).toInt(-1));
+ signalHandler.disconnectFrom(object, payload.value(KEY_SIGNAL).toInt(-1));
return true;
} else if (type == TYPE_SET_PROPERTY) {
const int propertyIdx = payload.value(KEY_PROPERTY).toInt(-1);
@@ -534,65 +503,36 @@ bool QMetaObjectPublisher::handleRequest(const QJsonObject &message)
void QMetaObjectPublisher::handleRawMessage(const QString &message)
{
- QJsonParseError error;
- const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error);
- if (error.error) {
- qWarning() << "Could not parse raw input message as JSON: " << error.errorString() << "Message was: " << message;
- } else if (doc.isObject() && !handleRequest(doc.object())) {
- qWarning() << "Could not handle raw message as meta object request: " << message;
- }
-}
-
-QWebChannel *QMetaObjectPublisher::webChannel() const
-{
- return d->webChannel;
-}
-
-void QMetaObjectPublisher::setWebChannel(QWebChannel *webChannel)
-{
- if (d->webChannel == webChannel) {
- return;
+ const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
+ if (doc.isObject()) {
+ handleRequest(doc.object());
}
-
- d->webChannel = webChannel;
-
- emit webChannelChanged(webChannel);
-}
-
-bool QMetaObjectPublisher::blockUpdates() const
-{
- return d->blockUpdates;
}
void QMetaObjectPublisher::setBlockUpdates(bool block)
{
- if (d->blockUpdates == block) {
+ if (blockUpdates == block) {
return;
}
- d->blockUpdates = block;
+ blockUpdates = block;
- if (!d->blockUpdates) {
- if (d->pendingInit) {
- d->initializeClients();
+ if (!blockUpdates) {
+ if (pendingInit) {
+ initializeClients();
} else {
- d->sendPendingPropertyUpdates();
+ sendPendingPropertyUpdates();
}
- } else if (d->timer.isActive()) {
- d->timer.stop();
+ } else if (timer.isActive()) {
+ timer.stop();
}
emit blockUpdatesChanged(block);
}
-bool QMetaObjectPublisher::test_clientIsIdle() const
-{
- return d->clientIsIdle;
-}
-
void QMetaObjectPublisher::timerEvent(QTimerEvent *event)
{
- if (event->timerId() == d->timer.timerId()) {
- d->sendPendingPropertyUpdates();
+ if (event->timerId() == timer.timerId()) {
+ sendPendingPropertyUpdates();
} else {
QObject::timerEvent(event);
}