summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/qtobject/main.qml13
-rw-r--r--examples/standalone/main.cpp9
-rw-r--r--src/imports/webchannel/plugin.cpp6
-rw-r--r--src/imports/webchannel/qmlwebchannel.cpp75
-rw-r--r--src/imports/webchannel/qmlwebchannel.h63
-rw-r--r--src/imports/webchannel/webchannel.pro11
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp344
-rw-r--r--src/webchannel/qmetaobjectpublisher.h121
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h62
-rw-r--r--src/webchannel/qwebchannel.cpp149
-rw-r--r--src/webchannel/qwebchannel.h35
-rw-r--r--src/webchannel/qwebchannel_p.h60
-rw-r--r--src/webchannel/qwebchannelsocket.cpp99
-rw-r--r--src/webchannel/qwebchannelsocket_p.h74
-rw-r--r--src/webchannel/signalhandler_p.h2
-rw-r--r--src/webchannel/webchannel.pro9
-rw-r--r--tests/qml/tst_bench.qml14
-rw-r--r--tests/qml/tst_metaobjectpublisher.qml21
-rw-r--r--tests/webchannel/tst_webchannel.cpp80
19 files changed, 711 insertions, 536 deletions
diff --git a/examples/qtobject/main.qml b/examples/qtobject/main.qml
index 76ef3d9..7bfe714 100644
--- a/examples/qtobject/main.qml
+++ b/examples/qtobject/main.qml
@@ -55,22 +55,11 @@ Rectangle {
objectName: "initialTestObject"
}
- MetaObjectPublisher {
- id: publisher
- webChannel: webChannel
- }
-
WebChannel {
id: webChannel
- onRawMessageReceived: {
- if (!publisher.handleRawMessage(rawMessage)) {
- console.log("unhandled request: ", rawMessage);
- }
- }
-
onInitialized: {
- publisher.registerObjects({
+ registerObjects({
"testObjectFactory": factory,
"initialTestObject": testObject
});
diff --git a/examples/standalone/main.cpp b/examples/standalone/main.cpp
index 6970633..1187950 100644
--- a/examples/standalone/main.cpp
+++ b/examples/standalone/main.cpp
@@ -41,7 +41,6 @@
****************************************************************************/
#include "qwebchannel.h"
-#include "qmetaobjectpublisher.h"
#include <QApplication>
#include <QDialog>
@@ -111,16 +110,10 @@ int main(int argc, char** argv)
QApplication app(argc, argv);
QWebChannel channel;
- QMetaObjectPublisher publisher;
- publisher.setWebChannel(&channel);
- QObject::connect(&channel, SIGNAL(rawMessageReceived(QString)),
- &publisher, SLOT(handleRawMessage(QString)));
Dialog dialog(&channel);
- QVariantMap objects;
- objects[QStringLiteral("dialog")] = QVariant::fromValue(&dialog);
- publisher.registerObjects(objects);
+ channel.registerObject(QStringLiteral("dialog"), &dialog);
return app.exec();
}
diff --git a/src/imports/webchannel/plugin.cpp b/src/imports/webchannel/plugin.cpp
index a5e9895..0835c2b 100644
--- a/src/imports/webchannel/plugin.cpp
+++ b/src/imports/webchannel/plugin.cpp
@@ -43,8 +43,7 @@
#include <qqml.h>
#include <QtQml/QQmlExtensionPlugin>
-#include "qwebchannel.h"
-#include "qmetaobjectpublisher.h"
+#include "qmlwebchannel.h"
QT_USE_NAMESPACE
@@ -61,8 +60,7 @@ void QWebChannelPlugin::registerTypes(const char *uri)
{
int major = 1;
int minor = 0;
- qmlRegisterType<QWebChannel>(uri, major, minor, "WebChannel");
- qmlRegisterType<QMetaObjectPublisher>(uri, major, minor, "MetaObjectPublisher");
+ qmlRegisterType<QmlWebChannel>(uri, major, minor, "WebChannel");
}
diff --git a/src/imports/webchannel/qmlwebchannel.cpp b/src/imports/webchannel/qmlwebchannel.cpp
new file mode 100644
index 0000000..a7a6db0
--- /dev/null
+++ b/src/imports/webchannel/qmlwebchannel.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt-project.org/legal
+*
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmlwebchannel.h"
+
+#include "qwebchannel_p.h"
+#include "qmetaobjectpublisher_p.h"
+
+QmlWebChannel::QmlWebChannel(QObject *parent)
+ : QWebChannel(parent)
+{
+
+}
+
+QmlWebChannel::~QmlWebChannel()
+{
+
+}
+
+void QmlWebChannel::registerObjects(const QVariantMap &objects)
+{
+ QMap<QString, QVariant>::const_iterator it = objects.constBegin();
+ for (; it != objects.constEnd(); ++it) {
+ QObject *object = it.value().value<QObject*>();
+ if (!object) {
+ qWarning("Invalid QObject given to register under name %s", qPrintable(it.key()));
+ continue;
+ }
+ d->publisher->registerObject(it.key(), object);
+ }
+}
+
+bool QmlWebChannel::test_clientIsIdle() const
+{
+ return d->publisher->clientIsIdle;
+}
diff --git a/src/imports/webchannel/qmlwebchannel.h b/src/imports/webchannel/qmlwebchannel.h
new file mode 100644
index 0000000..b52bc8b
--- /dev/null
+++ b/src/imports/webchannel/qmlwebchannel.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt-project.org/legal
+*
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLWEBCHANNEL_H
+#define QMLWEBCHANNEL_H
+
+#include <qwebchannel.h>
+
+class QmlWebChannel : public QWebChannel
+{
+ Q_OBJECT
+
+public:
+ QmlWebChannel(QObject *parent = 0);
+ virtual ~QmlWebChannel();
+
+ // TODO: replace by list property
+ Q_INVOKABLE void registerObjects(const QVariantMap &objects);
+
+ // TODO: remove this by replacing QML with C++ tests
+ Q_INVOKABLE bool test_clientIsIdle() const;
+};
+
+#endif // QMLWEBCHANNEL_H
diff --git a/src/imports/webchannel/webchannel.pro b/src/imports/webchannel/webchannel.pro
index 1753cee..3051077 100644
--- a/src/imports/webchannel/webchannel.pro
+++ b/src/imports/webchannel/webchannel.pro
@@ -1,6 +1,13 @@
-QT = core quick webchannel
+QT = core quick webchannel-private
+
+INCLUDEPATH += ../../webchannel
+VPATH += ../../webchannel
SOURCES += \
- plugin.cpp
+ plugin.cpp \
+ qmlwebchannel.cpp
+
+HEADERS += \
+ qmlwebchannel.h
load(qml_plugin)
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);
}
diff --git a/src/webchannel/qmetaobjectpublisher.h b/src/webchannel/qmetaobjectpublisher.h
deleted file mode 100644
index 9d17aff..0000000
--- a/src/webchannel/qmetaobjectpublisher.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
-** Contact: http://www.qt-project.org/legal
-*
-** This file is part of the QtWebChannel module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QTMETAOBJECTPUBLISHER_H
-#define QTMETAOBJECTPUBLISHER_H
-
-#include <QObject>
-#include <QVariant>
-#include <QJsonObject>
-
-#include "qwebchannelglobal.h"
-
-class QWebChannel;
-
-struct QMetaObjectPublisherPrivate;
-
-class Q_WEBCHANNEL_EXPORT QMetaObjectPublisher : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QWebChannel *webChannel READ webChannel WRITE setWebChannel NOTIFY webChannelChanged);
- Q_PROPERTY(bool blockUpdates READ blockUpdates WRITE setBlockUpdates NOTIFY blockUpdatesChanged);
-
-public:
- explicit QMetaObjectPublisher(QObject *parent = 0);
- virtual ~QMetaObjectPublisher();
-
- Q_INVOKABLE QJsonObject classInfoForObjects(const QVariantMap &objects) const;
- Q_INVOKABLE QJsonObject classInfoForObject(QObject *object) const;
-
- /**
- * Register a map of string ID to QObject* objects.
- *
- * The properties, signals and public methods of the QObject are
- * published to the remote client, where an object with the given identifier
- * is constructed.
- *
- * TODO: This must be called, before clients are initialized.
- */
- Q_INVOKABLE void registerObjects(const QVariantMap &objects);
-
- /**
- * Handle the given WebChannel client request and potentially give a response.
- *
- * @return true if the request was handled, false otherwise.
- */
- Q_INVOKABLE bool handleRequest(const QJsonObject &message);
-
- QWebChannel *webChannel() const;
- void setWebChannel(QWebChannel *webChannel);
-
- /**
- * When updates are blocked, no property updates are transmitted to remote clients.
- */
- bool blockUpdates() const;
- void setBlockUpdates(bool block);
-
- /// TODO: cleanup: rewrite tests in C++ and access PIMPL data from there
- Q_INVOKABLE bool test_clientIsIdle() const;
-
-signals:
- void webChannelChanged(QWebChannel *channel);
- void blockUpdatesChanged(bool block);
-
-public slots:
- /**
- * Helper slot which you can connect directly to WebChannel's rawMessageReceived signal.
- *
- * This slot then tries to parse the message as JSON and if it succeeds, calls handleRequest
- * with the obtained JSON object.
- */
- void handleRawMessage(const QString &message);
-
-protected:
- void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
-
-private:
- QScopedPointer<QMetaObjectPublisherPrivate> d;
- friend struct QMetaObjectPublisherPrivate;
- friend class TestWebChannel;
-};
-
-#endif // QMETAOBJECTPUBLISHER_H
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index 4df5b38..a7342d0 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -51,14 +51,40 @@
#include <QBasicTimer>
#include <QPointer>
-class QMetaObjectPublisher;
class QWebChannel;
#include "qwebchannelglobal.h"
-struct Q_WEBCHANNEL_EXPORT QMetaObjectPublisherPrivate
+class Q_WEBCHANNEL_EXPORT QMetaObjectPublisher : public QObject
{
- QMetaObjectPublisherPrivate(QMetaObjectPublisher *q);
+ Q_OBJECT
+
+public:
+ QMetaObjectPublisher(QWebChannel *webChannel);
+ virtual ~QMetaObjectPublisher();
+
+ /**
+ * Register @p object nuder the given @p id.
+ *
+ * The properties, signals and public methods of the QObject are
+ * published to the remote client, where an object with the given identifier
+ * is constructed.
+ *
+ * TODO: This must be called, before clients are initialized.
+ */
+ void registerObject(const QString &id, QObject *object);
+
+ /**
+ * Handle the given WebChannel client request and potentially give a response.
+ *
+ * @return true if the request was handled, false otherwise.
+ */
+ bool handleRequest(const QJsonObject &message);
+
+ /**
+ * Serialize the QMetaObject of @p object and return it in JSON form.
+ */
+ QJsonObject classInfoForObject(const QObject *object) const;
/**
* Set the client to idle or busy, based on the value of @p isIdle.
@@ -128,9 +154,33 @@ struct Q_WEBCHANNEL_EXPORT QMetaObjectPublisherPrivate
*/
void deleteWrappedObject(QObject *object) const;
- QMetaObjectPublisher *q;
- QPointer<QWebChannel> webChannel;
- SignalHandler<QMetaObjectPublisherPrivate> signalHandler;
+ /**
+ * When updates are blocked, no property updates are transmitted to remote clients.
+ */
+ void setBlockUpdates(bool block);
+
+public slots:
+ /**
+ * Helper slot which you can connect directly to WebChannel's rawMessageReceived signal.
+ *
+ * This slot then tries to parse the message as JSON and if it succeeds, calls handleRequest
+ * with the obtained JSON object.
+ */
+ void handleRawMessage(const QString &message);
+
+signals:
+ void blockUpdatesChanged(bool block);
+
+protected:
+ void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
+
+private:
+ friend class QmlWebChannel;
+ friend class QWebChannel;
+ friend class TestWebChannel;
+
+ QWebChannel *webChannel;
+ SignalHandler<QMetaObjectPublisher> signalHandler;
// true when the client is idle, false otherwise
bool clientIsIdle;
diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp
index 8dc7e5e..026b31e 100644
--- a/src/webchannel/qwebchannel.cpp
+++ b/src/webchannel/qwebchannel.cpp
@@ -41,93 +41,14 @@
****************************************************************************/
#include "qwebchannel.h"
+#include "qwebchannel_p.h"
+#include "qmetaobjectpublisher_p.h"
+#include "qwebchannelsocket_p.h"
-#include <QUuid>
-#include <QStringList>
-#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
-#include "qwebsocketserver_p.h"
-
-class QWebChannelPrivate : public QWebSocketServer
-{
- Q_OBJECT
-public:
- QByteArray m_secret;
- bool m_useSecret;
-
- QString m_baseUrl;
- bool m_starting;
-
- QWebChannelPrivate(QObject* parent)
- : QWebSocketServer(parent)
- , m_useSecret(true)
- , m_starting(false)
- {
- connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
- SLOT(socketError()));
- }
-
- void initLater()
- {
- if (m_starting)
- return;
- metaObject()->invokeMethod(this, "init", Qt::QueuedConnection);
- m_starting = true;
- }
-
- void sendJSONMessage(const QJsonValue& id, const QJsonValue& data, bool response) const;
-
-signals:
- void failed(const QString& reason);
- void initialized();
-
-protected:
- bool isValid(const HeaderData& connection) Q_DECL_OVERRIDE;
-
-private slots:
- void init();
- void socketError();
-};
-
-bool QWebChannelPrivate::isValid(const HeaderData& connection)
-{
- if (!QWebSocketServer::isValid(connection)) {
- return false;
- }
- return connection.protocol == QByteArrayLiteral("QWebChannel")
- && connection.path == m_secret;
-}
-
-void QWebChannelPrivate::init()
-{
- close();
-
- m_starting = false;
- if (m_useSecret) {
- m_secret = QUuid::createUuid().toByteArray();
- // replace { by /
- m_secret[0] = '/';
- // chop of trailing }
- m_secret.chop(1);
- }
-
- if (!listen(QHostAddress::LocalHost)) {
- emit failed(errorString());
- return;
- }
-
- m_baseUrl = QStringLiteral("127.0.0.1:%1%2").arg(port()).arg(QString::fromLatin1(m_secret));
- emit initialized();
-}
-
-void QWebChannelPrivate::socketError()
-{
- emit failed(errorString());
-}
-
-void QWebChannelPrivate::sendJSONMessage(const QJsonValue& id, const QJsonValue& data, bool response) const
+void QWebChannelPrivate::sendJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response) const
{
QJsonObject obj;
if (response) {
@@ -138,22 +59,31 @@ void QWebChannelPrivate::sendJSONMessage(const QJsonValue& id, const QJsonValue&
obj[QStringLiteral("data")] = data;
}
QJsonDocument doc(obj);
- sendMessage(doc.toJson(QJsonDocument::Compact));
+ socket->sendMessage(doc.toJson(QJsonDocument::Compact));
}
QWebChannel::QWebChannel(QObject *parent)
: QObject(parent)
-, d(new QWebChannelPrivate(this))
+, d(new QWebChannelPrivate)
{
- connect(d, SIGNAL(textDataReceived(QString)),
+ d->socket = new QWebChannelSocket(this);
+
+ connect(d->socket, SIGNAL(textDataReceived(QString)),
SIGNAL(rawMessageReceived(QString)));
- connect(d, SIGNAL(failed(QString)),
+ connect(d->socket, SIGNAL(failed(QString)),
SIGNAL(failed(QString)));
- connect(d, SIGNAL(initialized()),
+ connect(d->socket, SIGNAL(initialized()),
SLOT(onInitialized()));
- connect(d, SIGNAL(pongReceived()),
+ connect(d->socket, SIGNAL(pongReceived()),
SIGNAL(pongReceived()));
- d->initLater();
+
+ d->socket->initLater();
+
+ d->publisher = new QMetaObjectPublisher(this);
+ connect(d->publisher, SIGNAL(blockUpdatesChanged(bool)),
+ SIGNAL(blockUpdatesChanged(bool)));
+ connect(d->socket, SIGNAL(textDataReceived(QString)),
+ d->publisher, SLOT(handleRawMessage(QString)));
}
QWebChannel::~QWebChannel()
@@ -162,26 +92,49 @@ QWebChannel::~QWebChannel()
QString QWebChannel::baseUrl() const
{
- return d->m_baseUrl;
+ return d->socket->m_baseUrl;
}
void QWebChannel::setUseSecret(bool s)
{
- if (d->m_useSecret == s)
+ if (d->socket->m_useSecret == s)
return;
- d->m_useSecret = s;
- d->initLater();
+ d->socket->m_useSecret = s;
+ d->socket->initLater();
}
bool QWebChannel::useSecret() const
{
- return d->m_useSecret;
+ return d->socket->m_useSecret;
+}
+
+void QWebChannel::registerObjects(const QHash< QString, QObject * > &objects)
+{
+ const QHash<QString, QObject *>::const_iterator end = objects.constEnd();
+ for (QHash<QString, QObject *>::const_iterator it = objects.constBegin(); it != end; ++it) {
+ d->publisher->registerObject(it.key(), it.value());
+ }
+}
+
+void QWebChannel::registerObject(const QString &id, QObject *object)
+{
+ d->publisher->registerObject(id, object);
+}
+
+bool QWebChannel::blockUpdates() const
+{
+ return d->publisher->blockUpdates;
+}
+
+void QWebChannel::setBlockUpdates(bool block)
+{
+ d->publisher->setBlockUpdates(block);
}
void QWebChannel::onInitialized()
{
emit initialized();
- emit baseUrlChanged(d->m_baseUrl);
+ emit baseUrlChanged(d->socket->m_baseUrl);
}
void QWebChannel::respond(const QJsonValue& messageId, const QJsonValue& data) const
@@ -196,12 +149,12 @@ void QWebChannel::sendMessage(const QJsonValue& id, const QJsonValue& data) cons
void QWebChannel::sendRawMessage(const QString& message) const
{
- d->sendMessage(message.toUtf8());
+ d->socket->sendMessage(message.toUtf8());
}
void QWebChannel::ping() const
{
- d->ping();
+ d->socket->ping();
}
#include "qwebchannel.moc"
diff --git a/src/webchannel/qwebchannel.h b/src/webchannel/qwebchannel.h
index 321706e..ec62208 100644
--- a/src/webchannel/qwebchannel.h
+++ b/src/webchannel/qwebchannel.h
@@ -56,16 +56,43 @@ class Q_WEBCHANNEL_EXPORT QWebChannel : public QObject
Q_DISABLE_COPY(QWebChannel)
Q_PROPERTY(QString baseUrl READ baseUrl NOTIFY baseUrlChanged)
Q_PROPERTY(bool useSecret READ useSecret WRITE setUseSecret)
+ Q_PROPERTY(bool blockUpdates READ blockUpdates WRITE setBlockUpdates NOTIFY blockUpdatesChanged);
public:
QWebChannel(QObject *parent = 0);
~QWebChannel();
- QString baseUrl() const;
+ QString baseUrl() const;
void setUseSecret(bool);
bool useSecret() const;
+ /**
+ * Register a map of string ID to QObject* objects.
+ *
+ * The properties, signals and public methods of the QObject are
+ * published to the remote client, where an object with the given identifier
+ * is constructed.
+ *
+ * TODO: This must be called, before clients are initialized.
+ */
+ void registerObjects(const QHash<QString, QObject*> &objects);
+ void registerObject(const QString &id, QObject *object);
+
+ /**
+ * @return true when property updates are blocked, false otherwise.
+ */
+ bool blockUpdates() const;
+
+ /**
+ * Set whether property updates should be blocked or not.
+ *
+ * When they are blocked, the remote clients will not be notified about
+ * property changes. The changes are recorded and sent to the clients once
+ * setBlockUpdates(false) is called.
+ */
+ void setBlockUpdates(bool block);
+
signals:
void baseUrlChanged(const QString& baseUrl);
void rawMessageReceived(const QString& rawMessage);
@@ -74,6 +101,8 @@ signals:
void failed(const QString& reason);
+ void blockUpdatesChanged(bool block);
+
public slots:
void sendMessage(const QJsonValue& id, const QJsonValue& data = QJsonValue()) const;
void respond(const QJsonValue& messageId, const QJsonValue& data = QJsonValue()) const;
@@ -84,7 +113,9 @@ private slots:
void onInitialized();
private:
- QWebChannelPrivate* d;
+ QScopedPointer<QWebChannelPrivate> d;
+ friend class QmlWebChannel;
+ friend class TestWebChannel;
};
#endif // QWEBCHANNEL_H
diff --git a/src/webchannel/qwebchannel_p.h b/src/webchannel/qwebchannel_p.h
new file mode 100644
index 0000000..3b8a34b
--- /dev/null
+++ b/src/webchannel/qwebchannel_p.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt-project.org/legal
+*
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBCHANNEL_P_H
+#define QWEBCHANNEL_P_H
+
+#include "qwebchannelglobal.h"
+
+class QJsonValue;
+class QWebChannelSocket;
+class QMetaObjectPublisher;
+
+struct Q_WEBCHANNEL_EXPORT QWebChannelPrivate
+{
+ QWebChannelSocket *socket;
+ QMetaObjectPublisher *publisher;
+
+ void sendJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response) const;
+};
+
+#endif // QWEBCHANNEL_P_H
diff --git a/src/webchannel/qwebchannelsocket.cpp b/src/webchannel/qwebchannelsocket.cpp
new file mode 100644
index 0000000..c5275d0
--- /dev/null
+++ b/src/webchannel/qwebchannelsocket.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt-project.org/legal
+*
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebchannelsocket_p.h"
+
+#include <QUuid>
+#include <QDebug>
+
+QWebChannelSocket::QWebChannelSocket(QObject *parent)
+ : QWebSocketServer(parent)
+ , m_useSecret(true)
+ , m_starting(false)
+{
+ connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
+ SLOT(socketError()));
+}
+
+void QWebChannelSocket::initLater()
+{
+ if (m_starting)
+ return;
+ metaObject()->invokeMethod(this, "init", Qt::QueuedConnection);
+ m_starting = true;
+}
+
+bool QWebChannelSocket::isValid(const HeaderData &connection)
+{
+ if (!QWebSocketServer::isValid(connection)) {
+ return false;
+ }
+ return connection.protocol == QByteArrayLiteral("QWebChannel")
+ && connection.path == m_secret;
+}
+
+void QWebChannelSocket::init()
+{
+ close();
+
+ m_starting = false;
+ if (m_useSecret) {
+ m_secret = QUuid::createUuid().toByteArray();
+ // replace { by /
+ m_secret[0] = '/';
+ // chop of trailing }
+ m_secret.chop(1);
+ }
+
+ if (!listen(QHostAddress::LocalHost)) {
+ emit failed(errorString());
+ return;
+ }
+
+ m_baseUrl = QStringLiteral("127.0.0.1:%1%2").arg(port()).arg(QString::fromLatin1(m_secret));
+ emit initialized();
+}
+
+void QWebChannelSocket::socketError()
+{
+ emit failed(errorString());
+}
diff --git a/src/webchannel/qwebchannelsocket_p.h b/src/webchannel/qwebchannelsocket_p.h
new file mode 100644
index 0000000..234c200
--- /dev/null
+++ b/src/webchannel/qwebchannelsocket_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt-project.org/legal
+*
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBCHANNELSOCKET_P_H
+#define QWEBCHANNELSOCKET_P_H
+
+#include "qwebsocketserver_p.h"
+
+class QWebChannelSocket : public QWebSocketServer
+{
+ Q_OBJECT
+public:
+ QByteArray m_secret;
+ QString m_baseUrl;
+
+ bool m_useSecret;
+ bool m_starting;
+
+ QWebChannelSocket(QObject *parent);
+
+ void initLater();
+
+signals:
+ void failed(const QString &reason);
+ void initialized();
+
+protected:
+ bool isValid(const HeaderData &connection) Q_DECL_OVERRIDE;
+
+private slots:
+ void init();
+ void socketError();
+};
+
+#endif // QWEBCHANNELSOCKET_P_H
diff --git a/src/webchannel/signalhandler_p.h b/src/webchannel/signalhandler_p.h
index 66bdb28..8c49346 100644
--- a/src/webchannel/signalhandler_p.h
+++ b/src/webchannel/signalhandler_p.h
@@ -141,7 +141,7 @@ SignalHandler<Receiver>::SignalHandler(Receiver *receiver, QObject *parent)
*
* The return value is also verified to ensure it is a signal.
*/
-QMetaMethod findSignal(const QMetaObject *metaObject, const int signalIndex)
+inline QMetaMethod findSignal(const QMetaObject *metaObject, const int signalIndex)
{
QMetaMethod signal = metaObject->method(signalIndex);
if (!signal.isValid()) {
diff --git a/src/webchannel/webchannel.pro b/src/webchannel/webchannel.pro
index 376711d..0678346 100644
--- a/src/webchannel/webchannel.pro
+++ b/src/webchannel/webchannel.pro
@@ -11,17 +11,20 @@ OTHER_FILES = \
qwebchannel.js
PUBLIC_HEADERS += \
- qwebchannel.h \
- qmetaobjectpublisher.h
+ qwebchannel.h
PRIVATE_HEADERS += \
+ qwebchannel_p.h \
+ qmetaobjectpublisher_p.h \
qwebsocketserver_p.h \
+ qwebchannelsocket_p.h \
variantargument_p.h \
signalhandler_p.h
SOURCES += \
qwebchannel.cpp \
qmetaobjectpublisher.cpp \
- qwebsocketserver.cpp
+ qwebsocketserver.cpp \
+ qwebchannelsocket.cpp
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
diff --git a/tests/qml/tst_bench.qml b/tests/qml/tst_bench.qml
index 46eb6be..c6359ce 100644
--- a/tests/qml/tst_bench.qml
+++ b/tests/qml/tst_bench.qml
@@ -75,18 +75,6 @@ WebChannelTest {
}
}
- MetaObjectPublisher {
- id: publisher
- webChannel: test.webChannel
- }
-
- Connections {
- target: test.webChannel
- onRawMessageReceived: {
- publisher.handleRequest(JSON.parse(rawMessage));
- }
- }
-
property var objects: ({})
function initTestCase()
@@ -97,7 +85,7 @@ WebChannelTest {
objects[id] = component.createObject(test, properties);
}
- publisher.registerObjects(objects);
+ webChannel.registerObjects(objects);
}
function benchmark_init_baseline()
diff --git a/tests/qml/tst_metaobjectpublisher.qml b/tests/qml/tst_metaobjectpublisher.qml
index 5e1e825..cc679ad 100644
--- a/tests/qml/tst_metaobjectpublisher.qml
+++ b/tests/qml/tst_metaobjectpublisher.qml
@@ -78,26 +78,9 @@ WebChannelTest {
}
}
- MetaObjectPublisher {
- id: publisher
- webChannel: test.webChannel
- }
-
- Connections {
- target: test.webChannel
- onRawMessageReceived: {
- var message = JSON.parse(rawMessage);
- verify(message);
- var handled = publisher.handleRequest(message);
- if (message.data && message.data.type) {
- verify(handled);
- }
- }
- }
-
function initTestCase()
{
- publisher.registerObjects({
+ webChannel.registerObjects({
"myObj": myObj,
"myOtherObj": myOtherObj,
"myFactory": myFactory
@@ -119,7 +102,7 @@ WebChannelTest {
verify(msg);
verify(msg.data);
compare(msg.data.type, "Qt.idle");
- verify(publisher.test_clientIsIdle())
+ verify(webChannel.test_clientIsIdle())
}
function awaitMessageSkipIdle()
diff --git a/tests/webchannel/tst_webchannel.cpp b/tests/webchannel/tst_webchannel.cpp
index 3b90744..7fbe5f9 100644
--- a/tests/webchannel/tst_webchannel.cpp
+++ b/tests/webchannel/tst_webchannel.cpp
@@ -43,7 +43,7 @@
#include "tst_webchannel.h"
#include <qwebchannel.h>
-#include <qmetaobjectpublisher.h>
+#include <qwebchannel_p.h>
#include <qmetaobjectpublisher_p.h>
#include <QtTest>
@@ -58,7 +58,7 @@ TestWebChannel::~TestWebChannel()
}
- void TestWebChannel::testInitChannel()
+void TestWebChannel::testInitChannel()
{
QWebChannel channel;
@@ -76,26 +76,24 @@ TestWebChannel::~TestWebChannel()
void TestWebChannel::testRegisterObjects()
{
QWebChannel channel;
- QMetaObjectPublisher publisher;
- publisher.setWebChannel(&channel);
-
QObject plain;
- QVariantMap objects;
- objects["plain"] = QVariant::fromValue(&plain);
- objects["channel"] = QVariant::fromValue(&channel);
- objects["publisher"] = QVariant::fromValue(&publisher);
- objects["test"] = QVariant::fromValue(this);
+ QHash<QString, QObject*> objects;
+ objects[QStringLiteral("plain")] = &plain;
+ objects[QStringLiteral("channel")] = &channel;
+ objects[QStringLiteral("publisher")] = channel.d->publisher;
+ objects[QStringLiteral("test")] = this;
- publisher.registerObjects(objects);
+ channel.registerObjects(objects);
}
void TestWebChannel::testInfoForObject()
{
TestObject obj;
obj.setObjectName("myTestObject");
- QMetaObjectPublisher publisher;
- const QJsonObject info = publisher.classInfoForObject(&obj);
+
+ QWebChannel channel;
+ const QJsonObject info = channel.d->publisher->classInfoForObject(&obj);
QCOMPARE(info.keys(), QStringList() << "enums" << "methods" << "properties" << "signals");
@@ -216,12 +214,13 @@ void TestWebChannel::testInfoForObject()
}
}
-static QVariantMap createObjects(QObject *parent)
+static QHash<QString, QObject*> createObjects(QObject *parent)
{
const int num = 100;
- QVariantMap objects;
+ QHash<QString, QObject*> objects;
+ objects.reserve(num);
for (int i = 0; i < num; ++i) {
- objects[QStringLiteral("obj%1").arg(i)] = QVariant::fromValue(new BenchObject(parent));
+ objects[QStringLiteral("obj%1").arg(i)] = new BenchObject(parent);
}
return objects;
}
@@ -232,14 +231,13 @@ void TestWebChannel::benchClassInfo()
QSignalSpy initSpy(&channel, SIGNAL(initialized()));
QVERIFY(initSpy.wait());
- QMetaObjectPublisher publisher;
- publisher.setWebChannel(&channel);
-
QObject parent;
- const QVariantMap objects = createObjects(&parent);
+ const QHash<QString, QObject*> objects = createObjects(&parent);
QBENCHMARK {
- publisher.classInfoForObjects(objects);
+ foreach (const QObject *object, objects) {
+ channel.d->publisher->classInfoForObject(object);
+ }
}
}
@@ -249,19 +247,16 @@ void TestWebChannel::benchInitializeClients()
QSignalSpy initSpy(&channel, SIGNAL(initialized()));
QVERIFY(initSpy.wait());
- QMetaObjectPublisher publisher;
- publisher.setWebChannel(&channel);
-
QObject parent;
- const QVariantMap objects = createObjects(&parent);
- publisher.registerObjects(objects);
+ channel.registerObjects(createObjects(&parent));
+ QMetaObjectPublisher *publisher = channel.d->publisher;
QBENCHMARK {
- publisher.d->initializeClients();
+ publisher->initializeClients();
- publisher.d->propertyUpdatesInitialized = false;
- publisher.d->signalToPropertyMap.clear();
- publisher.d->signalHandler.clear();
+ publisher->propertyUpdatesInitialized = false;
+ publisher->signalToPropertyMap.clear();
+ publisher->signalHandler.clear();
}
}
@@ -271,26 +266,24 @@ void TestWebChannel::benchPropertyUpdates()
QSignalSpy initSpy(&channel, SIGNAL(initialized()));
QVERIFY(initSpy.wait());
- QMetaObjectPublisher publisher;
- publisher.setWebChannel(&channel);
-
QObject parent;
- const QVariantMap objects = createObjects(&parent);
+ const QHash<QString, QObject*> objects = createObjects(&parent);
QVector<BenchObject*> objectList;
- foreach (const QVariant &var, objects) {
- objectList << var.value<BenchObject*>();
+ objectList.reserve(objects.size());
+ foreach (QObject *obj, objects) {
+ objectList << qobject_cast<BenchObject*>(obj);
}
- publisher.registerObjects(objects);
- publisher.d->initializeClients();
+ channel.registerObjects(objects);
+ channel.d->publisher->initializeClients();
QBENCHMARK {
foreach (BenchObject *obj, objectList) {
obj->change();
}
- publisher.d->clientIsIdle = true;
- publisher.d->sendPendingPropertyUpdates();
+ channel.d->publisher->clientIsIdle = true;
+ channel.d->publisher->sendPendingPropertyUpdates();
}
}
@@ -300,14 +293,11 @@ void TestWebChannel::benchRegisterObjects()
QSignalSpy initSpy(&channel, SIGNAL(initialized()));
QVERIFY(initSpy.wait());
- QMetaObjectPublisher publisher;
- publisher.setWebChannel(&channel);
-
QObject parent;
- const QVariantMap objects = createObjects(&parent);
+ const QHash<QString, QObject*> objects = createObjects(&parent);
QBENCHMARK {
- publisher.registerObjects(objects);
+ channel.registerObjects(objects);
}
}