diff options
author | Christiaan Janssen <christiaan.janssen@digia.com> | 2013-06-06 16:14:37 +0200 |
---|---|---|
committer | Kai Koehne <kai.koehne@digia.com> | 2013-06-11 14:42:50 +0300 |
commit | 89f7206fea7d18d621779e0aba30d390f196e374 (patch) | |
tree | 9bf518a68c301fc945884bc154883fce018bb866 | |
parent | e96414023748a03f63a88886b163177a7a862946 (diff) | |
download | qt-creator-89f7206fea7d18d621779e0aba30d390f196e374.tar.gz |
Import QmlDebug library
Change-Id: Ia714aa86537aa0bdabc098cb4bee3d08abe93723
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
33 files changed, 4637 insertions, 0 deletions
diff --git a/libs/qmldebug/baseenginedebugclient.cpp b/libs/qmldebug/baseenginedebugclient.cpp new file mode 100644 index 0000000000..6e47669214 --- /dev/null +++ b/libs/qmldebug/baseenginedebugclient.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "baseenginedebugclient.h" +#include "qmldebugconstants.h" + +namespace QmlDebug { + +struct QmlObjectData { + QUrl url; + int lineNumber; + int columnNumber; + QString idString; + QString objectName; + QString objectType; + int objectId; + int contextId; +}; + +QDataStream &operator>>(QDataStream &ds, QmlObjectData &data) +{ + ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString + >> data.objectName >> data.objectType >> data.objectId >> data.contextId; + return ds; +} + +struct QmlObjectProperty { + enum Type { Unknown, Basic, Object, List, SignalProperty, Variant }; + Type type; + QString name; + QVariant value; + QString valueTypeName; + QString binding; + bool hasNotifySignal; +}; + +QDataStream &operator>>(QDataStream &ds, QmlObjectProperty &data) +{ + int type; + ds >> type >> data.name >> data.value >> data.valueTypeName + >> data.binding >> data.hasNotifySignal; + data.type = (QmlObjectProperty::Type)type; + return ds; +} + +void BaseEngineDebugClient::decode(QDataStream &ds, + ObjectReference &o, + bool simple) +{ + QmlObjectData data; + ds >> data; + int parentId = -1; + // qt > 4.8.3 + if (objectName() != QLatin1String(Constants::QDECLARATIVE_ENGINE)) + ds >> parentId; + o.m_debugId = data.objectId; + o.m_className = data.objectType; + o.m_idString = data.idString; + o.m_name = data.objectName; + o.m_source.m_url = data.url; + o.m_source.m_lineNumber = data.lineNumber; + o.m_source.m_columnNumber = data.columnNumber; + o.m_contextDebugId = data.contextId; + o.m_needsMoreData = simple; + o.m_parentId = parentId; + + if (simple) + return; + + int childCount; + bool recur; + ds >> childCount >> recur; + + for (int ii = 0; ii < childCount; ++ii) { + o.m_children.append(ObjectReference()); + decode(ds, o.m_children.last(), !recur); + } + + int propCount; + ds >> propCount; + + for (int ii = 0; ii < propCount; ++ii) { + QmlObjectProperty data; + ds >> data; + PropertyReference prop; + prop.m_objectDebugId = o.m_debugId; + prop.m_name = data.name; + prop.m_binding = data.binding; + prop.m_hasNotifySignal = data.hasNotifySignal; + prop.m_valueTypeName = data.valueTypeName; + switch (data.type) { + case QmlObjectProperty::Basic: + case QmlObjectProperty::List: + case QmlObjectProperty::SignalProperty: + case QmlObjectProperty::Variant: + { + prop.m_value = data.value; + break; + } + case QmlObjectProperty::Object: + { + ObjectReference obj; + obj.m_debugId = prop.m_value.toInt(); + prop.m_value = qVariantFromValue(obj); + break; + } + case QmlObjectProperty::Unknown: + break; + } + o.m_properties << prop; + } +} + +void BaseEngineDebugClient::decode(QDataStream &ds, + QVariantList &o, + bool simple) +{ + int count; + ds >> count; + for (int i = 0; i < count; i++) { + ObjectReference obj; + decode(ds, obj, simple); + o << QVariant::fromValue(obj); + } +} + +void BaseEngineDebugClient::decode(QDataStream &ds, + ContextReference &c) +{ + ds >> c.m_name >> c.m_debugId; + + int contextCount; + ds >> contextCount; + + for (int ii = 0; ii < contextCount; ++ii) { + c.m_contexts.append(ContextReference()); + decode(ds, c.m_contexts.last()); + } + + int objectCount; + ds >> objectCount; + + for (int ii = 0; ii < objectCount; ++ii) { + ObjectReference obj; + decode(ds, obj, true); + obj.m_contextDebugId = c.m_debugId; + c.m_objects << obj; + } +} + +void BaseEngineDebugClient::statusChanged(ClientStatus status) +{ + emit newStatus(status); +} + +void BaseEngineDebugClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + int queryId; + QByteArray type; + ds >> type >> queryId; + + if (type == "OBJECT_CREATED") { + int engineId; + int objectId; + int parentId; + ds >> engineId >> objectId >> parentId; + emit newObject(engineId, objectId, parentId); + } else if (type == "LIST_ENGINES_R") { + int count; + ds >> count; + QList<EngineReference> engines; + for (int ii = 0; ii < count; ++ii) { + EngineReference eng; + ds >> eng.m_name; + ds >> eng.m_debugId; + engines << eng; + } + emit result(queryId, QVariant::fromValue(engines), type); + } else if (type == "LIST_OBJECTS_R") { + ContextReference rootContext; + if (!ds.atEnd()) + decode(ds, rootContext); + emit result(queryId, QVariant::fromValue(rootContext), type); + } else if (type == "FETCH_OBJECT_R") { + ObjectReference object; + if (!ds.atEnd()) + decode(ds, object, false); + emit result(queryId, QVariant::fromValue(object), type); + } else if (type == "FETCH_OBJECTS_FOR_LOCATION_R") { + QVariantList objects; + if (!ds.atEnd()) + decode(ds, objects, false); + emit result(queryId, objects, type); + } else if (type == "EVAL_EXPRESSION_R") {; + QVariant exprResult; + ds >> exprResult; + emit result(queryId, exprResult, type); + } else if (type == "WATCH_PROPERTY_R" || + type == "WATCH_OBJECT_R" || + type == "WATCH_EXPR_OBJECT_R" || + type == "SET_BINDING_R" || + type == "RESET_BINDING_R" || + type == "SET_METHOD_BODY_R") { + bool valid; + ds >> valid; + emit result(queryId, valid, type); + } else if (type == "UPDATE_WATCH") { + int debugId; + QByteArray name; + QVariant value; + ds >> debugId >> name >> value; + emit valueChanged(debugId, name, value); + } +} + +BaseEngineDebugClient::BaseEngineDebugClient(const QString &clientName, + QmlDebugConnection *conn) + : QmlDebugClient(clientName, conn), + m_nextId(1) +{ + setObjectName(clientName); +} + +quint32 BaseEngineDebugClient::addWatch(const PropertyReference &property) +{ + quint32 id = 0; + if (status() == Enabled) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("WATCH_PROPERTY") << id << property.m_objectDebugId + << property.m_name.toUtf8(); + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::addWatch(const ContextReference &/*context*/, + const QString &/*id*/) +{ + qWarning("QmlEngineDebugClient::addWatch(): Not implemented"); + return 0; +} + +quint32 BaseEngineDebugClient::addWatch(const ObjectReference &object, + const QString &expr) +{ + quint32 id = 0; + if (status() == Enabled) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("WATCH_EXPR_OBJECT") << id << object.m_debugId << expr; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::addWatch(int objectDebugId) +{ + quint32 id = 0; + if (status() == Enabled) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("WATCH_OBJECT") << id << objectDebugId; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::addWatch(const FileReference &/*file*/) +{ + qWarning("QmlEngineDebugClient::addWatch(): Not implemented"); + return 0; +} + +void BaseEngineDebugClient::removeWatch(quint32 id) +{ + if (status() == Enabled) { + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("NO_WATCH") << id; + sendMessage(message); + } +} + +quint32 BaseEngineDebugClient::queryAvailableEngines() +{ + quint32 id = 0; + if (status() == Enabled) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("LIST_ENGINES") << id; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::queryRootContexts(const EngineReference &engine) +{ + quint32 id = 0; + if (status() == Enabled && engine.m_debugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("LIST_OBJECTS") << id << engine.m_debugId; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::queryObject(int objectId) +{ + quint32 id = 0; + if (status() == Enabled && objectId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("FETCH_OBJECT") << id << objectId << false << + true; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::queryObjectRecursive(int objectId) +{ + quint32 id = 0; + if (status() == Enabled && objectId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("FETCH_OBJECT") << id << objectId << true << + true; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::queryExpressionResult(int objectDebugId, + const QString &expr, + int engineId) +{ + quint32 id = 0; + if (status() == Enabled && objectDebugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr + << engineId; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::setBindingForObject( + int objectDebugId, + const QString &propertyName, + const QVariant &bindingExpression, + bool isLiteralValue, + QString source, int line) +{ + quint32 id = 0; + if (status() == Enabled && objectDebugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("SET_BINDING") << id << objectDebugId << propertyName + << bindingExpression << isLiteralValue << source << line; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::resetBindingForObject( + int objectDebugId, + const QString &propertyName) +{ + quint32 id = 0; + if (status() == Enabled && objectDebugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("RESET_BINDING") << id << objectDebugId << propertyName; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::setMethodBody( + int objectDebugId, const QString &methodName, + const QString &methodBody) +{ + quint32 id = 0; + if (status() == Enabled && objectDebugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("SET_METHOD_BODY") << id << objectDebugId + << methodName << methodBody; + sendMessage(message); + } + return id; +} + +quint32 BaseEngineDebugClient::queryObjectsForLocation( + const QString &fileName, int lineNumber, int columnNumber) +{ + quint32 id = 0; + if (status() == Enabled) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("FETCH_OBJECTS_FOR_LOCATION") << id << + fileName << lineNumber << columnNumber << false << + true; + sendMessage(message); + } + return id; +} + +} // namespace QmlDebug diff --git a/libs/qmldebug/baseenginedebugclient.h b/libs/qmldebug/baseenginedebugclient.h new file mode 100644 index 0000000000..b4a39090c4 --- /dev/null +++ b/libs/qmldebug/baseenginedebugclient.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef BASEENGINEDEBUGCLIENT_H +#define BASEENGINEDEBUGCLIENT_H + +#include "qmldebug_global.h" +#include "qmldebugclient.h" +#include <qurl.h> +#include <qvariant.h> + +namespace QmlDebug { + +class QmlDebugConnection; +class PropertyReference; +class ContextReference; +class ObjectReference; +class FileReference; +class EngineReference; + +class QMLDEBUG_EXPORT BaseEngineDebugClient : public QmlDebugClient +{ + Q_OBJECT +public: + BaseEngineDebugClient(const QString &clientName, + QmlDebugConnection *conn); + + quint32 addWatch(const PropertyReference &property); + quint32 addWatch(const ContextReference &context, const QString &id); + quint32 addWatch(const ObjectReference &object, const QString &expr); + quint32 addWatch(int objectDebugId); + quint32 addWatch(const FileReference &file); + + void removeWatch(quint32 watch); + + quint32 queryAvailableEngines(); + quint32 queryRootContexts(const EngineReference &context); + quint32 queryObject(int objectId); + quint32 queryObjectRecursive(int objectId); + quint32 queryExpressionResult(int objectDebugId, + const QString &expr, int engineId = -1); + virtual quint32 setBindingForObject(int objectDebugId, const QString &propertyName, + const QVariant &bindingExpression, + bool isLiteralValue, + QString source, int line); + virtual quint32 resetBindingForObject(int objectDebugId, + const QString &propertyName); + virtual quint32 setMethodBody(int objectDebugId, const QString &methodName, + const QString &methodBody); + + virtual quint32 queryObjectsForLocation(const QString &fileName, int lineNumber, + int columnNumber); + +signals: + void newStatus(QmlDebug::ClientStatus status); + void newObject(int engineId, int objectId, int parentId); + void valueChanged(int debugId, const QByteArray &name, + const QVariant &value); + void result(quint32 queryId, const QVariant &result, const QByteArray &type); + +protected: + virtual void statusChanged(ClientStatus status); + virtual void messageReceived(const QByteArray &); + + quint32 getId() { return m_nextId++; } + + void decode(QDataStream &d, ContextReference &context); + void decode(QDataStream &d, ObjectReference &object, bool simple); + void decode(QDataStream &d, QVariantList &object, bool simple); + +private: + quint32 m_nextId; +}; + +class FileReference +{ +public: + FileReference() : m_lineNumber(-1), m_columnNumber(-1) {} + FileReference(const QUrl &url, int line, int column) + : m_url(url), + m_lineNumber(line), + m_columnNumber(column) + { + } + + QUrl url() const { return m_url; } + int lineNumber() const { return m_lineNumber; } + int columnNumber() const { return m_columnNumber; } + +private: + friend class BaseEngineDebugClient; + QUrl m_url; + int m_lineNumber; + int m_columnNumber; +}; + +class EngineReference +{ +public: + EngineReference() : m_debugId(-1) {} + explicit EngineReference(int id) : m_debugId(id) {} + + int debugId() const { return m_debugId; } + QString name() const { return m_name; } + +private: + friend class BaseEngineDebugClient; + int m_debugId; + QString m_name; +}; + +class ObjectReference +{ +public: + ObjectReference() + : m_debugId(-1), m_parentId(-1), m_contextDebugId(-1), m_needsMoreData(false) + { + } + explicit ObjectReference(int id) + : m_debugId(id), m_parentId(-1), m_contextDebugId(-1), m_needsMoreData(false) + { + } + ObjectReference(int id, int parentId, const FileReference &source) + : m_debugId(id), m_parentId(parentId), m_source(source), + m_contextDebugId(-1), m_needsMoreData(false) + { + } + + int debugId() const { return m_debugId; } + int parentId() const { return m_parentId; } + QString className() const { return m_className; } + QString idString() const { return m_idString; } + QString name() const { return m_name; } + bool isValid() const { return m_debugId != -1; } + + FileReference source() const { return m_source; } + int contextDebugId() const { return m_contextDebugId; } + bool needsMoreData() const { return m_needsMoreData; } + + QList<PropertyReference> properties() const { return m_properties; } + QList<ObjectReference> children() const { return m_children; } + + int insertObjectInTree(const ObjectReference &obj) + { + for (int i = 0; i < m_children.count(); i++) { + if (m_children[i].debugId() == obj.debugId()) { + m_children.replace(i, obj); + return debugId(); + } else { + if (m_children[i].insertObjectInTree(obj)) + return debugId(); + } + } + return -1; + } + + bool operator ==(const ObjectReference &obj) const + { + return m_debugId == obj.debugId(); + } + +private: + friend class BaseEngineDebugClient; + int m_debugId; + int m_parentId; + QString m_className; + QString m_idString; + QString m_name; + FileReference m_source; + int m_contextDebugId; + bool m_needsMoreData; + QList<PropertyReference> m_properties; + QList<ObjectReference> m_children; +}; + +class ContextReference +{ +public: + ContextReference() : m_debugId(-1) {} + + int debugId() const { return m_debugId; } + QString name() const { return m_name; } + + QList<ObjectReference> objects() const { return m_objects; } + QList<ContextReference> contexts() const { return m_contexts; } + +private: + friend class BaseEngineDebugClient; + int m_debugId; + QString m_name; + QList<ObjectReference> m_objects; + QList<ContextReference> m_contexts; +}; + +class PropertyReference +{ +public: + PropertyReference() : m_objectDebugId(-1), m_hasNotifySignal(false) {} + + int debugId() const { return m_objectDebugId; } + QString name() const { return m_name; } + QVariant value() const { return m_value; } + QString valueTypeName() const { return m_valueTypeName; } + QString binding() const { return m_binding; } + bool hasNotifySignal() const { return m_hasNotifySignal; } + +private: + friend class BaseEngineDebugClient; + int m_objectDebugId; + QString m_name; + QVariant m_value; + QString m_valueTypeName; + QString m_binding; + bool m_hasNotifySignal; +}; + +} // namespace QmlDebug + +Q_DECLARE_METATYPE(QmlDebug::ObjectReference) +Q_DECLARE_METATYPE(QmlDebug::EngineReference) +Q_DECLARE_METATYPE(QList<QmlDebug::EngineReference>) +Q_DECLARE_METATYPE(QmlDebug::ContextReference) + +QT_BEGIN_NAMESPACE +inline QDebug operator<<(QDebug dbg, const QmlDebug::EngineReference &ref) { + dbg.nospace() << "(Engine " << ref.debugId() << "/" << ref.name() << ")"; + return dbg.space(); +} + +inline QDebug operator<<(QDebug dbg, const QmlDebug::ContextReference &ref) { + dbg.nospace() << "(Context " << ref.debugId() << "/" << ref.name() << ")"; + return dbg.space(); +} + +inline QDebug operator<<(QDebug dbg, const QmlDebug::ObjectReference &ref) { + dbg.nospace() << "(Object " << ref.debugId() << "/" + << (ref.idString().isEmpty() ? ref.idString() : ref.className()) << ")"; + return dbg.space(); +} +QT_END_NAMESPACE + +#endif // BASEENGINEDEBUGCLIENT_H diff --git a/libs/qmldebug/basetoolsclient.cpp b/libs/qmldebug/basetoolsclient.cpp new file mode 100644 index 0000000000..351a351e8a --- /dev/null +++ b/libs/qmldebug/basetoolsclient.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "basetoolsclient.h" + +namespace QmlDebug { + +BaseToolsClient::BaseToolsClient(QmlDebugConnection* client, QLatin1String clientName) + : QmlDebugClient(clientName, client) +{ + setObjectName(clientName); +} + +void BaseToolsClient::statusChanged(ClientStatus status) +{ + emit newStatus(status); +} + +void BaseToolsClient::recurseObjectIdList(const ObjectReference &ref, + QList<int> &debugIds, QList<QString> &objectIds) +{ + debugIds << ref.debugId(); + objectIds << ref.idString(); + foreach (const ObjectReference &child, ref.children()) + recurseObjectIdList(child, debugIds, objectIds); +} + +} // namespace QmlDebug diff --git a/libs/qmldebug/basetoolsclient.h b/libs/qmldebug/basetoolsclient.h new file mode 100644 index 0000000000..9342118f5a --- /dev/null +++ b/libs/qmldebug/basetoolsclient.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef BASETOOLSCLIENT_H +#define BASETOOLSCLIENT_H + +#include "qmldebugclient.h" +#include "baseenginedebugclient.h" + +namespace QmlDebug { + +class QMLDEBUG_EXPORT BaseToolsClient : public QmlDebugClient +{ + Q_OBJECT +public: + BaseToolsClient(QmlDebugConnection *client, QLatin1String clientName); + + virtual void setCurrentObjects(const QList<int> &debugIds) = 0; + virtual void reload(const QHash<QString, QByteArray> &changesHash) = 0; + virtual bool supportReload() const = 0; + virtual void setDesignModeBehavior(bool inDesignMode) = 0; + virtual void setAnimationSpeed(qreal slowDownFactor) = 0; + virtual void setAnimationPaused(bool paused) = 0; + virtual void changeToSelectTool() = 0; + virtual void changeToSelectMarqueeTool() = 0; + virtual void changeToZoomTool() = 0; + virtual void showAppOnTop(bool showOnTop) = 0; + + virtual void createQmlObject(const QString &qmlText, int parentDebugId, + const QStringList &imports, const QString &filename, + int order) = 0; + virtual void destroyQmlObject(int debugId) = 0; + virtual void reparentQmlObject(int debugId, int newParent) = 0; + + virtual void applyChangesToQmlFile() = 0; + virtual void applyChangesFromQmlFile() = 0; + + virtual QList<int> currentObjects() const = 0; + + // ### Qt 4.8: remove if we can have access to qdeclarativecontextdata or id's + virtual void setObjectIdList( + const QList<ObjectReference> &objectRoots) = 0; + + virtual void clearComponentCache() = 0; + +signals: + void newStatus(QmlDebug::ClientStatus status); + + void currentObjectsChanged(const QList<int> &debugIds); + void selectToolActivated(); + void selectMarqueeToolActivated(); + void zoomToolActivated(); + void animationSpeedChanged(qreal slowdownFactor); + void animationPausedChanged(bool paused); + void designModeBehaviorChanged(bool inDesignMode); + void showAppOnTopChanged(bool showAppOnTop); + void reloaded(); // the server has reloaded the document + void destroyedObject(int); + + void logActivity(QString client, QString message); + +protected: + void statusChanged(ClientStatus status); + + void recurseObjectIdList(const ObjectReference &ref, + QList<int> &debugIds, QList<QString> &objectIds); +protected: + enum LogDirection { + LogSend, + LogReceive + }; +}; + +} // namespace QmlDebug + +#endif // BASETOOLSCLIENT_H diff --git a/libs/qmldebug/declarativeenginedebugclient.cpp b/libs/qmldebug/declarativeenginedebugclient.cpp new file mode 100644 index 0000000000..662e32d03b --- /dev/null +++ b/libs/qmldebug/declarativeenginedebugclient.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "declarativeenginedebugclient.h" +#include "qmldebugconstants.h" + +namespace QmlDebug { + +DeclarativeEngineDebugClient::DeclarativeEngineDebugClient( + QmlDebugConnection *connection) + : BaseEngineDebugClient(QLatin1String(Constants::QDECLARATIVE_ENGINE), connection) +{ +} + +quint32 DeclarativeEngineDebugClient::setBindingForObject( + int objectDebugId, + const QString &propertyName, + const QVariant &bindingExpression, + bool isLiteralValue, + QString source, int line) +{ + quint32 id = 0; + if (status() == Enabled && objectDebugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("SET_BINDING") << objectDebugId << propertyName + << bindingExpression << isLiteralValue << source << line; + sendMessage(message); + } + return id; +} + +quint32 DeclarativeEngineDebugClient::resetBindingForObject( + int objectDebugId, + const QString &propertyName) +{ + quint32 id = 0; + if (status() == Enabled && objectDebugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("RESET_BINDING") << objectDebugId << propertyName; + sendMessage(message); + } + return id; +} + +quint32 DeclarativeEngineDebugClient::setMethodBody( + int objectDebugId, const QString &methodName, + const QString &methodBody) +{ + quint32 id = 0; + if (status() == Enabled && objectDebugId != -1) { + id = getId(); + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray("SET_METHOD_BODY") << objectDebugId + << methodName << methodBody; + sendMessage(message); + } + return id; +} + +void DeclarativeEngineDebugClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray type; + ds >> type; + + if (type == "OBJECT_CREATED") { + int engineId; + int objectId; + ds >> engineId >> objectId; + emit newObject(engineId, objectId, -1); + return; + } else { + BaseEngineDebugClient::messageReceived(data); + } +} +} // namespace QmlDebug diff --git a/libs/qmldebug/declarativeenginedebugclient.h b/libs/qmldebug/declarativeenginedebugclient.h new file mode 100644 index 0000000000..3dc9102547 --- /dev/null +++ b/libs/qmldebug/declarativeenginedebugclient.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DECLARATIVEENGINEDEBUGCLIENT_H +#define DECLARATIVEENGINEDEBUGCLIENT_H + +#include "baseenginedebugclient.h" + +namespace QmlDebug { + +class QmlDebugConnection; + +class QMLDEBUG_EXPORT DeclarativeEngineDebugClient : public BaseEngineDebugClient +{ + Q_OBJECT +public: + explicit DeclarativeEngineDebugClient(QmlDebugConnection *conn); + + quint32 setBindingForObject(int objectDebugId, const QString &propertyName, + const QVariant &bindingExpression, + bool isLiteralValue, + QString source, int line); + quint32 resetBindingForObject(int objectDebugId, const QString &propertyName); + quint32 setMethodBody(int objectDebugId, const QString &methodName, + const QString &methodBody); + +protected: + void messageReceived(const QByteArray &data); +}; + +} // namespace QmlDebug + +#endif // DECLARATIVEENGINEDEBUGCLIENT_H diff --git a/libs/qmldebug/declarativeenginedebugclientv2.h b/libs/qmldebug/declarativeenginedebugclientv2.h new file mode 100644 index 0000000000..aeb2ef6ee2 --- /dev/null +++ b/libs/qmldebug/declarativeenginedebugclientv2.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DECLARATIVEENGINEDEBUGCLIENTV2_H +#define DECLARATIVEENGINEDEBUGCLIENTV2_H + +#include "baseenginedebugclient.h" + +namespace QmlDebug { + +class QmlDebugConnection; + +class QMLDEBUG_EXPORT DeclarativeEngineDebugClientV2 : public BaseEngineDebugClient +{ + Q_OBJECT +public: + explicit DeclarativeEngineDebugClientV2(QmlDebugConnection *conn) + : BaseEngineDebugClient(QLatin1String("DeclarativeDebugger"), conn) + { + } +}; + +} // namespace QmlDebug + +#endif // DECLARATIVEENGINEDEBUGCLIENTV2_H diff --git a/libs/qmldebug/declarativetoolsclient.cpp b/libs/qmldebug/declarativetoolsclient.cpp new file mode 100644 index 0000000000..ba540eeb6b --- /dev/null +++ b/libs/qmldebug/declarativetoolsclient.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "declarativetoolsclient.h" +#include <QMetaEnum> +#include <QStringList> + +namespace QmlDebug { +namespace Internal { + +namespace Constants { + +enum DesignTool { + NoTool = 0, + SelectionToolMode = 1, + MarqueeSelectionToolMode = 2, + MoveToolMode = 3, + ResizeToolMode = 4, + ZoomMode = 6 +}; + +} + +class InspectorProtocol : public QObject +{ + Q_OBJECT + Q_ENUMS(Message Tool) + +public: + enum Message { + AnimationSpeedChanged = 0, + AnimationPausedChanged = 19, // highest value + ChangeTool = 1, + ClearComponentCache = 2, + ColorChanged = 3, + CreateObject = 5, + CurrentObjectsChanged = 6, + DestroyObject = 7, + MoveObject = 8, + ObjectIdList = 9, + Reload = 10, + Reloaded = 11, + SetAnimationSpeed = 12, + SetAnimationPaused = 18, + SetCurrentObjects = 14, + SetDesignMode = 15, + ShowAppOnTop = 16, + ToolChanged = 17 + }; + + enum Tool { + ColorPickerTool, + SelectMarqueeTool, + SelectTool, + ZoomTool + }; + + static inline QString toString(Message message) + { + return QString::fromUtf8(staticMetaObject.enumerator(0).valueToKey(message)); + } + + static inline QString toString(Tool tool) + { + return QString::fromUtf8(staticMetaObject.enumerator(1).valueToKey(tool)); + } +}; + +inline QDataStream & operator<< (QDataStream &stream, InspectorProtocol::Message message) +{ + return stream << static_cast<quint32>(message); +} + +inline QDataStream & operator>> (QDataStream &stream, InspectorProtocol::Message &message) +{ + quint32 i; + stream >> i; + message = static_cast<InspectorProtocol::Message>(i); + return stream; +} + +inline QDebug operator<< (QDebug dbg, InspectorProtocol::Message message) +{ + dbg << InspectorProtocol::toString(message); + return dbg; +} + +inline QDataStream & operator<< (QDataStream &stream, InspectorProtocol::Tool tool) +{ + return stream << static_cast<quint32>(tool); +} + +inline QDataStream & operator>> (QDataStream &stream, InspectorProtocol::Tool &tool) +{ + quint32 i; + stream >> i; + tool = static_cast<InspectorProtocol::Tool>(i); + return stream; +} + +inline QDebug operator<< (QDebug dbg, InspectorProtocol::Tool tool) +{ + dbg << InspectorProtocol::toString(tool); + return dbg; +} + +} // internal + +using namespace Internal; + +DeclarativeToolsClient::DeclarativeToolsClient(QmlDebugConnection *client) + : BaseToolsClient(client,QLatin1String("QDeclarativeObserverMode")), + m_connection(client) +{ + setObjectName(name()); +} + +void DeclarativeToolsClient::messageReceived(const QByteArray &message) +{ + QDataStream ds(message); + + InspectorProtocol::Message type; + ds >> type; + + switch (type) { + case InspectorProtocol::CurrentObjectsChanged: { + int objectCount; + ds >> objectCount; + + log(LogReceive, type, QString::fromLatin1("%1 [list of debug ids]").arg(objectCount)); + + m_currentDebugIds.clear(); + + for (int i = 0; i < objectCount; ++i) { + int debugId; + ds >> debugId; + if (debugId != -1) + m_currentDebugIds << debugId; + } + + emit currentObjectsChanged(m_currentDebugIds); + break; + } + case InspectorProtocol::ToolChanged: { + int toolId; + ds >> toolId; + + log(LogReceive, type, QString::number(toolId)); + + if (toolId == Constants::ZoomMode) + emit zoomToolActivated(); + else if (toolId == Constants::SelectionToolMode) + emit selectToolActivated(); + else if (toolId == Constants::MarqueeSelectionToolMode) + emit selectMarqueeToolActivated(); + break; + } + case InspectorProtocol::AnimationSpeedChanged: { + qreal slowDownFactor; + ds >> slowDownFactor; + + log(LogReceive, type, QString::number(slowDownFactor)); + + emit animationSpeedChanged(slowDownFactor); + break; + } + case InspectorProtocol::AnimationPausedChanged: { + bool paused; + ds >> paused; + + log(LogReceive, type, paused ? QLatin1String("true") + : QLatin1String("false")); + + emit animationPausedChanged(paused); + break; + } + case InspectorProtocol::SetDesignMode: { + bool inDesignMode; + ds >> inDesignMode; + + log(LogReceive, type, QLatin1String(inDesignMode ? "true" : "false")); + + emit designModeBehaviorChanged(inDesignMode); + break; + } + case InspectorProtocol::ShowAppOnTop: { + bool showAppOnTop; + ds >> showAppOnTop; + + log(LogReceive, type, QLatin1String(showAppOnTop ? "true" : "false")); + + emit showAppOnTopChanged(showAppOnTop); + break; + } + case InspectorProtocol::Reloaded: { + log(LogReceive, type); + emit reloaded(); + break; + } + default: + log(LogReceive, type, QLatin1String("Warning: Not handling message")); + } +} + +QList<int> DeclarativeToolsClient::currentObjects() const +{ + return m_currentDebugIds; +} + +void DeclarativeToolsClient::setCurrentObjects(const QList<int> &debugIds) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + if (debugIds == m_currentDebugIds) + return; + + m_currentDebugIds = debugIds; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::SetCurrentObjects; + ds << cmd + << debugIds.length(); + + foreach (int id, debugIds) { + ds << id; + } + + log(LogSend, cmd, QString::fromLatin1("%1 [list of ids]").arg(debugIds.length())); + + sendMessage(message); +} + +void DeclarativeToolsClient::setObjectIdList( + const QList<ObjectReference> &objectRoots) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + QList<int> debugIds; + QList<QString> objectIds; + + foreach (const ObjectReference &ref, objectRoots) + recurseObjectIdList(ref, debugIds, objectIds); + + InspectorProtocol::Message cmd = InspectorProtocol::ObjectIdList; + ds << cmd + << debugIds.length(); + + Q_ASSERT(debugIds.length() == objectIds.length()); + + for (int i = 0; i < debugIds.length(); ++i) { + ds << debugIds[i] << objectIds[i]; + } + + log(LogSend, cmd, + QString::fromLatin1("%1 %2 [list of debug / object ids]").arg(debugIds.length())); + + sendMessage(message); +} + +void DeclarativeToolsClient::clearComponentCache() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::ClearComponentCache; + ds << cmd; + + log(LogSend, cmd); + + sendMessage(message); +} + +void DeclarativeToolsClient::reload(const QHash<QString, + QByteArray> &changesHash) +{ + Q_UNUSED(changesHash); + + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::Reload; + ds << cmd; + + log(LogSend, cmd); + + sendMessage(message); +} + +void DeclarativeToolsClient::setDesignModeBehavior(bool inDesignMode) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::SetDesignMode; + ds << cmd + << inDesignMode; + + log(LogSend, cmd, QLatin1String(inDesignMode ? "true" : "false")); + + sendMessage(message); +} + +void DeclarativeToolsClient::setAnimationSpeed(qreal slowDownFactor) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::SetAnimationSpeed; + ds << cmd + << slowDownFactor; + + + log(LogSend, cmd, QString::number(slowDownFactor)); + + sendMessage(message); +} + +void DeclarativeToolsClient::setAnimationPaused(bool paused) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::SetAnimationPaused; + ds << cmd + << paused; + + log(LogSend, cmd, paused ? QLatin1String("true") : QLatin1String("false")); + + sendMessage(message); +} + +void DeclarativeToolsClient::changeToSelectTool() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::ChangeTool; + InspectorProtocol::Tool tool = InspectorProtocol::SelectTool; + ds << cmd + << tool; + + log(LogSend, cmd, InspectorProtocol::toString(tool)); + + sendMessage(message); +} + +void DeclarativeToolsClient::changeToSelectMarqueeTool() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::ChangeTool; + InspectorProtocol::Tool tool = InspectorProtocol::SelectMarqueeTool; + ds << cmd + << tool; + + log(LogSend, cmd, InspectorProtocol::toString(tool)); + + sendMessage(message); +} + +void DeclarativeToolsClient::changeToZoomTool() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::ChangeTool; + InspectorProtocol::Tool tool = InspectorProtocol::ZoomTool; + ds << cmd + << tool; + + log(LogSend, cmd, InspectorProtocol::toString(tool)); + + sendMessage(message); +} + +void DeclarativeToolsClient::showAppOnTop(bool showOnTop) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::ShowAppOnTop; + ds << cmd << showOnTop; + + log(LogSend, cmd, QLatin1String(showOnTop ? "true" : "false")); + + sendMessage(message); +} + +void DeclarativeToolsClient::createQmlObject(const QString &qmlText, + int parentDebugId, + const QStringList &imports, + const QString &filename, int order) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::CreateObject; + ds << cmd + << qmlText + << parentDebugId + << imports + << filename + << order; + + log(LogSend, cmd, QString::fromLatin1("%1 %2 [%3] %4").arg(qmlText, + QString::number(parentDebugId), + imports.join(QLatin1String(",")), filename)); + + sendMessage(message); +} + +void DeclarativeToolsClient::destroyQmlObject(int debugId) +{ + if (!m_connection || !m_connection->isConnected()) + return; + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::DestroyObject; + ds << cmd << debugId; + + log(LogSend, cmd, QString::number(debugId)); + + sendMessage(message); +} + +void DeclarativeToolsClient::reparentQmlObject(int debugId, int newParent) +{ + if (!m_connection || !m_connection->isConnected()) + return; + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + + InspectorProtocol::Message cmd = InspectorProtocol::MoveObject; + ds << cmd + << debugId + << newParent; + + log(LogSend, cmd, QString::fromLatin1("%1 %2").arg(QString::number(debugId), + QString::number(newParent))); + + sendMessage(message); +} + + +void DeclarativeToolsClient::applyChangesToQmlFile() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + // TODO +} + +void DeclarativeToolsClient::applyChangesFromQmlFile() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + // TODO +} + +void DeclarativeToolsClient::log(LogDirection direction, + int message, + const QString &extra) +{ + QString msg; + if (direction == LogSend) + msg += QLatin1String("sending "); + else + msg += QLatin1String("receiving "); + + InspectorProtocol::Message msgType + = static_cast<InspectorProtocol::Message>(message); + msg += InspectorProtocol::toString(msgType); + msg += QLatin1Char(' '); + msg += extra; + emit logActivity(name(), msg); +} + +} // namespace QmlDebug + +#include "declarativetoolsclient.moc" diff --git a/libs/qmldebug/declarativetoolsclient.h b/libs/qmldebug/declarativetoolsclient.h new file mode 100644 index 0000000000..4f942639a9 --- /dev/null +++ b/libs/qmldebug/declarativetoolsclient.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef DECLARATIVETOOLSCLIENT_H +#define DECLARATIVETOOLSCLIENT_H + +#include "basetoolsclient.h" + +namespace QmlDebug { + +class QMLDEBUG_EXPORT DeclarativeToolsClient : public BaseToolsClient +{ + Q_OBJECT +public: + DeclarativeToolsClient(QmlDebugConnection *client); + + void setCurrentObjects(const QList<int> &debugIds); + void reload(const QHash<QString, QByteArray> &changesHash); + bool supportReload() const { return false; } + void setDesignModeBehavior(bool inDesignMode); + void setAnimationSpeed(qreal slowDownFactor); + void setAnimationPaused(bool paused); + void changeToSelectTool(); + void changeToSelectMarqueeTool(); + void changeToZoomTool(); + void showAppOnTop(bool showOnTop); + + void createQmlObject(const QString &qmlText, int parentDebugId, + const QStringList &imports, const QString &filename, + int order); + void destroyQmlObject(int debugId); + void reparentQmlObject(int debugId, int newParent); + + void applyChangesToQmlFile(); + void applyChangesFromQmlFile(); + + QList<int> currentObjects() const; + + // ### Qt 4.8: remove if we can have access to qdeclarativecontextdata or id's + void setObjectIdList(const QList<ObjectReference> &objectRoots); + + void clearComponentCache(); + +protected: + void messageReceived(const QByteArray &); + +private: + void log(LogDirection direction, + int message, + const QString &extra = QString()); + +private: + QList<int> m_currentDebugIds; + QmlDebugConnection *m_connection; +}; + +} // namespace QmlDebug + +#endif // DECLARATIVETOOLSCLIENT_H diff --git a/libs/qmldebug/qdebugmessageclient.cpp b/libs/qmldebug/qdebugmessageclient.cpp new file mode 100644 index 0000000000..8b9517bcf8 --- /dev/null +++ b/libs/qmldebug/qdebugmessageclient.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qdebugmessageclient.h" + +#include <QDataStream> + +namespace QmlDebug { + +QDebugMessageClient::QDebugMessageClient(QmlDebugConnection *client) + : QmlDebugClient(QLatin1String("DebugMessages"), client) +{ +} + +QDebugMessageClient::~QDebugMessageClient() +{ +} + +void QDebugMessageClient::statusChanged(ClientStatus status) +{ + emit newStatus(status); +} + +void QDebugMessageClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray command; + ds >> command; + + if (command == "MESSAGE") { + int type; + int line; + QByteArray debugMessage; + QByteArray file; + QByteArray function; + ds >> type >> debugMessage >> file >> line >> function; + QDebugContextInfo info; + info.line = line; + info.file = QString::fromUtf8(file); + info.function = QString::fromUtf8(function); + emit message(QtMsgType(type), QString::fromUtf8(debugMessage), info); + } +} + +} // namespace QmlDebug diff --git a/libs/qmldebug/qdebugmessageclient.h b/libs/qmldebug/qdebugmessageclient.h new file mode 100644 index 0000000000..60bf0769f5 --- /dev/null +++ b/libs/qmldebug/qdebugmessageclient.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QDEBUGMESSAGECLIENT_H +#define QDEBUGMESSAGECLIENT_H + +#include "qmldebugclient.h" +#include "qmldebug_global.h" + +namespace QmlDebug { + +class QDebugMessageClientPrivate; +struct QDebugContextInfo +{ + int line; + QString file; + QString function; +}; + +class QMLDEBUG_EXPORT QDebugMessageClient : public QmlDebugClient +{ + Q_OBJECT + +public: + explicit QDebugMessageClient(QmlDebugConnection *client); + ~QDebugMessageClient(); + +protected: + virtual void statusChanged(ClientStatus status); + virtual void messageReceived(const QByteArray &); + +signals: + void newStatus(QmlDebug::ClientStatus); + void message(QtMsgType, const QString &, + const QmlDebug::QDebugContextInfo &); + +private: + class QDebugMessageClientPrivate *d; + Q_DISABLE_COPY(QDebugMessageClient) +}; + +} // namespace QmlDebug + +#endif // QDEBUGMESSAGECLIENT_H diff --git a/libs/qmldebug/qmldebug-lib.pri b/libs/qmldebug/qmldebug-lib.pri new file mode 100644 index 0000000000..54f09258b0 --- /dev/null +++ b/libs/qmldebug/qmldebug-lib.pri @@ -0,0 +1,40 @@ +contains(CONFIG, dll) { + DEFINES += QMLDEBUG_LIB +} else { + DEFINES += QMLDEBUG_STATIC_LIB +} + +INCLUDEPATH += $$PWD/.. + +HEADERS += \ + $$PWD/qmlprofilereventlocation.h \ + $$PWD/qmldebugclient.h \ + $$PWD/baseenginedebugclient.h \ + $$PWD/declarativeenginedebugclient.h \ + $$PWD/declarativeenginedebugclientv2.h \ + $$PWD/qmloutputparser.h \ + $$PWD/qmldebug_global.h \ + $$PWD/qmlprofilereventtypes.h \ + $$PWD/qmlprofilertraceclient.h \ + $$PWD/qpacketprotocol.h \ + $$PWD/qv8profilerclient.h \ + $$PWD/qmldebugconstants.h \ + $$PWD/qdebugmessageclient.h \ + $$PWD/qmlenginedebugclient.h \ + $$PWD/basetoolsclient.h \ + $$PWD/declarativetoolsclient.h \ + $$PWD/qmltoolsclient.h + +SOURCES += \ + $$PWD/qmldebugclient.cpp \ + $$PWD/baseenginedebugclient.cpp \ + $$PWD/qmloutputparser.cpp \ + $$PWD/qmlprofilertraceclient.cpp \ + $$PWD/qpacketprotocol.cpp \ + $$PWD/qv8profilerclient.cpp \ + $$PWD/qdebugmessageclient.cpp \ + $$PWD/basetoolsclient.cpp \ + $$PWD/declarativetoolsclient.cpp \ + $$PWD/qmltoolsclient.cpp \ + $$PWD/declarativeenginedebugclient.cpp + diff --git a/libs/qmldebug/qmldebug.pro b/libs/qmldebug/qmldebug.pro new file mode 100644 index 0000000000..38b901f860 --- /dev/null +++ b/libs/qmldebug/qmldebug.pro @@ -0,0 +1,8 @@ +QT += network + +include(../../qtcreatorlibrary.pri) +include(qmldebug-lib.pri) + +OTHER_FILES += \ + qmldebug.pri + diff --git a/libs/qmldebug/qmldebug.qbs b/libs/qmldebug/qmldebug.qbs new file mode 100644 index 0000000000..beba356937 --- /dev/null +++ b/libs/qmldebug/qmldebug.qbs @@ -0,0 +1,43 @@ +import qbs.base 1.0 +import "../QtcLibrary.qbs" as QtcLibrary + +QtcLibrary { + name: "QmlDebug" + + cpp.defines: base.concat("QMLDEBUG_LIB") + + Depends { name: "cpp" } + Depends { name: "Qt"; submodules: ["gui", "network"] } + + files: [ + "baseenginedebugclient.cpp", + "baseenginedebugclient.h", + "basetoolsclient.cpp", + "basetoolsclient.h", + "declarativeenginedebugclient.cpp", + "declarativeenginedebugclient.h", + "declarativeenginedebugclientv2.h", + "declarativetoolsclient.cpp", + "declarativetoolsclient.h", + "qdebugmessageclient.cpp", + "qdebugmessageclient.h", + "qmldebug_global.h", + "qmldebugclient.cpp", + "qmldebugclient.h", + "qmldebugconstants.h", + "qmlenginedebugclient.h", + "qmloutputparser.cpp", + "qmloutputparser.h", + "qmlprofilereventlocation.h", + "qmlprofilereventtypes.h", + "qmlprofilertraceclient.cpp", + "qmlprofilertraceclient.h", + "qmltoolsclient.cpp", + "qmltoolsclient.h", + "qpacketprotocol.cpp", + "qpacketprotocol.h", + "qv8profilerclient.cpp", + "qv8profilerclient.h", + ] +} + diff --git a/libs/qmldebug/qmldebug_dependencies.pri b/libs/qmldebug/qmldebug_dependencies.pri new file mode 100644 index 0000000000..a99b82c4ef --- /dev/null +++ b/libs/qmldebug/qmldebug_dependencies.pri @@ -0,0 +1 @@ +QTC_LIB_NAME = QmlDebug diff --git a/libs/qmldebug/qmldebug_global.h b/libs/qmldebug/qmldebug_global.h new file mode 100644 index 0000000000..9d41083577 --- /dev/null +++ b/libs/qmldebug/qmldebug_global.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLDEBUG_GLOBAL_H +#define QMLDEBUG_GLOBAL_H + +#if defined(QMLDEBUG_LIB) +# define QMLDEBUG_EXPORT Q_DECL_EXPORT +#elif defined(QMLDEBUG_STATIC_LIB) +# define QMLDEBUG_EXPORT +#else +# define QMLDEBUG_EXPORT Q_DECL_IMPORT +#endif + +#endif // QMLDEBUG_GLOBAL_H diff --git a/libs/qmldebug/qmldebugclient.cpp b/libs/qmldebug/qmldebugclient.cpp new file mode 100644 index 0000000000..6fd35d3c13 --- /dev/null +++ b/libs/qmldebug/qmldebugclient.cpp @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmldebugclient.h" + +#include "qpacketprotocol.h" + +#include <qdebug.h> +#include <qstringlist.h> +#include <qnetworkproxy.h> + +namespace QmlDebug { + +const int protocolVersion = 1; +const QString serverId = QLatin1String("QDeclarativeDebugServer"); +const QString clientId = QLatin1String("QDeclarativeDebugClient"); + +class QmlDebugClientPrivate +{ + // Q_DECLARE_PUBLIC(QmlDebugClient) +public: + QmlDebugClientPrivate(); + + QString name; + QmlDebugConnection *connection; +}; + +class QmlDebugConnectionPrivate : public QObject +{ + Q_OBJECT +public: + QmlDebugConnectionPrivate(QmlDebugConnection *c); + QmlDebugConnection *q; + QPacketProtocol *protocol; + QIODevice *device; // Currently a QTcpSocket + + bool gotHello; + QHash <QString, float> serverPlugins; + QHash<QString, QmlDebugClient *> plugins; + + void advertisePlugins(); + void connectDeviceSignals(); + +public Q_SLOTS: + void connected(); + void readyRead(); + void deviceAboutToClose(); +}; + +QmlDebugConnectionPrivate::QmlDebugConnectionPrivate(QmlDebugConnection *c) + : QObject(c), q(c), protocol(0), device(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 QmlDebugConnectionPrivate::advertisePlugins() +{ + if (!q->isConnected() || !gotHello) + return; + + QPacket pack; + pack << serverId << 1 << plugins.keys(); + protocol->send(pack); + q->flush(); +} + +void QmlDebugConnectionPrivate::connected() +{ + QPacket pack; + pack << serverId << 0 << protocolVersion << plugins.keys(); + protocol->send(pack); + q->flush(); +} + +void QmlDebugConnectionPrivate::readyRead() +{ + 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) { + QStringList pluginNames; + QList<float> pluginVersions; + pack >> pluginNames; + if (!pack.isEmpty()) + 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); + serverPlugins.insert(pluginNames.at(i), pluginVersion); + } + + validHello = true; + } + } + } + + if (!validHello) { + qWarning("QML Debug Client: Invalid hello message"); + QObject::disconnect(protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); + return; + } + gotHello = true; + + QHash<QString, QmlDebugClient *>::Iterator iter = plugins.begin(); + for (; iter != plugins.end(); ++iter) { + ClientStatus newStatus = Unavailable; + if (serverPlugins.contains(iter.key())) + newStatus = Enabled; + iter.value()->statusChanged(newStatus); + } + } + + while (protocol->packetsAvailable()) { + QPacket pack = protocol->read(); + QString name; + pack >> name; + + if (name == clientId) { + int op = -1; + pack >> op; + + if (op == 1) { + // Service Discovery + QHash<QString, float> oldServerPlugins = serverPlugins; + serverPlugins.clear(); + + QStringList pluginNames; + QList<float> pluginVersions; + pack >> pluginNames; + if (!pack.isEmpty()) + 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); + serverPlugins.insert(pluginNames.at(i), pluginVersion); + } + + QHash<QString, QmlDebugClient *>::Iterator iter = plugins.begin(); + for (; iter != plugins.end(); ++iter) { + const QString pluginName = iter.key(); + ClientStatus newStatus = Unavailable; + if (serverPlugins.contains(pluginName)) + newStatus = Enabled; + + if (oldServerPlugins.contains(pluginName) + != serverPlugins.contains(pluginName)) { + iter.value()->statusChanged(newStatus); + } + } + } else { + qWarning() << "QML Debug Client: Unknown control message id" << op; + } + } else { + QByteArray message; + pack >> message; + + QHash<QString, QmlDebugClient *>::Iterator iter = + plugins.find(name); + if (iter == plugins.end()) + qWarning() << "QML Debug Client: Message received for missing plugin" << name; + else + (*iter)->messageReceived(message); + } + } +} + +void QmlDebugConnectionPrivate::deviceAboutToClose() +{ + // This is nasty syntax but we want to emit our own aboutToClose signal (by calling QIODevice::close()) + // without calling the underlying device close fn as that would cause an infinite loop + q->QIODevice::close(); +} + +QmlDebugConnection::QmlDebugConnection(QObject *parent) + : QIODevice(parent), d(new QmlDebugConnectionPrivate(this)) +{ +} + +QmlDebugConnection::~QmlDebugConnection() +{ + QHash<QString, QmlDebugClient*>::iterator iter = d->plugins.begin(); + for (; iter != d->plugins.end(); ++iter) { + iter.value()->d_func()->connection = 0; + iter.value()->statusChanged(NotConnected); + } +} + +bool QmlDebugConnection::isConnected() const +{ + return state() == QAbstractSocket::ConnectedState; +} + +qint64 QmlDebugConnection::readData(char *data, qint64 maxSize) +{ + return d->device->read(data, maxSize); +} + +qint64 QmlDebugConnection::writeData(const char *data, qint64 maxSize) +{ + return d->device->write(data, maxSize); +} + +void QmlDebugConnection::internalError(QAbstractSocket::SocketError socketError) +{ + setErrorString(d->device->errorString()); + emit error(socketError); +} + +qint64 QmlDebugConnection::bytesAvailable() const +{ + return d->device->bytesAvailable(); +} + +bool QmlDebugConnection::isSequential() const +{ + return true; +} + +void QmlDebugConnection::close() +{ + if (isOpen()) { + QIODevice::close(); + d->device->close(); + emit stateChanged(QAbstractSocket::UnconnectedState); + + QHash<QString, QmlDebugClient*>::iterator iter = d->plugins.begin(); + for (; iter != d->plugins.end(); ++iter) { + iter.value()->statusChanged(NotConnected); + } + } +} + +bool QmlDebugConnection::waitForConnected(int msecs) +{ + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(d->device); + if (socket) + return socket->waitForConnected(msecs); + return false; +} + +// For ease of refactoring we use QAbstractSocket's states even if we're actually using a OstChannel underneath +// since serial ports have a subset of the socket states afaics +QAbstractSocket::SocketState QmlDebugConnection::state() const +{ + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(d->device); + if (socket) + return socket->state(); + + return QAbstractSocket::UnconnectedState; +} + +void QmlDebugConnection::flush() +{ + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(d->device); + if (socket) { + socket->flush(); + return; + } +} + +void QmlDebugConnection::connectToHost(const QString &hostName, quint16 port) +{ + QTcpSocket *socket = new QTcpSocket(d); + socket->setProxy(QNetworkProxy::NoProxy); + d->device = socket; + d->connectDeviceSignals(); + d->gotHello = false; + connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SIGNAL(stateChanged(QAbstractSocket::SocketState))); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(internalError(QAbstractSocket::SocketError))); + connect(socket, SIGNAL(connected()), this, SIGNAL(connected())); + socket->connectToHost(hostName, port); + QIODevice::open(ReadWrite | Unbuffered); +} + +void QmlDebugConnectionPrivate::connectDeviceSignals() +{ + connect(device, SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64))); + connect(device, SIGNAL(readyRead()), q, SIGNAL(readyRead())); + connect(device, SIGNAL(aboutToClose()), this, SLOT(deviceAboutToClose())); +} + +// + +QmlDebugClientPrivate::QmlDebugClientPrivate() + : connection(0) +{ +} + +QmlDebugClient::QmlDebugClient(const QString &name, + QmlDebugConnection *parent) + : QObject(parent), d_ptr(new QmlDebugClientPrivate()) +{ + Q_D(QmlDebugClient); + d->name = name; + d->connection = parent; + + if (!d->connection) + return; + + if (d->connection->d->plugins.contains(name)) { + qWarning() << "QML Debug Client: Conflicting plugin name" << name; + d->connection = 0; + } else { + d->connection->d->plugins.insert(name, this); + d->connection->d->advertisePlugins(); + } +} + +QmlDebugClient::~QmlDebugClient() +{ + Q_D(const QmlDebugClient); + if (d->connection && d->connection->d) { + d->connection->d->plugins.remove(d->name); + d->connection->d->advertisePlugins(); + } +} + +QString QmlDebugClient::name() const +{ + Q_D(const QmlDebugClient); + return d->name; +} + +float QmlDebugClient::serviceVersion() const +{ + Q_D(const QmlDebugClient); + if (d->connection && d->connection->d->serverPlugins.contains(d->name)) + return d->connection->d->serverPlugins.value(d->name); + return -1; +} + +ClientStatus QmlDebugClient::status() const +{ + Q_D(const QmlDebugClient); + if (!d->connection + || !d->connection->isConnected() + || !d->connection->d->gotHello) + return NotConnected; + + if (d->connection->d->serverPlugins.contains(d->name)) + return Enabled; + + return Unavailable; +} + +void QmlDebugClient::sendMessage(const QByteArray &message) +{ + Q_D(QmlDebugClient); + if (status() != Enabled) + return; + + QPacket pack; + pack << d->name << message; + d->connection->d->protocol->send(pack); + d->connection->flush(); +} + +void QmlDebugClient::statusChanged(ClientStatus) +{ +} + +void QmlDebugClient::messageReceived(const QByteArray &) +{ +} + +} // namespace QmlDebug + +#include <qmldebugclient.moc> diff --git a/libs/qmldebug/qmldebugclient.h b/libs/qmldebug/qmldebugclient.h new file mode 100644 index 0000000000..d33b639fec --- /dev/null +++ b/libs/qmldebug/qmldebugclient.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLDEBUGCLIENT_H +#define QMLDEBUGCLIENT_H + +#include "qmldebug_global.h" +#include <qtcpsocket.h> + +namespace QmlDebug { + +class QmlDebugConnectionPrivate; +class QMLDEBUG_EXPORT QmlDebugConnection : public QIODevice +{ + Q_OBJECT + Q_DISABLE_COPY(QmlDebugConnection) +public: + QmlDebugConnection(QObject * = 0); + ~QmlDebugConnection(); + + void connectToHost(const QString &hostName, quint16 port); + + qint64 bytesAvailable() const; + bool isConnected() const; + QAbstractSocket::SocketState state() const; + void flush(); + bool isSequential() const; + void close(); + bool waitForConnected(int msecs = 30000); + +signals: + void connected(); + void stateChanged(QAbstractSocket::SocketState socketState); + void error(QAbstractSocket::SocketError socketError); + +protected: + qint64 readData(char *data, qint64 maxSize); + qint64 writeData(const char *data, qint64 maxSize); + +private slots: + void internalError(QAbstractSocket::SocketError error); + +private: + QmlDebugConnectionPrivate *d; + friend class QmlDebugClient; + friend class QmlDebugClientPrivate; +}; + +enum ClientStatus { + NotConnected, + Unavailable, + Enabled +}; + +class QmlDebugClientPrivate; +class QMLDEBUG_EXPORT QmlDebugClient : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QmlDebugClient) + Q_DISABLE_COPY(QmlDebugClient) + +public: + QmlDebugClient(const QString &, QmlDebugConnection *parent); + ~QmlDebugClient(); + + QString name() const; + float serviceVersion() const; + ClientStatus status() const; + + virtual void sendMessage(const QByteArray &); + +protected: + virtual void statusChanged(ClientStatus); + virtual void messageReceived(const QByteArray &); + +private: + friend class QmlDebugConnection; + friend class QmlDebugConnectionPrivate; + QScopedPointer<QmlDebugClientPrivate> d_ptr; +}; + +} // namespace QmlDebug + +#endif // QMLDEBUGCLIENT_H diff --git a/libs/qmldebug/qmldebugconstants.h b/libs/qmldebug/qmldebugconstants.h new file mode 100644 index 0000000000..8f207a1af5 --- /dev/null +++ b/libs/qmldebug/qmldebugconstants.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLDEBUGCONSTANTS_H +#define QMLDEBUGCONSTANTS_H + +namespace QmlDebug { +namespace Constants { + +const char STR_WAITING_FOR_CONNECTION[] = "Waiting for connection "; +const char STR_ON_PORT_PATTERN[] = "on port (\\d+)"; +const char STR_UNABLE_TO_LISTEN[] = "Unable to listen "; +const char STR_IGNORING_DEBUGGER[] = "Ignoring \"-qmljsdebugger="; +const char STR_IGNORING_DEBUGGER2[] = "Ignoring\"-qmljsdebugger="; // There is (was?) a bug in one of the error strings - safest to handle both +const char STR_CONNECTION_ESTABLISHED[] = "Connection established"; + +const char QDECLARATIVE_ENGINE[] = "QDeclarativeEngine"; + +} // namespace Constants +} // namespace QmlDebug + +#endif // QMLDEBUGCONSTANTS_H diff --git a/libs/qmldebug/qmlenginedebugclient.h b/libs/qmldebug/qmlenginedebugclient.h new file mode 100644 index 0000000000..9d0fcf0ca4 --- /dev/null +++ b/libs/qmldebug/qmlenginedebugclient.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLENGINEDEBUGCLIENT_H +#define QMLENGINEDEBUGCLIENT_H + +#include "baseenginedebugclient.h" + +namespace QmlDebug { + +class QmlDebugConnection; + +class QMLDEBUG_EXPORT QmlEngineDebugClient : public BaseEngineDebugClient +{ + Q_OBJECT +public: + explicit QmlEngineDebugClient(QmlDebugConnection *conn) + : BaseEngineDebugClient(QLatin1String("QmlDebugger"), conn) + { + } +}; + +} // namespace QmlDebug + +#endif // QMLENGINEDEBUGCLIENT_H diff --git a/libs/qmldebug/qmloutputparser.cpp b/libs/qmldebug/qmloutputparser.cpp new file mode 100644 index 0000000000..33a44a3086 --- /dev/null +++ b/libs/qmldebug/qmloutputparser.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmloutputparser.h" +#include "qmldebugconstants.h" +#include <QRegExp> + +namespace QmlDebug { + +QmlOutputParser::QmlOutputParser(QObject *parent) + : QObject(parent) +{ +} + +void QmlOutputParser::setNoOutputText(const QString &text) +{ + m_noOutputText = text; +} + +void QmlOutputParser::processOutput(const QString &output) +{ + m_buffer.append(output); + + while (true) { + const int nlIndex = m_buffer.indexOf(QLatin1Char('\n')); + if (nlIndex < 0) // no further complete lines + break; + + const QString msg = m_buffer.left(nlIndex); + m_buffer = m_buffer.right(m_buffer.size() - nlIndex - 1); + + // used in Qt4 + static const QString qddserver4 = QLatin1String("QDeclarativeDebugServer: "); + // used in Qt5 + static const QString qddserver5 = QLatin1String("QML Debugger: "); + + QString status; + int index = msg.indexOf(qddserver4); + if (index != -1) { + status = msg; + status.remove(0, index + qddserver4.length()); // chop of 'QDeclarativeDebugServer: ' + } else { + index = msg.indexOf(qddserver5); + if (index != -1) { + status = msg; + status.remove(0, index + qddserver5.length()); // chop of 'QML Debugger: ' + } + } + if (!status.isEmpty()) { + static QString waitingForConnection = QLatin1String(Constants::STR_WAITING_FOR_CONNECTION); + static QString unableToListen = QLatin1String(Constants::STR_UNABLE_TO_LISTEN); + static QString debuggingNotEnabled = QLatin1String(Constants::STR_IGNORING_DEBUGGER); + static QString debuggingNotEnabled2 = QLatin1String(Constants::STR_IGNORING_DEBUGGER2); + static QString connectionEstablished = QLatin1String(Constants::STR_CONNECTION_ESTABLISHED); + + if (status.startsWith(waitingForConnection)) { + status.remove(0, waitingForConnection.size()); // chop of 'Waiting for connection ' + + static QRegExp waitingTcp( + QString::fromLatin1(Constants::STR_ON_PORT_PATTERN)); + if (waitingTcp.indexIn(status) > -1) { + bool canConvert; + quint16 port = waitingTcp.cap(1).toUShort(&canConvert); + if (canConvert) + emit waitingForConnectionOnPort(port); + continue; + } + } else if (status.startsWith(unableToListen)) { + //: Error message shown after 'Could not connect ... debugger:" + emit errorMessage(tr("The port seems to be in use.")); + } else if (status.startsWith(debuggingNotEnabled) || status.startsWith(debuggingNotEnabled2)) { + //: Error message shown after 'Could not connect ... debugger:" + emit errorMessage(tr("The application is not set up for QML/JS debugging.")); + } else if (status.startsWith(connectionEstablished)) { + emit connectionEstablishedMessage(); + } else { + emit unknownMessage(status); + } + } else if (msg.contains(m_noOutputText)) { + emit noOutputMessage(); + } + + + } +} + +} // namespace QmlDebug diff --git a/libs/qmldebug/qmloutputparser.h b/libs/qmldebug/qmloutputparser.h new file mode 100644 index 0000000000..8d394c789f --- /dev/null +++ b/libs/qmldebug/qmloutputparser.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLOUTPUTPARSER_H +#define QMLOUTPUTPARSER_H + +#include "qmldebug_global.h" + +#include <QObject> + +namespace QmlDebug { + +class QMLDEBUG_EXPORT QmlOutputParser : public QObject +{ + Q_OBJECT +public: + QmlOutputParser(QObject *parent = 0); + + void setNoOutputText(const QString &text); + void processOutput(const QString &output); + +signals: + void waitingForConnectionOnPort(quint16 port); + void connectionEstablishedMessage(); + void errorMessage(const QString &detailedError); + void unknownMessage(const QString &unknownMessage); + void noOutputMessage(); + +private: + QString m_noOutputText; + QString m_buffer; +}; + +} // namespace QmlDebug + +#endif // QMLOUTPUTPARSER_H diff --git a/libs/qmldebug/qmlprofilereventlocation.h b/libs/qmldebug/qmlprofilereventlocation.h new file mode 100644 index 0000000000..8190490654 --- /dev/null +++ b/libs/qmldebug/qmlprofilereventlocation.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLPROFILEREVENTLOCATION_H +#define QMLPROFILEREVENTLOCATION_H + +#include "qmldebug_global.h" + +#include <QString> + +namespace QmlDebug { + +struct QMLDEBUG_EXPORT QmlEventLocation +{ + QmlEventLocation() : line(-1),column(-1) {} + QmlEventLocation(const QString &file, int lineNumber, int columnNumber) : filename(file), line(lineNumber), column(columnNumber) {} + QString filename; + int line; + int column; +}; + +} + +#endif // QMLPROFILEREVENTLOCATION_H diff --git a/libs/qmldebug/qmlprofilereventtypes.h b/libs/qmldebug/qmlprofilereventtypes.h new file mode 100644 index 0000000000..7928bdf984 --- /dev/null +++ b/libs/qmldebug/qmlprofilereventtypes.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLPROFILEREVENTTYPES_H +#define QMLPROFILEREVENTTYPES_H + +namespace QmlDebug { + +enum QmlEventType { + Painting, + Compiling, + Creating, + Binding, + HandlingSignal, + PixmapCacheEvent, + SceneGraphFrameEvent, + + MaximumQmlEventType +}; + +enum BindingType { + QmlBinding, + V8Binding, + OptimizedBinding, + + MaximumBindingType +}; + +namespace Constants { +const char TYPE_PAINTING_STR[] = "Painting"; +const char TYPE_COMPILING_STR[] = "Compiling"; +const char TYPE_CREATING_STR[] = "Creating"; +const char TYPE_BINDING_STR[] = "Binding"; +const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal"; +const char PROFILER_FILE_VERSION[] = "1.02"; +const int QML_MIN_LEVEL = 1; +} + +} // namespace QmlDebug + +#endif //QMLPROFILEREVENTTYPES_H diff --git a/libs/qmldebug/qmlprofilertraceclient.cpp b/libs/qmldebug/qmlprofilertraceclient.cpp new file mode 100644 index 0000000000..c7c99d353d --- /dev/null +++ b/libs/qmldebug/qmlprofilertraceclient.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmlprofilertraceclient.h" + +namespace QmlDebug { + +class QmlProfilerTraceClientPrivate { +public: + QmlProfilerTraceClientPrivate(QmlProfilerTraceClient *_q) + : q(_q) + , inProgressRanges(0) + , maximumTime(0) + , recording(false) + { + ::memset(rangeCount, 0, MaximumQmlEventType * sizeof(int)); + } + + void sendRecordingStatus(); + + QmlProfilerTraceClient *q; + qint64 inProgressRanges; + QStack<qint64> rangeStartTimes[MaximumQmlEventType]; + QStack<QStringList> rangeDatas[MaximumQmlEventType]; + QStack<QmlEventLocation> rangeLocations[MaximumQmlEventType]; + QStack<BindingType> bindingTypes; + int rangeCount[MaximumQmlEventType]; + qint64 maximumTime; + bool recording; +}; + +} // namespace QmlDebug + +using namespace QmlDebug; + +static const int GAP_TIME = 150; + +void QmlProfilerTraceClientPrivate::sendRecordingStatus() +{ + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + stream << recording; + q->sendMessage(ba); +} + +QmlProfilerTraceClient::QmlProfilerTraceClient(QmlDebugConnection *client) + : QmlDebugClient(QLatin1String("CanvasFrameRate"), client) + , d(new QmlProfilerTraceClientPrivate(this)) +{ +} + +QmlProfilerTraceClient::~QmlProfilerTraceClient() +{ + //Disable profiling if started by client + //Profiling data will be lost!! + if (isRecording()) + setRecording(false); + delete d; +} + +void QmlProfilerTraceClient::clearData() +{ + ::memset(d->rangeCount, 0, MaximumQmlEventType * sizeof(int)); + for (int eventType = 0; eventType < MaximumQmlEventType; eventType++) { + d->rangeDatas[eventType].clear(); + d->rangeLocations[eventType].clear(); + d->rangeStartTimes[eventType].clear(); + } + d->bindingTypes.clear(); + emit cleared(); +} + +void QmlProfilerTraceClient::sendRecordingStatus() +{ + d->sendRecordingStatus(); +} + +bool QmlProfilerTraceClient::isEnabled() const +{ + return status() == Enabled; +} + +bool QmlProfilerTraceClient::isRecording() const +{ + return d->recording; +} + +void QmlProfilerTraceClient::setRecording(bool v) +{ + if (v == d->recording) + return; + + d->recording = v; + + if (status() == Enabled) + sendRecordingStatus(); + + emit recordingChanged(v); +} + +void QmlProfilerTraceClient::setRecordingFromServer(bool v) +{ + if (v == d->recording) + return; + d->recording = v; + emit recordingChanged(v); +} + +void QmlProfilerTraceClient::statusChanged(ClientStatus /*status*/) +{ + emit enabledChanged(); +} + +void QmlProfilerTraceClient::messageReceived(const QByteArray &data) +{ + QByteArray rwData = data; + QDataStream stream(&rwData, QIODevice::ReadOnly); + + qint64 time; + int messageType; + + stream >> time >> messageType; + + if (messageType >= MaximumMessage) + return; + + if (time > (d->maximumTime + GAP_TIME) && 0 == d->inProgressRanges) + emit gap(time); + + if (messageType == Event) { + int event; + stream >> event; + + // stop with the first data + if (d->recording && event != StartTrace) + setRecordingFromServer(false); + else if ((!d->recording) && event == StartTrace) + setRecordingFromServer(true); + + if (event == EndTrace) { + emit this->traceFinished(time); + d->maximumTime = time; + d->maximumTime = qMax(time, d->maximumTime); + } else if (event == AnimationFrame) { + int frameRate, animationCount; + stream >> frameRate >> animationCount; + emit this->frame(time, frameRate, animationCount); + d->maximumTime = qMax(time, d->maximumTime); + } else if (event == StartTrace) { + emit this->traceStarted(time); + d->maximumTime = time; + } else if (event < MaximumEventType) { + emit this->event((EventType)event, time); + d->maximumTime = qMax(time, d->maximumTime); + } + } else if (messageType == Complete) { + emit complete(); + } else if (messageType == SceneGraphFrame) { + int sgEventType; + int count = 0; + qint64 params[5]; + + stream >> sgEventType; + while (!stream.atEnd()) { + stream >> params[count++]; + } + emit sceneGraphFrame(SceneGraphFrameEvent, sgEventType, time, params[0], params[1], params[2], params[3], params[4]); + } else { + int range; + stream >> range; + + if (range >= MaximumQmlEventType) + return; + + if (messageType == RangeStart) { + d->rangeStartTimes[range].push(time); + d->inProgressRanges |= (static_cast<qint64>(1) << range); + ++d->rangeCount[range]; + + // read binding type + if ((QmlEventType)range == Binding) { + int bindingType = (int)QmlBinding; + if (!stream.atEnd()) + stream >> bindingType; + d->bindingTypes.push((BindingType)bindingType); + } + + // stop with the first data + if (d->recording) + setRecordingFromServer(false); + } else if (messageType == RangeData) { + QString data; + stream >> data; + + int count = d->rangeCount[range]; + if (count > 0) { + while (d->rangeDatas[range].count() < count) + d->rangeDatas[range].push(QStringList()); + d->rangeDatas[range][count-1] << data; + } + + } else if (messageType == RangeLocation) { + QString fileName; + int line; + int column = -1; + stream >> fileName >> line; + + if (!stream.atEnd()) + stream >> column; + + if (d->rangeCount[range] > 0) + d->rangeLocations[range].push(QmlEventLocation(fileName, line, column)); + } else { + if (d->rangeCount[range] > 0) { + --d->rangeCount[range]; + if (d->inProgressRanges & (static_cast<qint64>(1) << range)) + d->inProgressRanges &= ~(static_cast<qint64>(1) << range); + + d->maximumTime = qMax(time, d->maximumTime); + QStringList data = d->rangeDatas[range].count() ? d->rangeDatas[range].pop() : QStringList(); + QmlEventLocation location = d->rangeLocations[range].count() ? d->rangeLocations[range].pop() : QmlEventLocation(); + + qint64 startTime = d->rangeStartTimes[range].pop(); + BindingType bindingType = QmlBinding; + if ((QmlEventType)range == Binding) + bindingType = d->bindingTypes.pop(); + emit this->range((QmlEventType)range, bindingType, startTime, time - startTime, data, location); + if (d->rangeCount[range] == 0) { + int count = d->rangeDatas[range].count() + + d->rangeStartTimes[range].count() + + d->rangeLocations[range].count(); + if (count != 0) + qWarning() << "incorrectly nested data"; + } + } + } + } +} diff --git a/libs/qmldebug/qmlprofilertraceclient.h b/libs/qmldebug/qmlprofilertraceclient.h new file mode 100644 index 0000000000..83c4e8772c --- /dev/null +++ b/libs/qmldebug/qmlprofilertraceclient.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLPROFILERTRACECLIENT_H +#define QMLPROFILERTRACECLIENT_H + +#include "qmldebugclient.h" +#include "qmlprofilereventtypes.h" +#include "qmlprofilereventlocation.h" +#include "qmldebug_global.h" + +#include <QStack> +#include <QStringList> + +namespace QmlDebug { + +class QMLDEBUG_EXPORT QmlProfilerTraceClient : public QmlDebug::QmlDebugClient +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool recording READ isRecording WRITE setRecording NOTIFY recordingChanged) + + // don't hide by signal + using QObject::event; + +public: + QmlProfilerTraceClient(QmlDebugConnection *client); + ~QmlProfilerTraceClient(); + + enum EventType { + FramePaint, + Mouse, + Key, + AnimationFrame, + EndTrace, + StartTrace, + + MaximumEventType + }; + + enum Message { + Event, + RangeStart, + RangeData, + RangeLocation, + RangeEnd, + Complete, + PixmapCacheEvent, + SceneGraphFrame, + + MaximumMessage + }; + + bool isEnabled() const; + bool isRecording() const; + void setRecording(bool); + +public slots: + void clearData(); + void sendRecordingStatus(); + +signals: + void complete(); + void gap(qint64 time); + void event(int event, qint64 time); + void traceFinished( qint64 time ); + void traceStarted( qint64 time ); + void range(int type, int bindingType, qint64 startTime, qint64 length, + const QStringList &data, const QmlDebug::QmlEventLocation &location); + void frame(qint64 time, int frameRate, int animationCount); + void sceneGraphFrame(int eventType, int sgEventType, qint64 time, qint64 param1, qint64 param2, qint64 param3, qint64 param4, qint64 param5); + + void recordingChanged(bool arg); + + void enabledChanged(); + void cleared(); + +protected: + virtual void statusChanged(ClientStatus status); + virtual void messageReceived(const QByteArray &); + +private: + void setRecordingFromServer(bool); + +private: + class QmlProfilerTraceClientPrivate *d; +}; + +} // namespace QmlDebug + +#endif // QMLPROFILERTRACECLIENT_H diff --git a/libs/qmldebug/qmltoolsclient.cpp b/libs/qmldebug/qmltoolsclient.cpp new file mode 100644 index 0000000000..12f876b2b8 --- /dev/null +++ b/libs/qmldebug/qmltoolsclient.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qmltoolsclient.h" +#include <QStringList> + +//INSPECTOR SERVICE PROTOCOL +// <HEADER><COMMAND><DATA> +// <HEADER> : <type{request, response, event}><requestId/eventId>[<response_success_bool>] +// <COMMAND> : {"enable", "disable", "select", "reload", "setAnimationSpeed", +// "showAppOnTop", "createObject", "destroyObject", "moveObject", +// "clearCache"} +// <DATA> : select: <debugIds_int_list> +// reload: <hash<changed_filename_string, filecontents_bytearray>> +// setAnimationSpeed: <speed_real> +// showAppOnTop: <set_bool> +// createObject: <qml_string><parentId_int><imports_string_list><filename_string> +// destroyObject: <debugId_int> +// moveObject: <debugId_int><newParentId_int> +// clearCache: void + +const char REQUEST[] = "request"; +const char RESPONSE[] = "response"; +const char EVENT[] = "event"; +const char ENABLE[] = "enable"; +const char DISABLE[] = "disable"; +const char SELECT[] = "select"; +const char RELOAD[] = "reload"; +const char SET_ANIMATION_SPEED[] = "setAnimationSpeed"; +const char SHOW_APP_ON_TOP[] = "showAppOnTop"; +const char CREATE_OBJECT[] = "createObject"; +const char DESTROY_OBJECT[] = "destroyObject"; +const char MOVE_OBJECT[] = "moveObject"; +const char CLEAR_CACHE[] = "clearCache"; + +namespace QmlDebug { + +QmlToolsClient::QmlToolsClient(QmlDebugConnection *client) + : BaseToolsClient(client, QLatin1String("QmlInspector")), + m_connection(client), + m_requestId(0), + m_reloadQueryId(-1), + m_slowDownFactor(1), + m_destroyObjectQueryId(-1) +{ + setObjectName(name()); +} + +void QmlToolsClient::messageReceived(const QByteArray &message) +{ + QDataStream ds(message); + + QByteArray type; + int requestId; + ds >> type >> requestId; + + if (type == QByteArray(RESPONSE)) { + bool success = false; + ds >> success; + + if ((m_reloadQueryId != -1) && (m_reloadQueryId == requestId) && success) + emit reloaded(); + + if ((m_destroyObjectQueryId != -1) && (m_destroyObjectQueryId == requestId) + && success && !ds.atEnd()) { + int objectDebugId; + ds >> objectDebugId; + emit destroyedObject(objectDebugId); + } + + log(LogReceive, type, QString(QLatin1String("requestId: %1 success: %2")) + .arg(QString::number(requestId)).arg(QString::number(success))); + } else if (type == QByteArray(EVENT)) { + QByteArray event; + ds >> event; + if (event == QByteArray(SELECT)) { + m_currentDebugIds.clear(); + QList<int> debugIds; + ds >> debugIds; + + QStringList debugIdStrings; + foreach (int debugId, debugIds) { + if (debugId != -1) { + m_currentDebugIds << debugId; + debugIdStrings << QString::number(debugId); + } + } + log(LogReceive, type + ':' + event, + QString::fromLatin1("[%1]").arg(debugIdStrings.join(QLatin1String(",")))); + emit currentObjectsChanged(m_currentDebugIds); + } + } else { + log(LogReceive, type, QLatin1String("Warning: Not handling message")); + } +} + +QList<int> QmlToolsClient::currentObjects() const +{ + return m_currentDebugIds; +} + +void QmlToolsClient::setCurrentObjects(const QList<int> &debugIds) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + if (debugIds == m_currentDebugIds) + return; + + m_currentDebugIds = debugIds; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(SELECT) << m_currentDebugIds; + + log(LogSend, SELECT, QString::fromLatin1("%1 [list of ids]").arg(debugIds.length())); + + sendMessage(message); +} + +void QmlToolsClient::setObjectIdList( + const QList<ObjectReference> &/*objectRoots*/) +{ + //NOT IMPLEMENTED +} + +void QmlToolsClient::clearComponentCache() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(CLEAR_CACHE); + + log(LogSend, CLEAR_CACHE); + + sendMessage(message); +} + +void QmlToolsClient::reload(const QHash<QString, QByteArray> &changesHash) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + m_reloadQueryId = m_requestId; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(RELOAD) << changesHash; + + log(LogSend, RELOAD); + + sendMessage(message); +} + +void QmlToolsClient::setDesignModeBehavior(bool inDesignMode) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++; + if (inDesignMode) + ds << QByteArray(ENABLE); + else + ds << QByteArray(DISABLE); + + log(LogSend, ENABLE, QLatin1String(inDesignMode ? "true" : "false")); + + sendMessage(message); +} + +void QmlToolsClient::setAnimationSpeed(qreal slowDownFactor) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(SET_ANIMATION_SPEED) << slowDownFactor; + + log(LogSend, SET_ANIMATION_SPEED, QString::number(slowDownFactor)); + + sendMessage(message); + //Cache non-zero values + if (slowDownFactor) + m_slowDownFactor = slowDownFactor; +} + +void QmlToolsClient::setAnimationPaused(bool paused) +{ + if (paused) + setAnimationSpeed(0); + else + setAnimationSpeed(m_slowDownFactor); +} + +void QmlToolsClient::changeToSelectTool() +{ +// NOT IMPLEMENTED +} + +void QmlToolsClient::changeToSelectMarqueeTool() +{ +// NOT IMPLEMENTED +} + +void QmlToolsClient::changeToZoomTool() +{ +// NOT IMPLEMENTED +} + +void QmlToolsClient::showAppOnTop(bool showOnTop) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(SHOW_APP_ON_TOP) << showOnTop; + + log(LogSend, SHOW_APP_ON_TOP, QLatin1String(showOnTop ? "true" : "false")); + + sendMessage(message); +} + +void QmlToolsClient::createQmlObject(const QString &qmlText, + int parentDebugId, + const QStringList &imports, + const QString &filename, int order) +{ + if (!m_connection || !m_connection->isConnected()) + return; + + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(CREATE_OBJECT) + << qmlText + << parentDebugId + << imports + << filename + << order; + + log(LogSend, CREATE_OBJECT, QString::fromLatin1("%1 %2 [%3] %4").arg(qmlText, + QString::number(parentDebugId), + imports.join(QLatin1String(",")), filename)); + + sendMessage(message); +} + +void QmlToolsClient::destroyQmlObject(int debugId) +{ + if (!m_connection || !m_connection->isConnected()) + return; + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + m_destroyObjectQueryId = m_requestId; + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(DESTROY_OBJECT) << debugId; + + log(LogSend, DESTROY_OBJECT, QString::number(debugId)); + + sendMessage(message); +} + +void QmlToolsClient::reparentQmlObject(int debugId, int newParent) +{ + if (!m_connection || !m_connection->isConnected()) + return; + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << QByteArray(REQUEST) << m_requestId++ + << QByteArray(MOVE_OBJECT) << debugId << newParent; + + log(LogSend, MOVE_OBJECT, QString::fromLatin1("%1 %2").arg(QString::number(debugId), + QString::number(newParent))); + + sendMessage(message); +} + + +void QmlToolsClient::applyChangesToQmlFile() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + // TODO +} + +void QmlToolsClient::applyChangesFromQmlFile() +{ + if (!m_connection || !m_connection->isConnected()) + return; + + // TODO +} + +void QmlToolsClient::log(LogDirection direction, + const QByteArray &message, + const QString &extra) +{ + QString msg; + if (direction == LogSend) + msg += QLatin1String("sending "); + else + msg += QLatin1String("receiving "); + + msg += QLatin1String(message); + msg += QLatin1Char(' '); + msg += extra; + emit logActivity(name(), msg); +} + +} // namespace QmlDebug diff --git a/libs/qmldebug/qmltoolsclient.h b/libs/qmldebug/qmltoolsclient.h new file mode 100644 index 0000000000..b153842da1 --- /dev/null +++ b/libs/qmldebug/qmltoolsclient.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QMLTOOLSCLIENT_H +#define QMLTOOLSCLIENT_H + +#include "basetoolsclient.h" + +namespace QmlDebug { + +class QMLDEBUG_EXPORT QmlToolsClient : public BaseToolsClient +{ + Q_OBJECT +public: + explicit QmlToolsClient(QmlDebugConnection *client); + + void setCurrentObjects(const QList<int> &debugIds); + void reload(const QHash<QString, QByteArray> &changesHash); + bool supportReload() const { return true; } + void setDesignModeBehavior(bool inDesignMode); + void setAnimationSpeed(qreal slowDownFactor); + void setAnimationPaused(bool paused); + void changeToSelectTool(); + void changeToSelectMarqueeTool(); + void changeToZoomTool(); + void showAppOnTop(bool showOnTop); + + void createQmlObject(const QString &qmlText, int parentDebugId, + const QStringList &imports, const QString &filename, + int order); + void destroyQmlObject(int debugId); + void reparentQmlObject(int debugId, int newParent); + + void applyChangesToQmlFile(); + void applyChangesFromQmlFile(); + + QList<int> currentObjects() const; + + // ### Qt 4.8: remove if we can have access to qdeclarativecontextdata or id's + void setObjectIdList(const QList<ObjectReference> &objectRoots); + + void clearComponentCache(); + +protected: + void messageReceived(const QByteArray &); + +private: + void log(LogDirection direction, + const QByteArray &message, + const QString &extra = QString()); + +private: + QList<int> m_currentDebugIds; + QmlDebugConnection *m_connection; + int m_requestId; + int m_reloadQueryId; + qreal m_slowDownFactor; + int m_destroyObjectQueryId; +}; + +} // namespace QmlDebug + +#endif // QMLTOOLSCLIENT_H diff --git a/libs/qmldebug/qpacketprotocol.cpp b/libs/qmldebug/qpacketprotocol.cpp new file mode 100644 index 0000000000..d9aa7493a9 --- /dev/null +++ b/libs/qmldebug/qpacketprotocol.cpp @@ -0,0 +1,541 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qpacketprotocol.h" + +#include <qbuffer.h> +#include <qelapsedtimer.h> + +namespace QmlDebug { + +static const unsigned int MAX_PACKET_SIZE = 0x7FFFFFFF; + +/*! + \class QPacketProtocol + \internal + + \brief The QPacketProtocol class encapsulates communicating discrete packets + across fragmented IO channels, such as TCP sockets. + + QPacketProtocol makes it simple to send arbitrary sized data "packets" across + fragmented transports such as TCP and UDP. + + As transmission boundaries are not respected, sending packets over protocols + like TCP frequently involves "stitching" them back together at the receiver. + QPacketProtocol makes this easier by performing this task for you. Packet + data sent using QPacketProtocol is prepended with a 4-byte size header + allowing the receiving QPacketProtocol to buffer the packet internally until + it has all been received. QPacketProtocol does not perform any sanity + checking on the size or on the data, so this class should only be used in + prototyping or trusted situations where DOS attacks are unlikely. + + QPacketProtocol does not perform any communications itself. Instead it can + operate on any QIODevice that supports the QIODevice::readyRead() signal. A + logical "packet" is encapsulated by the companion QPacket class. The + following example shows two ways to send data using QPacketProtocol. The + transmitted data is equivalent in both. + + \code + QTcpSocket socket; + // ... connect socket ... + + QPacketProtocol protocol(&socket); + + // Send packet the quick way + protocol.send() << "Hello world" << 123; + + // Send packet the longer way + QPacket packet; + packet << "Hello world" << 123; + protocol.send(packet); + \endcode + + Likewise, the following shows how to read data from QPacketProtocol, assuming + that the QPacketProtocol::readyRead() signal has been emitted. + + \code + // ... QPacketProtocol::readyRead() is emitted ... + + int a; + QByteArray b; + + // Receive packet the quick way + protocol.read() >> a >> b; + + // Receive packet the longer way + QPacket packet = protocol.read(); + p >> a >> b; + \endcode + + \ingroup io + \sa QPacket +*/ + +class QPacketProtocolPrivate : public QObject +{ + Q_OBJECT + +public: + QPacketProtocolPrivate(QPacketProtocol *parent, QIODevice *_dev) + : QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE), + waitingForPacket(false), dev(_dev) + { + Q_ASSERT(4 == sizeof(qint32)); + + QObject::connect(this, SIGNAL(readyRead()), + parent, SIGNAL(readyRead())); + QObject::connect(this, SIGNAL(packetWritten()), + parent, SIGNAL(packetWritten())); + QObject::connect(this, SIGNAL(invalidPacket()), + parent, SIGNAL(invalidPacket())); + QObject::connect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::connect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::connect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + } + +Q_SIGNALS: + void readyRead(); + void packetWritten(); + void invalidPacket(); + +public Q_SLOTS: + void aboutToClose() + { + inProgress.clear(); + sendingPackets.clear(); + inProgressSize = -1; + } + + void bytesWritten(qint64 bytes) + { + Q_ASSERT(!sendingPackets.isEmpty()); + + while (bytes) { + if (sendingPackets.at(0) > bytes) { + sendingPackets[0] -= bytes; + bytes = 0; + } else { + bytes -= sendingPackets.at(0); + sendingPackets.removeFirst(); + emit packetWritten(); + } + } + } + + void readyToRead() + { + while (true) { + // Need to get trailing data + if (-1 == inProgressSize) { + // We need a size header of sizeof(qint32) + if (sizeof(qint32) > (uint)dev->bytesAvailable()) + return; + + // Read size header + int read = dev->read((char *)&inProgressSize, sizeof(qint32)); + Q_ASSERT(read == sizeof(qint32)); + Q_UNUSED(read); + + // Check sizing constraints + if (inProgressSize > maxPacketSize) { + QObject::disconnect(dev, SIGNAL(readyRead()), + this, SLOT(readyToRead())); + QObject::disconnect(dev, SIGNAL(aboutToClose()), + this, SLOT(aboutToClose())); + QObject::disconnect(dev, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64))); + dev = 0; + emit invalidPacket(); + return; + } + + inProgressSize -= sizeof(qint32); + } else { + inProgress.append(dev->read(inProgressSize - inProgress.size())); + + if (inProgressSize == inProgress.size()) { + // Packet has arrived! + packets.append(inProgress); + inProgressSize = -1; + inProgress.clear(); + + waitingForPacket = false; + emit readyRead(); + } else + return; + } + } + } + +public: + QList<qint64> sendingPackets; + QList<QByteArray> packets; + QByteArray inProgress; + qint32 inProgressSize; + qint32 maxPacketSize; + bool waitingForPacket; + QIODevice *dev; +}; + +/*! + Construct a QPacketProtocol instance that works on \a dev with the + specified \a parent. + */ +QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) + : QObject(parent), d(new QPacketProtocolPrivate(this, dev)) +{ + Q_ASSERT(dev); +} + +/*! + Destroys the QPacketProtocol instance. + */ +QPacketProtocol::~QPacketProtocol() +{ +} + +/*! + Returns the maximum packet size allowed. By default this is + 2,147,483,647 bytes. + + If a packet claiming to be larger than the maximum packet size is received, + the QPacketProtocol::invalidPacket() signal is emitted. + + \sa QPacketProtocol::setMaximumPacketSize() + */ +qint32 QPacketProtocol::maximumPacketSize() const +{ + return d->maxPacketSize; +} + +/*! + Sets the maximum allowable packet size to \a max. + + \sa QPacketProtocol::maximumPacketSize() + */ +qint32 QPacketProtocol::setMaximumPacketSize(qint32 max) +{ + if (max > (signed)sizeof(qint32)) + d->maxPacketSize = max; + return d->maxPacketSize; +} + +/*! + Returns a streamable object that is transmitted on destruction. For example + + \code + protocol.send() << "Hello world" << 123; + \endcode + + will send a packet containing "Hello world" and 123. To construct more + complex packets, explicitly construct a QPacket instance. + */ +QPacketAutoSend QPacketProtocol::send() +{ + return QPacketAutoSend(this); +} + +/*! + \fn void QPacketProtocol::send(const QPacket & packet) + + Transmit the \a packet. + */ +void QPacketProtocol::send(const QPacket & p) +{ + if (p.b.isEmpty()) + return; // We don't send empty packets + + qint64 sendSize = p.b.size() + sizeof(qint32); + + d->sendingPackets.append(sendSize); + qint32 sendSize32 = sendSize; + qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); + Q_ASSERT(writeBytes == sizeof(qint32)); + writeBytes = d->dev->write(p.b); + Q_ASSERT(writeBytes == p.b.size()); + Q_UNUSED(writeBytes); // For building in release mode. +} + +/*! + Returns the number of received packets yet to be read. + */ +qint64 QPacketProtocol::packetsAvailable() const +{ + return d->packets.count(); +} + +/*! + Discard any unread packets. + */ +void QPacketProtocol::clear() +{ + d->packets.clear(); +} + +/*! + Return the next unread packet, or an invalid QPacket instance if no packets + are available. This method does NOT block. + */ +QPacket QPacketProtocol::read() +{ + if (0 == d->packets.count()) + return QPacket(); + + QPacket rv(d->packets.at(0)); + d->packets.removeFirst(); + return rv; +} + + +/* + Returns the difference between msecs and elapsed. If msecs is -1, + however, -1 is returned. +*/ +static int qt_timeout_value(int msecs, int elapsed) +{ + if (msecs == -1) + return -1; + + int timeout = msecs - elapsed; + return timeout < 0 ? 0 : timeout; +} + +/*! + This function locks until a new packet is available for reading and the + \l{QIODevice::}{readyRead()} signal has been emitted. The function + will timeout after \a msecs milliseconds; the default timeout is + 30000 milliseconds. + + The function returns true if the readyRead() signal is emitted and + there is new data available for reading; otherwise it returns false + (if an error occurred or the operation timed out). + */ + +bool QPacketProtocol::waitForReadyRead(int msecs) +{ + if (!d->packets.isEmpty()) + return true; + + QElapsedTimer stopWatch; + stopWatch.start(); + + d->waitingForPacket = true; + do { + if (!d->dev->waitForReadyRead(msecs)) + return false; + if (!d->waitingForPacket) + return true; + msecs = qt_timeout_value(msecs, stopWatch.elapsed()); + } while (true); +} + +/*! + Return the QIODevice passed to the QPacketProtocol constructor. +*/ +QIODevice *QPacketProtocol::device() +{ + return d->dev; +} + +/*! + \fn void QPacketProtocol::readyRead() + + Emitted whenever a new packet is received. Applications may use + QPacketProtocol::read() to retrieve this packet. + */ + +/*! + \fn void QPacketProtocol::invalidPacket() + + A packet larger than the maximum allowable packet size was received. The + packet will be discarded and, as it indicates corruption in the protocol, no + further packets will be received. + */ + +/*! + \fn void QPacketProtocol::packetWritten() + + Emitted each time a packet is completing written to the device. This signal + may be used for communications flow control. + */ + +/*! + \class QPacket + \internal + + \brief The QPacket class encapsulates an unfragmentable packet of data to be + transmitted by QPacketProtocol. + + The QPacket class works together with QPacketProtocol to make it simple to + send arbitrary sized data "packets" across fragmented transports such as TCP + and UDP. + + QPacket provides a QDataStream interface to an unfragmentable packet. + Applications should construct a QPacket, propagate it with data and then + transmit it over a QPacketProtocol instance. For example: + \code + QPacketProtocol protocol(...); + + QPacket myPacket; + myPacket << "Hello world!" << 123; + protocol.send(myPacket); + \endcode + + As long as both ends of the connection are using the QPacketProtocol class, + the data within this packet will be delivered unfragmented at the other end, + ready for extraction. + + \code + QByteArray greeting; + int count; + + QPacket myPacket = protocol.read(); + + myPacket >> greeting >> count; + \endcode + + Only packets returned from QPacketProtocol::read() may be read from. QPacket + instances constructed by directly by applications are for transmission only + and are considered "write only". Attempting to read data from them will + result in undefined behavior. + + \ingroup io + \sa QPacketProtocol + */ + +/*! + Constructs an empty write-only packet. + */ +QPacket::QPacket() + : QDataStream(), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::WriteOnly); + setDevice(buf); + setVersion(QDataStream::Qt_4_7); +} + +/*! + Destroys the QPacket instance. + */ +QPacket::~QPacket() +{ + if (buf) { + delete buf; + buf = 0; + } +} + +/*! + Creates a copy of \a other. The initial stream positions are shared, but the + two packets are otherwise independent. + */ +QPacket::QPacket(const QPacket & other) + : QDataStream(), b(other.b), buf(0) +{ + buf = new QBuffer(&b); + buf->open(other.buf->openMode()); + setDevice(buf); +} + +/*! + \internal + */ +QPacket::QPacket(const QByteArray & ba) + : QDataStream(), b(ba), buf(0) +{ + buf = new QBuffer(&b); + buf->open(QIODevice::ReadOnly); + setDevice(buf); +} + +/*! + Returns true if this packet is empty - that is, contains no data. + */ +bool QPacket::isEmpty() const +{ + return b.isEmpty(); +} + +/*! + Returns raw packet data. + */ +QByteArray QPacket::data() const +{ + return b; +} + +/*! + Clears data in the packet. This is useful for reusing one writable packet. + For example + \code + QPacketProtocol protocol(...); + + QPacket packet; + + packet << "Hello world!" << 123; + protocol.send(packet); + + packet.clear(); + packet << "Goodbyte world!" << 789; + protocol.send(packet); + \endcode + */ +void QPacket::clear() +{ + QBuffer::OpenMode oldMode = buf->openMode(); + buf->close(); + b.clear(); + buf->setBuffer(&b); // reset QBuffer internals with new size of b. + buf->open(oldMode); +} + +/*! + \class QPacketAutoSend + \internal + + \internal + */ +QPacketAutoSend::QPacketAutoSend(QPacketProtocol *_p) + : QPacket(), p(_p) +{ +} + +QPacketAutoSend::~QPacketAutoSend() +{ + if (!b.isEmpty()) + p->send(*this); +} + +} // namespace QmlDebug + +#include <qpacketprotocol.moc> diff --git a/libs/qmldebug/qpacketprotocol.h b/libs/qmldebug/qpacketprotocol.h new file mode 100644 index 0000000000..b1ce313809 --- /dev/null +++ b/libs/qmldebug/qpacketprotocol.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QPACKETPROTOCOL_H +#define QPACKETPROTOCOL_H + +#include <qobject.h> +#include <qdatastream.h> + +QT_BEGIN_NAMESPACE +class QIODevice; +class QBuffer; +QT_END_NAMESPACE + +namespace QmlDebug { + +class QPacket; +class QPacketAutoSend; + +class QPacketProtocolPrivate; + +class QPacketProtocol : public QObject +{ + Q_OBJECT + +public: + explicit QPacketProtocol(QIODevice *dev, QObject *parent = 0); + virtual ~QPacketProtocol(); + + qint32 maximumPacketSize() const; + qint32 setMaximumPacketSize(qint32); + + QPacketAutoSend send(); + void send(const QPacket &); + + qint64 packetsAvailable() const; + QPacket read(); + + bool waitForReadyRead(int msecs = 3000); + + void clear(); + + QIODevice *device(); + +Q_SIGNALS: + void readyRead(); + void invalidPacket(); + void packetWritten(); + +private: + QPacketProtocolPrivate *d; +}; + + +class QPacket : public QDataStream +{ +public: + QPacket(); + QPacket(const QPacket &); + virtual ~QPacket(); + + void clear(); + bool isEmpty() const; + QByteArray data() const; + +protected: + friend class QPacketProtocol; + QPacket(const QByteArray &ba); + QByteArray b; + QBuffer *buf; +}; + +class QPacketAutoSend : public QPacket +{ +public: + virtual ~QPacketAutoSend(); + +private: + friend class QPacketProtocol; + QPacketAutoSend(QPacketProtocol *); + QPacketProtocol *p; +}; + +} // QmlDebug + +#endif diff --git a/libs/qmldebug/qv8profilerclient.cpp b/libs/qmldebug/qv8profilerclient.cpp new file mode 100644 index 0000000000..f93335c4fe --- /dev/null +++ b/libs/qmldebug/qv8profilerclient.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qv8profilerclient.h" + +namespace QmlDebug { + +class QV8ProfilerClientPrivate { +public: + QV8ProfilerClientPrivate(QV8ProfilerClient *_q) + : q(_q) + , recording(false) + { + } + + void sendRecordingStatus(); + + QV8ProfilerClient *q; + bool recording; +}; + +} // namespace QmlDebug + +using namespace QmlDebug; + +void QV8ProfilerClientPrivate::sendRecordingStatus() +{ + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + QByteArray cmd("V8PROFILER"); + QByteArray option(""); + QByteArray title(""); + + if (recording) + option = "start"; + else + option = "stop"; + stream << cmd << option << title; + q->sendMessage(ba); +} + +QV8ProfilerClient::QV8ProfilerClient(QmlDebugConnection *client) + : QmlDebugClient(QLatin1String("V8Profiler"), client) + , d(new QV8ProfilerClientPrivate(this)) +{ +} + +QV8ProfilerClient::~QV8ProfilerClient() +{ + //Disable profiling if started by client + //Profiling data will be lost!! + if (isRecording()) + setRecording(false); + delete d; +} + +void QV8ProfilerClient::clearData() +{ + emit cleared(); +} + +bool QV8ProfilerClient::isEnabled() const +{ + return status() == Enabled; +} + +void QV8ProfilerClient::sendRecordingStatus() +{ + d->sendRecordingStatus(); +} + +bool QV8ProfilerClient::isRecording() const +{ + return d->recording; +} + +void QV8ProfilerClient::setRecording(bool v) +{ + if (v == d->recording) + return; + + d->recording = v; + + if (status() == Enabled) + sendRecordingStatus(); + + emit recordingChanged(v); +} + +void QV8ProfilerClient::setRecordingFromServer(bool v) +{ + if (v == d->recording) + return; + + d->recording = v; + + emit recordingChanged(v); +} + +void QV8ProfilerClient::statusChanged(ClientStatus /*status*/) +{ + emit enabledChanged(); +} + +void QV8ProfilerClient::messageReceived(const QByteArray &data) +{ + QByteArray rwData = data; + QDataStream stream(&rwData, QIODevice::ReadOnly); + + int messageType; + + stream >> messageType; + + if (messageType == V8Complete) { + setRecordingFromServer(false); + emit complete(); + } else if (messageType == V8ProfilingStarted) { + setRecordingFromServer(true); + } else if (messageType == V8Entry) { + QString filename; + QString function; + int lineNumber; + double totalTime; + double selfTime; + int depth; + + stream >> filename >> function >> lineNumber >> totalTime >> selfTime >> depth; + emit this->v8range(depth, function, filename, lineNumber, totalTime, selfTime); + } +} + diff --git a/libs/qmldebug/qv8profilerclient.h b/libs/qmldebug/qv8profilerclient.h new file mode 100644 index 0000000000..39e77bd98f --- /dev/null +++ b/libs/qmldebug/qv8profilerclient.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QV8PROFILERCLIENT_H +#define QV8PROFILERCLIENT_H + +#include "qmldebugclient.h" +#include "qmlprofilereventtypes.h" +#include "qmldebug_global.h" + +#include <QStack> +#include <QStringList> + +namespace QmlDebug { + +class QMLDEBUG_EXPORT QV8ProfilerClient : public QmlDebugClient +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool recording READ isRecording WRITE setRecording NOTIFY recordingChanged) + +public: + enum Message { + V8Entry, + V8Complete, + V8SnapshotChunk, + V8SnapshotComplete, + V8ProfilingStarted, + + V8MaximumMessage + }; + + QV8ProfilerClient(QmlDebugConnection *client); + ~QV8ProfilerClient(); + + bool isEnabled() const; + bool isRecording() const; + void setRecording(bool); + +public slots: + void clearData(); + void sendRecordingStatus(); + +signals: + void complete(); + void v8range(int depth, const QString &function, const QString &filename, + int lineNumber, double totalTime, double selfTime); + + void recordingChanged(bool arg); + + void enabledChanged(); + void cleared(); + +private: + void setRecordingFromServer(bool); + +protected: + virtual void statusChanged(ClientStatus); + virtual void messageReceived(const QByteArray &); + +private: + class QV8ProfilerClientPrivate *d; +}; + +} // namespace QmlDebug + +#endif // QV8PROFILERCLIENT_H diff --git a/qtcreatorlibrary.pri b/qtcreatorlibrary.pri new file mode 100644 index 0000000000..9e525ccfdf --- /dev/null +++ b/qtcreatorlibrary.pri @@ -0,0 +1,7 @@ +IDE_SOURCE_TREE=$$(IDE_SOURCE_TREE) +IDE_BUILD_TREE=$$(IDE_BUILD_TREE) + +isEmpty(IDE_SOURCE_TREE):error(Set IDE_SOURCE_TREE environment variable) +isEmpty(IDE_BUILD_TREE):error(Set IDE_BUILD_TREE environment variable) + +include($$IDE_SOURCE_TREE/src/qtcreatorlibrary.pri) |