Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldebugconnection.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include <private/qpacketprotocol_p.h>
8#include <private/qpacket_p.h>
9#include <private/qobject_p.h>
10
11#include <QtCore/qeventloop.h>
12#include <QtCore/qtimer.h>
13#include <QtCore/QHash>
14#include <QtCore/qdatastream.h>
15#include <QtNetwork/qlocalserver.h>
16#include <QtNetwork/qlocalsocket.h>
17#include <QtNetwork/qtcpsocket.h>
18
20
21static const int protocolVersion = 1;
22static const QString serverId = QLatin1String("QDeclarativeDebugServer");
23static const QString clientId = QLatin1String("QDeclarativeDebugClient");
24
48
49QQmlDebugConnectionPrivate::QQmlDebugConnectionPrivate()
50{
51 handshakeTimer.setSingleShot(true);
52 handshakeTimer.setInterval(3000);
53}
54
56{
57 Q_Q(QQmlDebugConnection);
58 if (!q->isConnected())
59 return;
60
61 QPacket pack(currentDataStreamVersion);
62 pack << serverId << 1 << plugins.keys();
63 protocol->send(pack.data());
64 flush();
65}
66
67void QQmlDebugConnection::socketConnected()
68{
69 Q_D(QQmlDebugConnection);
70 QPacket pack(d->currentDataStreamVersion);
71 pack << serverId << 0 << protocolVersion << d->plugins.keys() << d->maximumDataStreamVersion
72 << true; // We accept multiple messages per packet
73 d->protocol->send(pack.data());
74 d->flush();
75}
76
77void QQmlDebugConnection::socketDisconnected()
78{
79 Q_D(QQmlDebugConnection);
80 d->gotHello = false;
81 emit disconnected();
82}
83
84void QQmlDebugConnection::protocolReadyRead()
85{
86 Q_D(QQmlDebugConnection);
87 if (!d->gotHello) {
88 QPacket pack(d->currentDataStreamVersion, d->protocol->read());
89 QString name;
90
91 pack >> name;
92
93 bool validHello = false;
94 if (name == clientId) {
95 int op = -1;
96 pack >> op;
97 if (op == 0) {
98 int version = -1;
99 pack >> version;
100 if (version == protocolVersion) {
101 QStringList pluginNames;
102 QList<float> pluginVersions;
103 pack >> pluginNames;
104 if (!pack.atEnd())
105 pack >> pluginVersions;
106
107 const int pluginNamesSize = pluginNames.size();
108 const int pluginVersionsSize = pluginVersions.size();
109 for (int i = 0; i < pluginNamesSize; ++i) {
110 float pluginVersion = 1.0;
111 if (i < pluginVersionsSize)
112 pluginVersion = pluginVersions.at(i);
113 d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
114 }
115
116 pack >> d->currentDataStreamVersion;
117 validHello = true;
118 }
119 }
120 }
121
122 if (!validHello) {
123 qWarning("QQmlDebugConnection: Invalid hello message");
124 close();
125 return;
126 }
127 d->gotHello = true;
128 emit connected();
129
130 QHash<QString, QQmlDebugClient *>::Iterator iter = d->plugins.begin();
131 for (; iter != d->plugins.end(); ++iter) {
132 QQmlDebugClient::State newState = QQmlDebugClient::Unavailable;
133 if (d->serverPlugins.contains(iter.key()))
134 newState = QQmlDebugClient::Enabled;
135 iter.value()->stateChanged(newState);
136 }
137
138 d->handshakeTimer.stop();
139 d->handshakeEventLoop.quit();
140 }
141
142 while (d->protocol->packetsAvailable()) {
143 QPacket pack(d->currentDataStreamVersion, d->protocol->read());
144 QString name;
145 pack >> name;
146
147 if (name == clientId) {
148 int op = -1;
149 pack >> op;
150
151 if (op == 1) {
152 // Service Discovery
153 QHash<QString, float> oldServerPlugins = d->serverPlugins;
154 d->serverPlugins.clear();
155
156 QStringList pluginNames;
157 QList<float> pluginVersions;
158 pack >> pluginNames;
159 if (!pack.atEnd())
160 pack >> pluginVersions;
161
162 const int pluginNamesSize = pluginNames.size();
163 const int pluginVersionsSize = pluginVersions.size();
164 for (int i = 0; i < pluginNamesSize; ++i) {
165 float pluginVersion = 1.0;
166 if (i < pluginVersionsSize)
167 pluginVersion = pluginVersions.at(i);
168 d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
169 }
170
171 QHash<QString, QQmlDebugClient *>::Iterator iter = d->plugins.begin();
172 for (; iter != d->plugins.end(); ++iter) {
173 const QString &pluginName = iter.key();
174 QQmlDebugClient::State newState = QQmlDebugClient::Unavailable;
175 if (d->serverPlugins.contains(pluginName))
176 newState = QQmlDebugClient::Enabled;
177
178 if (oldServerPlugins.contains(pluginName)
179 != d->serverPlugins.contains(pluginName)) {
180 iter.value()->stateChanged(newState);
181 }
182 }
183 } else {
184 qWarning() << "QQmlDebugConnection: Unknown control message id" << op;
185 }
186 } else {
187 QHash<QString, QQmlDebugClient *>::Iterator iter = d->plugins.find(name);
188 if (iter == d->plugins.end()) {
189 // We can get more messages for plugins we have removed because it takes time to
190 // send the advertisement message but the removal is instant locally.
191 if (!d->removedPlugins.contains(name)) {
192 qWarning() << "QQmlDebugConnection: Message received for missing plugin"
193 << name;
194 }
195 } else {
196 QQmlDebugClient *client = *iter;
197 QByteArray message;
198 while (!pack.atEnd()) {
199 pack >> message;
200 client->messageReceived(message);
201 }
202 }
203 }
204 }
205}
206
207void QQmlDebugConnection::handshakeTimeout()
208{
209 Q_D(QQmlDebugConnection);
210 if (!d->gotHello) {
211 qWarning() << "QQmlDebugConnection: Did not get handshake answer in time";
212 d->handshakeEventLoop.quit();
213 }
214}
215
216QQmlDebugConnection::QQmlDebugConnection(QObject *parent) :
217 QObject(*(new QQmlDebugConnectionPrivate), parent)
218{
219 Q_D(QQmlDebugConnection);
220 connect(&d->handshakeTimer, &QTimer::timeout, this, &QQmlDebugConnection::handshakeTimeout);
221}
222
224{
225 Q_D(QQmlDebugConnection);
226 QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin();
227 for (; iter != d->plugins.end(); ++iter)
228 emit iter.value()->stateChanged(QQmlDebugClient::NotConnected);
229}
230
232{
233 Q_D(const QQmlDebugConnection);
234 return d->currentDataStreamVersion;
235}
236
238{
239 Q_D(QQmlDebugConnection);
240 d->maximumDataStreamVersion = maximumVersion;
241}
242
244{
245 Q_D(const QQmlDebugConnection);
246 return d->gotHello;
247}
248
250{
251 Q_D(const QQmlDebugConnection);
252 return !d->gotHello && d->device;
253}
254
256{
257 Q_D(QQmlDebugConnection);
258 if (d->gotHello) {
259 d->gotHello = false;
260 d->device->close();
261
262 QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin();
263 for (; iter != d->plugins.end(); ++iter)
264 emit iter.value()->stateChanged(QQmlDebugClient::NotConnected);
265 }
266
267 if (d->device) {
268 d->device->deleteLater();
269 d->device = nullptr;
270 }
271}
272
274{
275 Q_D(QQmlDebugConnection);
276 auto socket = qobject_cast<QAbstractSocket*>(d->device);
277 if (!socket) {
278 if (!d->server || (!d->server->hasPendingConnections() &&
279 !d->server->waitForNewConnection(msecs)))
280 return false;
281 } else if (!socket->waitForConnected(msecs)) {
282 return false;
283 }
284 // wait for handshake
285 d->handshakeTimer.start();
286 d->handshakeEventLoop.exec();
287 return d->gotHello;
288}
289
290QQmlDebugClient *QQmlDebugConnection::client(const QString &name) const
291{
292 Q_D(const QQmlDebugConnection);
293 return d->plugins.value(name, nullptr);
294}
295
296bool QQmlDebugConnection::addClient(const QString &name, QQmlDebugClient *client)
297{
298 Q_D(QQmlDebugConnection);
299 if (d->plugins.contains(name))
300 return false;
301 d->removedPlugins.removeAll(name);
302 d->plugins.insert(name, client);
303 d->advertisePlugins();
304 return true;
305}
306
307bool QQmlDebugConnection::removeClient(const QString &name)
308{
309 Q_D(QQmlDebugConnection);
310 if (!d->plugins.contains(name))
311 return false;
312 d->plugins.remove(name);
313 d->removedPlugins.append(name);
314 d->advertisePlugins();
315 return true;
316}
317
318float QQmlDebugConnection::serviceVersion(const QString &serviceName) const
319{
320 Q_D(const QQmlDebugConnection);
321 return d->serverPlugins.value(serviceName, -1);
322}
323
324bool QQmlDebugConnection::sendMessage(const QString &name, const QByteArray &message)
325{
326 Q_D(QQmlDebugConnection);
327 if (!isConnected() || !d->serverPlugins.contains(name))
328 return false;
329
330 QPacket pack(d->currentDataStreamVersion);
331 pack << name << message;
332 d->protocol->send(pack.data());
333 d->flush();
334
335 return true;
336}
337
339{
340 if (auto socket = qobject_cast<QAbstractSocket *>(device))
341 socket->flush();
342 else if (auto socket = qobject_cast<QLocalSocket *>(device))
343 socket->flush();
344}
345
346void QQmlDebugConnection::connectToHost(const QString &hostName, quint16 port)
347{
348 Q_D(QQmlDebugConnection);
349 if (d->gotHello)
350 close();
351 auto socket = new QTcpSocket(this);
352 d->device = socket;
353 d->createProtocol();
354 connect(socket, &QAbstractSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected);
355 connect(socket, &QAbstractSocket::connected, this, &QQmlDebugConnection::socketConnected);
356 connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(
357 &QAbstractSocket::errorOccurred), this, &QQmlDebugConnection::socketError);
358 connect(socket, &QAbstractSocket::stateChanged, this, &QQmlDebugConnection::socketStateChanged);
359 socket->connectToHost(hostName, port);
360}
361
362void QQmlDebugConnection::startLocalServer(const QString &fileName)
363{
364 Q_D(QQmlDebugConnection);
365 if (d->gotHello)
366 close();
367 if (d->server)
368 d->server->deleteLater();
369 d->server = new QLocalServer(this);
370 // QueuedConnection so that waitForNewConnection() returns true.
371 connect(d->server, &QLocalServer::newConnection,
372 this, &QQmlDebugConnection::newConnection, Qt::QueuedConnection);
373 d->server->listen(fileName);
374}
375
401
402void QQmlDebugConnection::newConnection()
403{
404 Q_D(QQmlDebugConnection);
405 delete d->device;
406 QLocalSocket *socket = d->server->nextPendingConnection();
407 d->server->close();
408 d->device = socket;
409 d->createProtocol();
410 connect(socket, &QLocalSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected);
411 auto translator = new LocalSocketSignalTranslator(socket);
412
413 connect(translator, &LocalSocketSignalTranslator::socketError,
414 this, &QQmlDebugConnection::socketError);
415 connect(translator, &LocalSocketSignalTranslator::socketStateChanged,
416 this, &QQmlDebugConnection::socketStateChanged);
417 socketConnected();
418}
419
421{
422 Q_Q(QQmlDebugConnection);
423 delete protocol;
424 protocol = new QPacketProtocol(device, q);
425 QObject::connect(protocol, &QPacketProtocol::readyRead,
426 q, &QQmlDebugConnection::protocolReadyRead);
427}
428
429QT_END_NAMESPACE
430
431#include <qqmldebugconnection.moc>
432
433#include "moc_qqmldebugconnection_p.cpp"
void socketStateChanged(QAbstractSocket::SocketState)
QHash< QString, float > serverPlugins
QHash< QString, QQmlDebugClient * > plugins
bool addClient(const QString &name, QQmlDebugClient *client)
void setMaximumDataStreamVersion(int maximumVersion)
bool sendMessage(const QString &name, const QByteArray &message)
bool removeClient(const QString &name)
QQmlDebugClient * client(const QString &name) const
float serviceVersion(const QString &serviceName) const
bool waitForConnected(int msecs=30000)
void startLocalServer(const QString &fileName)
void connectToHost(const QString &hostName, quint16 port)
Combined button and popup list for selecting options.
static const QString clientId
static const QString serverId
static QT_BEGIN_NAMESPACE const int protocolVersion