| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> |
| 5 | ** Contact: https://www.qt.io/licensing/ |
| 6 | ** |
| 7 | ** This file is part of the QtWebChannel module of the Qt Toolkit. |
| 8 | ** |
| 9 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 10 | ** Commercial License Usage |
| 11 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 12 | ** accordance with the commercial license agreement provided with the |
| 13 | ** Software or, alternatively, in accordance with the terms contained in |
| 14 | ** a written agreement between you and The Qt Company. For licensing terms |
| 15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 16 | ** information use the contact form at https://www.qt.io/contact-us. |
| 17 | ** |
| 18 | ** GNU Lesser General Public License Usage |
| 19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 20 | ** General Public License version 3 as published by the Free Software |
| 21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 22 | ** packaging of this file. Please review the following information to |
| 23 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 25 | ** |
| 26 | ** GNU General Public License Usage |
| 27 | ** Alternatively, this file may be used under the terms of the GNU |
| 28 | ** General Public License version 2.0 or (at your option) the GNU General |
| 29 | ** Public license version 3 or any later version approved by the KDE Free |
| 30 | ** Qt Foundation. The licenses are as published by the Free Software |
| 31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 32 | ** included in the packaging of this file. Please review the following |
| 33 | ** information to ensure the GNU General Public License requirements will |
| 34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 36 | ** |
| 37 | ** $QT_END_LICENSE$ |
| 38 | ** |
| 39 | ****************************************************************************/ |
| 40 | |
| 41 | #include "qwebchannel.h" |
| 42 | #include "qwebchannel_p.h" |
| 43 | #include "qmetaobjectpublisher_p.h" |
| 44 | #include "qwebchannelabstracttransport.h" |
| 45 | |
| 46 | #include <QJsonDocument> |
| 47 | #include <QJsonObject> |
| 48 | |
| 49 | #include <algorithm> |
| 50 | |
| 51 | QT_BEGIN_NAMESPACE |
| 52 | |
| 53 | /*! |
| 54 | \class QWebChannel |
| 55 | |
| 56 | \inmodule QtWebChannel |
| 57 | \brief Exposes QObjects to remote HTML clients. |
| 58 | \since 5.4 |
| 59 | |
| 60 | The QWebChannel fills the gap between C++ applications and HTML/JavaScript |
| 61 | applications. By publishing a QObject derived object to a QWebChannel and |
| 62 | using the \l{Qt WebChannel JavaScript API}{qwebchannel.js} on the HTML side, one can transparently access |
| 63 | properties and public slots and methods of the QObject. No manual message |
| 64 | passing and serialization of data is required, property updates and signal emission |
| 65 | on the C++ side get automatically transmitted to the potentially remotely running HTML clients. |
| 66 | On the client side, a JavaScript object will be created for any published C++ QObject. It mirrors the |
| 67 | C++ object's API and thus is intuitively useable. |
| 68 | |
| 69 | The C++ QWebChannel API makes it possible to talk to any HTML client, which could run on a local |
| 70 | or even remote machine. The only limitation is that the HTML client supports the JavaScript |
| 71 | features used by \c{qwebchannel.js}. As such, one can interact |
| 72 | with basically any modern HTML browser or standalone JavaScript runtime, such as node.js. |
| 73 | |
| 74 | There also exists a declarative \l{Qt WebChannel QML Types}{WebChannel API}. |
| 75 | |
| 76 | \sa {Qt WebChannel Standalone Example}, {Qt WebChannel JavaScript API}{JavaScript API} |
| 77 | */ |
| 78 | |
| 79 | /*! |
| 80 | \internal |
| 81 | |
| 82 | Remove a destroyed transport object from the list of known transports. |
| 83 | */ |
| 84 | void QWebChannelPrivate::_q_transportDestroyed(QObject *object) |
| 85 | { |
| 86 | auto it = std::find(first: transports.begin(), last: transports.end(), val: object); |
| 87 | if (it != transports.end()) { |
| 88 | auto *transport = *it; |
| 89 | transports.erase(pos: it); |
| 90 | publisher->transportRemoved(transport); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | /*! |
| 95 | \internal |
| 96 | |
| 97 | Shared code to initialize the QWebChannel from both constructors. |
| 98 | */ |
| 99 | void QWebChannelPrivate::init() |
| 100 | { |
| 101 | Q_Q(QWebChannel); |
| 102 | publisher = new QMetaObjectPublisher(q); |
| 103 | QObject::connect(sender: publisher, SIGNAL(blockUpdatesChanged(bool)), |
| 104 | receiver: q, SIGNAL(blockUpdatesChanged(bool))); |
| 105 | } |
| 106 | |
| 107 | /*! |
| 108 | Constructs the QWebChannel object with the given \a parent. |
| 109 | |
| 110 | Note that a QWebChannel is only fully operational once you connect it to a |
| 111 | QWebChannelAbstractTransport. The HTML clients also need to be setup appropriately |
| 112 | using \l{qtwebchannel-javascript.html}{\c qwebchannel.js}. |
| 113 | */ |
| 114 | QWebChannel::QWebChannel(QObject *parent) |
| 115 | : QObject(*(new QWebChannelPrivate), parent) |
| 116 | { |
| 117 | Q_D(QWebChannel); |
| 118 | d->init(); |
| 119 | } |
| 120 | |
| 121 | /*! |
| 122 | \internal |
| 123 | |
| 124 | Construct a QWebChannel from an ancestor class with the given \a parent. |
| 125 | |
| 126 | \sa QQmlWebChannel |
| 127 | */ |
| 128 | QWebChannel::QWebChannel(QWebChannelPrivate &dd, QObject *parent) |
| 129 | : QObject(dd, parent) |
| 130 | { |
| 131 | Q_D(QWebChannel); |
| 132 | d->init(); |
| 133 | } |
| 134 | |
| 135 | /*! |
| 136 | Destroys the QWebChannel. |
| 137 | */ |
| 138 | QWebChannel::~QWebChannel() |
| 139 | { |
| 140 | } |
| 141 | |
| 142 | /*! |
| 143 | Registers a group of objects to the QWebChannel. |
| 144 | |
| 145 | The properties, signals and public invokable methods of the objects are published to the remote clients. |
| 146 | There, an object with the identifier used as key in the \a objects map is then constructed. |
| 147 | |
| 148 | \note A current limitation is that objects must be registered before any client is initialized. |
| 149 | |
| 150 | \sa QWebChannel::registerObject(), QWebChannel::deregisterObject(), QWebChannel::registeredObjects() |
| 151 | */ |
| 152 | void QWebChannel::registerObjects(const QHash< QString, QObject * > &objects) |
| 153 | { |
| 154 | Q_D(QWebChannel); |
| 155 | const QHash<QString, QObject *>::const_iterator end = objects.constEnd(); |
| 156 | for (QHash<QString, QObject *>::const_iterator it = objects.constBegin(); it != end; ++it) { |
| 157 | d->publisher->registerObject(id: it.key(), object: it.value()); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /*! |
| 162 | Returns the map of registered objects that are published to remote clients. |
| 163 | |
| 164 | \sa QWebChannel::registerObjects(), QWebChannel::registerObject(), QWebChannel::deregisterObject() |
| 165 | */ |
| 166 | QHash<QString, QObject *> QWebChannel::registeredObjects() const |
| 167 | { |
| 168 | Q_D(const QWebChannel); |
| 169 | return d->publisher->registeredObjects; |
| 170 | } |
| 171 | |
| 172 | /*! |
| 173 | Registers a single object to the QWebChannel. |
| 174 | |
| 175 | The properties, signals and public methods of the \a object are published to the remote clients. |
| 176 | There, an object with the identifier \a id is then constructed. |
| 177 | |
| 178 | \note A current limitation is that objects must be registered before any client is initialized. |
| 179 | |
| 180 | \sa QWebChannel::registerObjects(), QWebChannel::deregisterObject(), QWebChannel::registeredObjects() |
| 181 | */ |
| 182 | void QWebChannel::registerObject(const QString &id, QObject *object) |
| 183 | { |
| 184 | Q_D(QWebChannel); |
| 185 | d->publisher->registerObject(id, object); |
| 186 | } |
| 187 | |
| 188 | /*! |
| 189 | Deregisters the given \a object from the QWebChannel. |
| 190 | |
| 191 | Remote clients will receive a \c destroyed signal for the given object. |
| 192 | |
| 193 | \sa QWebChannel::registerObjects(), QWebChannel::registerObject(), QWebChannel::registeredObjects() |
| 194 | */ |
| 195 | void QWebChannel::deregisterObject(QObject *object) |
| 196 | { |
| 197 | Q_D(QWebChannel); |
| 198 | // handling of deregistration is analogously to handling of a destroyed signal |
| 199 | d->publisher->signalEmitted(object, signalIndex: s_destroyedSignalIndex, arguments: QVariantList() << QVariant::fromValue(value: object)); |
| 200 | } |
| 201 | |
| 202 | /*! |
| 203 | \property QWebChannel::blockUpdates |
| 204 | |
| 205 | \brief When set to true, updates are blocked and remote clients will not be notified about property changes. |
| 206 | |
| 207 | The changes are recorded and sent to the clients once updates become unblocked again by setting |
| 208 | this property to false. By default, updates are not blocked. |
| 209 | */ |
| 210 | |
| 211 | |
| 212 | bool QWebChannel::blockUpdates() const |
| 213 | { |
| 214 | Q_D(const QWebChannel); |
| 215 | return d->publisher->blockUpdates; |
| 216 | } |
| 217 | |
| 218 | void QWebChannel::setBlockUpdates(bool block) |
| 219 | { |
| 220 | Q_D(QWebChannel); |
| 221 | d->publisher->setBlockUpdates(block); |
| 222 | } |
| 223 | |
| 224 | /*! |
| 225 | Connects the QWebChannel to the given \a transport object. |
| 226 | |
| 227 | The transport object then handles the communication between the C++ application and a remote |
| 228 | HTML client. |
| 229 | |
| 230 | \sa QWebChannelAbstractTransport, QWebChannel::disconnectFrom() |
| 231 | */ |
| 232 | void QWebChannel::connectTo(QWebChannelAbstractTransport *transport) |
| 233 | { |
| 234 | Q_D(QWebChannel); |
| 235 | Q_ASSERT(transport); |
| 236 | if (!d->transports.contains(t: transport)) { |
| 237 | d->transports << transport; |
| 238 | connect(sender: transport, signal: &QWebChannelAbstractTransport::messageReceived, |
| 239 | receiver: d->publisher, slot: &QMetaObjectPublisher::handleMessage, |
| 240 | type: Qt::UniqueConnection); |
| 241 | connect(sender: transport, SIGNAL(destroyed(QObject*)), |
| 242 | receiver: this, SLOT(_q_transportDestroyed(QObject*))); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | /*! |
| 247 | Disconnects the QWebChannel from the \a transport object. |
| 248 | |
| 249 | \sa QWebChannel::connectTo() |
| 250 | */ |
| 251 | void QWebChannel::disconnectFrom(QWebChannelAbstractTransport *transport) |
| 252 | { |
| 253 | Q_D(QWebChannel); |
| 254 | const int idx = d->transports.indexOf(t: transport); |
| 255 | if (idx != -1) { |
| 256 | disconnect(sender: transport, signal: 0, receiver: this, member: 0); |
| 257 | disconnect(sender: transport, signal: 0, receiver: d->publisher, member: 0); |
| 258 | d->transports.remove(i: idx); |
| 259 | d->publisher->transportRemoved(transport); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | QT_END_NAMESPACE |
| 264 | |
| 265 | #include "moc_qwebchannel.cpp" |
| 266 | |