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 | |