From fd9771c29d401d88779ab7c5d7715c9ca41dd723 Mon Sep 17 00:00:00 2001
From: Kai Koehne <kai.koehne@nokia.com>
Date: Wed, 22 Sep 2010 21:03:57 +0200
Subject: Make QmlDebug protocol more robust

The protocol so far was client->server only. That is, there was no
sane way for a client to check whether a plugin on the server (service)
was available or not. E.g. calling Client::setEnabled(true) 'succeeded',
without a check whether there is actually a service to talk to.

The new protocol replaces this shortcoming by a service discovery
mechanism: Both client & service announce their available plugins at
handshake time, and later on if there are changes. The status is
reflected in Client::status() and Service::Status() , which are either

  NotConnected - no network connection, or not registered properly
  Unavailable - TCP/IP connection works, but no plugin with the same
name on the other side
  Enabled - You can connect to plugin on other side

The status changes happen automatically (no setEnabled() anymore).

Furthermore a version ID was added to the handshake, so that we can
extend the protocol further in the future :)
---
 src/declarative/debugger/qdeclarativedebug.cpp     |  56 ++++--
 src/declarative/debugger/qdeclarativedebug_p.h     |   7 +-
 .../debugger/qdeclarativedebugclient.cpp           | 199 +++++++++++++++------
 .../debugger/qdeclarativedebugclient_p.h           |  10 +-
 .../debugger/qdeclarativedebugservice.cpp          | 126 ++++++++++---
 .../debugger/qdeclarativedebugservice_p.h          |   9 +-
 .../debugger/qdeclarativedebugtrace.cpp            |   8 +-
 .../qdeclarativedebug/tst_qdeclarativedebug.cpp    |   5 +-
 .../tst_qdeclarativedebugclient.cpp                |  60 +++----
 .../tst_qdeclarativedebugservice.cpp               |  41 ++---
 tests/auto/declarative/shared/debugutil.cpp        |  13 +-
 tests/auto/declarative/shared/debugutil_p.h        |   8 +-
 12 files changed, 362 insertions(+), 180 deletions(-)

diff --git a/src/declarative/debugger/qdeclarativedebug.cpp b/src/declarative/debugger/qdeclarativedebug.cpp
index 1ffe441..7a5e5f6 100644
--- a/src/declarative/debugger/qdeclarativedebug.cpp
+++ b/src/declarative/debugger/qdeclarativedebug.cpp
@@ -55,10 +55,12 @@ public:
     QDeclarativeEngineDebugClient(QDeclarativeDebugConnection *client, QDeclarativeEngineDebugPrivate *p);
 
 protected:
+    virtual void statusChanged(Status status);
     virtual void messageReceived(const QByteArray &);
 
 private:
     QDeclarativeEngineDebugPrivate *priv;
+    friend class QDeclarativeEngineDebugPrivate;
 };
 
 class QDeclarativeEngineDebugPrivate : public QObjectPrivate
@@ -66,7 +68,9 @@ class QDeclarativeEngineDebugPrivate : public QObjectPrivate
     Q_DECLARE_PUBLIC(QDeclarativeEngineDebug)
 public:
     QDeclarativeEngineDebugPrivate(QDeclarativeDebugConnection *);
+    ~QDeclarativeEngineDebugPrivate();
 
+    void statusChanged(QDeclarativeEngineDebug::Status status);
     void message(const QByteArray &);
 
     QDeclarativeEngineDebugClient *client;
@@ -93,12 +97,18 @@ QDeclarativeEngineDebugClient::QDeclarativeEngineDebugClient(QDeclarativeDebugCo
                                            QDeclarativeEngineDebugPrivate *p)
 : QDeclarativeDebugClient(QLatin1String("QDeclarativeEngine"), client), priv(p)
 {
-    setEnabled(true);
+}
+
+void QDeclarativeEngineDebugClient::statusChanged(Status status)
+{
+    if (priv)
+        priv->statusChanged(static_cast<QDeclarativeEngineDebug::Status>(status));
 }
 
 void QDeclarativeEngineDebugClient::messageReceived(const QByteArray &data)
 {
-    priv->message(data);
+    if (priv)
+        priv->message(data);
 }
 
 QDeclarativeEngineDebugPrivate::QDeclarativeEngineDebugPrivate(QDeclarativeDebugConnection *c)
