1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qqmldebugpluginmanager_p.h"
41 #include "qqmldebugconnector_p.h"
42 #include "qqmldebugservicefactory_p.h"
43 #include <QtCore/QPluginLoader>
44 #include <QtCore/QCoreApplication>
45 #include <QtCore/QDir>
46 #include <QtCore/QDebug>
47 #include <QtCore/QJsonArray>
48 #include <QtCore/QDataStream>
49 
50 #include <private/qcoreapplication_p.h>
51 #include <private/qqmlengine_p.h>
52 
53 QT_BEGIN_NAMESPACE
54 
55 // Connectors. We could add more plugins here, and distinguish by arguments to instance()
56 Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugConnector)
57 
58 // Services
59 Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugService)
60 
61 int QQmlDebugConnector::s_dataStreamVersion = QDataStream::Qt_4_7;
62 
63 struct QQmlDebugConnectorParams {
64     QString pluginKey;
65     QStringList services;
66     QString arguments;
67     QQmlDebugConnector *instance;
68 
QQmlDebugConnectorParamsQQmlDebugConnectorParams69     QQmlDebugConnectorParams() : instance(nullptr)
70     {
71         if (qApp) {
72             QCoreApplicationPrivate *appD =
73                     static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp));
74             if (appD)
75                 arguments = appD->qmljsDebugArgumentsString();
76         }
77     }
78 };
79 
Q_GLOBAL_STATIC(QQmlDebugConnectorParams,qmlDebugConnectorParams)80 Q_GLOBAL_STATIC(QQmlDebugConnectorParams, qmlDebugConnectorParams)
81 
82 void QQmlDebugConnector::setPluginKey(const QString &key)
83 {
84     QQmlDebugConnectorParams *params = qmlDebugConnectorParams();
85     if (params && params->pluginKey != key) {
86         if (params->instance)
87             qWarning() << "QML debugger: Cannot set plugin key after loading the plugin.";
88         else
89             params->pluginKey = key;
90     }
91 }
92 
setServices(const QStringList & services)93 void QQmlDebugConnector::setServices(const QStringList &services)
94 {
95     QQmlDebugConnectorParams *params = qmlDebugConnectorParams();
96     if (params)
97         params->services = services;
98 }
99 
commandLineArguments()100 QString QQmlDebugConnector::commandLineArguments()
101 {
102     QQmlDebugConnectorParams *params = qmlDebugConnectorParams();
103     if (!params)
104         return QString();
105     return params->arguments;
106 }
107 
instance()108 QQmlDebugConnector *QQmlDebugConnector::instance()
109 {
110     QQmlDebugConnectorParams *params = qmlDebugConnectorParams();
111     if (!params)
112         return nullptr;
113 
114     if (!QQmlEnginePrivate::qml_debugging_enabled) {
115         if (!params->arguments.isEmpty()) {
116             qWarning().noquote() << QString::fromLatin1(
117                                         "QML Debugger: Ignoring \"-qmljsdebugger=%1\". Debugging "
118                                         "has not been enabled.").arg(params->arguments);
119             params->arguments.clear();
120         }
121         return nullptr;
122     }
123 
124     if (!params->instance) {
125         if (!params->pluginKey.isEmpty()) {
126             params->instance = loadQQmlDebugConnector(params->pluginKey);
127         } else if (params->arguments.isEmpty()) {
128             return nullptr; // no explicit class name given and no command line arguments
129         } else if (params->arguments.startsWith(QLatin1String("connector:"))) {
130             static const int connectorBegin = int(strlen("connector:"));
131 
132             int connectorEnd = params->arguments.indexOf(QLatin1Char(','), connectorBegin);
133             if (connectorEnd == -1)
134                 connectorEnd = params->arguments.length();
135 
136             params->instance = loadQQmlDebugConnector(params->arguments.mid(
137                                                           connectorBegin,
138                                                           connectorEnd - connectorBegin));
139         } else {
140             params->instance = loadQQmlDebugConnector(
141                         params->arguments.startsWith(QLatin1String("native")) ?
142                             QStringLiteral("QQmlNativeDebugConnector") :
143                             QStringLiteral("QQmlDebugServer"));
144         }
145 
146         if (params->instance) {
147             const auto metaData = metaDataForQQmlDebugService();
148             for (const QJsonObject &object : metaData) {
149                 const auto keys = object.value(QLatin1String("MetaData")).toObject()
150                         .value(QLatin1String("Keys")).toArray();
151                 for (const QJsonValue &key : keys) {
152                     QString keyString = key.toString();
153                     if (params->services.isEmpty() || params->services.contains(keyString))
154                         loadQQmlDebugService(keyString);
155                 }
156             }
157         }
158     }
159 
160     return params->instance;
161 }
162 
~QQmlDebugConnectorFactory()163 QQmlDebugConnectorFactory::~QQmlDebugConnectorFactory()
164 {
165     // This is triggered when the plugin is unloaded.
166     QQmlDebugConnectorParams *params = qmlDebugConnectorParams();
167     if (params) {
168         params->pluginKey.clear();
169         params->arguments.clear();
170         params->services.clear();
171         delete params->instance;
172         params->instance = nullptr;
173     }
174 }
175 
176 QT_END_NAMESPACE
177 
178 #include "moc_qqmldebugconnector_p.cpp"
179