| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qqmlwebchannel.h" |
| |
| #include "qwebchannel_p.h" |
| #include "qmetaobjectpublisher_p.h" |
| #include "qwebchannelabstracttransport.h" |
| |
| #include <QtQml/QQmlContext> |
| |
| #include "qqmlwebchannelattached_p.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \qmltype WebChannel |
| \instantiates QQmlWebChannel |
| |
| \inqmlmodule QtWebChannel |
| \ingroup webchannel-qml |
| \brief QML interface to QWebChannel. |
| \since 5.4 |
| |
| The WebChannel provides a mechanism to transparently access QObject or QML objects from HTML |
| clients. All properties, signals and public slots can be used from the HTML clients. |
| |
| \sa QWebChannel, {Qt WebChannel JavaScript API}{JavaScript API} |
| */ |
| |
| /*! |
| \qmlproperty QQmlListProperty<QObject> WebChannel::transports |
| A list of transport objects, which implement QWebChannelAbstractTransport. The transports |
| are used to talk to the remote clients. |
| |
| \sa connectTo(), disconnectFrom() |
| */ |
| |
| /*! |
| \qmlproperty QQmlListProperty<QObject> WebChannel::registeredObjects |
| |
| \brief A list of objects which should be accessible to remote clients. |
| |
| The objects must have the attached \l id property set to an identifier, under which the |
| object is then known on the HTML side. |
| |
| Once registered, all signals and property changes are automatically propagated to the clients. |
| Public invokable methods, including slots, are also accessible to the clients. |
| |
| If one needs to register objects which are not available when the component is created, use the |
| imperative registerObjects method. |
| |
| \sa registerObjects(), id |
| */ |
| |
| class QQmlWebChannelPrivate : public QWebChannelPrivate |
| { |
| Q_DECLARE_PUBLIC(QQmlWebChannel) |
| public: |
| QVector<QObject*> registeredObjects; |
| |
| void _q_objectIdChanged(const QString &newId); |
| }; |
| |
| /*! |
| \internal |
| |
| Update the name of the sender object, when its attached WebChannel.id property changed. |
| This is required, since during startup the property is empty and only gets set later on. |
| */ |
| void QQmlWebChannelPrivate::_q_objectIdChanged(const QString &newId) |
| { |
| Q_Q(QQmlWebChannel); |
| const QQmlWebChannelAttached *const attached = qobject_cast<QQmlWebChannelAttached*>(q->sender()); |
| Q_ASSERT(attached); |
| Q_ASSERT(attached->parent()); |
| Q_ASSERT(registeredObjects.contains(attached->parent())); |
| |
| QObject *const object = attached->parent(); |
| const QString &oldId = publisher->registeredObjectIds.value(object); |
| |
| if (!oldId.isEmpty()) { |
| q->deregisterObject(object); |
| } |
| |
| q->registerObject(newId, object); |
| } |
| |
| QQmlWebChannel::QQmlWebChannel(QObject *parent) |
| : QWebChannel(*(new QQmlWebChannelPrivate), parent) |
| { |
| } |
| |
| QQmlWebChannel::~QQmlWebChannel() |
| { |
| |
| } |
| |
| /*! |
| \qmlmethod void WebChannel::registerObjects(QVariantMap objects) |
| Registers the specified \a objects to make them accessible to HTML clients. |
| The key of the map is used as an identifier for the object on the client side. |
| |
| Once registered, all signals and property changes are automatically propagated to the clients. |
| Public invokable methods, including slots, are also accessible to the clients. |
| |
| This imperative API can be used to register objects on the fly. For static objects, the declarative |
| registeredObjects property should be preferred. |
| |
| \sa registeredObjects |
| */ |
| void QQmlWebChannel::registerObjects(const QVariantMap &objects) |
| { |
| Q_D(QQmlWebChannel); |
| 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); |
| } |
| } |
| |
| QQmlWebChannelAttached *QQmlWebChannel::qmlAttachedProperties(QObject *obj) |
| { |
| return new QQmlWebChannelAttached(obj); |
| } |
| |
| /*! |
| \qmlmethod void WebChannel::connectTo(QWebChannelAbstractTransport transport) |
| |
| \brief Connects to the \a transport, which represents a communication |
| channel to a single client. |
| |
| The transport object must be an implementation of QWebChannelAbstractTransport. |
| |
| \sa transports, disconnectFrom() |
| */ |
| void QQmlWebChannel::connectTo(QObject *transport) |
| { |
| if (QWebChannelAbstractTransport *realTransport = qobject_cast<QWebChannelAbstractTransport*>(transport)) { |
| QWebChannel::connectTo(realTransport); |
| } else { |
| qWarning() << "Cannot connect to transport" << transport << " - it is not a QWebChannelAbstractTransport."; |
| } |
| } |
| |
| /*! |
| \qmlmethod void WebChannel::disconnectFrom(QWebChannelAbstractTransport transport) |
| |
| \brief Disconnects the \a transport from this WebChannel. |
| |
| The client will not be able to communicate with the WebChannel anymore, nor will it receive any |
| signals or property updates. |
| |
| \sa connectTo() |
| */ |
| void QQmlWebChannel::disconnectFrom(QObject *transport) |
| { |
| if (QWebChannelAbstractTransport *realTransport = qobject_cast<QWebChannelAbstractTransport*>(transport)) { |
| QWebChannel::disconnectFrom(realTransport); |
| } else { |
| qWarning() << "Cannot disconnect from transport" << transport << " - it is not a QWebChannelAbstractTransport."; |
| } |
| } |
| |
| QQmlListProperty<QObject> QQmlWebChannel::registeredObjects() |
| { |
| return QQmlListProperty<QObject>(this, 0, |
| registeredObjects_append, |
| registeredObjects_count, |
| registeredObjects_at, |
| registeredObjects_clear); |
| } |
| |
| void QQmlWebChannel::registeredObjects_append(QQmlListProperty<QObject> *prop, QObject *object) |
| { |
| const QQmlWebChannelAttached *const attached = qobject_cast<QQmlWebChannelAttached*>( |
| qmlAttachedPropertiesObject<QQmlWebChannel>(object, false /* don't create */)); |
| if (!attached) { |
| const QQmlContext *const context = qmlContext(object); |
| qWarning() << "Cannot register object" << context->nameForObject(object) << '(' << object << ") without attached WebChannel.id property. Did you forget to set it?"; |
| return; |
| } |
| QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object); |
| if (!attached->id().isEmpty()) { |
| // TODO: warning in such cases? |
| channel->registerObject(attached->id(), object); |
| } |
| channel->d_func()->registeredObjects.append(object); |
| connect(attached, SIGNAL(idChanged(QString)), channel, SLOT(_q_objectIdChanged(QString))); |
| } |
| |
| int QQmlWebChannel::registeredObjects_count(QQmlListProperty<QObject> *prop) |
| { |
| return static_cast<QQmlWebChannel*>(prop->object)->d_func()->registeredObjects.size(); |
| } |
| |
| QObject *QQmlWebChannel::registeredObjects_at(QQmlListProperty<QObject> *prop, int index) |
| { |
| return static_cast<QQmlWebChannel*>(prop->object)->d_func()->registeredObjects.at(index); |
| } |
| |
| void QQmlWebChannel::registeredObjects_clear(QQmlListProperty<QObject> *prop) |
| { |
| QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object); |
| foreach (QObject *object, channel->d_func()->registeredObjects) { |
| channel->deregisterObject(object); |
| } |
| return channel->d_func()->registeredObjects.clear(); |
| } |
| |
| QQmlListProperty<QObject> QQmlWebChannel::transports() |
| { |
| return QQmlListProperty<QObject>(this, 0, |
| transports_append, |
| transports_count, |
| transports_at, |
| transports_clear); |
| } |
| |
| void QQmlWebChannel::transports_append(QQmlListProperty<QObject> *prop, QObject *transport) |
| { |
| QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object); |
| channel->connectTo(transport); |
| } |
| |
| int QQmlWebChannel::transports_count(QQmlListProperty<QObject> *prop) |
| { |
| return static_cast<QQmlWebChannel*>(prop->object)->d_func()->transports.size(); |
| } |
| |
| QObject *QQmlWebChannel::transports_at(QQmlListProperty<QObject> *prop, int index) |
| { |
| QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object); |
| return channel->d_func()->transports.at(index); |
| } |
| |
| void QQmlWebChannel::transports_clear(QQmlListProperty<QObject> *prop) |
| { |
| QWebChannel *channel = static_cast<QWebChannel*>(prop->object); |
| foreach (QWebChannelAbstractTransport *transport, channel->d_func()->transports) { |
| channel->disconnectFrom(transport); |
| } |
| Q_ASSERT(channel->d_func()->transports.isEmpty()); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qqmlwebchannel.cpp" |