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#ifndef QMETAOBJECTPUBLISHER_P_H
41#define QMETAOBJECTPUBLISHER_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include "variantargument_p.h"
55#include "signalhandler_p.h"
56
57#include <QStringList>
58#include <QMetaObject>
59#include <QBasicTimer>
60#include <QPointer>
61#include <QJsonObject>
62
63#include "qwebchannelglobal.h"
64
65QT_BEGIN_NAMESPACE
66
67// NOTE: keep in sync with corresponding maps in qwebchannel.js and WebChannelTest.qml
68enum MessageType {
69 TypeInvalid = 0,
70
71 TYPES_FIRST_VALUE = 1,
72
73 TypeSignal = 1,
74 TypePropertyUpdate = 2,
75 TypeInit = 3,
76 TypeIdle = 4,
77 TypeDebug = 5,
78 TypeInvokeMethod = 6,
79 TypeConnectToSignal = 7,
80 TypeDisconnectFromSignal = 8,
81 TypeSetProperty = 9,
82 TypeResponse = 10,
83
84 TYPES_LAST_VALUE = 10
85};
86
87class QWebChannel;
88class QWebChannelAbstractTransport;
89class Q_WEBCHANNEL_EXPORT QMetaObjectPublisher : public QObject
90{
91 Q_OBJECT
92public:
93 explicit QMetaObjectPublisher(QWebChannel *webChannel);
94 virtual ~QMetaObjectPublisher();
95
96 /**
97 * Register @p object under the given @p id.
98 *
99 * The properties, signals and public methods of the QObject are
100 * published to the remote client, where an object with the given identifier
101 * is constructed.
102 *
103 * TODO: This must be called, before clients are initialized.
104 */
105 void registerObject(const QString &id, QObject *object);
106
107 /**
108 * Send the given message to all known transports.
109 */
110 void broadcastMessage(const QJsonObject &message) const;
111
112 /**
113 * Serialize the QMetaObject of @p object and return it in JSON form.
114 */
115 QJsonObject classInfoForObject(const QObject *object, QWebChannelAbstractTransport *transport);
116
117 /**
118 * Set the client to idle or busy, based on the value of @p isIdle.
119 *
120 * When the value changed, start/stop the property update timer accordingly.
121 */
122 void setClientIsIdle(bool isIdle);
123
124 /**
125 * Initialize clients by sending them the class information of the registered objects.
126 *
127 * Furthermore, if that was not done already, connect to their property notify signals.
128 */
129 QJsonObject initializeClient(QWebChannelAbstractTransport *transport);
130
131 /**
132 * Go through all properties of the given object and connect to their notify signal.
133 *
134 * When receiving a notify signal, it will store the information in pendingPropertyUpdates which
135 * gets send via a Qt.propertyUpdate message to the server when the grouping timer timeouts.
136 */
137 void initializePropertyUpdates(const QObject *const object, const QJsonObject &objectInfo);
138
139 /**
140 * Send the clients the new property values since the last time this function was invoked.
141 *
142 * This is a grouped batch of all properties for which their notify signal was emitted.
143 * The list of signals as well as the arguments they contained, are also transmitted to
144 * the remote clients.
145 *
146 * @sa timer, initializePropertyUpdates
147 */
148 void sendPendingPropertyUpdates();
149
150 /**
151 * Invoke the @p method on @p object with the arguments @p args.
152 *
153 * The return value of the method invocation is then serialized and a response message
154 * is returned.
155 */
156 QVariant invokeMethod(QObject *const object, const QMetaMethod &method, const QJsonArray &args);
157
158 /**
159 * Invoke the method of index @p methodIndex on @p object with the arguments @p args.
160 *
161 * The return value of the method invocation is then serialized and a response message
162 * is returned.
163 */
164 QVariant invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args);
165
166 /**
167 * Invoke the method of name @p methodName on @p object with the arguments @p args.
168 *
169 * This method performs overload resolution on @p methodName.
170 *
171 * The return value of the method invocation is then serialized and a response message
172 * is returned.
173 */
174 QVariant invokeMethod(QObject *const object, const QByteArray &methodName, const QJsonArray &args);
175
176 /**
177 * Set the value of property @p propertyIndex on @p object to @p value.
178 */
179 void setProperty(QObject *object, const int propertyIndex, const QJsonValue &value);
180
181 /**
182 * Callback of the signalHandler which forwards the signal invocation to the webchannel clients.
183 */
184 void signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments);
185
186 /**
187 * Callback for registered or wrapped objects which erases all data related to @p object.
188 *
189 * @sa signalEmitted
190 */
191 void objectDestroyed(const QObject *object);
192
193 QObject *unwrapObject(const QString &objectId) const;
194
195 QVariant toVariant(const QJsonValue &value, int targetType) const;
196
197 /**
198 * Assigns a score for the conversion from @p value to @p targetType.
199 *
200 * Scores can be compared to find the best match. The lower the score, the
201 * more preferable is the conversion.
202 *
203 * @sa invokeMethod, methodOverloadBadness
204 */
205 int conversionScore(const QJsonValue &value, int targetType) const;
206
207 /**
208 * Scores @p method against @p args.
209 *
210 * Scores can be compared to find the best match from a set of overloads.
211 * The lower the score, the more preferable is the method.
212 *
213 * @sa invokeMethod, conversionScore
214 */
215 int methodOverloadBadness(const QMetaMethod &method, const QJsonArray &args) const;
216
217 /**
218 * Remove wrapped objects which last transport relation is with the passed transport object.
219 */
220 void transportRemoved(QWebChannelAbstractTransport *transport);
221
222 /**
223 * Given a QVariant containing a QObject*, wrap the object and register for property updates
224 * return the objects class information.
225 *
226 * All other input types are returned as-is.
227 */
228 QJsonValue wrapResult(const QVariant &result, QWebChannelAbstractTransport *transport,
229 const QString &parentObjectId = QString());
230
231 /**
232 * Convert a list of variant values for consumption by the client.
233 *
234 * This properly handles QML values and also wraps the result if required.
235 */
236 QJsonArray wrapList(const QVariantList &list, QWebChannelAbstractTransport *transport,
237 const QString &parentObjectId = QString());
238
239 /**
240 * Convert a variant map for consumption by the client.
241 *
242 * This properly handles QML values and also wraps the result if required.
243 */
244 QJsonObject wrapMap(const QVariantMap &map, QWebChannelAbstractTransport *transport,
245 const QString &parentObjectId = QString());
246
247 /**
248 * Invoke delete later on @p object.
249 */
250 void deleteWrappedObject(QObject *object) const;
251
252 /**
253 * When updates are blocked, no property updates are transmitted to remote clients.
254 */
255 void setBlockUpdates(bool block);
256
257Q_SIGNALS:
258 void blockUpdatesChanged(bool block);
259
260public Q_SLOTS:
261 /**
262 * Handle the @p message and if needed send a response to @p transport.
263 */
264 void handleMessage(const QJsonObject &message, QWebChannelAbstractTransport *transport);
265
266protected:
267 void timerEvent(QTimerEvent *) override;
268
269private:
270 friend class QQmlWebChannelPrivate;
271 friend class QWebChannel;
272 friend class TestWebChannel;
273
274 QWebChannel *webChannel;
275 SignalHandler<QMetaObjectPublisher> signalHandler;
276
277 // true when the client is idle, false otherwise
278 bool clientIsIdle;
279
280 // true when no property updates should be sent, false otherwise
281 bool blockUpdates;
282
283 // true when at least one client was initialized and thus
284 // the property updates have been initialized and the
285 // object info map set.
286 bool propertyUpdatesInitialized;
287
288 // Map of registered objects indexed by their id.
289 QHash<QString, QObject *> registeredObjects;
290
291 // Map the registered objects to their id.
292 QHash<const QObject *, QString> registeredObjectIds;
293
294 // Groups individually wrapped objects with their class information and the transports that have access to it.
295 // Also tags objects that are in the process of being wrapped to prevent infinite recursion.
296 struct ObjectInfo
297 {
298 ObjectInfo(QObject *o = nullptr)
299 : object(o), isBeingWrapped(false)
300 {}
301 QObject *object;
302 QVector<QWebChannelAbstractTransport*> transports;
303 bool isBeingWrapped;
304 };
305
306 // Map of objects wrapped from invocation returns
307 QHash<QString, ObjectInfo> wrappedObjects;
308 // Map of transports to wrapped object ids
309 QMultiHash<QWebChannelAbstractTransport*, QString> transportedWrappedObjects;
310
311 // Map of objects to maps of signal indices to a set of all their property indices.
312 // The last value is a set as a signal can be the notify signal of multiple properties.
313 typedef QHash<int, QSet<int> > SignalToPropertyNameMap;
314 QHash<const QObject *, SignalToPropertyNameMap> signalToPropertyMap;
315
316 // Objects that changed their properties and are waiting for idle client.
317 // map of object name to map of signal index to arguments
318 typedef QHash<int, QVariantList> SignalToArgumentsMap;
319 typedef QHash<const QObject *, SignalToArgumentsMap> PendingPropertyUpdates;
320 PendingPropertyUpdates pendingPropertyUpdates;
321
322 // Aggregate property updates since we get multiple Qt.idle message when we have multiple
323 // clients. They all share the same QWebProcess though so we must take special care to
324 // prevent message flooding.
325 QBasicTimer timer;
326};
327
328QT_END_NAMESPACE
329
330#endif // QMETAOBJECTPUBLISHER_P_H
331

source code of qtwebchannel/src/webchannel/qmetaobjectpublisher_p.h