QmlDebug: Move QmlDebugConnection into own file

Change-Id: I761658de7c19754cfaadf8cbbad7596a9bcdfbf3
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Ulf Hermann
2017-09-28 11:32:42 +02:00
parent 451f4a1e73
commit 2f9d82791e
10 changed files with 530 additions and 441 deletions

View File

@@ -24,6 +24,7 @@
****************************************************************************/ ****************************************************************************/
#include "declarativetoolsclient.h" #include "declarativetoolsclient.h"
#include "qmldebugconnection.h"
#include <QMetaEnum> #include <QMetaEnum>
#include <QStringList> #include <QStringList>

View File

@@ -19,7 +19,8 @@ HEADERS += \
$$PWD/declarativetoolsclient.h \ $$PWD/declarativetoolsclient.h \
$$PWD/qmltoolsclient.h \ $$PWD/qmltoolsclient.h \
$$PWD/qmlenginecontrolclient.h \ $$PWD/qmlenginecontrolclient.h \
$$PWD/qmldebugcommandlinearguments.h $$PWD/qmldebugcommandlinearguments.h \
$$PWD/qmldebugconnection.h
SOURCES += \ SOURCES += \
$$PWD/qmldebugclient.cpp \ $$PWD/qmldebugclient.cpp \
@@ -31,5 +32,5 @@ SOURCES += \
$$PWD/declarativetoolsclient.cpp \ $$PWD/declarativetoolsclient.cpp \
$$PWD/qmltoolsclient.cpp \ $$PWD/qmltoolsclient.cpp \
$$PWD/declarativeenginedebugclient.cpp \ $$PWD/declarativeenginedebugclient.cpp \
$$PWD/qmlenginecontrolclient.cpp $$PWD/qmlenginecontrolclient.cpp \
$$PWD/qmldebugconnection.cpp

View File

