QDeclarativeDebug: Decouple QDDServer, QDDService classes
[qt:qt.git] / src / declarative / debugger / qdeclarativedebugserver.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativedebugserver_p.h"
43 #include "private/qdeclarativedebugservice_p.h"
44 #include "private/qdeclarativedebugservice_p_p.h"
45 #include "private/qdeclarativeengine_p.h"
46
47 #include "private/qpacketprotocol_p.h"
48
49 #include <QtCore/QStringList>
50
51 #include <QtNetwork/qtcpserver.h>
52 #include <QtNetwork/qtcpsocket.h>
53
54 #include <private/qobject_p.h>
55 #include <private/qapplication_p.h>
56
57 QT_BEGIN_NAMESPACE
58
59 /*
60   QDeclarativeDebug Protocol (Version 1):
61
62   handshake:
63     1. Client sends
64          "QDeclarativeDebugServer" 0 version pluginNames
65        version: an int representing the highest protocol version the client knows
66        pluginNames: plugins available on client side
67     2. Server sends
68          "QDeclarativeDebugClient" 0 version pluginNames
69        version: an int representing the highest protocol version the client & server know
70        pluginNames: plugins available on server side. plugins both in the client and server message are enabled.
71   client plugin advertisement
72     1. Client sends
73          "QDeclarativeDebugServer" 1 pluginNames
74   server plugin advertisement
75     1. Server sends
76          "QDeclarativeDebugClient" 1 pluginNames
77   plugin communication:
78        Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin.
79   */
80
81 const int protocolVersion = 1;
82
83
84 class QDeclarativeDebugServerPrivate : public QObjectPrivate
85 {
86     Q_DECLARE_PUBLIC(QDeclarativeDebugServer)
87 public:
88     QDeclarativeDebugServerPrivate();
89
90     void advertisePlugins();
91
92     int port;
93     QTcpSocket *connection;
94     QPacketProtocol *protocol;
95     QHash<QString, QDeclarativeDebugService *> plugins;
96     QStringList clientPlugins;
97     QTcpServer *tcpServer;
98     bool gotHello;
99 };
100
101 QDeclarativeDebugServerPrivate::QDeclarativeDebugServerPrivate()
102 : connection(0), protocol(0), gotHello(false)
103 {
104 }
105
106 void QDeclarativeDebugServerPrivate::advertisePlugins()
107 {
108     if (!connection
109             || connection->state() != QTcpSocket::ConnectedState
110             || !gotHello)
111         return;
112
113     QPacket pack;
114     pack << QString(QLatin1String("QDeclarativeDebugClient")) << 1 << plugins.keys();
115     protocol->send(pack);
116     connection->flush();
117 }
118
119 void QDeclarativeDebugServer::listen()
120 {
121     Q_D(QDeclarativeDebugServer);
122
123     d->tcpServer = new QTcpServer(this);
124     QObject::connect(d->tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
125     if (d->tcpServer->listen(QHostAddress::Any, d->port))
126         qWarning("QDeclarativeDebugServer: Waiting for connection on port %d...", d->port);
127     else
128         qWarning("QDeclarativeDebugServer: Unable to listen on port %d", d->port);
129 }
130
131 void QDeclarativeDebugServer::waitForConnection()
132 {
133     Q_D(QDeclarativeDebugServer);
134     d->tcpServer->waitForNewConnection(-1);
135 }
136
137 void QDeclarativeDebugServer::newConnection()
138 {
139     Q_D(QDeclarativeDebugServer);
140
141     if (d->connection) {
142         qWarning("QDeclarativeDebugServer error: another client is already connected");
143         QTcpSocket *faultyConnection = d->tcpServer->nextPendingConnection();
144         delete faultyConnection;
145         return;
146     }
147
148     d->connection = d->tcpServer->nextPendingConnection();
149     d->connection->setParent(this);
150     d->protocol = new QPacketProtocol(d->connection, this);
151     QObject::connect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead()));
152 }
153
154 bool QDeclarativeDebugServer::hasDebuggingClient() const
155 {
156     Q_D(const QDeclarativeDebugServer);
157     return d->connection
158             && (d->connection->state() == QTcpSocket::ConnectedState)
159             && d->gotHello;
160 }
161
162 QDeclarativeDebugServer *QDeclarativeDebugServer::instance()
163 {
164     static bool commandLineTested = false;
165     static QDeclarativeDebugServer *server = 0;
166
167     if (!commandLineTested) {
168         commandLineTested = true;
169
170 #ifndef QDECLARATIVE_NO_DEBUG_PROTOCOL
171         QApplicationPrivate *appD = static_cast<QApplicationPrivate*>(QObjectPrivate::get(qApp));
172         // ### remove port definition when protocol is changed
173         int port = 0;
174         bool block = false;
175         bool ok = false;
176
177         // format: qmljsdebugger=port:3768[,block]
178         if (!appD->qmljsDebugArgumentsString().isEmpty()) {
179             if (!QDeclarativeEnginePrivate::qml_debugging_enabled) {
180                 const QString message =
181                     QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
182                               "Debugging has not been enabled.").arg(
183                                   appD->qmljsDebugArgumentsString());
184                 qWarning("%s", qPrintable(message));
185                 return 0;
186             }
187
188             if (appD->qmljsDebugArgumentsString().indexOf(QLatin1String("port:")) == 0) {
189                 int separatorIndex = appD->qmljsDebugArgumentsString().indexOf(QLatin1Char(','));
190                 port = appD->qmljsDebugArgumentsString().mid(5, separatorIndex - 5).toInt(&ok);
191             }
192             block = appD->qmljsDebugArgumentsString().contains(QLatin1String("block"));
193
194             if (ok) {
195                 server = new QDeclarativeDebugServer(port);
196                 server->listen();
197                 if (block) {
198                     server->waitForConnection();
199                 }
200             } else {
201                 qWarning(QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
202                                             "Format is -qmljsdebugger=port:<port>[,block]").arg(
203                              appD->qmljsDebugArgumentsString()).toAscii().constData());
204             }
205         }
206 #endif
207     }
208
209     return server;
210 }
211
212 QDeclarativeDebugServer::QDeclarativeDebugServer(int port)
213 : QObject(*(new QDeclarativeDebugServerPrivate))
214 {
215     Q_D(QDeclarativeDebugServer);
216     d->port = port;
217 }
218
219 void QDeclarativeDebugServer::readyRead()
220 {
221     Q_D(QDeclarativeDebugServer);
222
223     if (!d->gotHello) {
224         QPacket hello = d->protocol->read();
225
226         QString name;
227         int op;
228         hello >> name >> op;
229
230         if (name != QLatin1String("QDeclarativeDebugServer")
231                 || op != 0) {
232             qWarning("QDeclarativeDebugServer: Invalid hello message");
233             QObject::disconnect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead()));
234             d->protocol->deleteLater();
235             d->protocol = 0;
236             d->connection->deleteLater();
237             d->connection = 0;
238             return;
239         }
240
241         int version;
242         hello >> version >> d->clientPlugins;
243
244         QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin();
245         for (; iter != d->plugins.end(); ++iter) {
246             QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
247             if (d->clientPlugins.contains(iter.key()))
248                 newStatus = QDeclarativeDebugService::Enabled;
249             iter.value()->d_func()->status = newStatus;
250             iter.value()->statusChanged(newStatus);
251         }
252
253         QPacket helloAnswer;
254         helloAnswer << QString(QLatin1String("QDeclarativeDebugClient")) << 0 << protocolVersion << d->plugins.keys();
255         d->protocol->send(helloAnswer);
256         d->connection->flush();
257
258         d->gotHello = true;
259         qWarning("QDeclarativeDebugServer: Connection established");
260     }
261
262     QString debugServer(QLatin1String("QDeclarativeDebugServer"));
263
264     while (d->protocol->packetsAvailable()) {
265         QPacket pack = d->protocol->read();
266
267         QString name;
268         pack >> name;
269
270         if (name == debugServer) {
271             int op = -1;
272             pack >> op;
273
274             if (op == 1) {
275                 // Service Discovery
276                 QStringList oldClientPlugins = d->clientPlugins;
277                 pack >> d->clientPlugins;
278
279                 QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin();
280                 for (; iter != d->plugins.end(); ++iter) {
281                     const QString pluginName = iter.key();
282                     QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
283                     if (d->clientPlugins.contains(pluginName))
284                         newStatus = QDeclarativeDebugService::Enabled;
285
286                     if (oldClientPlugins.contains(pluginName)
287                             != d->clientPlugins.contains(pluginName)) {
288                         iter.value()->d_func()->status = newStatus;
289                         iter.value()->statusChanged(newStatus);
290                     }
291                 }
292             } else {
293                 qWarning("QDeclarativeDebugServer: Invalid control message %d", op);
294             }
295         } else {
296             QByteArray message;
297             pack >> message;
298
299             QHash<QString, QDeclarativeDebugService *>::Iterator iter =
300                 d->plugins.find(name);
301             if (iter == d->plugins.end()) {
302                 qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << name;
303             } else {
304                 (*iter)->messageReceived(message);
305             }
306         }
307     }
308 }
309
310
311 QList<QDeclarativeDebugService*> QDeclarativeDebugServer::services() const
312 {
313     const Q_D(QDeclarativeDebugServer);
314     return d->plugins.values();
315 }
316
317 QStringList QDeclarativeDebugServer::serviceNames() const
318 {
319     const Q_D(QDeclarativeDebugServer);
320     return d->plugins.keys();
321 }
322
323 bool QDeclarativeDebugServer::addService(QDeclarativeDebugService *service)
324 {
325     Q_D(QDeclarativeDebugServer);
326     if (!service || d->plugins.contains(service->name()))
327         return false;
328
329     d->plugins.insert(service->name(), service);
330     d->advertisePlugins();
331
332     QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
333     if (d->clientPlugins.contains(service->name()))
334         newStatus = QDeclarativeDebugService::Enabled;
335     service->d_func()->status = newStatus;
336     service->statusChanged(newStatus);
337     return true;
338 }
339
340 bool QDeclarativeDebugServer::removeService(QDeclarativeDebugService *service)
341 {
342     Q_D(QDeclarativeDebugServer);
343     if (!service || !d->plugins.contains(service->name()))
344         return false;
345
346     d->plugins.remove(service->name());
347     d->advertisePlugins();
348
349     QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::NotConnected;
350     service->d_func()->server = 0;
351     service->d_func()->status = newStatus;
352     service->statusChanged(newStatus);
353     return true;
354 }
355
356 void QDeclarativeDebugServer::sendMessage(QDeclarativeDebugService *service,
357                                           const QByteArray &message)
358 {
359     Q_D(QDeclarativeDebugServer);
360     QPacket pack;
361     pack << service->name() << message;
362     d->protocol->send(pack);
363     d->connection->flush();
364 }
365
366 QT_END_NAMESPACE