@@ -106,6 +116,12 @@ QDeclarativeEngineDebugPrivate::QDeclarativeEngineDebugPrivate(QDeclarativeDebug
 {
 }
 
+QDeclarativeEngineDebugPrivate::~QDeclarativeEngineDebugPrivate()
+{
+    if (client)
+        client->priv = 0;
+}
+
 int QDeclarativeEngineDebugPrivate::getId()
 {
     return nextId++;
@@ -228,6 +244,11 @@ void QDeclarativeEngineDebugPrivate::decode(QDataStream &ds, QDeclarativeDebugCo
     }
 }
 
+void QDeclarativeEngineDebugPrivate::statusChanged(QDeclarativeEngineDebug::Status status)
+{
+    emit q_func()->statusChanged(status);
+}
+
 void QDeclarativeEngineDebugPrivate::message(const QByteArray &data)
 {
     QDataStream ds(data);
@@ -350,12 +371,19 @@ QDeclarativeEngineDebug::QDeclarativeEngineDebug(QDeclarativeDebugConnection *cl
 {
 }
 
+QDeclarativeEngineDebug::Status QDeclarativeEngineDebug::status() const
+{
+    Q_D(const QDeclarativeEngineDebug);
+
+    return static_cast<QDeclarativeEngineDebug::Status>(d->client->status());
+}
+
 QDeclarativeDebugPropertyWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebugPropertyReference &property, QObject *parent)
 {
     Q_D(QDeclarativeEngineDebug);
 
     QDeclarativeDebugPropertyWatch *watch = new QDeclarativeDebugPropertyWatch(parent);
-    if (d->client->isConnected()) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled) {
         int queryId = d->getId();
         watch->m_queryId = queryId;
         watch->m_client = this;
@@ -384,7 +412,7 @@ QDeclarativeDebugObjectExpressionWatch *QDeclarativeEngineDebug::addWatch(const
 {
     Q_D(QDeclarativeEngineDebug);
     QDeclarativeDebugObjectExpressionWatch *watch = new QDeclarativeDebugObjectExpressionWatch(parent);
-    if (d->client->isConnected()) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled) {
         int queryId = d->getId();
         watch->m_queryId = queryId;
         watch->m_client = this;
@@ -407,7 +435,7 @@ QDeclarativeDebugWatch *QDeclarativeEngineDebug::addWatch(const QDeclarativeDebu
     Q_D(QDeclarativeEngineDebug);
 
     QDeclarativeDebugWatch *watch = new QDeclarativeDebugWatch(parent);
-    if (d->client->isConnected()) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled) {
         int queryId = d->getId();
         watch->m_queryId = queryId;
         watch->m_client = this;
@@ -443,7 +471,7 @@ void QDeclarativeEngineDebug::removeWatch(QDeclarativeDebugWatch *watch)
     
     d->watched.remove(watch->queryId());
 
-    if (d->client && d->client->isConnected()) {
+    if (d->client && d->client->status() == QDeclarativeDebugClient::Enabled) {
         QByteArray message;
         QDataStream ds(&message, QIODevice::WriteOnly);
         ds << QByteArray("NO_WATCH") << watch->queryId();
@@ -456,7 +484,7 @@ QDeclarativeDebugEnginesQuery *QDeclarativeEngineDebug::queryAvailableEngines(QO
     Q_D(QDeclarativeEngineDebug);
 
     QDeclarativeDebugEnginesQuery *query = new QDeclarativeDebugEnginesQuery(parent);
-    if (d->client->isConnected()) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled) {
         query->m_client = this;
         int queryId = d->getId();
         query->m_queryId = queryId;
@@ -478,7 +506,7 @@ QDeclarativeDebugRootContextQuery *QDeclarativeEngineDebug::queryRootContexts(co
     Q_D(QDeclarativeEngineDebug);
 
     QDeclarativeDebugRootContextQuery *query = new QDeclarativeDebugRootContextQuery(parent);
-    if (d->client->isConnected() && engine.debugId() != -1) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled && engine.debugId() != -1) {
         query->m_client = this;
         int queryId = d->getId();
         query->m_queryId = queryId;
@@ -500,7 +528,7 @@ QDeclarativeDebugObjectQuery *QDeclarativeEngineDebug::queryObject(const QDeclar
     Q_D(QDeclarativeEngineDebug);
 
     QDeclarativeDebugObjectQuery *query = new QDeclarativeDebugObjectQuery(parent);
-    if (d->client->isConnected() && object.debugId() != -1) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled && object.debugId() != -1) {
         query->m_client = this;
         int queryId = d->getId();
         query->m_queryId = queryId;
@@ -523,7 +551,7 @@ QDeclarativeDebugObjectQuery *QDeclarativeEngineDebug::queryObjectRecursive(cons
     Q_D(QDeclarativeEngineDebug);
 
     QDeclarativeDebugObjectQuery *query = new QDeclarativeDebugObjectQuery(parent);
-    if (d->client->isConnected() && object.debugId() != -1) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled && object.debugId() != -1) {
         query->m_client = this;
         int queryId = d->getId();
         query->m_queryId = queryId;
@@ -546,7 +574,7 @@ QDeclarativeDebugExpressionQuery *QDeclarativeEngineDebug::queryExpressionResult
     Q_D(QDeclarativeEngineDebug);
 
     QDeclarativeDebugExpressionQuery *query = new QDeclarativeDebugExpressionQuery(parent);
-    if (d->client->isConnected() && objectDebugId != -1) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
         query->m_client = this;
         query->m_expr = expr;
         int queryId = d->getId();
@@ -570,7 +598,7 @@ bool QDeclarativeEngineDebug::setBindingForObject(int objectDebugId, const QStri
 {
     Q_D(QDeclarativeEngineDebug);
 
-    if (d->client->isConnected() && objectDebugId != -1) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
         QByteArray message;
         QDataStream ds(&message, QIODevice::WriteOnly);
         ds << QByteArray("SET_BINDING") << objectDebugId << propertyName << bindingExpression << isLiteralValue;
@@ -585,7 +613,7 @@ bool QDeclarativeEngineDebug::resetBindingForObject(int objectDebugId, const QSt
 {
     Q_D(QDeclarativeEngineDebug);
 
-    if (d->client->isConnected() && objectDebugId != -1) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
         QByteArray message;
         QDataStream ds(&message, QIODevice::WriteOnly);
         ds << QByteArray("RESET_BINDING") << objectDebugId << propertyName;
@@ -601,7 +629,7 @@ bool QDeclarativeEngineDebug::setMethodBody(int objectDebugId, const QString &me
 {
     Q_D(QDeclarativeEngineDebug);
 
-    if (d->client->isConnected() && objectDebugId != -1) {
+    if (d->client->status() == QDeclarativeDebugClient::Enabled && objectDebugId != -1) {
         QByteArray message;
         QDataStream ds(&message, QIODevice::WriteOnly);
         ds << QByteArray("SET_METHOD_BODY") << objectDebugId << methodName << methodBody;
diff --git a/src/declarative/debugger/qdeclarativedebug_p.h b/src/declarative/debugger/qdeclarativedebug_p.h
index 2b1a115..3d83e8a 100644
--- a/src/declarative/debugger/qdeclarativedebug_p.h
+++ b/src/declarative/debugger/qdeclarativedebug_p.h
@@ -69,7 +69,11 @@ class Q_DECLARATIVE_EXPORT QDeclarativeEngineDebug : public QObject
 {
 Q_OBJECT
 public:
-    QDeclarativeEngineDebug(QDeclarativeDebugConnection *, QObject * = 0);
+    enum Status { NotConnected, Unavailable, Enabled };
+
+    explicit QDeclarativeEngineDebug(QDeclarativeDebugConnection *, QObject * = 0);
+
+    Status status() const;
 
     QDeclarativeDebugPropertyWatch *addWatch(const QDeclarativeDebugPropertyReference &,
                             QObject *parent = 0);
@@ -101,6 +105,7 @@ public:
 
 Q_SIGNALS:
     void newObjects();
+    void statusChanged(Status status);
 
 private:
     Q_DECLARE_PRIVATE(QDeclarativeEngineDebug)
diff --git a/src/declarative/debugger/qdeclarativedebugclient.cpp b/src/declarative/debugger/qdeclarativedebugclient.cpp
index 2e52b40..f7d7243 100644
--- a/src/declarative/debugger/qdeclarativedebugclient.cpp
+++ b/src/declarative/debugger/qdeclarativedebugclient.cpp
@@ -50,6 +50,20 @@
 
 QT_BEGIN_NAMESPACE
 
+const int protocolVersion = 1;
+const QString serverId = QLatin1String("QDeclarativeDebugServer");
+const QString clientId = QLatin1String("QDeclarativeDebugClient");
+
+class QDeclarativeDebugClientPrivate : public QObjectPrivate
+{
+    Q_DECLARE_PUBLIC(QDeclarativeDebugClient)
+public:
+    QDeclarativeDebugClientPrivate();
+
+    QString name;
+    QDeclarativeDebugConnection *client;
+};
+
 class QDeclarativeDebugConnectionPrivate : public QObject
 {
     Q_OBJECT
@@ -58,40 +72,123 @@ public:
     QDeclarativeDebugConnection *q;
     QPacketProtocol *protocol;
 
-    QStringList enabled;
+    bool gotHello;
+    QStringList serverPlugins;
     QHash<QString, QDeclarativeDebugClient *> plugins;
+
+    void advertisePlugins();
+
 public Q_SLOTS:
     void connected();
     void readyRead();
 };
 
 QDeclarativeDebugConnectionPrivate::QDeclarativeDebugConnectionPrivate(QDeclarativeDebugConnection *c)
-: QObject(c), q(c), protocol(0)
+: QObject(c), q(c), protocol(0), gotHello(false)
 {
     protocol = new QPacketProtocol(q, this);
     QObject::connect(c, SIGNAL(connected()), this, SLOT(connected()));
     QObject::connect(protocol, SIGNAL(readyRead()), this, SLOT(readyRead()));
 }
 
+void QDeclarativeDebugConnectionPrivate::advertisePlugins()
+{
+    if (!q->isConnected() || !gotHello)
+        return;
+
+    QPacket pack;
+    pack << serverId << 1 << plugins.keys();
+    protocol->send(pack);
+}
+
 void QDeclarativeDebugConnectionPrivate::connected()
 {
     QPacket pack;
-    pack << QString(QLatin1String("QDeclarativeDebugServer")) << enabled;
+    pack << serverId << 0 << protocolVersion << plugins.keys();
     protocol->send(pack);
 }
 
 void QDeclarativeDebugConnectionPrivate::readyRead()
 {
-    QPacket pack = protocol->read();
-    QString name; QByteArray message;
-    pack >> name >> message;
+    if (!gotHello) {
+        QPacket pack = protocol->read();
+        QString name;
+
+        pack >> name;
+
+        bool validHello = false;
+        if (name == clientId) {
+            int op = -1;
+            pack >> op;
+            if (op == 0) {
+                int version = -1;
+                pack >> version;
+                if (version == protocolVersion) {
+                    pack >> serverPlugins;
+                    validHello = true;
+                }
+            }
+        }
 
-    QHash<QString, QDeclarativeDebugClient *>::Iterator iter = 
-        plugins.find(name);
-    if (iter == plugins.end()) {
-        qWarning() << "QDeclarativeDebugConnection: Message received for missing plugin" << name;
-    } else {
-        (*iter)->messageReceived(message);
+        if (!validHello) {
+            qWarning("QDeclarativeDebugConnection: Invalid hello message");
+            QObject::disconnect(protocol, SIGNAL(readyRead()), this, SLOT(readyRead()));
+            return;
+        }
+
+        qDebug() << "Available server side plugins: " << serverPlugins;
+
+        QHash<QString, QDeclarativeDebugClient *>::Iterator iter = plugins.begin();
+        for (; iter != plugins.end(); ++iter) {
+            QDeclarativeDebugClient::Status newStatus = QDeclarativeDebugClient::Unavailable;
+            if (serverPlugins.contains(iter.key()))
+                newStatus = QDeclarativeDebugClient::Enabled;
+            iter.value()->statusChanged(newStatus);
+        }
+        gotHello = true;
+    }
+
+    while (protocol->packetsAvailable()) {
+        QPacket pack = protocol->read();
+        QString name;
+        pack >> name;
+
+        if (name == clientId) {
+            int op = -1;
+            pack >> op;
+
+            if (op == 1) {
+                // Service Discovery
+                QStringList oldServerPlugins = serverPlugins;
+                pack >> serverPlugins;
+
+                QHash<QString, QDeclarativeDebugClient *>::Iterator iter = plugins.begin();
+                for (; iter != plugins.end(); ++iter) {
+                    const QString pluginName = iter.key();
+                    QDeclarativeDebugClient::Status newStatus = QDeclarativeDebugClient::Unavailable;
+                    if (serverPlugins.contains(pluginName))
+                        newStatus = QDeclarativeDebugClient::Enabled;
+
+                    if (oldServerPlugins.contains(pluginName)
+                            != serverPlugins.contains(pluginName)) {
+                        iter.value()->statusChanged(newStatus);
+                    }
+                }
+            } else {
+                qWarning() << "QDeclarativeDebugConnection: Unknown control message id" << op;
+            }
+        } else {
+            QByteArray message;
+            pack >> message;
+
+            QHash<QString, QDeclarativeDebugClient *>::Iterator iter =
+                plugins.find(name);
+            if (iter == plugins.end()) {
+                qWarning() << "QDeclarativeDebugConnection: Message received for missing plugin" << name;
+            } else {
+                (*iter)->messageReceived(message);
+            }
+        }
     }
 }
 
@@ -100,24 +197,22 @@ QDeclarativeDebugConnection::QDeclarativeDebugConnection(QObject *parent)
 {
 }
 
-bool QDeclarativeDebugConnection::isConnected() const
+QDeclarativeDebugConnection::~QDeclarativeDebugConnection()
 {
-    return state() == ConnectedState;
+    QHash<QString, QDeclarativeDebugClient*>::iterator iter = d->plugins.begin();
+    for (; iter != d->plugins.end(); ++iter) {
+         iter.value()->d_func()->client = 0;
+         iter.value()->statusChanged(QDeclarativeDebugClient::NotConnected);
+    }
 }
 
-class QDeclarativeDebugClientPrivate : public QObjectPrivate
+bool QDeclarativeDebugConnection::isConnected() const
 {
-    Q_DECLARE_PUBLIC(QDeclarativeDebugClient)
-public:
-    QDeclarativeDebugClientPrivate();
-
-    QString name;
-    QDeclarativeDebugConnection *client;
-    bool enabled;
-};
+    return state() == ConnectedState;
+}
 
 QDeclarativeDebugClientPrivate::QDeclarativeDebugClientPrivate()
-: client(0), enabled(false)
+: client(0)
 {
 }
 
@@ -137,60 +232,44 @@ QDeclarativeDebugClient::QDeclarativeDebugClient(const QString &name,
         d->client = 0;
     } else {
         d->client->d->plugins.insert(name, this);
+        d->client->d->advertisePlugins();
     }
 }
 
-QString QDeclarativeDebugClient::name() const
+QDeclarativeDebugClient::~QDeclarativeDebugClient()
 {
     Q_D(const QDeclarativeDebugClient);
-    return d->name;
+    if (d->client && d->client->d) {
+        d->client->d->plugins.remove(d->name);
+        d->client->d->advertisePlugins();
+    }
 }
 
-bool QDeclarativeDebugClient::isEnabled() const
+QString QDeclarativeDebugClient::name() const
 {
     Q_D(const QDeclarativeDebugClient);
-    return d->enabled;
-}
-
-void QDeclarativeDebugClient::setEnabled(bool e)
-{
-    Q_D(QDeclarativeDebugClient);
-    if (e == d->enabled)
-        return;
-
-    d->enabled = e;
-
-    if (d->client) {
-        if (e) 
-            d->client->d->enabled.append(d->name);
-        else
-            d->client->d->enabled.removeAll(d->name);
-
-        if (d->client->state() == QTcpSocket::ConnectedState) {
-            QPacket pack;
-            pack << QString(QLatin1String("QDeclarativeDebugServer"));
-            if (e) pack << (int)1;
-            else pack << (int)2;
-            pack << d->name;
-            d->client->d->protocol->send(pack);
-        }
-    }
+    return d->name;
 }
 
-bool QDeclarativeDebugClient::isConnected() const
+QDeclarativeDebugClient::Status QDeclarativeDebugClient::status() const
 {
     Q_D(const QDeclarativeDebugClient);
+    if (!d->client
+            || !d->client->isConnected()
+            || !d->client->d->gotHello)
+        return NotConnected;
 
-    if (!d->client)
-        return false;
-    return d->client->isConnected();
+    if (d->client->d->serverPlugins.contains(d->name))
+        return Enabled;
+
+    return Unavailable;
 }
 
 void QDeclarativeDebugClient::sendMessage(const QByteArray &message)
 {
     Q_D(QDeclarativeDebugClient);
 
-    if (!d->client || !d->client->isConnected())
+    if (status() != Enabled)
         return;
 
     QPacket pack;
@@ -198,6 +277,10 @@ void QDeclarativeDebugClient::sendMessage(const QByteArray &message)
     d->client->d->protocol->send(pack);
 }
 
+void QDeclarativeDebugClient::statusChanged(Status status)
+{
+}
+
 void QDeclarativeDebugClient::messageReceived(const QByteArray &)
 {
 }
diff --git a/src/declarative/debugger/qdeclarativedebugclient_p.h b/src/declarative/debugger/qdeclarativedebugclient_p.h
index 4144a66..8d1706d 100644
--- a/src/declarative/debugger/qdeclarativedebugclient_p.h
+++ b/src/declarative/debugger/qdeclarativedebugclient_p.h
@@ -57,6 +57,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativeDebugConnection : public QTcpSocket
     Q_DISABLE_COPY(QDeclarativeDebugConnection)
 public:
     QDeclarativeDebugConnection(QObject * = 0);
+    ~QDeclarativeDebugConnection();
 
     bool isConnected() const;
 private:
@@ -73,18 +74,19 @@ class Q_DECLARATIVE_EXPORT QDeclarativeDebugClient : public QObject
     Q_DISABLE_COPY(QDeclarativeDebugClient)
 
 public:
+    enum Status { NotConnected, Unavailable, Enabled };
+
     QDeclarativeDebugClient(const QString &, QDeclarativeDebugConnection *parent);
+    ~QDeclarativeDebugClient();
 
     QString name() const;
 
-    bool isEnabled() const;
-    void setEnabled(bool);
-
-    bool isConnected() const;
+    Status status() const;
 
     void sendMessage(const QByteArray &);
 
 protected:
+    virtual void statusChanged(Status);
     virtual void messageReceived(const QByteArray &);
 
 private:
diff --git a/src/declarative/debugger/qdeclarativedebugservice.cpp b/src/declarative/debugger/qdeclarativedebugservice.cpp
index 1f2bf4f..0fbc1e3 100644
--- a/src/declarative/debugger/qdeclarativedebugservice.cpp
+++ b/src/declarative/debugger/qdeclarativedebugservice.cpp
@@ -54,6 +54,30 @@
 
 QT_BEGIN_NAMESPACE
 
+/*
+  QDeclarativeDebug Protocol (Version 1):
+
+  handshake:
+    1. Client sends
+         "QDeclarativeDebugServer" 0 version pluginNames
+       version: an int representing the highest protocol version the client knows
+       pluginNames: plugins available on client side
+    2. Server sends
+         "QDeclarativeDebugClient" 0 version pluginNames
+       version: an int representing the highest protocol version the client & server know
+       pluginNames: plugins available on server side. plugins both in the client and server message are enabled.
+  client plugin advertisement
+    1. Client sends
+         "QDeclarativeDebugServer" 1 pluginNames
+  server plugin advertisement
+    1. Server sends
+         "QDeclarativeDebugClient" 1 pluginNames
+  plugin communication:
+       Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin.
+  */
+
+const int protocolVersion = 1;
+
 class QDeclarativeDebugServerPrivate;
 class QDeclarativeDebugServer : public QObject
 {
@@ -82,11 +106,13 @@ class QDeclarativeDebugServerPrivate : public QObjectPrivate
 public:
     QDeclarativeDebugServerPrivate();
 
+    void advertisePlugins();
+
     int port;
     QTcpSocket *connection;
     QPacketProtocol *protocol;
     QHash<QString, QDeclarativeDebugService *> plugins;
-    QStringList enabledPlugins;
+    QStringList clientPlugins;
     QTcpServer *tcpServer;
     bool gotHello;
 };
@@ -106,6 +132,18 @@ QDeclarativeDebugServerPrivate::QDeclarativeDebugServerPrivate()
 {
 }
 
+void QDeclarativeDebugServerPrivate::advertisePlugins()
+{
+    if (!connection
+            || connection->state() != QTcpSocket::ConnectedState
+            || !gotHello)
+        return;
+
+    QPacket pack;
+    pack << QString(QLatin1String("QDeclarativeDebugClient")) << 1 << plugins.keys();
+    protocol->send(pack);
+}
+
 void QDeclarativeDebugServer::listen()
 {
     Q_D(QDeclarativeDebugServer);
@@ -202,9 +240,13 @@ void QDeclarativeDebugServer::readyRead()
 
     if (!d->gotHello) {
         QPacket hello = d->protocol->read();
-        QString name; 
-        hello >> name >> d->enabledPlugins;
-        if (name != QLatin1String("QDeclarativeDebugServer")) {
+
+        QString name;
+        int op;
+        hello >> name >> op;
+
+        if (name != QLatin1String("QDeclarativeDebugServer")
+                || op != 0) {
             qWarning("QDeclarativeDebugServer: Invalid hello message");
             QObject::disconnect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead()));
             d->protocol->deleteLater();
@@ -213,6 +255,23 @@ void QDeclarativeDebugServer::readyRead()
             d->connection = 0;
             return;
         }
+
+        int version;
+        hello >> version >> d->clientPlugins;
+
+        QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin();
+        for (; iter != d->plugins.end(); ++iter) {
+            QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
+            if (d->clientPlugins.contains(iter.key()))
+                newStatus = QDeclarativeDebugService::Enabled;
+            iter.value()->statusChanged(newStatus);
+        }
+
+        QPacket helloAnswer;
+        helloAnswer << QString(QLatin1String("QDeclarativeDebugClient")) << 0 << protocolVersion << d->plugins.keys();
+        d->protocol->send(helloAnswer);
+        d->connection->flush();
+
         d->gotHello = true;
         qWarning("QDeclarativeDebugServer: Connection established");
     }
@@ -226,32 +285,29 @@ void QDeclarativeDebugServer::readyRead()
         pack >> name;
 
         if (name == debugServer) {
-            int op = -1; QString plugin;
-            pack >> op >> plugin;
+            int op = -1;
+            pack >> op;
 
             if (op == 1) {
-                // Enable
-                if (!d->enabledPlugins.contains(plugin)) {
-                    d->enabledPlugins.append(plugin);
-                    QHash<QString, QDeclarativeDebugService *>::Iterator iter = 
-                        d->plugins.find(plugin);
-                    if (iter != d->plugins.end())
-                        (*iter)->enabledChanged(true);
-                }
-
-            } else if (op == 2) {
-                // Disable
-                if (d->enabledPlugins.contains(plugin)) {
-                    d->enabledPlugins.removeAll(plugin);
-                    QHash<QString, QDeclarativeDebugService *>::Iterator iter = 
-                        d->plugins.find(plugin);
-                    if (iter != d->plugins.end())
-                        (*iter)->enabledChanged(false);
+                // Service Discovery
+                QStringList oldClientPlugins = d->clientPlugins;
+                pack >> d->clientPlugins;
+
+                QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin();
+                for (; iter != d->plugins.end(); ++iter) {
+                    const QString pluginName = iter.key();
+                    QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
+                    if (d->clientPlugins.contains(pluginName))
+                        newStatus = QDeclarativeDebugService::Enabled;
+
+                    if (oldClientPlugins.contains(pluginName)
+                            != d->clientPlugins.contains(pluginName)) {
+                        iter.value()->statusChanged(newStatus);
+                    }
                 }
             } else {
                 qWarning("QDeclarativeDebugServer: Invalid control message %d", op);
             }
-
         } else {
             QByteArray message;
             pack >> message;
@@ -287,6 +343,16 @@ QDeclarativeDebugService::QDeclarativeDebugService(const QString &name, QObject
         d->server = 0;
     } else {
         d->server->d_func()->plugins.insert(name, this);
+        d->server->d_func()->advertisePlugins();
+    }
+}
+
+QDeclarativeDebugService::~QDeclarativeDebugService()
+{
+    Q_D(const QDeclarativeDebugService);
+    if (d->server) {
+        d->server->d_func()->plugins.remove(d->name);
+        d->server->d_func()->advertisePlugins();
     }
 }
 
@@ -296,10 +362,16 @@ QString QDeclarativeDebugService::name() const
     return d->name;
 }
 
-bool QDeclarativeDebugService::isEnabled() const
+QDeclarativeDebugService::Status QDeclarativeDebugService::status() const
 {
     Q_D(const QDeclarativeDebugService);
-    return (d->server && d->server->d_func()->enabledPlugins.contains(d->name));
+    if (!d->server
+            || !d->server->hasDebuggingClient())
+        return NotConnected;
+    if (d->server->d_func()->clientPlugins.contains(d->name))
+        return Enabled;
+
+    return Unavailable;
 }
 
 namespace {
@@ -422,7 +494,7 @@ void QDeclarativeDebugService::sendMessage(const QByteArray &message)
     d->server->d_func()->connection->flush();
 }
 
-void QDeclarativeDebugService::enabledChanged(bool)
+void QDeclarativeDebugService::statusChanged(Status)
 {
 }
 
diff --git a/src/declarative/debugger/qdeclarativedebugservice_p.h b/src/declarative/debugger/qdeclarativedebugservice_p.h
index c461ddf..0cadbe5 100644
--- a/src/declarative/debugger/qdeclarativedebugservice_p.h
+++ b/src/declarative/debugger/qdeclarativedebugservice_p.h
@@ -56,12 +56,15 @@ class Q_DECLARATIVE_EXPORT QDeclarativeDebugService : public QObject
     Q_OBJECT
     Q_DECLARE_PRIVATE(QDeclarativeDebugService)
     Q_DISABLE_COPY(QDeclarativeDebugService)
+
 public:
-    QDeclarativeDebugService(const QString &, QObject *parent = 0);
+    explicit QDeclarativeDebugService(const QString &, QObject *parent = 0);
+    ~QDeclarativeDebugService();
 
     QString name() const;
 
-    bool isEnabled() const;
+    enum Status { NotConnected, Unavailable, Enabled };
+    Status status() const;
 
     void sendMessage(const QByteArray &);
 
@@ -74,7 +77,7 @@ public:
     static bool hasDebuggingClient();
 
 protected:
-    virtual void enabledChanged(bool);
+    virtual void statusChanged(Status);
     virtual void messageReceived(const QByteArray &);
 
 private:
diff --git a/src/declarative/debugger/qdeclarativedebugtrace.cpp b/src/declarative/debugger/qdeclarativedebugtrace.cpp
index b2b0c8a..03e2d56 100644
--- a/src/declarative/debugger/qdeclarativedebugtrace.cpp
+++ b/src/declarative/debugger/qdeclarativedebugtrace.cpp
@@ -78,7 +78,7 @@ void QDeclarativeDebugTrace::endRange(RangeType t)
 
 void QDeclarativeDebugTrace::addEventImpl(EventType event)
 {
-    if (!isEnabled())
+    if (status() != Enabled)
         return;
 
     QByteArray data;
@@ -89,7 +89,7 @@ void QDeclarativeDebugTrace::addEventImpl(EventType event)
 
 void QDeclarativeDebugTrace::startRangeImpl(RangeType range)
 {
-    if (!isEnabled())
+    if (status() != Enabled)
         return;
 
     QByteArray data;
@@ -100,7 +100,7 @@ void QDeclarativeDebugTrace::startRangeImpl(RangeType range)
 
 void QDeclarativeDebugTrace::rangeDataImpl(RangeType range, const QUrl &u)
 {
-    if (!isEnabled())
+    if (status() != Enabled)
         return;
 
     QByteArray data;
@@ -111,7 +111,7 @@ void QDeclarativeDebugTrace::rangeDataImpl(RangeType range, const QUrl &u)
 
 void QDeclarativeDebugTrace::endRangeImpl(RangeType range)
 {
-    if (!isEnabled())
+    if (status() != Enabled)
         return;
 
     QByteArray data;
diff --git a/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp b/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp
index adba190..dd58baf 100644
--- a/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp
+++ b/tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp
@@ -324,13 +324,16 @@ void tst_QDeclarativeDebug::initTestCase()
     bool ok = m_conn->waitForConnected();
     Q_ASSERT(ok);
     QTRY_VERIFY(QDeclarativeDebugService::hasDebuggingClient());
-
     m_dbg = new QDeclarativeEngineDebug(m_conn, this);
+    QTRY_VERIFY(m_dbg->status() == QDeclarativeEngineDebug::Enabled);
 }
 
 void tst_QDeclarativeDebug::cleanupTestCase()
 {
+    delete m_dbg;
+    delete m_conn;
     qDeleteAll(m_components);
+    delete m_engine;
 }
 
 void tst_QDeclarativeDebug::setMethodBody()
diff --git a/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp b/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp
index 7db0e60..72af3eb 100644
--- a/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp
+++ b/tests/auto/declarative/qdeclarativedebugclient/tst_qdeclarativedebugclient.cpp
@@ -66,9 +66,7 @@ private slots:
     void initTestCase();
 
     void name();
-    void isEnabled();
-    void setEnabled();
-    void isConnected();
+    void status();
     void sendMessage();
 };
 
@@ -96,46 +94,33 @@ void tst_QDeclarativeDebugClient::name()
     QCOMPARE(client.name(), name);
 }
 
-void tst_QDeclarativeDebugClient::isEnabled()
+void tst_QDeclarativeDebugClient::status()
 {
-    QDeclarativeDebugClient client("tst_QDeclarativeDebugClient::isEnabled()", m_conn);
-    QCOMPARE(client.isEnabled(), false);
-}
+    {
+        QDeclarativeDebugConnection dummyConn;
+        QDeclarativeDebugClient client("tst_QDeclarativeDebugClient::status()", &dummyConn);
+        QCOMPARE(client.status(), QDeclarativeDebugClient::NotConnected);
+    }
 
-void tst_QDeclarativeDebugClient::setEnabled()
-{
-    QDeclarativeDebugTestService service("tst_QDeclarativeDebugClient::setEnabled()");
-    QDeclarativeDebugTestClient client("tst_QDeclarativeDebugClient::setEnabled()", m_conn);
+    QDeclarativeDebugTestClient client("tst_QDeclarativeDebugClient::status()", m_conn);
+    QCOMPARE(client.status(), QDeclarativeDebugClient::Unavailable);
 
-    QCOMPARE(service.isEnabled(), false);
+    {
+        QDeclarativeDebugTestService service("tst_QDeclarativeDebugClient::status()");
+        QDeclarativeDebugTest::waitForSignal(&client, SIGNAL(statusHasChanged()));
+        QCOMPARE(client.status(), QDeclarativeDebugClient::Enabled);
+    }
+    QDeclarativeDebugTest::waitForSignal(&client, SIGNAL(statusHasChanged()));
 
-    client.setEnabled(true);
-    QCOMPARE(client.isEnabled(), true);
-    QDeclarativeDebugTest::waitForSignal(&service, SIGNAL(enabledStateChanged()));
-    QCOMPARE(service.isEnabled(), true);
-
-    client.setEnabled(false);
-    QCOMPARE(client.isEnabled(), false);
-    QDeclarativeDebugTest::waitForSignal(&service, SIGNAL(enabledStateChanged()));
-    QCOMPARE(service.isEnabled(), false);
-}
-
-void tst_QDeclarativeDebugClient::isConnected()
-{
-    QDeclarativeDebugClient client1("tst_QDeclarativeDebugClient::isConnected() A", m_conn);
-    QCOMPARE(client1.isConnected(), true);
-
-    QDeclarativeDebugConnection conn;
-    QDeclarativeDebugClient client2("tst_QDeclarativeDebugClient::isConnected() B", &conn);
-    QCOMPARE(client2.isConnected(), false);
-
-    QDeclarativeDebugClient client3("tst_QDeclarativeDebugClient::isConnected() C", 0);
-    QCOMPARE(client3.isConnected(), false);
+    QCOMPARE(client.status(), QDeclarativeDebugClient::Unavailable);
 
     // duplicate plugin name
-    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugClient: Conflicting plugin name \"tst_QDeclarativeDebugClient::isConnected() A\" ");
-    QDeclarativeDebugClient client4("tst_QDeclarativeDebugClient::isConnected() A", m_conn);
-    QCOMPARE(client4.isConnected(), false);
+    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugClient: Conflicting plugin name \"tst_QDeclarativeDebugClient::status()\" ");
+    QDeclarativeDebugClient client2("tst_QDeclarativeDebugClient::status()", m_conn);
+    QCOMPARE(client2.status(), QDeclarativeDebugClient::NotConnected);
+
+    QDeclarativeDebugClient client3("tst_QDeclarativeDebugClient::status3()", 0);
+    QCOMPARE(client3.status(), QDeclarativeDebugClient::NotConnected);
 }
 
 void tst_QDeclarativeDebugClient::sendMessage()
@@ -145,6 +130,7 @@ void tst_QDeclarativeDebugClient::sendMessage()
 
     QByteArray msg = "hello!";
 
+    QDeclarativeDebugTest::waitForSignal(&client, SIGNAL(statusHasChanged()));
     client.sendMessage(msg);
     QByteArray resp = client.waitForResponse();
     QCOMPARE(resp, msg);
diff --git a/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp b/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp
index 4683199..bce4713 100644
--- a/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp
+++ b/tests/auto/declarative/qdeclarativedebugservice/tst_qdeclarativedebugservice.cpp
@@ -66,8 +66,7 @@ private slots:
     void initTestCase();
 
     void name();
-    void isEnabled();
-    void enabledChanged();
+    void status();
     void sendMessage();
     void idForObject();
     void objectForId();
@@ -97,31 +96,24 @@ void tst_QDeclarativeDebugService::name()
     QCOMPARE(service.name(), name);
 }
 
-void tst_QDeclarativeDebugService::isEnabled()
+void tst_QDeclarativeDebugService::status()
 {
-    QDeclarativeDebugTestService service("tst_QDeclarativeDebugService::isEnabled()", m_conn);
-    QCOMPARE(service.isEnabled(), false);
+    QDeclarativeDebugTestService service("tst_QDeclarativeDebugService::status()");
+    QCOMPARE(service.status(), QDeclarativeDebugService::Unavailable);
 
-    QDeclarativeDebugTestClient client("tst_QDeclarativeDebugService::isEnabled()", m_conn);
-    client.setEnabled(true);
-    QDeclarativeDebugTest::waitForSignal(&service, SIGNAL(enabledStateChanged()));
-    QCOMPARE(service.isEnabled(), true);
+    {
+        QDeclarativeDebugTestClient client("tst_QDeclarativeDebugService::status()", m_conn);
+        QDeclarativeDebugTest::waitForSignal(&service, SIGNAL(statusHasChanged()));
+        QCOMPARE(service.status(), QDeclarativeDebugService::Enabled);
+    }
 
-    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugService: Conflicting plugin name \"tst_QDeclarativeDebugService::isEnabled()\" ");
-    QDeclarativeDebugService duplicate("tst_QDeclarativeDebugService::isEnabled()", m_conn);
-    QCOMPARE(duplicate.isEnabled(), false);
-}
-
-void tst_QDeclarativeDebugService::enabledChanged()
-{
-    QDeclarativeDebugTestService service("tst_QDeclarativeDebugService::enabledChanged()");
-    QDeclarativeDebugTestClient client("tst_QDeclarativeDebugService::enabledChanged()", m_conn);
+    QDeclarativeDebugTest::waitForSignal(&service, SIGNAL(statusHasChanged()));
+    QCOMPARE(service.status(), QDeclarativeDebugService::Unavailable);
 
-    QCOMPARE(service.enabled, false);
+    QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugService: Conflicting plugin name \"tst_QDeclarativeDebugService::status()\" ");
 
-    client.setEnabled(true);
-    QDeclarativeDebugTest::waitForSignal(&service, SIGNAL(enabledStateChanged()));
-    QCOMPARE(service.enabled, true);
+    QDeclarativeDebugService duplicate("tst_QDeclarativeDebugService::status()");
+    QCOMPARE(duplicate.status(), QDeclarativeDebugService::NotConnected);
 }
 
 void tst_QDeclarativeDebugService::sendMessage()
@@ -131,6 +123,11 @@ void tst_QDeclarativeDebugService::sendMessage()
 
     QByteArray msg = "hello!";
 
+    if (service.status() != QDeclarativeDebugService::Enabled)
+        QDeclarativeDebugTest::waitForSignal(&service, SIGNAL(statusHasChanged()));
+    if (client.status() != QDeclarativeDebugClient::Enabled)
+        QDeclarativeDebugTest::waitForSignal(&client, SIGNAL(statusHasChanged()));
+
     client.sendMessage(msg);
     QByteArray resp = client.waitForResponse();
     QCOMPARE(resp, msg);
diff --git a/tests/auto/declarative/shared/debugutil.cpp b/tests/auto/declarative/shared/debugutil.cpp
index c0c3eca..5f68e44 100644
--- a/tests/auto/declarative/shared/debugutil.cpp
+++ b/tests/auto/declarative/shared/debugutil.cpp
@@ -60,7 +60,7 @@ bool QDeclarativeDebugTest::waitForSignal(QObject *receiver, const char *member,
 }
 
 QDeclarativeDebugTestService::QDeclarativeDebugTestService(const QString &s, QObject *parent)
-    : QDeclarativeDebugService(s, parent), enabled(false)
+    : QDeclarativeDebugService(s, parent)
 {
 }
 
@@ -69,10 +69,9 @@ void QDeclarativeDebugTestService::messageReceived(const QByteArray &ba)
     sendMessage(ba);
 }
 
-void QDeclarativeDebugTestService::enabledChanged(bool e)
+void QDeclarativeDebugTestService::statusChanged(Status)
 {
-    enabled = e;
-    emit enabledStateChanged();
+    emit statusHasChanged();
 }
 
 
@@ -92,9 +91,13 @@ QByteArray QDeclarativeDebugTestClient::waitForResponse()
     return lastMsg;
 }
 
+void QDeclarativeDebugTestClient::statusChanged(Status status)
+{
+    emit statusHasChanged();
+}
+
 void QDeclarativeDebugTestClient::messageReceived(const QByteArray &ba)
 {
     lastMsg = ba;
     emit serverMessage(ba);
 }
-
diff --git a/tests/auto/declarative/shared/debugutil_p.h b/tests/auto/declarative/shared/debugutil_p.h
index e6bb7ad..434e053 100644
--- a/tests/auto/declarative/shared/debugutil_p.h
+++ b/tests/auto/declarative/shared/debugutil_p.h
@@ -62,15 +62,13 @@ class QDeclarativeDebugTestService : public QDeclarativeDebugService
     Q_OBJECT
 public:
     QDeclarativeDebugTestService(const QString &s, QObject *parent = 0);
-    bool enabled;
 
 signals:
-    void enabledStateChanged();
+    void statusHasChanged();
 
 protected:
     virtual void messageReceived(const QByteArray &ba);
-
-    virtual void enabledChanged(bool e);
+    virtual void statusChanged(Status status);
 };
 
 class QDeclarativeDebugTestClient : public QDeclarativeDebugClient
@@ -82,9 +80,11 @@ public:
     QByteArray waitForResponse();
 
 signals:
+    void statusHasChanged();
     void serverMessage(const QByteArray &);
 
 protected:
+    virtual void statusChanged(Status status);
     virtual void messageReceived(const QByteArray &ba);
 
 private:
-- 
cgit v0.12