| /**************************************************************************** |
| ** |
| ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtDeclarative module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** 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, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "private/qdeclarativedebugserver_p.h" |
| #include "private/qdeclarativedebugservice_p.h" |
| #include "private/qdeclarativedebugservice_p_p.h" |
| #include "private/qdeclarativeengine_p.h" |
| |
| #include <QtCore/QDir> |
| #include <QtCore/QPluginLoader> |
| #include <QtCore/QStringList> |
| |
| #include <private/qobject_p.h> |
| #include <private/qapplication_p.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /* |
| QDeclarativeDebug Protocol (Version 1): |
| |
| handshake: |
| 1. Client sends |
| "QDeclarativeDebugServer" 0 version pluginNames |
| version: an int representing the highest protocol version the client knows |
| pluginNames: plugins available on client side |
| 2. Server sends |
| "QDeclarativeDebugClient" 0 version pluginNames |
| version: an int representing the highest protocol version the client & server know |
| pluginNames: plugins available on server side. plugins both in the client and server message are enabled. |
| client plugin advertisement |
| 1. Client sends |
| "QDeclarativeDebugServer" 1 pluginNames |
| server plugin advertisement |
| 1. Server sends |
| "QDeclarativeDebugClient" 1 pluginNames |
| plugin communication: |
| Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin. |
| */ |
| |
| const int protocolVersion = 1; |
| |
| |
| class QDeclarativeDebugServerPrivate : public QObjectPrivate |
| { |
| Q_DECLARE_PUBLIC(QDeclarativeDebugServer) |
| public: |
| QDeclarativeDebugServerPrivate(); |
| |
| void advertisePlugins(); |
| |
| QDeclarativeDebugServerConnection *connection; |
| QHash<QString, QDeclarativeDebugService *> plugins; |
| QStringList clientPlugins; |
| bool gotHello; |
| |
| static QDeclarativeDebugServerConnection *loadConnectionPlugin(const QString &pluginName); |
| }; |
| |
| QDeclarativeDebugServerPrivate::QDeclarativeDebugServerPrivate() : |
| connection(0), |
| gotHello(false) |
| { |
| } |
| |
| void QDeclarativeDebugServerPrivate::advertisePlugins() |
| { |
| if (!gotHello) |
| return; |
| |
| QByteArray message; |
| { |
| QDataStream out(&message, QIODevice::WriteOnly); |
| out << QString(QLatin1String("QDeclarativeDebugClient")) << 1 << plugins.keys(); |
| } |
| connection->send(message); |
| } |
| |
| QDeclarativeDebugServerConnection *QDeclarativeDebugServerPrivate::loadConnectionPlugin( |
| const QString &pluginName) |
| { |
| QStringList pluginCandidates; |
| const QStringList paths = QCoreApplication::libraryPaths(); |
| foreach (const QString &libPath, paths) { |
| const QDir dir(libPath + QLatin1String("/qmltooling")); |
| if (dir.exists()) { |
| QStringList plugins(dir.entryList(QDir::Files)); |
| foreach (const QString &pluginPath, plugins) { |
| if (QFileInfo(pluginPath).fileName().contains(pluginName)) |
| pluginCandidates << dir.absoluteFilePath(pluginPath); |
| } |
| } |
| } |
| |
| foreach (const QString &pluginPath, pluginCandidates) { |
| QPluginLoader loader(pluginPath); |
| if (!loader.load()) { |
| continue; |
| } |
| QDeclarativeDebugServerConnection *connection = 0; |
| if (QObject *instance = loader.instance()) |
| connection = qobject_cast<QDeclarativeDebugServerConnection*>(instance); |
| |
| if (connection) |
| return connection; |
| loader.unload(); |
| } |
| return 0; |
| } |
| |
| bool QDeclarativeDebugServer::hasDebuggingClient() const |
| { |
| Q_D(const QDeclarativeDebugServer); |
| return d->connection |
| && d->connection->isConnected() |
| && d->gotHello; |
| } |
| |
| QDeclarativeDebugServer *QDeclarativeDebugServer::instance() |
| { |
| static bool commandLineTested = false; |
| static QDeclarativeDebugServer *server = 0; |
| |
| if (!commandLineTested) { |
| commandLineTested = true; |
| |
| QApplicationPrivate *appD = static_cast<QApplicationPrivate*>(QObjectPrivate::get(qApp)); |
| #ifndef QDECLARATIVE_NO_DEBUG_PROTOCOL |
| // ### remove port definition when protocol is changed |
| int port = 0; |
| bool block = false; |
| bool ok = false; |
| |
| // format: qmljsdebugger=port:3768[,block] OR qmljsdebugger=ost[,block] |
| if (!appD->qmljsDebugArgumentsString().isEmpty()) { |
| if (!QDeclarativeEnginePrivate::qml_debugging_enabled) { |
| const QString message = |
| QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " |
| "Debugging has not been enabled.").arg( |
| appD->qmljsDebugArgumentsString()); |
| qWarning("%s", qPrintable(message)); |
| return 0; |
| } |
| |
| QString pluginName; |
| if (appD->qmljsDebugArgumentsString().indexOf(QLatin1String("port:")) == 0) { |
| int separatorIndex = appD->qmljsDebugArgumentsString().indexOf(QLatin1Char(',')); |
| port = appD->qmljsDebugArgumentsString().mid(5, separatorIndex - 5).toInt(&ok); |
| pluginName = QLatin1String("qmldbg_tcp"); |
| } else if (appD->qmljsDebugArgumentsString().contains("ost")) { |
| pluginName = QLatin1String("qmldbg_ost"); |
| ok = true; |
| } |
| |
| block = appD->qmljsDebugArgumentsString().contains(QLatin1String("block")); |
| |
| if (ok) { |
| server = new QDeclarativeDebugServer(); |
| |
| QDeclarativeDebugServerConnection *connection |
| = QDeclarativeDebugServerPrivate::loadConnectionPlugin(pluginName); |
| if (connection) { |
| server->d_func()->connection = connection; |
| |
| connection->setServer(server); |
| connection->setPort(port, block); |
| } else { |
| qWarning() << QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " |
| "Remote debugger plugin has not been found.").arg(appD->qmljsDebugArgumentsString()); |
| } |
| |
| } else { |
| qWarning(QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " |
| "Format is -qmljsdebugger=port:<port>[,block]").arg( |
| appD->qmljsDebugArgumentsString()).toAscii().constData()); |
| } |
| } |
| #else |
| if (!appD->qmljsDebugArgumentsString().isEmpty()) { |
| qWarning(QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " |
| "QtDeclarative is not configured for debugging.").arg( |
| appD->qmljsDebugArgumentsString()).toAscii().constData()); |
| } |
| #endif |
| } |
| |
| return server; |
| } |
| |
| QDeclarativeDebugServer::QDeclarativeDebugServer() |
| : QObject(*(new QDeclarativeDebugServerPrivate)) |
| { |
| } |
| |
| void QDeclarativeDebugServer::receiveMessage(const QByteArray &message) |
| { |
| Q_D(QDeclarativeDebugServer); |
| |
| QDataStream in(message); |
| if (!d->gotHello) { |
| |
| QString name; |
| int op; |
| in >> name >> op; |
| |
| if (name != QLatin1String("QDeclarativeDebugServer") |
| || op != 0) { |
| qWarning("QDeclarativeDebugServer: Invalid hello message"); |
| d->connection->disconnect(); |
| return; |
| } |
| |
| int version; |
| in >> version >> d->clientPlugins; |
| |
| QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin(); |
| for (; iter != d->plugins.end(); ++iter) { |
| QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable; |
| if (d->clientPlugins.contains(iter.key())) |
| newStatus = QDeclarativeDebugService::Enabled; |
| iter.value()->d_func()->status = newStatus; |
| iter.value()->statusChanged(newStatus); |
| } |
| |
| QByteArray helloAnswer; |
| { |
| QDataStream out(&helloAnswer, QIODevice::WriteOnly); |
| out << QString(QLatin1String("QDeclarativeDebugClient")) << 0 << protocolVersion << d->plugins.keys(); |
| } |
| d->connection->send(helloAnswer); |
| |
| d->gotHello = true; |
| qWarning("QDeclarativeDebugServer: Connection established"); |
| } else { |
| |
| QString debugServer(QLatin1String("QDeclarativeDebugServer")); |
| |
| QString name; |
| in >> name; |
| |
| if (name == debugServer) { |
| int op = -1; |
| in >> op; |
| |
| if (op == 1) { |
| // Service Discovery |
| QStringList oldClientPlugins = d->clientPlugins; |
| in >> d->clientPlugins; |
| |
| QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin(); |
| for (; iter != d->plugins.end(); ++iter) { |
| const QString pluginName = iter.key(); |
| QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable; |
| if (d->clientPlugins.contains(pluginName)) |
| newStatus = QDeclarativeDebugService::Enabled; |
| |
| if (oldClientPlugins.contains(pluginName) |
| != d->clientPlugins.contains(pluginName)) { |
| iter.value()->d_func()->status = newStatus; |
| iter.value()->statusChanged(newStatus); |
| } |
| } |
| } else { |
| qWarning("QDeclarativeDebugServer: Invalid control message %d", op); |
| } |
| } else { |
| QByteArray message; |
| in >> message; |
| |
| QHash<QString, QDeclarativeDebugService *>::Iterator iter = |
| d->plugins.find(name); |
| if (iter == d->plugins.end()) { |
| qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << name; |
| } else { |
| (*iter)->messageReceived(message); |
| } |
| } |
| } |
| } |
| |
| QList<QDeclarativeDebugService*> QDeclarativeDebugServer::services() const |
| { |
| const Q_D(QDeclarativeDebugServer); |
| return d->plugins.values(); |
| } |
| |
| QStringList QDeclarativeDebugServer::serviceNames() const |
| { |
| const Q_D(QDeclarativeDebugServer); |
| return d->plugins.keys(); |
| } |
| |
| bool QDeclarativeDebugServer::addService(QDeclarativeDebugService *service) |
| { |
| Q_D(QDeclarativeDebugServer); |
| if (!service || d->plugins.contains(service->name())) |
| return false; |
| |
| d->plugins.insert(service->name(), service); |
| d->advertisePlugins(); |
| |
| QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable; |
| if (d->clientPlugins.contains(service->name())) |
| newStatus = QDeclarativeDebugService::Enabled; |
| service->d_func()->status = newStatus; |
| service->statusChanged(newStatus); |
| return true; |
| } |
| |
| bool QDeclarativeDebugServer::removeService(QDeclarativeDebugService *service) |
| { |
| Q_D(QDeclarativeDebugServer); |
| if (!service || !d->plugins.contains(service->name())) |
| return false; |
| |
| d->plugins.remove(service->name()); |
| d->advertisePlugins(); |
| |
| QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::NotConnected; |
| service->d_func()->server = 0; |
| service->d_func()->status = newStatus; |
| service->statusChanged(newStatus); |
| return true; |
| } |
| |
| void QDeclarativeDebugServer::sendMessage(QDeclarativeDebugService *service, |
| const QByteArray &message) |
| { |
| Q_D(QDeclarativeDebugServer); |
| QByteArray msg; |
| { |
| QDataStream out(&msg, QIODevice::WriteOnly); |
| out << service->name() << message; |
| } |
| d->connection->send(msg); |
| } |
| |
| QT_END_NAMESPACE |