@@ -27,6 +27,8 @@ Project {
"qmldebugclient.cpp", "qmldebugclient.cpp",
"qmldebugclient.h", "qmldebugclient.h",
"qmldebugcommandlinearguments.h", "qmldebugcommandlinearguments.h",
"qmldebugconnection.cpp",
"qmldebugconnection.h",
"qmldebugconstants.h", "qmldebugconstants.h",
"qmlenginecontrolclient.cpp", "qmlenginecontrolclient.cpp",
"qmlenginecontrolclient.h", "qmlenginecontrolclient.h",

View File

@@ -24,25 +24,16 @@
****************************************************************************/ ****************************************************************************/
#include "qmldebugclient.h" #include "qmldebugclient.h"
#include "qmldebugconnection.h"
#include "qpacketprotocol.h" #include "qpacketprotocol.h"
#include <qdebug.h> #include <qdebug.h>
#include <qstringlist.h> #include <qstringlist.h>
#include <qnetworkproxy.h>
#include <qlocalserver.h>
#include <qlocalsocket.h>
#include <QPointer> #include <QPointer>
namespace QmlDebug { namespace QmlDebug {
const int protocolVersion = 1;
const int minimumDataStreamVersion = QDataStream::Qt_4_7;
const QString serverId = QLatin1String("QDeclarativeDebugServer");
const QString clientId = QLatin1String("QDeclarativeDebugClient");
class QmlDebugClientPrivate class QmlDebugClientPrivate
{ {
public: public:
@@ -52,387 +43,6 @@ public:
QPointer<QmlDebugConnection> connection; QPointer<QmlDebugConnection> connection;
}; };
class QmlDebugConnectionPrivate
{
public:
QmlDebugConnectionPrivate();
QPacketProtocol *protocol;
QLocalServer *server;
QIODevice *device; // Currently a QTcpSocket or a QLocalSocket
bool gotHello;
QHash <QString, float> serverPlugins;
QHash<QString, QmlDebugClient *> plugins;
int currentDataStreamVersion;
int maximumDataStreamVersion;
void advertisePlugins();
void flush();
};
static QString socketStateToString(QAbstractSocket::SocketState state)
{
QString stateString;
QDebug(&stateString) << state;
return QmlDebugConnection::tr("Socket state changed to %1").arg(stateString);
}
static QString socketErrorToString(QAbstractSocket::SocketError error)
{
QString errorString;
QDebug(&errorString) << error;
return QmlDebugConnection::tr("Error: %1").arg(errorString);
}
QmlDebugConnectionPrivate::QmlDebugConnectionPrivate() :
protocol(0), server(0), device(0), gotHello(false),
currentDataStreamVersion(minimumDataStreamVersion),
maximumDataStreamVersion(QDataStream::Qt_DefaultCompiledVersion)
{
}
void QmlDebugConnectionPrivate::advertisePlugins()
{
if (!gotHello)
return;
QPacket pack(currentDataStreamVersion);
pack << serverId << 1 << plugins.keys();
protocol->send(pack.data());
flush();
}
void QmlDebugConnection::socketConnected()
{
Q_D(QmlDebugConnection);
QPacket pack(d->currentDataStreamVersion);
pack << serverId << 0 << protocolVersion << d->plugins.keys() << d->maximumDataStreamVersion
<< true; // We accept multiple messages per packet
d->protocol->send(pack.data());
d->flush();
}
void QmlDebugConnection::socketDisconnected()
{
Q_D(QmlDebugConnection);
if (d->gotHello) {
d->gotHello = false;
QHash<QString, QmlDebugClient*>::iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter)
iter.value()->stateChanged(QmlDebugClient::NotConnected);
emit disconnected();
} else if (d->device) {
emit connectionFailed();
}
if (d->protocol) {
d->protocol->disconnect();
d->protocol->deleteLater();
d->protocol = 0;
}
if (d->device) {
// Don't allow any "connected()" or "disconnected()" signals to be triggered anymore.
// As the protocol is gone this would lead to crashes.
d->device->disconnect();
// Don't immediately delete it as it may do some cleanup on returning from a signal.
d->device->deleteLater();
d->device = 0;
}
}
void QmlDebugConnection::protocolReadyRead()
{
Q_D(QmlDebugConnection);
if (!d->gotHello) {
QPacket pack(d->currentDataStreamVersion, d->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) {
QStringList pluginNames;
QList<float> pluginVersions;
pack >> pluginNames;
if (!pack.atEnd())
pack >> pluginVersions;
const int pluginNamesSize = pluginNames.size();
const int pluginVersionsSize = pluginVersions.size();
for (int i = 0; i < pluginNamesSize; ++i) {
float pluginVersion = 1.0;
if (i < pluginVersionsSize)
pluginVersion = pluginVersions.at(i);
d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
}
if (!pack.atEnd()) {
pack >> d->currentDataStreamVersion;
if (d->currentDataStreamVersion > d->maximumDataStreamVersion)
qWarning() << "Server returned invalid data stream version!";
}
validHello = true;
}
}
}
if (!validHello) {
qWarning("QML Debug Client: Invalid hello message");
close();
return;
}
d->gotHello = true;
QHash<QString, QmlDebugClient *>::Iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter) {
QmlDebugClient::State newState = QmlDebugClient::Unavailable;
if (d->serverPlugins.contains(iter.key()))
newState = QmlDebugClient::Enabled;
iter.value()->stateChanged(newState);
}
emit connected();
}
while (d->protocol && d->protocol->packetsAvailable()) {
QPacket pack(d->currentDataStreamVersion, d->protocol->read());
QString name;
pack >> name;
if (name == clientId) {
int op = -1;
pack >> op;
if (op == 1) {
// Service Discovery
QHash<QString, float> oldServerPlugins = d->serverPlugins;
d->serverPlugins.clear();
QStringList pluginNames;
QList<float> pluginVersions;
pack >> pluginNames;
if (!pack.atEnd())
pack >> pluginVersions;
const int pluginNamesSize = pluginNames.size();
const int pluginVersionsSize = pluginVersions.size();
for (int i = 0; i < pluginNamesSize; ++i) {
float pluginVersion = 1.0;
if (i < pluginVersionsSize)
pluginVersion = pluginVersions.at(i);
d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
}
QHash<QString, QmlDebugClient *>::Iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter) {
const QString pluginName = iter.key();
QmlDebugClient::State newState = QmlDebugClient::Unavailable;
if (d->serverPlugins.contains(pluginName))
newState = QmlDebugClient::Enabled;
if (oldServerPlugins.contains(pluginName)
!= d->serverPlugins.contains(pluginName)) {
iter.value()->stateChanged(newState);
}
}
} else {
qWarning() << "QML Debug Client: Unknown control message id" << op;
}
} else {
QHash<QString, QmlDebugClient *>::Iterator iter = d->plugins.find(name);
if (iter == d->plugins.end()) {
qWarning() << "QML Debug Client: Message received for missing plugin" << name;
} else {
QmlDebugClient *client = *iter;
QByteArray message;
while (!pack.atEnd()) {
pack >> message;
client->messageReceived(message);
}
}
}
}
}
QmlDebugConnection::QmlDebugConnection(QObject *parent)
: QObject(parent), d_ptr(new QmlDebugConnectionPrivate)
{
}
QmlDebugConnection::~QmlDebugConnection()
{
socketDisconnected();
}
bool QmlDebugConnection::isConnected() const
{
Q_D(const QmlDebugConnection);
// gotHello can only be set if the connection is open.
return d->gotHello;
}
bool QmlDebugConnection::isConnecting() const
{
Q_D(const QmlDebugConnection);
return !d->gotHello && d->device;
}
void QmlDebugConnection::close()
{
Q_D(QmlDebugConnection);
if (d->device && d->device->isOpen())
d->device->close(); // will trigger disconnected() at some point.
}
QmlDebugClient *QmlDebugConnection::client(const QString &name) const
{
Q_D(const QmlDebugConnection);
return d->plugins.value(name, 0);
}
bool QmlDebugConnection::addClient(const QString &name, QmlDebugClient *client)
{
Q_D(QmlDebugConnection);
if (d->plugins.contains(name))
return false;
d->plugins.insert(name, client);
d->advertisePlugins();
return true;
}
bool QmlDebugConnection::removeClient(const QString &name)
{
Q_D(QmlDebugConnection);
if (!d->plugins.contains(name))
return false;
d->plugins.remove(name);
d->advertisePlugins();
return true;
}
float QmlDebugConnection::serviceVersion(const QString &serviceName) const
{
Q_D(const QmlDebugConnection);
return d->serverPlugins.value(serviceName, -1);
}
bool QmlDebugConnection::sendMessage(const QString &name, const QByteArray &message)
{
Q_D(QmlDebugConnection);
if (!d->gotHello || !d->serverPlugins.contains(name))
return false;
QPacket pack(d->currentDataStreamVersion);
pack << name << message;
d->protocol->send(pack.data());
d->flush();
return true;
}
void QmlDebugConnectionPrivate::flush()
{
if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(device))
socket->flush();
else if (QLocalSocket *socket = qobject_cast<QLocalSocket *>(device))
socket->flush();
}
void QmlDebugConnection::connectToHost(const QString &hostName, quint16 port)
{
Q_D(QmlDebugConnection);
socketDisconnected();
QTcpSocket *socket = new QTcpSocket(this);
socket->setProxy(QNetworkProxy::NoProxy);
d->device = socket;
d->protocol = new QPacketProtocol(socket, this);
QObject::connect(d->protocol, &QPacketProtocol::readyRead,
this, &QmlDebugConnection::protocolReadyRead);
connect(socket, &QAbstractSocket::stateChanged,
this, [this](QAbstractSocket::SocketState state) {
emit logStateChange(socketStateToString(state));
});
connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>
(&QAbstractSocket::error), this, [this](QAbstractSocket::SocketError error) {
emit logError(socketErrorToString(error));
socketDisconnected();
});
connect(socket, &QAbstractSocket::connected, this, &QmlDebugConnection::socketConnected);
connect(socket, &QAbstractSocket::disconnected, this, &QmlDebugConnection::socketDisconnected);
socket->connectToHost(hostName.isEmpty() ? QString("localhost") : hostName, port);
}
void QmlDebugConnection::startLocalServer(const QString &fileName)
{
Q_D(QmlDebugConnection);
if (d->gotHello)
close();
if (d->server)
d->server->deleteLater();
d->server = new QLocalServer(this);
// QueuedConnection so that waitForNewConnection() returns true.
connect(d->server, &QLocalServer::newConnection,
this, &QmlDebugConnection::newConnection, Qt::QueuedConnection);
if (!d->server->listen(fileName))
emit connectionFailed();
}
void QmlDebugConnection::newConnection()
{
Q_D(QmlDebugConnection);
delete d->device;
QLocalSocket *socket = d->server->nextPendingConnection();
d->server->close();
d->device = socket;
delete d->protocol;
d->protocol = new QPacketProtocol(socket, this);
QObject::connect(d->protocol, &QPacketProtocol::readyRead,
this, &QmlDebugConnection::protocolReadyRead);
connect(socket, &QLocalSocket::disconnected, this, &QmlDebugConnection::socketDisconnected);
connect(socket, static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>
(&QLocalSocket::error), this, [this](QLocalSocket::LocalSocketError error) {
emit logError(socketErrorToString(static_cast<QAbstractSocket::SocketError>(error)));
socketDisconnected();
});
connect(socket, &QLocalSocket::stateChanged,
this, [this](QLocalSocket::LocalSocketState state) {
logStateChange(socketStateToString(static_cast<QAbstractSocket::SocketState>(state)));
});
socketConnected();
}
int QmlDebugConnection::currentDataStreamVersion() const
{
Q_D(const QmlDebugConnection);
return d->currentDataStreamVersion;
}
void QmlDebugConnection::setMaximumDataStreamVersion(int maximumVersion)
{
Q_D(QmlDebugConnection);
d->maximumDataStreamVersion = maximumVersion;
}
QAbstractSocket::SocketState QmlDebugConnection::socketState() const
{
Q_D(const QmlDebugConnection);
if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(d->device))
return socket->state();
else if (QLocalSocket *socket = qobject_cast<QLocalSocket *>(d->device))
return static_cast<QAbstractSocket::SocketState>(socket->state());
else
return QAbstractSocket::UnconnectedState;
}
QmlDebugClientPrivate::QmlDebugClientPrivate() QmlDebugClientPrivate::QmlDebugClientPrivate()
{ {
} }
@@ -497,7 +107,8 @@ QmlDebugConnection *QmlDebugClient::connection() const
int QmlDebugClient::dataStreamVersion() const int QmlDebugClient::dataStreamVersion() const
{ {
Q_D(const QmlDebugClient); Q_D(const QmlDebugClient);
return (d->connection ? d->connection->currentDataStreamVersion() : minimumDataStreamVersion); return (d->connection ? d->connection->currentDataStreamVersion()
: QmlDebugConnection::minimumDataStreamVersion());
} }
void QmlDebugClient::sendMessage(const QByteArray &message) void QmlDebugClient::sendMessage(const QByteArray &message)

