1/****************************************************************************
2**
3** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWebChannel module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlwebchannel.h"
41
42#include "qwebchannel_p.h"
43#include "qmetaobjectpublisher_p.h"
44#include "qwebchannelabstracttransport.h"
45
46#include <QtQml/QQmlContext>
47
48#include "qqmlwebchannelattached_p.h"
49
50QT_BEGIN_NAMESPACE
51
52/*!
53 \qmltype WebChannel
54 \instantiates QQmlWebChannel
55
56 \inqmlmodule QtWebChannel
57 \ingroup webchannel-qml
58 \brief QML interface to QWebChannel.
59 \since 5.4
60
61 The WebChannel provides a mechanism to transparently access QObject or QML objects from HTML
62 clients. All properties, signals and public slots can be used from the HTML clients.
63
64 \sa QWebChannel, {Qt WebChannel JavaScript API}{JavaScript API}
65*/
66
67/*!
68 \qmlproperty QQmlListProperty<QObject> WebChannel::transports
69 A list of transport objects, which implement QWebChannelAbstractTransport. The transports
70 are used to talk to the remote clients.
71
72 \sa connectTo(), disconnectFrom()
73*/
74
75/*!
76 \qmlproperty QQmlListProperty<QObject> WebChannel::registeredObjects
77
78 \brief A list of objects which should be accessible to remote clients.
79
80 The objects must have the attached \l id property set to an identifier, under which the
81 object is then known on the HTML side.
82
83 Once registered, all signals and property changes are automatically propagated to the clients.
84 Public invokable methods, including slots, are also accessible to the clients.
85
86 If one needs to register objects which are not available when the component is created, use the
87 imperative registerObjects method.
88
89 \sa registerObjects(), id
90*/
91
92class QQmlWebChannelPrivate : public QWebChannelPrivate
93{
94 Q_DECLARE_PUBLIC(QQmlWebChannel)
95public:
96 QVector<QObject*> registeredObjects;
97
98 void _q_objectIdChanged(const QString &newId);
99};
100
101/*!
102 \internal
103
104 Update the name of the sender object, when its attached WebChannel.id property changed.
105 This is required, since during startup the property is empty and only gets set later on.
106*/
107void QQmlWebChannelPrivate::_q_objectIdChanged(const QString &newId)
108{
109 Q_Q(QQmlWebChannel);
110 const QQmlWebChannelAttached *const attached = qobject_cast<QQmlWebChannelAttached*>(object: q->sender());
111 Q_ASSERT(attached);
112 Q_ASSERT(attached->parent());
113 Q_ASSERT(registeredObjects.contains(attached->parent()));
114
115 QObject *const object = attached->parent();
116 const QString &oldId = publisher->registeredObjectIds.value(key: object);
117
118 if (!oldId.isEmpty()) {
119 q->deregisterObject(object);
120 }
121
122 q->registerObject(id: newId, object);
123}
124
125QQmlWebChannel::QQmlWebChannel(QObject *parent)
126 : QWebChannel(*(new QQmlWebChannelPrivate), parent)
127{
128}
129
130QQmlWebChannel::~QQmlWebChannel()
131{
132
133}
134
135/*!
136 \qmlmethod void WebChannel::registerObjects(QVariantMap objects)
137 Registers the specified \a objects to make them accessible to HTML clients.
138 The key of the map is used as an identifier for the object on the client side.
139
140 Once registered, all signals and property changes are automatically propagated to the clients.
141 Public invokable methods, including slots, are also accessible to the clients.
142
143 This imperative API can be used to register objects on the fly. For static objects, the declarative
144 registeredObjects property should be preferred.
145
146 \sa registeredObjects
147*/
148void QQmlWebChannel::registerObjects(const QVariantMap &objects)
149{
150 Q_D(QQmlWebChannel);
151 QMap<QString, QVariant>::const_iterator it = objects.constBegin();
152 for (; it != objects.constEnd(); ++it) {
153 QObject *object = it.value().value<QObject*>();
154 if (!object) {
155 qWarning(msg: "Invalid QObject given to register under name %s", qPrintable(it.key()));
156 continue;
157 }
158 d->publisher->registerObject(id: it.key(), object);
159 }
160}
161
162QQmlWebChannelAttached *QQmlWebChannel::qmlAttachedProperties(QObject *obj)
163{
164 return new QQmlWebChannelAttached(obj);
165}
166
167/*!
168 \qmlmethod void WebChannel::connectTo(QWebChannelAbstractTransport transport)
169
170 \brief Connects to the \a transport, which represents a communication
171 channel to a single client.
172
173 The transport object must be an implementation of QWebChannelAbstractTransport.
174
175 \sa transports, disconnectFrom()
176*/
177void QQmlWebChannel::connectTo(QObject *transport)
178{
179 if (QWebChannelAbstractTransport *realTransport = qobject_cast<QWebChannelAbstractTransport*>(object: transport)) {
180 QWebChannel::connectTo(transport: realTransport);
181 } else {
182 qWarning() << "Cannot connect to transport" << transport << " - it is not a QWebChannelAbstractTransport.";
183 }
184}
185
186/*!
187 \qmlmethod void WebChannel::disconnectFrom(QWebChannelAbstractTransport transport)
188
189 \brief Disconnects the \a transport from this WebChannel.
190
191 The client will not be able to communicate with the WebChannel anymore, nor will it receive any
192 signals or property updates.
193
194 \sa connectTo()
195*/
196void QQmlWebChannel::disconnectFrom(QObject *transport)
197{
198 if (QWebChannelAbstractTransport *realTransport = qobject_cast<QWebChannelAbstractTransport*>(object: transport)) {
199 QWebChannel::disconnectFrom(transport: realTransport);
200 } else {
201 qWarning() << "Cannot disconnect from transport" << transport << " - it is not a QWebChannelAbstractTransport.";
202 }
203}
204
205QQmlListProperty<QObject> QQmlWebChannel::registeredObjects()
206{
207 return QQmlListProperty<QObject>(this, 0,
208 registeredObjects_append,
209 registeredObjects_count,
210 registeredObjects_at,
211 registeredObjects_clear);
212}
213
214void QQmlWebChannel::registeredObjects_append(QQmlListProperty<QObject> *prop, QObject *object)
215{
216 const QQmlWebChannelAttached *const attached = qobject_cast<QQmlWebChannelAttached*>(
217 object: qmlAttachedPropertiesObject<QQmlWebChannel>(obj: object, create: false /* don't create */));
218 if (!attached) {
219 const QQmlContext *const context = qmlContext(object);
220 qWarning() << "Cannot register object" << context->nameForObject(object) << '(' << object << ") without attached WebChannel.id property. Did you forget to set it?";
221 return;
222 }
223 QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object);
224 if (!attached->id().isEmpty()) {
225 // TODO: warning in such cases?
226 channel->registerObject(id: attached->id(), object);
227 }
228 channel->d_func()->registeredObjects.append(t: object);
229 connect(sender: attached, SIGNAL(idChanged(QString)), receiver: channel, SLOT(_q_objectIdChanged(QString)));
230}
231
232int QQmlWebChannel::registeredObjects_count(QQmlListProperty<QObject> *prop)
233{
234 return static_cast<QQmlWebChannel*>(prop->object)->d_func()->registeredObjects.size();
235}
236
237QObject *QQmlWebChannel::registeredObjects_at(QQmlListProperty<QObject> *prop, int index)
238{
239 return static_cast<QQmlWebChannel*>(prop->object)->d_func()->registeredObjects.at(i: index);
240}
241
242void QQmlWebChannel::registeredObjects_clear(QQmlListProperty<QObject> *prop)
243{
244 QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object);
245 foreach (QObject *object, channel->d_func()->registeredObjects) {
246 channel->deregisterObject(object);
247 }
248 return channel->d_func()->registeredObjects.clear();
249}
250
251QQmlListProperty<QObject> QQmlWebChannel::transports()
252{
253 return QQmlListProperty<QObject>(this, 0,
254 transports_append,
255 transports_count,
256 transports_at,
257 transports_clear);
258}
259
260void QQmlWebChannel::transports_append(QQmlListProperty<QObject> *prop, QObject *transport)
261{
262 QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object);
263 channel->connectTo(transport);
264}
265
266int QQmlWebChannel::transports_count(QQmlListProperty<QObject> *prop)
267{
268 return static_cast<QQmlWebChannel*>(prop->object)->d_func()->transports.size();
269}
270
271QObject *QQmlWebChannel::transports_at(QQmlListProperty<QObject> *prop, int index)
272{
273 QQmlWebChannel *channel = static_cast<QQmlWebChannel*>(prop->object);
274 return channel->d_func()->transports.at(i: index);
275}
276
277void QQmlWebChannel::transports_clear(QQmlListProperty<QObject> *prop)
278{
279 QWebChannel *channel = static_cast<QWebChannel*>(prop->object);
280 foreach (QWebChannelAbstractTransport *transport, channel->d_func()->transports) {
281 channel->disconnectFrom(transport);
282 }
283 Q_ASSERT(channel->d_func()->transports.isEmpty());
284}
285
286QT_END_NAMESPACE
287
288#include "moc_qqmlwebchannel.cpp"
289

source code of qtwebchannel/src/webchannel/qqmlwebchannel.cpp