Qt 4.8
qdeclarativedebugserver.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtDeclarative 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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 <QtCore/QDir>
48 #include <QtCore/QPluginLoader>
49 #include <QtCore/QStringList>
50 
51 #include <private/qobject_p.h>
52 #include <private/qcoreapplication_p.h>
53 
55 
56 /*
57  QDeclarativeDebug Protocol (Version 1):
58 
59  handshake:
60  1. Client sends
61  "QDeclarativeDebugServer" 0 version pluginNames
62  version: an int representing the highest protocol version the client knows
63  pluginNames: plugins available on client side
64  2. Server sends
65  "QDeclarativeDebugClient" 0 version pluginNames
66  version: an int representing the highest protocol version the client & server know
67  pluginNames: plugins available on server side. plugins both in the client and server message are enabled.
68  client plugin advertisement
69  1. Client sends
70  "QDeclarativeDebugServer" 1 pluginNames
71  server plugin advertisement
72  1. Server sends
73  "QDeclarativeDebugClient" 1 pluginNames
74  plugin communication:
75  Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin.
76  */
77 
78 const int protocolVersion = 1;
79 
80 
82 {
84 public:
86 
87  void advertisePlugins();
88 
92  bool gotHello;
95 
96 private:
97  // private slot
98  void _q_deliverMessage(const QString &serviceName, const QByteArray &message);
100 };
101 
103  connection(0),
104  gotHello(false),
106 {
107 }
108 
110 {
111  if (!gotHello)
112  return;
113 
114  QByteArray message;
115  {
116  QDataStream out(&message, QIODevice::WriteOnly);
117  out << QString(QLatin1String("QDeclarativeDebugClient")) << 1 << plugins.keys();
118  }
119  connection->send(message);
120 }
121 
123  QPluginLoader *loader, const QString &pluginName)
124 {
125 #ifndef QT_NO_LIBRARY
126  QStringList pluginCandidates;
128  foreach (const QString &libPath, paths) {
129  const QDir dir(libPath + QLatin1String("/qmltooling"));
130  if (dir.exists()) {
132  foreach (const QString &pluginPath, plugins) {
133  if (QFileInfo(pluginPath).fileName().contains(pluginName))
134  pluginCandidates << dir.absoluteFilePath(pluginPath);
135  }
136  }
137  }
138 
139  foreach (const QString &pluginPath, pluginCandidates) {
140  loader->setFileName(pluginPath);
141  if (!loader->load()) {
142  continue;
143  }
145  if (QObject *instance = loader->instance())
146  connection = qobject_cast<QDeclarativeDebugServerConnection*>(instance);
147 
148  if (connection)
149  return connection;
150  loader->unload();
151  }
152 #endif
153  return 0;
154 }
155 
157 {
159  return d->connection
160  && d->connection->isConnected()
161  && d->gotHello;
162 }
163 
165 {
166  static bool commandLineTested = false;
167  static QDeclarativeDebugServer *server = 0;
168 
169  if (!commandLineTested) {
170  commandLineTested = true;
171 
173 #ifndef QDECLARATIVE_NO_DEBUG_PROTOCOL
174  // ### remove port definition when protocol is changed
175  int port = 0;
176  bool block = false;
177  bool ok = false;
178 
179  // format: qmljsdebugger=port:3768[,block] OR qmljsdebugger=ost[,block]
180  if (!appD->qmljsDebugArguments().isEmpty()) {
183  "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
184  "Debugging has not been enabled.").arg(
185  appD->qmljsDebugArguments());
186  return 0;
187  }
188 
189  QString pluginName;
190  if (appD->qmljsDebugArguments().indexOf(QLatin1String("port:")) == 0) {
191  int separatorIndex = appD->qmljsDebugArguments().indexOf(QLatin1Char(','));
192  port = appD->qmljsDebugArguments().mid(5, separatorIndex - 5).toInt(&ok);
193  pluginName = QLatin1String("qmldbg_tcp");
194  } else if (appD->qmljsDebugArguments().contains(QLatin1String("ost"))) {
195  pluginName = QLatin1String("qmldbg_ost");
196  ok = true;
197  }
198 
199  block = appD->qmljsDebugArguments().contains(QLatin1String("block"));
200 
201  if (ok) {
202  server = new QDeclarativeDebugServer();
203 
204  QPluginLoader *loader = new QPluginLoader(server);
207  if (connection) {
208  server->d_func()->connection = connection;
209 
210  connection->setServer(server);
211  connection->setPort(port, block);
212  } else {
214  "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
215  "Remote debugger plugin has not been found.").arg(
216  appD->qmljsDebugArguments());
217  }
218 
219  } else {
221  "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
222  "Format is -qmljsdebugger=port:<port>[,block]").arg(
223  appD->qmljsDebugArguments());
224  }
225  }
226 #else
227  if (!appD->qmljsDebugArguments().isEmpty()) {
229  "QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". "
230  "QtDeclarative is not configured for debugging.").arg(
231  appD->qmljsDebugArguments());
232  }
233 #endif
234  }
235 
236  return server;
237 }
238 
241 {
242 }
243 
245 {
247 
248  QDataStream in(message);
249  if (!d->gotHello) {
250  QString name;
251  int op;
252  in >> name >> op;
253 
254  if (name != QLatin1String("QDeclarativeDebugServer")
255  || op != 0) {
256  qWarning("QDeclarativeDebugServer: Invalid hello message");
257  d->connection->disconnect();
258  return;
259  }
260 
261  int version;
262  in >> version >> d->clientPlugins;
263 
264  // Send the hello answer immediately, since it needs to arrive before
265  // the plugins below start sending messages.
266  QByteArray helloAnswer;
267  {
268  QDataStream out(&helloAnswer, QIODevice::WriteOnly);
269  out << QString(QLatin1String("QDeclarativeDebugClient")) << 0 << protocolVersion << d->plugins.keys();
270  }
271  d->connection->send(helloAnswer);
272 
273  d->gotHello = true;
274 
276  for (; iter != d->plugins.end(); ++iter) {
278  if (d->clientPlugins.contains(iter.key()))
280  iter.value()->d_func()->status = newStatus;
281  iter.value()->statusChanged(newStatus);
282  }
283 
284  qDebug("QDeclarativeDebugServer: Connection established");
285  } else {
286 
287  QString debugServer(QLatin1String("QDeclarativeDebugServer"));
288 
289  QString name;
290  in >> name;
291 
292  if (name == debugServer) {
293  int op = -1;
294  in >> op;
295 
296  if (op == 1) {
297  // Service Discovery
298  QStringList oldClientPlugins = d->clientPlugins;
299  in >> d->clientPlugins;
300 
302  for (; iter != d->plugins.end(); ++iter) {
303  const QString pluginName = iter.key();
305  if (d->clientPlugins.contains(pluginName))
307 
308  if (oldClientPlugins.contains(pluginName)
309  != d->clientPlugins.contains(pluginName)) {
310  iter.value()->d_func()->status = newStatus;
311  iter.value()->statusChanged(newStatus);
312  }
313  }
314  } else {
315  qWarning("QDeclarativeDebugServer: Invalid control message %d", op);
316  }
317  } else {
318  QByteArray message;
319  in >> message;
320 
321  if (d->waitingForMsgFromService == name) {
322  // deliver directly so that it is delivered before waitForMessage is returning.
323  d->_q_deliverMessage(name, message);
324  d->waitingForMsgSucceeded = true;
325  } else {
326  // deliver message in next event loop run.
327  // Fixes the case that the service does start it's own event loop ...,
328  // but the networking code doesn't deliver any new messages because readyRead
329  // hasn't returned.
330  QMetaObject::invokeMethod(this, "_q_deliverMessage", Qt::QueuedConnection,
331  Q_ARG(QString, name),
332  Q_ARG(QByteArray, message));
333  }
334  }
335  }
336 }
337 
339 {
340  QHash<QString, QDeclarativeDebugService *>::Iterator iter = plugins.find(serviceName);
341  if (iter == plugins.end()) {
342  qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << serviceName;
343  } else {
344  (*iter)->messageReceived(message);
345  }
346 }
347 
349 {
351  return d->plugins.values();
352 }
353 
355 {
357  return d->plugins.keys();
358 }
359 
361 {
363  if (!service || d->plugins.contains(service->name()))
364  return false;
365 
366  d->plugins.insert(service->name(), service);
367  d->advertisePlugins();
368 
370  if (d->clientPlugins.contains(service->name()))
372  service->d_func()->status = newStatus;
373  service->statusChanged(newStatus);
374  return true;
375 }
376 
378 {
380  if (!service || !d->plugins.contains(service->name()))
381  return false;
382 
383  d->plugins.remove(service->name());
384  d->advertisePlugins();
385 
387  service->d_func()->server = 0;
388  service->d_func()->status = newStatus;
389  service->statusChanged(newStatus);
390  return true;
391 }
392 
394  const QByteArray &message)
395 {
397  QByteArray msg;
398  {
400  out << service->name() << message;
401  }
402  d->connection->send(msg);
403 }
404 
406 {
408 
409  if (!service
410  || !d->plugins.contains(service->name())
411  || !d->waitingForMsgFromService.isEmpty())
412  return false;
413 
414  d->waitingForMsgFromService = service->name();
415 
416  do {
417  d->connection->waitForMessage();
418  } while (!d->waitingForMsgSucceeded);
419  d->waitingForMsgSucceeded = false;
420  d->waitingForMsgFromService.clear();
421  return true;
422 }
423 
425 
426 #include "moc_qdeclarativedebugserver_p.cpp"
The QDir class provides access to directory structures and their contents.
Definition: qdir.h:58
QBool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.h:904
double d
Definition: qnumeric_p.h:62
void _q_deliverMessage(const QString &serviceName, const QByteArray &message)
virtual void send(const QByteArray &message)=0
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
static QDeclarativeDebugServer * instance()
QString absoluteFilePath(const QString &fileName) const
Returns the absolute path name of a file in the directory.
Definition: qdir.cpp:701
int toInt(bool *ok=0, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition: qstring.cpp:6090
bool load()
Loads the plugin and returns true if the plugin was loaded successfully; otherwise returns false...
static QString qmljsDebugArguments()
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
QString fileName() const
Returns the name of the file, excluding the path.
Definition: qfileinfo.cpp:726
#define Q_ARG(type, data)
Definition: qobjectdefs.h:246
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
QHash< QString, QDeclarativeDebugService * > plugins
T & value() const
Returns a modifiable reference to the current item&#39;s value.
Definition: qhash.h:348
The QString class provides a Unicode character string.
Definition: qstring.h:83
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
#define Q_D(Class)
Definition: qglobal.h:2482
static QObjectPrivate * get(QObject *o)
Definition: qobject_p.h:177
bool exists() const
Returns true if the directory exists; otherwise returns false.
Definition: qdir.cpp:1560
Q_CORE_EXPORT void qDebug(const char *,...)
static QDeclarativeDebugServerConnection * loadConnectionPlugin(QPluginLoader *loader, const QString &pluginName)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
virtual void setPort(int port, bool bock)=0
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
#define qApp
const char * name
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
Q_CORE_EXPORT void qWarning(const char *,...)
int indexOf(QChar c, int from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:2838
virtual void setServer(QDeclarativeDebugServer *server)=0
QBool contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the list contains the string str; otherwise returns false.
Definition: qstringlist.h:172
void setFileName(const QString &fileName)
const Key & key() const
Returns the current item&#39;s key as a const reference.
Definition: qhash.h:347
void receiveMessage(const QByteArray &message)
void sendMessage(QDeclarativeDebugService *service, const QByteArray &message)
bool removeService(QDeclarativeDebugService *service)
QString mid(int position, int n=-1) const Q_REQUIRED_RESULT
Returns a string that contains n characters of this string, starting at the specified position index...
Definition: qstring.cpp:3706
bool addService(QDeclarativeDebugService *service)
static QAuServer & server()
Definition: qsound.cpp:79
QString arg(qlonglong a, int fieldwidth=0, int base=10, const QChar &fillChar=QLatin1Char(' ')) const Q_REQUIRED_RESULT
Definition: qstring.cpp:7186
#define Q_DECLARE_PUBLIC(Class)
Definition: qglobal.h:2477
static QString fromLatin1(const char *, int size=-1)
Returns a QString initialized with the first size characters of the Latin-1 string str...
Definition: qstring.cpp:4188
static QStringList libraryPaths()
Returns a list of paths that the application will search when dynamically loading libraries...
bool waitForMessage(QDeclarativeDebugService *service)
The QHash::iterator class provides an STL-style non-const iterator for QHash and QMultiHash.
Definition: qhash.h:330
QList< QDeclarativeDebugService * > services() const
const int protocolVersion
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(0), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
Invokes the member (a signal or a slot name) on the object obj.
QDeclarativeDebugServerConnection * connection
The QDataStream class provides serialization of binary data to a QIODevice.
Definition: qdatastream.h:71
QObject * instance()
Returns the root component object of the plugin.
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:60
bool unload()
Unloads the plugin and returns true if the plugin could be unloaded; otherwise returns false...
QStringList entryList(Filters filters=NoFilter, SortFlags sort=NoSort) const
Returns a list of the names of all the files and directories in the directory, ordered according to t...
Definition: qdir.cpp:1290
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition: qhash.h:648
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
The QList class is a template class that provides lists.
Definition: qdatastream.h:62
The QPluginLoader class loads a plugin at run-time.
Definition: qpluginloader.h:62