View File

@@ -32,52 +32,7 @@
namespace QmlDebug { namespace QmlDebug {
class QmlDebugClient; class QmlDebugConnection;
class QmlDebugConnectionPrivate;
class QMLDEBUG_EXPORT QmlDebugConnection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(QmlDebugConnection)
Q_DECLARE_PRIVATE(QmlDebugConnection)
public:
QmlDebugConnection(QObject * = 0);
~QmlDebugConnection();
void connectToHost(const QString &hostName, quint16 port);
void startLocalServer(const QString &fileName);
QAbstractSocket::SocketState socketState() const;
int currentDataStreamVersion() const;
void setMaximumDataStreamVersion(int maximumVersion);
bool isConnected() const;
bool isConnecting() const;
void close();
QmlDebugClient *client(const QString &name) const;
bool addClient(const QString &name, QmlDebugClient *client);
bool removeClient(const QString &name);
float serviceVersion(const QString &serviceName) const;
bool sendMessage(const QString &name, const QByteArray &message);
signals:
void connected();
void disconnected();
void connectionFailed();
void logError(const QString &error);
void logStateChange(const QString &state);
private:
void newConnection();
void socketConnected();
void socketDisconnected();
void protocolReadyRead();
QScopedPointer<QmlDebugConnectionPrivate> d_ptr;
};
class QmlDebugClientPrivate; class QmlDebugClientPrivate;
class QMLDEBUG_EXPORT QmlDebugClient : public QObject class QMLDEBUG_EXPORT QmlDebugClient : public QObject
{ {

View File

@@ -0,0 +1,431 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "qmldebugconnection.h"
#include "qmldebugclient.h"
#include "qpacketprotocol.h"
#include <utils/temporaryfile.h>
#include <QLocalServer>
#include <QLocalSocket>
#include <QNetworkProxy>
#include <QTcpServer>
#include <QTcpSocket>
namespace QmlDebug {
const int protocolVersion = 1;
const QString serverId = QLatin1String("QDeclarativeDebugServer");
const QString clientId = QLatin1String("QDeclarativeDebugClient");
class QmlDebugConnectionPrivate
{
public:
QmlDebugConnectionPrivate();
QPacketProtocol *protocol;
QLocalServer *server;
QIODevice *device; // Currently a QTcpSocket or a QLocalSocket
bool gotHello;
QHash <QString, float> serverPlugins;
QHash<QString, QmlDebugClient *> plugins;
int currentDataStreamVersion;
int maximumDataStreamVersion;
void advertisePlugins();
void flush();
};
static QString socketStateToString(QAbstractSocket::SocketState state)
{
QString stateString;
QDebug(&stateString) << state;
return QmlDebugConnection::tr("Socket state changed to %1").arg(stateString);
}
static QString socketErrorToString(QAbstractSocket::SocketError error)
{
QString errorString;
QDebug(&errorString) << error;
return QmlDebugConnection::tr("Error: %1").arg(errorString);
}
QmlDebugConnectionPrivate::QmlDebugConnectionPrivate() :
protocol(0), server(0), device(0), gotHello(false),
currentDataStreamVersion(QmlDebugConnection::minimumDataStreamVersion()),
maximumDataStreamVersion(QDataStream::Qt_DefaultCompiledVersion)
{
}
void QmlDebugConnectionPrivate::advertisePlugins()
{
if (!gotHello)
return;
QPacket pack(currentDataStreamVersion);
pack << serverId << 1 << plugins.keys();
protocol->send(pack.data());
flush();
}
void QmlDebugConnection::socketConnected()
{
Q_D(QmlDebugConnection);
QPacket pack(d->currentDataStreamVersion);
pack << serverId << 0 << protocolVersion << d->plugins.keys() << d->maximumDataStreamVersion
<< true; // We accept multiple messages per packet
d->protocol->send(pack.data());
d->flush();
}
void QmlDebugConnection::socketDisconnected()
{
Q_D(QmlDebugConnection);
if (d->gotHello) {
d->gotHello = false;
QHash<QString, QmlDebugClient*>::iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter)
iter.value()->stateChanged(QmlDebugClient::NotConnected);
emit disconnected();
} else if (d->device) {
emit connectionFailed();
}
if (d->protocol) {
d->protocol->disconnect();
d->protocol->deleteLater();
d->protocol = 0;
}
if (d->device) {
// Don't allow any "connected()" or "disconnected()" signals to be triggered anymore.
// As the protocol is gone this would lead to crashes.
d->device->disconnect();
// Don't immediately delete it as it may do some cleanup on returning from a signal.
d->device->deleteLater();
d->device = 0;
}
}
void QmlDebugConnection::protocolReadyRead()
{
Q_D(QmlDebugConnection);
if (!d->gotHello) {
QPacket pack(d->currentDataStreamVersion, d->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) {
QStringList pluginNames;
QList<float> pluginVersions;
pack >> pluginNames;
if (!pack.atEnd())
pack >> pluginVersions;
const int pluginNamesSize = pluginNames.size();
const int pluginVersionsSize = pluginVersions.size();
for (int i = 0; i < pluginNamesSize; ++i) {
float pluginVersion = 1.0;
if (i < pluginVersionsSize)
pluginVersion = pluginVersions.at(i);
d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
}
if (!pack.atEnd()) {
pack >> d->currentDataStreamVersion;
if (d->currentDataStreamVersion > d->maximumDataStreamVersion)
qWarning() << "Server returned invalid data stream version!";
}
validHello = true;
}
}
}
if (!validHello) {
qWarning("QML Debug Client: Invalid hello message");
close();
return;
}
d->gotHello = true;
QHash<QString, QmlDebugClient *>::Iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter) {
QmlDebugClient::State newState = QmlDebugClient::Unavailable;
if (d->serverPlugins.contains(iter.key()))
newState = QmlDebugClient::Enabled;
iter.value()->stateChanged(newState);
}
emit connected();
}
while (d->protocol && d->protocol->packetsAvailable()) {
QPacket pack(d->currentDataStreamVersion, d->protocol->read());
QString name;
pack >> name;
if (name == clientId) {
int op = -1;
pack >> op;
if (op == 1) {
// Service Discovery
QHash<QString, float> oldServerPlugins = d->serverPlugins;
d->serverPlugins.clear();
QStringList pluginNames;
QList<float> pluginVersions;
pack >> pluginNames;
if (!pack.atEnd())
pack >> pluginVersions;
const int pluginNamesSize = pluginNames.size();
const int pluginVersionsSize = pluginVersions.size();
for (int i = 0; i < pluginNamesSize; ++i) {
float pluginVersion = 1.0;
if (i < pluginVersionsSize)
pluginVersion = pluginVersions.at(i);
d->serverPlugins.insert(pluginNames.at(i), pluginVersion);
}
QHash<QString, QmlDebugClient *>::Iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter) {
const QString pluginName = iter.key();
QmlDebugClient::State newState = QmlDebugClient::Unavailable;
if (d->serverPlugins.contains(pluginName))
newState = QmlDebugClient::Enabled;
if (oldServerPlugins.contains(pluginName)
!= d->serverPlugins.contains(pluginName)) {
iter.value()->stateChanged(newState);
}
}
} else {
qWarning() << "QML Debug Client: Unknown control message id" << op;
}
} else {
QHash<QString, QmlDebugClient *>::Iterator iter = d->plugins.find(name);
if (iter == d->plugins.end()) {
qWarning() << "QML Debug Client: Message received for missing plugin" << name;
} else {
QmlDebugClient *client = *iter;
QByteArray message;
while (!pack.atEnd()) {
pack >> message;
client->messageReceived(message);
}
}
}
}
}
QmlDebugConnection::QmlDebugConnection(QObject *parent)
: QObject(parent), d_ptr(new QmlDebugConnectionPrivate)
{
}
QmlDebugConnection::~QmlDebugConnection()
{
socketDisconnected();
}
bool QmlDebugConnection::isConnected() const
{
Q_D(const QmlDebugConnection);
// gotHello can only be set if the connection is open.
return d->gotHello;
}
bool QmlDebugConnection::isConnecting() const
{
Q_D(const QmlDebugConnection);
return !d->gotHello && d->device;
}
void QmlDebugConnection::close()
{
Q_D(QmlDebugConnection);
if (d->device && d->device->isOpen())
d->device->close(); // will trigger disconnected() at some point.
}
QmlDebugClient *QmlDebugConnection::client(const QString &name) const
{
Q_D(const QmlDebugConnection);
return d->plugins.value(name, 0);
}
bool QmlDebugConnection::addClient(const QString &name, QmlDebugClient *client)
{
Q_D(QmlDebugConnection);
if (d->plugins.contains(name))
return false;
d->plugins.insert(name, client);
d->advertisePlugins();
return true;
}
bool QmlDebugConnection::removeClient(const QString &name)
{
Q_D(QmlDebugConnection);
if (!d->plugins.contains(name))
return false;
d->plugins.remove(name);
d->advertisePlugins();
return true;
}
float QmlDebugConnection::serviceVersion(const QString &serviceName) const
{
Q_D(const QmlDebugConnection);
return d->serverPlugins.value(serviceName, -1);
}
bool QmlDebugConnection::sendMessage(const QString &name, const QByteArray &message)
{
Q_D(QmlDebugConnection);
if (!d->gotHello || !d->serverPlugins.contains(name))
return false;
QPacket pack(d->currentDataStreamVersion);
pack << name << message;
d->protocol->send(pack.data());
d->flush();
return true;
}
int QmlDebugConnection::minimumDataStreamVersion()
{
return QDataStream::Qt_4_7;
}
void QmlDebugConnectionPrivate::flush()
{
if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(device))
socket->flush();
else if (QLocalSocket *socket = qobject_cast<QLocalSocket *>(device))
socket->flush();
}
void QmlDebugConnection::connectToHost(const QString &hostName, quint16 port)
{
Q_D(QmlDebugConnection);
socketDisconnected();
QTcpSocket *socket = new QTcpSocket(this);
socket->setProxy(QNetworkProxy::NoProxy);
d->device = socket;
d->protocol = new QPacketProtocol(socket, this);
QObject::connect(d->protocol, &QPacketProtocol::readyRead,
this, &QmlDebugConnection::protocolReadyRead);
connect(socket, &QAbstractSocket::stateChanged,
this, [this](QAbstractSocket::SocketState state) {
emit logStateChange(socketStateToString(state));
});
connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>
(&QAbstractSocket::error), this, [this](QAbstractSocket::SocketError error) {
emit logError(socketErrorToString(error));
socketDisconnected();
});
connect(socket, &QAbstractSocket::connected, this, &QmlDebugConnection::socketConnected);
connect(socket, &QAbstractSocket::disconnected, this, &QmlDebugConnection::socketDisconnected);
socket->connectToHost(hostName.isEmpty() ? QString("localhost") : hostName, port);
}
void QmlDebugConnection::startLocalServer(const QString &fileName)
{
Q_D(QmlDebugConnection);
if (d->gotHello)
close();
if (d->server)
d->server->deleteLater();
d->server = new QLocalServer(this);
// QueuedConnection so that waitForNewConnection() returns true.
connect(d->server, &QLocalServer::newConnection,
this, &QmlDebugConnection::newConnection, Qt::QueuedConnection);
if (!d->server->listen(fileName))
emit connectionFailed();
}
void QmlDebugConnection::newConnection()
{
Q_D(QmlDebugConnection);
delete d->device;
QLocalSocket *socket = d->server->nextPendingConnection();
d->server->close();
d->device = socket;
delete d->protocol;
d->protocol = new QPacketProtocol(socket, this);
QObject::connect(d->protocol, &QPacketProtocol::readyRead,
this, &QmlDebugConnection::protocolReadyRead);
connect(socket, &QLocalSocket::disconnected, this, &QmlDebugConnection::socketDisconnected);
connect(socket, static_cast<void (QLocalSocket::*)(QLocalSocket::LocalSocketError)>
(&QLocalSocket::error), this, [this](QLocalSocket::LocalSocketError error) {
emit logError(socketErrorToString(static_cast<QAbstractSocket::SocketError>(error)));
socketDisconnected();
});
connect(socket, &QLocalSocket::stateChanged,
this, [this](QLocalSocket::LocalSocketState state) {
logStateChange(socketStateToString(static_cast<QAbstractSocket::SocketState>(state)));
});
socketConnected();
}
int QmlDebugConnection::currentDataStreamVersion() const
{
Q_D(const QmlDebugConnection);
return d->currentDataStreamVersion;
}
void QmlDebugConnection::setMaximumDataStreamVersion(int maximumVersion)
{
Q_D(QmlDebugConnection);
d->maximumDataStreamVersion = maximumVersion;
}
QAbstractSocket::SocketState QmlDebugConnection::socketState() const
{
Q_D(const QmlDebugConnection);
if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(d->device))
return socket->state();
else if (QLocalSocket *socket = qobject_cast<QLocalSocket *>(d->device))
return static_cast<QAbstractSocket::SocketState>(socket->state());
else
return QAbstractSocket::UnconnectedState;
}
} // namespace QmlDebug

