Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlpreviewservice.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
8
9#include <QtCore/qpointer.h>
10#include <QtQml/qqmlengine.h>
11#include <QtQml/qqmlcomponent.h>
12#include <QtQuick/qquickwindow.h>
13#include <QtQuick/qquickitem.h>
14#include <QtGui/qguiapplication.h>
15
16#include <private/qquickpixmap_p.h>
17#include <private/qqmldebugconnector_p.h>
18#include <private/qversionedpacket_p.h>
19
21
22inline bool convertQmlPreviewHotReload(const char *v)
23{
24 // Either not set or explicitly "1" or "true";
25 return v == nullptr || qstrcmp(v, "1") == 0 || qstrcmp(v, "true") == 0;
26}
27
29{
30 static const bool enabled =
31 qmlGetConfigOption<bool, convertQmlPreviewHotReload>("QMLPREVIEW_HOTRELOAD");
32 return enabled;
33}
34
35const QString QQmlPreviewServiceImpl::s_key = QStringLiteral("QmlPreview");
36using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
37
41{
42 m_handler->connectToService(this);
43}
44
48
49void QQmlPreviewServiceImpl::switchToInPlaceHandler()
50{
51 // Transfer engines from the old handler to the new one
52 const auto engineList = m_handler->engines();
53 m_handler = std::make_unique<QQmlInPlacePreviewHandler>();
54 m_handler->connectToService(this);
55 for (QQmlEngine *engine : engineList)
56 m_handler->addEngine(engine);
57
58 // Send confirmation back to the client
59 QQmlDebugPacket response;
60 const bool enableInPlaceUpdates = true;
61 response << static_cast<qint8>(Confirmation) << enableInPlaceUpdates;
62 emit messageToClient(name(), response.data());
63}
64
65void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data)
66{
67 QQmlDebugPacket packet(data);
68 qint8 command;
69
70 packet >> command;
71 switch (command) {
72 case File: {
73 QString path;
74 QByteArray contents;
75 packet >> path >> contents;
76
77 const QUrl url = path.startsWith(QLatin1Char(':'))
78 ? QUrl(QLatin1String("qrc") + path)
79 : QUrl::fromLocalFile(path);
80
81 // Only a re-serve of a URL we already saw is an actual edit that needs relinking. The
82 // first time any file is served (e.g. while the whole app is still starting up) isn't
83 // dropping anything; treating it as such piles up the entire type registry as "dropped"
84 // and causes a mass relink storm on the first Load.
85 if (m_servedUrls.contains(url))
86 emit drop(url);
87 else
88 m_servedUrls.insert(url);
89 emit file(path, contents);
90
91 // Remember the first .qml file as the current URL, so that a Load command
92 // without an explicit URL can fall back to it.
93 if (m_currentUrl.isEmpty() && path.endsWith(".qml"))
94 m_currentUrl = url;
95
96 // Never auto-load here. The client must send an explicit Load command
97 // to trigger loading. Auto-loading based on File responses is racy
98 // (the Configuration message may not have arrived yet) and creates
99 // duplicate objects when the host process has already loaded the scene.
100 break;
101 }
102 case Directory: {
103 QString path;
104 QStringList entries;
105 packet >> path >> entries;
106 emit directory(path, entries);
107 break;
108 }
109 case Load: {
110 QUrl url;
111 packet >> url;
112 if (url.isEmpty())
113 url = m_currentUrl;
114 else
115 m_currentUrl = url;
116 emit load(url);
117 break;
118 }
119 case Error: {
120 QString file;
121 packet >> file;
122 emit error(file);
123 break;
124 }
125 case Rerun:
126 emit rerun();
127 break;
128 case ClearCache:
130 break;
131 case Zoom: {
132 float factor;
133 packet >> factor;
134 emit zoom(static_cast<qreal>(factor));
135 break;
136 }
137 case AnimationSpeed: {
138 float factor;
139 packet >> factor;
140 emit animationSpeed(qreal(factor));
141 break;
142 }
143 case Configuration: {
145 bool enableInPlaceUpdates;
146 packet >> enableInPlaceUpdates;
147 if (enableInPlaceUpdates
148 && !qobject_cast<QQmlInPlacePreviewHandler *>(m_handler.get())) {
149 // Schedule this to the main thread where the handler lives
150 // We're on the debug server thread here.
151 QMetaObject::invokeMethod(this, &QQmlPreviewServiceImpl::switchToInPlaceHandler);
152 } else if (!enableInPlaceUpdates
153 && qobject_cast<QQmlInPlacePreviewHandler *>(m_handler.get())) {
154 forwardError(QLatin1String("Cannot disable in-place updates once enabled"));
155 }
156 break;
157 } else {
158 Q_FALLTHROUGH();
159 }
160 }
161 default:
162 forwardError(QString::fromLatin1("Invalid command: %1").arg(command));
163 break;
164 }
165}
166
168{
169 if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine))
170 m_handler->addEngine(qmlEngine);
171 emit attachedToEngine(engine);
172}
173
175{
176 if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine))
177 m_handler->removeEngine(qmlEngine);
178 emit detachedFromEngine(engine);
179}
180
181void QQmlPreviewServiceImpl::stateChanged(QQmlDebugService::State state)
182{
183 if (state == Enabled) {
184 m_loader.reset(new QQmlPreviewFileLoader(this));
185 connect(this, &QQmlPreviewServiceImpl::load,
186 m_loader.data(), &QQmlPreviewFileLoader::whitelist, Qt::DirectConnection);
187 QV4::ExecutionEngine::setPreviewing(true);
188 m_fileEngine.reset(new QQmlPreviewFileEngineHandler(m_loader.data()));
189 } else {
190 QV4::ExecutionEngine::setPreviewing(false);
191 m_fileEngine.reset();
192 m_loader.reset();
193 }
194}
195
196void QQmlPreviewServiceImpl::forwardRequest(const QString &file)
197{
198 QQmlDebugPacket packet;
199 packet << static_cast<qint8>(Request) << file;
200 emit messageToClient(name(), packet.data());
201}
202
203void QQmlPreviewServiceImpl::forwardError(const QString &error)
204{
205 QQmlDebugPacket packet;
206 packet << static_cast<qint8>(Error) << error;
207 emit messageToClient(name(), packet.data());
208}
209
210void QQmlPreviewServiceImpl::forwardFps(const QQmlPreviewHandler::FpsInfo &frames)
211{
212 QQmlDebugPacket packet;
213 packet << static_cast<qint8>(Fps)
214 << frames.numSyncs << frames.minSync << frames.maxSync << frames.totalSync
215 << frames.numRenders << frames.minRender << frames.maxRender << frames.totalRender;
216 emit messageToClient(name(), packet.data());
217}
218
220{
221 QQmlDebugPacket packet;
222 packet << static_cast<qint8>(HotReloadFailure) << reason;
223 emit messageToClient(name(), packet.data());
224}
225
227{
228 return m_handler->currentRootItem();
229}
230
231QT_END_NAMESPACE
232
233#include "moc_qqmlpreviewservice.cpp"
void forwardRequest(const QString &file)
static const QString s_key
void engineAboutToBeRemoved(QJSEngine *engine) override
QQmlPreviewServiceImpl(QObject *parent=nullptr)
void forwardHotReloadFailure(const QString &reason)
void engineAboutToBeAdded(QJSEngine *engine) override
void forwardFps(const QQmlPreviewHandler::FpsInfo &frames)
void stateChanged(State state) override
void forwardError(const QString &error)
void messageReceived(const QByteArray &message) override
void clearCache()
Clears cached information about loaded files, including any type data, scripts and qmldir information...
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE bool convertQmlPreviewHotReload(const char *v)
static bool qmlPreviewHotReloadEnabled()