diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2014-01-10 14:21:05 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-06 16:32:20 +0100 |
commit | 54f66cc7a1e17155e90a1d3b5c33f627dbd0d50f (patch) | |
tree | 6dd4b35eda74487977bfae5bb0b81a133dfc1bf4 | |
parent | 05bafd509ca302fc63465fece7cd0c33ec602e31 (diff) | |
download | qtwebchannel-54f66cc7a1e17155e90a1d3b5c33f627dbd0d50f.tar.gz |
Make the underlying transport mechanism of the webchannel pluggable.
This enables us to optionally use navigator.qt instead of a WebSocket,
which is nicer setup-wise and is also slightly faster:
navigator.qt:
284.0 msecs per iteration (total: 2,840, iterations: 10)
WebSocket:
295.8 msecs per iteration (total: 2,959, iterations: 10)
The baseline is ca. 203 msecs, which would mean a performance boost
of ca. 12.7%.
Furthermore, this sets the fundation to eventually add a WebEngine
transport mechanism. The WebViewTransport should also be removed and
instead the WebView itself should directly implement the
WebChannelTransportInterface.
Change-Id: I368bb27e38ffa2f17ffeb7f5ae695690f6f5ad21
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
38 files changed, 813 insertions, 179 deletions
diff --git a/examples/qml/example.qml b/examples/qml/example.qml index f77b4c6..2d51f27 100644 --- a/examples/qml/example.qml +++ b/examples/qml/example.qml @@ -58,8 +58,11 @@ ApplicationWindow { WebChannel { id: webChannel - onRawMessageReceived: { - textEdit.text += "Received message: " + rawMessage + "\n"; + connections: WebViewTransport { + webViewExperimental: webView.experimental + onMessageReceived: { + textEdit.text += "Received message: " + message + "\n"; + } } } @@ -106,8 +109,9 @@ ApplicationWindow { Layout.fillHeight: true Layout.minimumWidth: window.width / 2 id: webView - url: webChannel.baseUrl ? ("index.html?webChannelBaseUrl=" + webChannel.baseUrl) : "about:blank" + url: "index.html" experimental.preferences.developerExtrasEnabled: true + experimental.preferences.navigatorQtObjectEnabled: true } } } diff --git a/examples/qml/index.html b/examples/qml/index.html index cd20ac9..8b65dd2 100644 --- a/examples/qml/index.html +++ b/examples/qml/index.html @@ -5,8 +5,7 @@ <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + new QWebChannel(navigator.qt, function(channel) { document.getElementById("send").onclick = function() { var input = document.getElementById("input"); var text = input.value; diff --git a/examples/standalone/main.cpp b/examples/standalone/main.cpp index 8546a12..836d324 100644 --- a/examples/standalone/main.cpp +++ b/examples/standalone/main.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qwebchannel.h" +#include "qwebsockettransport.h" #include <QApplication> #include <QDialog> @@ -55,14 +56,14 @@ class Dialog : public QObject Q_OBJECT public: - explicit Dialog(QWebChannel *channel, QObject *parent = 0) + explicit Dialog(QWebSocketTransport *transport, QObject *parent = 0) : QObject(parent) { ui.setupUi(&dialog); dialog.show(); connect(ui.send, SIGNAL(clicked()), SLOT(clicked())); - connect(channel, SIGNAL(baseUrlChanged(QString)), + connect(transport, SIGNAL(baseUrlChanged(QString)), SLOT(baseUrlChanged(QString))); } @@ -109,8 +110,10 @@ int main(int argc, char** argv) QApplication app(argc, argv); QWebChannel channel; + QWebSocketTransport transport; + channel.connectTo(&transport); - Dialog dialog(&channel); + Dialog dialog(&transport); channel.registerObject(QStringLiteral("dialog"), &dialog); diff --git a/src/imports/webchannel/plugin.cpp b/src/imports/webchannel/plugin.cpp index 92aac79..589e3cb 100644 --- a/src/imports/webchannel/plugin.cpp +++ b/src/imports/webchannel/plugin.cpp @@ -43,6 +43,11 @@ #include <QtQml/QQmlExtensionPlugin> #include "qmlwebchannel.h" +#include "qwebsockettransport.h" +#include "qmlwebviewtransport.h" +#include "qwebchanneltransportinterface.h" + +QML_DECLARE_INTERFACE_HASMETATYPE(QWebChannelTransportInterface); QT_BEGIN_NAMESPACE @@ -57,10 +62,13 @@ public: void QWebChannelPlugin::registerTypes(const char *uri) { + qmlRegisterInterface<QWebChannelTransportInterface>("QWebChannelTransportInterface"); + int major = 1; int minor = 0; qmlRegisterType<QmlWebChannel>(uri, major, minor, "WebChannel"); - + qmlRegisterType<QWebSocketTransport>(uri, major, minor, "WebSocketTransport"); + qmlRegisterType<QmlWebViewTransport>(uri, major, minor, "WebViewTransport"); } QT_END_NAMESPACE diff --git a/src/imports/webchannel/qmlwebchannel.cpp b/src/imports/webchannel/qmlwebchannel.cpp index 7c70f5c..dc7398d 100644 --- a/src/imports/webchannel/qmlwebchannel.cpp +++ b/src/imports/webchannel/qmlwebchannel.cpp @@ -51,7 +51,6 @@ QT_USE_NAMESPACE QmlWebChannel::QmlWebChannel(QObject *parent) : QWebChannel(parent) { - } QmlWebChannel::~QmlWebChannel() @@ -99,6 +98,37 @@ QmlWebChannelAttached *QmlWebChannel::qmlAttachedProperties(QObject *obj) return new QmlWebChannelAttached(obj); } +void QmlWebChannel::connectTo(QObject *transport) +{ + if (QWebChannelTransportInterface *iface = qobject_cast<QWebChannelTransportInterface*>(transport)) { + m_connectedObjects.insert(transport, iface); + QWebChannel::connectTo(iface); + connect(transport, SIGNAL(destroyed(QObject*)), SLOT(transportDestroyed(QObject*)), Qt::UniqueConnection); + } else { + qWarning() << "Cannot connect to transport" << transport << " - it does not implement the QWebChannelTransportInterface."; + } +} + +void QmlWebChannel::disconnectFrom(QObject *transport) +{ + if (QWebChannelTransportInterface *iface = qobject_cast<QWebChannelTransportInterface*>(transport)) { + QWebChannel::disconnectFrom(iface); + disconnect(transport, SIGNAL(destroyed(QObject*)), this, SLOT(transportDestroyed(QObject*))); + m_connectedObjects.remove(transport); + } else { + qWarning() << "Cannot disconnect from transport" << transport << " - it does not implement the QWebChannelTransportInterface."; + } +} + +void QmlWebChannel::transportDestroyed(QObject *transport) +{ + QWebChannelTransportInterface *iface = m_connectedObjects.take(transport); + const int idx = d->transports.indexOf(iface); + if (idx != -1) { + d->transports.remove(idx); + } +} + QQmlListProperty<QObject> QmlWebChannel::registeredObjects() { return QQmlListProperty<QObject>(this, 0, @@ -144,3 +174,37 @@ void QmlWebChannel::registeredObjects_clear(QQmlListProperty<QObject> *prop) } return channel->m_registeredObjects.clear(); } + +QQmlListProperty<QWebChannelTransportInterface> QmlWebChannel::transports() +{ + return QQmlListProperty<QWebChannelTransportInterface>(this, 0, + transports_append, + transports_count, + transports_at, + transports_clear); +} + +void QmlWebChannel::transports_append(QQmlListProperty<QWebChannelTransportInterface> *prop, QWebChannelTransportInterface *transport) +{ + QWebChannel *channel = static_cast<QWebChannel*>(prop->object); + channel->connectTo(transport); +} + +int QmlWebChannel::transports_count(QQmlListProperty<QWebChannelTransportInterface> *prop) +{ + return static_cast<QmlWebChannel*>(prop->object)->d->transports.size(); +} + +QWebChannelTransportInterface *QmlWebChannel::transports_at(QQmlListProperty<QWebChannelTransportInterface> *prop, int index) +{ + return static_cast<QmlWebChannel*>(prop->object)->d->transports.at(index); +} + +void QmlWebChannel::transports_clear(QQmlListProperty<QWebChannelTransportInterface> *prop) +{ + QWebChannel *channel = static_cast<QWebChannel*>(prop->object); + foreach (QWebChannelTransportInterface *transport, channel->d->transports) { + channel->disconnectFrom(transport); + } + Q_ASSERT(channel->d->transports.isEmpty()); +} diff --git a/src/imports/webchannel/qmlwebchannel.h b/src/imports/webchannel/qmlwebchannel.h index bcbb9cf..c45796f 100644 --- a/src/imports/webchannel/qmlwebchannel.h +++ b/src/imports/webchannel/qmlwebchannel.h @@ -45,6 +45,7 @@ #include <qwebchannel.h> #include "qmlwebchannelattached.h" +#include "qwebchanneltransportinterface.h" #include <QVector> @@ -53,13 +54,13 @@ QT_BEGIN_NAMESPACE -class QmlWebChannelAttached; - class QmlWebChannel : public QWebChannel { Q_OBJECT + Q_PROPERTY( QQmlListProperty<QWebChannelTransportInterface> connections READ transports ); Q_PROPERTY( QQmlListProperty<QObject> registeredObjects READ registeredObjects ) + public: QmlWebChannel(QObject *parent = 0); virtual ~QmlWebChannel(); @@ -67,13 +68,19 @@ public: Q_INVOKABLE void registerObjects(const QVariantMap& objects); QQmlListProperty<QObject> registeredObjects(); + QQmlListProperty<QWebChannelTransportInterface> transports(); + // TODO: remove this by replacing QML with C++ tests Q_INVOKABLE bool test_clientIsIdle() const; static QmlWebChannelAttached* qmlAttachedProperties(QObject *obj); + Q_INVOKABLE void connectTo(QObject *transport); + Q_INVOKABLE void disconnectFrom(QObject *transport); + private slots: void objectIdChanged(const QString &newId); + void transportDestroyed(QObject *transport); private: static void registeredObjects_append(QQmlListProperty<QObject> *prop, QObject* item); @@ -81,7 +88,14 @@ private: static QObject *registeredObjects_at(QQmlListProperty<QObject> *prop, int index); static void registeredObjects_clear(QQmlListProperty<QObject> *prop); + static void transports_append(QQmlListProperty<QWebChannelTransportInterface> *prop, QWebChannelTransportInterface* item); + static int transports_count(QQmlListProperty<QWebChannelTransportInterface> *prop); + static QWebChannelTransportInterface *transports_at(QQmlListProperty<QWebChannelTransportInterface> *prop, int index); + static void transports_clear(QQmlListProperty<QWebChannelTransportInterface> *prop); + QVector<QObject*> m_registeredObjects; + // required as when the object is destroyed, we must still find the address of the base class somehow + QHash<QObject*, QWebChannelTransportInterface*> m_connectedObjects; }; QML_DECLARE_TYPE( QmlWebChannel ) diff --git a/src/imports/webchannel/qmlwebviewtransport.cpp b/src/imports/webchannel/qmlwebviewtransport.cpp new file mode 100644 index 0000000..7fb9d16 --- /dev/null +++ b/src/imports/webchannel/qmlwebviewtransport.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 "qmlwebviewtransport.h" + +#include <QVariantMap> + +QT_USE_NAMESPACE + +QmlWebViewTransport::QmlWebViewTransport(QObject *parent) + : QObject(parent) + , m_webViewExperimental(Q_NULLPTR) + , m_handler(Q_NULLPTR) +{ +} + +QmlWebViewTransport::~QmlWebViewTransport() +{ + +} + +void QmlWebViewTransport::setWebViewExperimental(QObject *webViewExperimental) +{ + if (webViewExperimental != m_webViewExperimental) { + if (m_webViewExperimental) { + disconnect(m_webViewExperimental, 0, this, 0); + } + m_webViewExperimental = webViewExperimental; + connect(m_webViewExperimental, SIGNAL(messageReceived(QVariantMap)), this, SLOT(handleWebViewMessage(QVariantMap))); + emit webViewChanged(webViewExperimental); + } +} + +QObject *QmlWebViewTransport::webViewExperimental() const +{ + return m_webViewExperimental; +} + +void QmlWebViewTransport::sendMessage(const QString &message) const +{ + if (!m_webViewExperimental) { + qWarning("Cannot send message - did you forget to set the webViewExperimental property?"); + return; + } + QMetaObject::invokeMethod(m_webViewExperimental, "postMessage", Q_ARG(QString, message)); +} + +void QmlWebViewTransport::sendMessage(const QByteArray &message) const +{ + sendMessage(QString::fromUtf8(message)); +} + +void QmlWebViewTransport::handleWebViewMessage(const QVariantMap &message) +{ + if (m_handler) { + const QString &data = message[QStringLiteral("data")].toString(); + m_handler->handleMessage(data); + emit messageReceived(data); + } +} + +void QmlWebViewTransport::setMessageHandler(QWebChannelMessageHandlerInterface *handler) +{ + m_handler = handler; +} diff --git a/src/imports/webchannel/qmlwebviewtransport.h b/src/imports/webchannel/qmlwebviewtransport.h new file mode 100644 index 0000000..98dc504 --- /dev/null +++ b/src/imports/webchannel/qmlwebviewtransport.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 QMLWEBVIEWTRANSPORT_H +#define QMLWEBVIEWTRANSPORT_H + +#include <qwebchanneltransportinterface.h> + +QT_BEGIN_NAMESPACE + +class QmlWebViewTransport : public QObject, public QWebChannelTransportInterface +{ + Q_OBJECT + Q_INTERFACES(QWebChannelTransportInterface) + Q_PROPERTY(QObject *webViewExperimental READ webViewExperimental WRITE setWebViewExperimental NOTIFY webViewChanged) +public: + explicit QmlWebViewTransport(QObject *parent = 0); + ~QmlWebViewTransport() Q_DECL_OVERRIDE; + + void sendMessage(const QString &message) const Q_DECL_OVERRIDE; + void sendMessage(const QByteArray &message) const Q_DECL_OVERRIDE; + void setMessageHandler(QWebChannelMessageHandlerInterface *handler) Q_DECL_OVERRIDE; + + void setWebViewExperimental(QObject *webViewExperimental); + QObject *webViewExperimental() const; + +signals: + void webViewChanged(QObject *webViewExperimental); + void messageReceived(const QString &message); + +private slots: + void handleWebViewMessage(const QVariantMap &message); + +private: + QObject *m_webViewExperimental; + QWebChannelMessageHandlerInterface *m_handler; +}; + +QT_END_NAMESPACE + +#endif // QMLWEBVIEWTRANSPORT_H diff --git a/src/imports/webchannel/webchannel.pro b/src/imports/webchannel/webchannel.pro index c1959f5..3042854 100644 --- a/src/imports/webchannel/webchannel.pro +++ b/src/imports/webchannel/webchannel.pro @@ -6,10 +6,12 @@ VPATH += ../../webchannel SOURCES += \ plugin.cpp \ qmlwebchannel.cpp \ - qmlwebchannelattached.cpp + qmlwebchannelattached.cpp \ + qmlwebviewtransport.cpp HEADERS += \ qmlwebchannel.h \ - qmlwebchannelattached.h + qmlwebchannelattached.h \ + qmlwebviewtransport.h load(qml_plugin) diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp index bd2a22b..1ac3ad9 100644 --- a/src/webchannel/qmetaobjectpublisher.cpp +++ b/src/webchannel/qmetaobjectpublisher.cpp @@ -524,7 +524,7 @@ bool QMetaObjectPublisher::handleRequest(const QJsonObject &message) return false; } -void QMetaObjectPublisher::handleRawMessage(const QString &message) +void QMetaObjectPublisher::handleMessage(const QString &message) { const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8()); if (doc.isObject()) { diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h index 3e80d24..ff077e7 100644 --- a/src/webchannel/qmetaobjectpublisher_p.h +++ b/src/webchannel/qmetaobjectpublisher_p.h @@ -50,16 +50,17 @@ #include <QBasicTimer> #include <QPointer> +#include "qwebchannelglobal.h" +#include "qwebchanneltransportinterface.h" + QT_BEGIN_NAMESPACE class QWebChannel; -#include "qwebchannelglobal.h" - -class Q_WEBCHANNEL_EXPORT QMetaObjectPublisher : public QObject +class Q_WEBCHANNEL_EXPORT QMetaObjectPublisher : public QObject, public QWebChannelMessageHandlerInterface { Q_OBJECT - + Q_INTERFACES(QWebChannelMessageHandlerInterface) public: QMetaObjectPublisher(QWebChannel *webChannel); virtual ~QMetaObjectPublisher(); @@ -160,14 +161,10 @@ public: */ 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. + * Parse the message as JSON and if it succeeds, call handleRequest with the obtained JSON object. */ - void handleRawMessage(const QString &message); + void handleMessage(const QString &message) Q_DECL_OVERRIDE; signals: void blockUpdatesChanged(bool block); diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp index b51a056..a080aae 100644 --- a/src/webchannel/qwebchannel.cpp +++ b/src/webchannel/qwebchannel.cpp @@ -43,7 +43,7 @@ #include "qwebchannel.h" #include "qwebchannel_p.h" #include "qmetaobjectpublisher_p.h" -#include "qwebchannelsocket_p.h" +#include "qwebchanneltransportinterface.h" #include <QJsonDocument> #include <QJsonObject> @@ -52,6 +52,11 @@ QT_BEGIN_NAMESPACE void QWebChannelPrivate::sendJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response) const { + if (transports.isEmpty()) { + qWarning("QWebChannel is not connected to any transports, cannot send messages."); + return; + } + QJsonObject obj; if (response) { obj[QStringLiteral("response")] = true; @@ -61,55 +66,27 @@ void QWebChannelPrivate::sendJSONMessage(const QJsonValue &id, const QJsonValue obj[QStringLiteral("data")] = data; } QJsonDocument doc(obj); - socket->sendMessage(doc.toJson(QJsonDocument::Compact)); + const QByteArray &message = doc.toJson(QJsonDocument::Compact); + + foreach (QWebChannelTransportInterface *transport, transports) { + transport->sendMessage(message); + } } QWebChannel::QWebChannel(QObject *parent) : QObject(parent) , d(new QWebChannelPrivate) { - d->socket = new QWebChannelSocket(this); - - connect(d->socket, SIGNAL(textDataReceived(QString)), - SIGNAL(rawMessageReceived(QString))); - connect(d->socket, SIGNAL(failed(QString)), - SIGNAL(failed(QString))); - connect(d->socket, SIGNAL(initialized()), - SIGNAL(initialized())); - connect(d->socket, SIGNAL(baseUrlChanged(QString)), - SIGNAL(baseUrlChanged(QString))); - connect(d->socket, SIGNAL(pongReceived()), - SIGNAL(pongReceived())); - - 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() { -} - -QString QWebChannel::baseUrl() const -{ - return d->socket->m_baseUrl; -} - -void QWebChannel::setUseSecret(bool s) -{ - if (d->socket->m_useSecret == s) - return; - d->socket->m_useSecret = s; - d->socket->initLater(); -} - -bool QWebChannel::useSecret() const -{ - return d->socket->m_useSecret; + foreach (QWebChannelTransportInterface *transport, d->transports) { + transport->setMessageHandler(Q_NULLPTR); + } } void QWebChannel::registerObjects(const QHash< QString, QObject * > &objects) @@ -141,24 +118,32 @@ void QWebChannel::setBlockUpdates(bool block) d->publisher->setBlockUpdates(block); } -void QWebChannel::respond(const QJsonValue& messageId, const QJsonValue& data) const +void QWebChannel::connectTo(QWebChannelTransportInterface *transport) { - d->sendJSONMessage(messageId, data, true); + Q_ASSERT(transport); + if (!d->transports.contains(transport)) { + d->transports << transport; + transport->setMessageHandler(d->publisher); + } } -void QWebChannel::sendMessage(const QJsonValue& id, const QJsonValue& data) const +void QWebChannel::disconnectFrom(QWebChannelTransportInterface *transport) { - d->sendJSONMessage(id, data, false); + const int idx = d->transports.indexOf(transport); + if (idx != -1) { + transport->setMessageHandler(Q_NULLPTR); + d->transports.remove(idx); + } } -void QWebChannel::sendRawMessage(const QString& message) const +void QWebChannel::respond(const QJsonValue& messageId, const QJsonValue& data) const { - d->socket->sendMessage(message.toUtf8()); + d->sendJSONMessage(messageId, data, true); } -void QWebChannel::ping() const +void QWebChannel::sendMessage(const QJsonValue& id, const QJsonValue& data) const { - d->socket->ping(); + d->sendJSONMessage(id, data, false); } QT_END_NAMESPACE diff --git a/src/webchannel/qwebchannel.h b/src/webchannel/qwebchannel.h index bf9c5fd..f6965e0 100644 --- a/src/webchannel/qwebchannel.h +++ b/src/webchannel/qwebchannel.h @@ -51,24 +51,17 @@ QT_BEGIN_NAMESPACE struct QWebChannelPrivate; +class QWebChannelTransportInterface; class Q_WEBCHANNEL_EXPORT QWebChannel : public QObject { Q_OBJECT 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; - - void setUseSecret(bool); - bool useSecret() const; - /** * Register a map of string ID to QObject* objects. * @@ -96,21 +89,15 @@ public: */ void setBlockUpdates(bool block); -signals: - void baseUrlChanged(const QString& baseUrl); - void rawMessageReceived(const QString& rawMessage); - void pongReceived(); - void initialized(); - - void failed(const QString& reason); + void connectTo(QWebChannelTransportInterface *transport); + void disconnectFrom(QWebChannelTransportInterface *transport); +signals: 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; - void sendRawMessage(const QString& rawMessage) const; - void ping() const; private: QScopedPointer<QWebChannelPrivate> d; diff --git a/src/webchannel/qwebchannel.js b/src/webchannel/qwebchannel.js index 5ca051c..887c5a0 100644 --- a/src/webchannel/qwebchannel.js +++ b/src/webchannel/qwebchannel.js @@ -42,7 +42,7 @@ "use strict"; -var QWebChannel = function(baseUrl, initCallback, rawChannel) +var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel) { var channel = this; // support multiple channels listening to the same socket @@ -52,34 +52,14 @@ var QWebChannel = function(baseUrl, initCallback, rawChannel) var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); return v.toString(16); }); - ///TODO: use ssl? - var socketUrl = "ws://" + baseUrl; - this.socket = new WebSocket(socketUrl, "QWebChannel"); this.send = function(data) { if (typeof(data) !== "string") { data = JSON.stringify(data); } channel.socket.send(data); - }; - - this.socket.onopen = function() - { - if (rawChannel) { - initCallback(channel); - } else { - channel.initMetaObjectPublisher(initCallback); - } - }; - this.socket.onclose = function() - { - console.error("web channel closed"); - }; - this.socket.onerror = function(error) - { - console.error("web channel error: " + error); - }; - this.socket.onmessage = function(message) + } + this.messageReceived = function(message) { var jsonData = JSON.parse(message.data); if (jsonData.id === undefined) { @@ -99,7 +79,41 @@ var QWebChannel = function(baseUrl, initCallback, rawChannel) (callback)(jsonData.data); } ); } - }; + } + + this.initialized = function() + { + if (rawChannel) { + initCallback(channel); + } else { + channel.initMetaObjectPublisher(initCallback); + } + } + + if (typeof baseUrlOrSocket === 'object') { + this.socket = baseUrlOrSocket; + this.socket.send = function(data) + { + channel.socket.postMessage(data); + } + this.socket.onmessage = this.messageReceived + setTimeout(this.initialized, 0); + } else { + ///TODO: use ssl? + var socketUrl = "ws://" + baseUrlOrSocket; + this.socket = new WebSocket(socketUrl, "QWebChannel"); + + this.socket.onopen = this.initialized + this.socket.onclose = function() + { + console.error("web channel closed"); + }; + this.socket.onerror = function(error) + { + console.error("web channel error: " + error); + }; + this.socket.onmessage = this.messageReceived + } this.subscriptions = {}; this.subscribe = function(id, callback) @@ -136,7 +150,6 @@ var QWebChannel = function(baseUrl, initCallback, rawChannel) // prevent multiple initialization which might happen with multiple webchannel clients. var initialized = false; - console.log(channel); channel.subscribe( "Qt.signal", function(payload) { diff --git a/src/webchannel/qwebchannel_p.h b/src/webchannel/qwebchannel_p.h index 63f5a0a..954134e 100644 --- a/src/webchannel/qwebchannel_p.h +++ b/src/webchannel/qwebchannel_p.h @@ -43,16 +43,19 @@ #define QWEBCHANNEL_P_H #include "qwebchannelglobal.h" +#include "qwebchanneltransportinterface.h" + +#include <QVector> QT_BEGIN_NAMESPACE class QJsonValue; -class QWebChannelSocket; +class QWebChannelTransportInterface; class QMetaObjectPublisher; struct Q_WEBCHANNEL_EXPORT QWebChannelPrivate { - QWebChannelSocket *socket; + QVector<QWebChannelTransportInterface*> transports; QMetaObjectPublisher *publisher; void sendJSONMessage(const QJsonValue &id, const QJsonValue &data, bool response) const; diff --git a/src/webchannel/qwebchannelsocket.cpp b/src/webchannel/qwebchannelsocket.cpp index 84e6685..b3daf8f 100644 --- a/src/webchannel/qwebchannelsocket.cpp +++ b/src/webchannel/qwebchannelsocket.cpp @@ -48,11 +48,14 @@ QT_BEGIN_NAMESPACE QWebChannelSocket::QWebChannelSocket(QObject *parent) : QWebSocketServer(parent) + , m_messageHandler(Q_NULLPTR) , m_useSecret(true) , m_starting(false) { connect(this, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError())); + connect(this, SIGNAL(textDataReceived(QString)), + SLOT(messageReceived(QString))); } void QWebChannelSocket::initLater() @@ -100,4 +103,11 @@ void QWebChannelSocket::socketError() emit failed(errorString()); } +void QWebChannelSocket::messageReceived(const QString &message) +{ + if (m_messageHandler) { + m_messageHandler->handleMessage(message); + } +} + QT_END_NAMESPACE diff --git a/src/webchannel/qwebchannelsocket_p.h b/src/webchannel/qwebchannelsocket_p.h index 3f58f91..9d6cde5 100644 --- a/src/webchannel/qwebchannelsocket_p.h +++ b/src/webchannel/qwebchannelsocket_p.h @@ -43,6 +43,7 @@ #define QWEBCHANNELSOCKET_P_H #include "qwebsocketserver_p.h" +#include "qwebchanneltransportinterface.h" QT_BEGIN_NAMESPACE @@ -52,11 +53,12 @@ class QWebChannelSocket : public QWebSocketServer public: QByteArray m_secret; QString m_baseUrl; + QWebChannelMessageHandlerInterface *m_messageHandler; bool m_useSecret; bool m_starting; - QWebChannelSocket(QObject *parent); + explicit QWebChannelSocket(QObject *parent = 0); void initLater(); @@ -71,6 +73,7 @@ protected: private slots: void init(); void socketError(); + void messageReceived(const QString &message); }; QT_END_NAMESPACE diff --git a/src/webchannel/qwebchanneltransportinterface.h b/src/webchannel/qwebchanneltransportinterface.h new file mode 100644 index 0000000..9044c68 --- /dev/null +++ b/src/webchannel/qwebchanneltransportinterface.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 QWEBCHANNELTRANSPORTINTERFACE_H +#define QWEBCHANNELTRANSPORTINTERFACE_H + +#include <QObject> + +#include <QtWebChannel/qwebchannelglobal.h> + +QT_BEGIN_NAMESPACE + +class Q_WEBCHANNEL_EXPORT QWebChannelMessageHandlerInterface +{ +public: + virtual ~QWebChannelMessageHandlerInterface() {} + + /** + * Handle a text message from a web channel client. + */ + virtual void handleMessage(const QString &message) = 0; +}; + +#define QWebChannelMessageHandlerInterface_iid "org.qt-project.Qt.QWebChannelMessageHandlerInterface" +Q_DECLARE_INTERFACE(QWebChannelMessageHandlerInterface, QWebChannelMessageHandlerInterface_iid); +Q_DECLARE_METATYPE(QWebChannelMessageHandlerInterface*) + +class Q_WEBCHANNEL_EXPORT QWebChannelTransportInterface +{ +public: + virtual ~QWebChannelTransportInterface() {} + + /** + * Send a text message to all web channel clients. + */ + virtual void sendMessage(const QString &message) const = 0; + + /** + * Send a binary message to all web channel clients. + */ + virtual void sendMessage(const QByteArray &message) const = 0; + + /** + * Sets the message handler that will be called on incoming messages from web channel clients. + */ + virtual void setMessageHandler(QWebChannelMessageHandlerInterface *handler) = 0; +}; + +#define QWebChannelTransportInterface_iid "org.qt-project.Qt.QWebChannelTransportInterface" +Q_DECLARE_INTERFACE(QWebChannelTransportInterface, QWebChannelTransportInterface_iid); +Q_DECLARE_METATYPE(QWebChannelTransportInterface*) + +QT_END_NAMESPACE + +#endif // QWEBCHANNELTRANSPORTINTERFACE_H diff --git a/src/webchannel/qwebsocketserver.cpp b/src/webchannel/qwebsocketserver.cpp index 4404dc1..5a5b5a7 100644 --- a/src/webchannel/qwebsocketserver.cpp +++ b/src/webchannel/qwebsocketserver.cpp @@ -387,6 +387,9 @@ void QWebSocketServer::sendMessage(const QByteArray& message) const void QWebSocketServer::sendFrame(Frame::Opcode opcode, const QByteArray& data) const { + if (m_connections.isEmpty()) { + return; + } const QByteArray& header = frameHeader(opcode, data.size()); QHash< QTcpSocket*, Connection >::const_iterator it = m_connections.constBegin(); while (it != m_connections.constEnd()) { diff --git a/src/webchannel/qwebsockettransport.cpp b/src/webchannel/qwebsockettransport.cpp new file mode 100644 index 0000000..be991a8 --- /dev/null +++ b/src/webchannel/qwebsockettransport.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 "qwebsockettransport.h" +#include "qwebchannelsocket_p.h" + +QT_BEGIN_NAMESPACE + +QWebSocketTransport::QWebSocketTransport(QObject *parent) + : QObject(parent) + , d(new QWebChannelSocket) +{ + connect(d.data(), SIGNAL(textDataReceived(QString)), + SIGNAL(messageReceived(QString))); + connect(d.data(), SIGNAL(failed(QString)), + SIGNAL(failed(QString))); + connect(d.data(), SIGNAL(initialized()), + SIGNAL(initialized())); + connect(d.data(), SIGNAL(baseUrlChanged(QString)), + SIGNAL(baseUrlChanged(QString))); + + d->initLater(); +} + +QWebSocketTransport::~QWebSocketTransport() +{ + +} + +void QWebSocketTransport::sendMessage(const QByteArray &message) const +{ + d->sendMessage(message); +} + +void QWebSocketTransport::sendMessage(const QString &message) const +{ + d->sendMessage(message.toUtf8()); +} + +void QWebSocketTransport::setMessageHandler(QWebChannelMessageHandlerInterface *handler) +{ + d->m_messageHandler = handler; +} + +QString QWebSocketTransport::baseUrl() const +{ + return d->m_baseUrl; +} + +void QWebSocketTransport::setUseSecret(bool s) +{ + if (d->m_useSecret == s) + return; + d->m_useSecret = s; + d->initLater(); +} + +bool QWebSocketTransport::useSecret() const +{ + return d->m_useSecret; +} + +QT_END_NAMESPACE diff --git a/src/webchannel/qwebsockettransport.h b/src/webchannel/qwebsockettransport.h new file mode 100644 index 0000000..d8d164a --- /dev/null +++ b/src/webchannel/qwebsockettransport.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 QWEBSOCKETTRANSPORT_H +#define QWEBSOCKETTRANSPORT_H + +#include "qwebchanneltransportinterface.h" + +QT_BEGIN_NAMESPACE + +class QWebChannelSocket; +class Q_WEBCHANNEL_EXPORT QWebSocketTransport : public QObject, public QWebChannelTransportInterface +{ + Q_OBJECT + Q_INTERFACES(QWebChannelTransportInterface) + Q_PROPERTY(QString baseUrl READ baseUrl NOTIFY baseUrlChanged) + Q_PROPERTY(bool useSecret READ useSecret WRITE setUseSecret) +public: + explicit QWebSocketTransport(QObject *parent = 0); + ~QWebSocketTransport() Q_DECL_OVERRIDE; + + void sendMessage(const QByteArray &message) const Q_DECL_OVERRIDE; + void sendMessage(const QString &message) const Q_DECL_OVERRIDE; + void setMessageHandler(QWebChannelMessageHandlerInterface *handler) Q_DECL_OVERRIDE; + + QString baseUrl() const; + + void setUseSecret(bool); + bool useSecret() const; + +Q_SIGNALS: + void baseUrlChanged(const QString &baseUrl); + void failed(const QString &reason); + void initialized(); + void messageReceived(const QString &message); + +private: + QScopedPointer<QWebChannelSocket> d; +}; + +QT_END_NAMESPACE + +#endif // QWEBSOCKETTRANSPORT_H diff --git a/src/webchannel/webchannel.pro b/src/webchannel/webchannel.pro index 36cd686..279eb37 100644 --- a/src/webchannel/webchannel.pro +++ b/src/webchannel/webchannel.pro @@ -11,7 +11,9 @@ OTHER_FILES = \ qwebchannel.js PUBLIC_HEADERS += \ - qwebchannel.h + qwebchannel.h \ + qwebchanneltransport.h \ + qwebsockettransport.h PRIVATE_HEADERS += \ qwebchannel_p.h \ @@ -25,7 +27,8 @@ SOURCES += \ qwebchannel.cpp \ qmetaobjectpublisher.cpp \ qwebsocketserver.cpp \ - qwebchannelsocket.cpp + qwebchannelsocket.cpp \ + qwebsockettransport.cpp qtHaveModule(qml) { QT += qml diff --git a/tests/qml/WebChannelTest.qml b/tests/qml/WebChannelTest.qml index 1f84c1b..b3121d3 100644 --- a/tests/qml/WebChannelTest.qml +++ b/tests/qml/WebChannelTest.qml @@ -47,15 +47,25 @@ import QtWebKit 3.0 import QtWebKit.experimental 1.0 TestCase { + property var lastLoadStatus + property bool useWebViewTransport: false + // only run after the webchannel has finished initialization - when: webChannel.baseUrl != "" + when: webSocketTransport.baseUrl != "" - property var lastLoadStatus + WebViewTransport { + id: webViewTransport + webViewExperimental: view.experimental + } + WebSocketTransport { + id: webSocketTransport + } WebView { id: view experimental.preferences.developerExtrasEnabled: true + experimental.preferences.navigatorQtObjectEnabled: true onLoadingChanged: { // NOTE: we cannot use spy.signalArguments nor save the loadRequest anywhere, as it gets @@ -70,6 +80,7 @@ TestCase { signalName: "onLoadingChanged" } } + property var view: view WebChannel { id: webChannel @@ -78,23 +89,23 @@ TestCase { SignalSpy { id: rawMessageSpy - target: webChannel - signalName: "onRawMessageReceived" + target: useWebViewTransport ? webViewTransport : webSocketTransport; + signalName: "onMessageReceived" } property var rawMessageSpy: rawMessageSpy property var rawMessageIdx: 0; - SignalSpy { - id: pongSpy - target: webChannel - signalName: "onPongReceived" - } - property var pongSpy: pongSpy - function loadUrl(url) { - verify(webChannel.baseUrl != "", "webChannel.baseUrl is empty"); - view.url = "data/" + url + "?webChannelBaseUrl=" + webChannel.baseUrl; + if (useWebViewTransport) { + webChannel.disconnectFrom(webSocketTransport); + webChannel.connectTo(webViewTransport); + } else { + webChannel.disconnectFrom(webViewTransport); + webChannel.connectTo(webSocketTransport); + } + verify(useWebViewTransport || webSocketTransport.baseUrl != "", "webSocketTransport.baseUrl is empty"); + view.url = "data/" + url + (!useWebViewTransport ? "?webChannelBaseUrl=" + webSocketTransport.baseUrl : ""); // now wait for page to finish loading do { loadingSpy.wait(500); @@ -108,7 +119,6 @@ TestCase { loadingSpy.clear(); rawMessageSpy.clear(); rawMessageIdx = 0; - pongSpy.clear(); } function awaitRawMessage() diff --git a/tests/qml/data/bench_init.html b/tests/qml/data/bench_init.html index cb1b488..7d3af5b 100644 --- a/tests/qml/data/bench_init.html +++ b/tests/qml/data/bench_init.html @@ -1,10 +1,10 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) {}); + createWebChannel(function(channel) {}); //END SETUP </script> </head> diff --git a/tests/qml/data/disconnect.html b/tests/qml/data/disconnect.html index dc771b9..a9a479c 100644 --- a/tests/qml/data/disconnect.html +++ b/tests/qml/data/disconnect.html @@ -1,10 +1,10 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { myObj.mySignal.connect(function(arg) { channel.exec({label: "mySignalReceived", args: [arg]}); myObj.mySignal.disconnect(this); diff --git a/tests/qml/data/grouping.html b/tests/qml/data/grouping.html index 2a6c158..60fa0f8 100644 --- a/tests/qml/data/grouping.html +++ b/tests/qml/data/grouping.html @@ -1,10 +1,10 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - window.channel = new QWebChannel(baseUrl, function(channel) { + window.channel = createWebChannel(function(channel) { channel.subscribe("Qt.propertyUpdate", function() { channel.exec({label: "gotPropertyUpdate", values: [myObj.myProperty(), myOtherObj.foo(), myOtherObj.bar()]}); }); diff --git a/tests/qml/data/method.html b/tests/qml/data/method.html index 04048a6..6dbaa90 100644 --- a/tests/qml/data/method.html +++ b/tests/qml/data/method.html @@ -1,10 +1,10 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { channel.subscribe("invokeMethod", function(arg) { myObj.myMethod(arg); }); diff --git a/tests/qml/data/property.html b/tests/qml/data/property.html index 5d03118..9565aaa 100644 --- a/tests/qml/data/property.html +++ b/tests/qml/data/property.html @@ -1,10 +1,10 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { channel.exec({label: "init", value: myObj.myProperty()}); myObj.myPropertyChanged.connect(function() { channel.exec({label: "changed", value: myObj.myProperty()}); diff --git a/tests/qml/data/receiveRaw.html b/tests/qml/data/receiveRaw.html index 7c98bab..139b2b1 100644 --- a/tests/qml/data/receiveRaw.html +++ b/tests/qml/data/receiveRaw.html @@ -1,12 +1,12 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { channel.send("foobar"); - }, true); + }, true /* raw */); //END SETUP </script> </head> diff --git a/tests/qml/data/respond.html b/tests/qml/data/respond.html index 6c8db0d..5f0e7fc 100644 --- a/tests/qml/data/respond.html +++ b/tests/qml/data/respond.html @@ -1,14 +1,14 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { channel.exec("foobar", function(response) { channel.send("received:"+response); }); - }, true); + }, true /* raw */); //END SETUP </script> </head> diff --git a/tests/qml/data/send.html b/tests/qml/data/send.html index 7d601a8..c60fbf4 100644 --- a/tests/qml/data/send.html +++ b/tests/qml/data/send.html @@ -1,14 +1,14 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { channel.subscribe("myMessage", function(payload) { channel.send("myMessagePong:" + payload); }); - }, true); + }, true /* raw */); //END SETUP </script> </head> diff --git a/tests/qml/data/signal.html b/tests/qml/data/signal.html index 92b0ed5..bdce0c7 100644 --- a/tests/qml/data/signal.html +++ b/tests/qml/data/signal.html @@ -1,10 +1,10 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { myObj.mySignal.connect(function(arg) { channel.exec({label: "signalReceived", value: arg}); }); diff --git a/tests/qml/data/testsetup.js b/tests/qml/data/testsetup.js new file mode 100644 index 0000000..c0db83a --- /dev/null +++ b/tests/qml/data/testsetup.js @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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$ +** +****************************************************************************/ + +window.createWebChannel = function(callback, raw) +{ + var baseUrlMatch = /[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search); + var transport = baseUrlMatch ? baseUrlMatch[1] : navigator.qt; + return new QWebChannel(transport, callback, raw); +} diff --git a/tests/qml/data/wrapper.html b/tests/qml/data/wrapper.html index 9c4386c..a556486 100644 --- a/tests/qml/data/wrapper.html +++ b/tests/qml/data/wrapper.html @@ -1,10 +1,10 @@ <html> <head> <script type="text/javascript" src="qrc:///qwebchannel/qwebchannel.js"></script> + <script type="text/javascript" src="testsetup.js"></script> <script type="text/javascript"> //BEGIN SETUP - var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]); - new QWebChannel(baseUrl, function(channel) { + createWebChannel(function(channel) { myFactory.create("testObj", function(obj) { window[obj.objectName()] = obj; obj.mySignal.connect(function(arg1, arg2) { diff --git a/tests/qml/tst_bench.qml b/tests/qml/tst_bench.qml index 405bc08..056dda9 100644 --- a/tests/qml/tst_bench.qml +++ b/tests/qml/tst_bench.qml @@ -101,8 +101,19 @@ WebChannelTest { loadUrl("bench_init.html"); } - function benchmark_init() + function benchmark_init_webview() { + useWebViewTransport = true; + loadUrl("bench_init.html"); + // init + awaitMessage(); + // idle + awaitMessage(); + } + + function benchmark_init_websocket() + { + useWebViewTransport = false; loadUrl("bench_init.html"); // init awaitMessage(); diff --git a/tests/qml/tst_webchannel.qml b/tests/qml/tst_webchannel.qml index b357358..160a1af 100644 --- a/tests/qml/tst_webchannel.qml +++ b/tests/qml/tst_webchannel.qml @@ -66,12 +66,4 @@ WebChannelTest { webChannel.respond(msg.id, "barfoo"); compare(awaitRawMessage(), "received:barfoo"); } - - function test_ping() - { - loadUrl("respond.html"); - webChannel.ping(); - pongSpy.wait(500); - compare(pongSpy.count, 1); - } } diff --git a/tests/webchannel/tst_webchannel.cpp b/tests/webchannel/tst_webchannel.cpp index 0d05913..bb08c21 100644 --- a/tests/webchannel/tst_webchannel.cpp +++ b/tests/webchannel/tst_webchannel.cpp @@ -44,11 +44,13 @@ #include <qwebchannel.h> #include <qwebchannel_p.h> #include <qmetaobjectpublisher_p.h> +#include <qwebsockettransport.h> #include <QtTest> TestWebChannel::TestWebChannel(QObject *parent) : QObject(parent) + , m_dummyTransport(new DummyTransport(this)) , m_lastInt(0) , m_lastDouble(0) { @@ -74,19 +76,18 @@ void TestWebChannel::setVariant(const QVariant &v) m_lastVariant = v; } -void TestWebChannel::testInitChannel() +void TestWebChannel::testInitWebSocketTransport() { - QWebChannel channel; - - QSignalSpy initSpy(&channel, SIGNAL(initialized())); - QSignalSpy baseUrlSpy(&channel, SIGNAL(baseUrlChanged(QString))); + QWebSocketTransport transport; + QSignalSpy initSpy(&transport, SIGNAL(initialized())); + QSignalSpy baseUrlSpy(&transport, SIGNAL(baseUrlChanged(QString))); QVERIFY(initSpy.wait()); QCOMPARE(initSpy.size(), 1); QCOMPARE(baseUrlSpy.size(), 1); QCOMPARE(baseUrlSpy.first().size(), 1); - QCOMPARE(channel.baseUrl(), baseUrlSpy.first().first().toString()); - QVERIFY(!channel.baseUrl().isEmpty()); + QCOMPARE(transport.baseUrl(), baseUrlSpy.first().first().toString()); + QVERIFY(!transport.baseUrl().isEmpty()); } void TestWebChannel::testRegisterObjects() @@ -233,6 +234,7 @@ void TestWebChannel::testInfoForObject() void TestWebChannel::testInvokeMethodConversion() { QWebChannel channel; + channel.connectTo(m_dummyTransport); QJsonArray args; args.append(QJsonValue(1000)); @@ -271,8 +273,7 @@ static QHash<QString, QObject*> createObjects(QObject *parent) void TestWebChannel::benchClassInfo() { QWebChannel channel; - QSignalSpy initSpy(&channel, SIGNAL(initialized())); - QVERIFY(initSpy.wait()); + channel.connectTo(m_dummyTransport); QObject parent; const QHash<QString, QObject*> objects = createObjects(&parent); @@ -287,8 +288,7 @@ void TestWebChannel::benchClassInfo() void TestWebChannel::benchInitializeClients() { QWebChannel channel; - QSignalSpy initSpy(&channel, SIGNAL(initialized())); - QVERIFY(initSpy.wait()); + channel.connectTo(m_dummyTransport); QObject parent; channel.registerObjects(createObjects(&parent)); @@ -306,8 +306,7 @@ void TestWebChannel::benchInitializeClients() void TestWebChannel::benchPropertyUpdates() { QWebChannel channel; - QSignalSpy initSpy(&channel, SIGNAL(initialized())); - QVERIFY(initSpy.wait()); + channel.connectTo(m_dummyTransport); QObject parent; const QHash<QString, QObject*> objects = createObjects(&parent); @@ -333,8 +332,7 @@ void TestWebChannel::benchPropertyUpdates() void TestWebChannel::benchRegisterObjects() { QWebChannel channel; - QSignalSpy initSpy(&channel, SIGNAL(initialized())); - QVERIFY(initSpy.wait()); + channel.connectTo(m_dummyTransport); QObject parent; const QHash<QString, QObject*> objects = createObjects(&parent); diff --git a/tests/webchannel/tst_webchannel.h b/tests/webchannel/tst_webchannel.h index 7fd0bcc..26aa913 100644 --- a/tests/webchannel/tst_webchannel.h +++ b/tests/webchannel/tst_webchannel.h @@ -44,6 +44,25 @@ #include <QObject> #include <QVariant> +#include <qwebchanneltransportinterface.h> + +class DummyTransport : public QObject, public QWebChannelTransportInterface +{ + Q_OBJECT + Q_INTERFACES(QWebChannelTransportInterface) +public: + explicit DummyTransport(QObject *parent) + : QObject(parent) + {} + ~DummyTransport() {}; + + void sendMessage(const QString &/*message*/) const Q_DECL_OVERRIDE + {} + void sendMessage(const QByteArray &/*message*/) const Q_DECL_OVERRIDE + {} + void setMessageHandler(QWebChannelMessageHandlerInterface * /*handler*/) Q_DECL_OVERRIDE + {} +}; class TestObject : public QObject { @@ -197,7 +216,7 @@ public: Q_INVOKABLE void setVariant(const QVariant &v); private slots: - void testInitChannel(); + void testInitWebSocketTransport(); void testRegisterObjects(); void testInfoForObject(); void testInvokeMethodConversion(); @@ -208,6 +227,8 @@ private slots: void benchRegisterObjects(); private: + DummyTransport *m_dummyTransport; + int m_lastInt; double m_lastDouble; QVariant m_lastVariant; |