View File

@@ -0,0 +1,84 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "qmldebug_global.h"
#include <QObject>
#include <QUrl>
#include <QAbstractSocket>
namespace QmlDebug {
class QmlDebugClient;
class QmlDebugConnectionPrivate;
class QMLDEBUG_EXPORT QmlDebugConnection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(QmlDebugConnection)
Q_DECLARE_PRIVATE(QmlDebugConnection)
public:
QmlDebugConnection(QObject * = 0);
~QmlDebugConnection();
void connectToHost(const QString &hostName, quint16 port);
void startLocalServer(const QString &fileName);
QAbstractSocket::SocketState socketState() const;
int currentDataStreamVersion() const;
void setMaximumDataStreamVersion(int maximumVersion);
bool isConnected() const;
bool isConnecting() const;
void close();
QmlDebugClient *client(const QString &name) const;
bool addClient(const QString &name, QmlDebugClient *client);
bool removeClient(const QString &name);
float serviceVersion(const QString &serviceName) const;
bool sendMessage(const QString &name, const QByteArray &message);
static int minimumDataStreamVersion();
signals:
void connected();
void disconnected();
void connectionFailed();
void logError(const QString &error);
void logStateChange(const QString &state);
private:
void newConnection();
void socketConnected();
void socketDisconnected();
void protocolReadyRead();
QScopedPointer<QmlDebugConnectionPrivate> d_ptr;
};
} // namespace QmlDebug

View File

@@ -24,6 +24,7 @@
****************************************************************************/ ****************************************************************************/
#include "qmltoolsclient.h" #include "qmltoolsclient.h"
#include "qmldebugconnection.h"
#include <qmldebug/qpacketprotocol.h> #include <qmldebug/qpacketprotocol.h>
#include <QStringList> #include <QStringList>

View File

@@ -52,6 +52,7 @@
#include <qmljseditor/qmljseditorconstants.h> #include <qmljseditor/qmljseditorconstants.h>
#include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmldebug/qmldebugconnection.h>
#include <qmldebug/qpacketprotocol.h> #include <qmldebug/qpacketprotocol.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>

View File

@@ -28,6 +28,8 @@
#include "qmlprofilermodelmanager.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilerstatemanager.h" #include "qmlprofilerstatemanager.h"
#include <qmldebug/qmldebugconnection.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <projectexplorer/runnables.h> #include <projectexplorer/runnables.h>