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
qqmlpreviewfileengine.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
6
7#include <QtCore/qlibraryinfo.h>
8#include <QtCore/qthread.h>
9#include <QtCore/qwaitcondition.h>
10
11#include <cstring>
12
14
15static bool isRelative(const QString &path)
16{
17 if (path.isEmpty())
18 return true;
19 if (path.at(0) == '/')
20 return false;
21 if (path.at(0) == ':' && path.size() >= 2 && path.at(1) == '/')
22 return false;
23#ifdef Q_OS_WIN
24 if (path.length() >= 2 && path.at(1) == ':')
25 return false;
26#endif
27 return true;
28}
29
30static QString absolutePath(const QString &path)
31{
32 return QDir::cleanPath(isRelative(path) ? (QDir::currentPath() + '/' + path) : path);
33}
34
35bool isRootPath(const QString &path)
36{
37 return QFileSystemEntry::isRootPath(path);
38}
39
41{
42public:
43 QQmlPreviewFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters,
44 const QStringList &filterNames, const QStringList &m_entries);
46
48 QString currentFileName() const override;
49
50private:
51 const QStringList m_entries;
52 int m_index;
53};
54
56 QDirListing::IteratorFlags filters,
57 const QStringList &filterNames,
58 const QStringList &entries)
60{
61}
62
66
68{
69 if (m_index >= m_entries.size())
70 return false;
71
72 ++m_index;
73 return true;
74}
75
77{
78 if (m_index == 0 || m_index > m_entries.size())
79 return QString();
80 return m_entries.at(m_index - 1);
81}
82
83QQmlPreviewFileEngine::QQmlPreviewFileEngine(const QString &file, const QString &absolute,
84 QQmlPreviewFileLoader *loader) :
85 m_name(file), m_absolute(absolute), m_loader(loader)
86{
87 load();
88}
89
90void QQmlPreviewFileEngine::setFileName(const QString &file)
91{
92 m_name = file;
93 m_absolute = absolutePath(file);
94 m_fallback.reset();
95 m_contents.close();
96 m_contents.setData(QByteArray());
97 m_entries.clear();
98 load();
99}
100
101bool QQmlPreviewFileEngine::open(QIODevice::OpenMode flags,
102 std::optional<QFile::Permissions> permissions)
103{
104 switch (m_result) {
105 case QQmlPreviewFileLoader::File:
106 return m_contents.open(flags);
107 case QQmlPreviewFileLoader::Directory:
108 return false;
109 case QQmlPreviewFileLoader::Fallback:
110 return m_fallback->open(flags, permissions);
111 default:
112 Q_UNREACHABLE_RETURN(false);
113 }
114}
115
116bool QQmlPreviewFileEngine::close()
117{
118 switch (m_result) {
119 case QQmlPreviewFileLoader::Fallback:
120 return m_fallback->close();
121 case QQmlPreviewFileLoader::File:
122 m_contents.close();
123 return true;
124 case QQmlPreviewFileLoader::Directory:
125 return false;
126 default:
127 Q_UNREACHABLE_RETURN(false);
128 }
129}
130
131qint64 QQmlPreviewFileEngine::size() const
132{
133 return m_fallback ? m_fallback->size() : m_contents.size();
134}
135
136qint64 QQmlPreviewFileEngine::pos() const
137{
138 return m_fallback ? m_fallback->pos() : m_contents.pos();
139}
140
141bool QQmlPreviewFileEngine::seek(qint64 newPos)
142{
143 return m_fallback? m_fallback->seek(newPos) : m_contents.seek(newPos);
144}
145
146qint64 QQmlPreviewFileEngine::read(char *data, qint64 maxlen)
147{
148 return m_fallback ? m_fallback->read(data, maxlen) : m_contents.read(data, maxlen);
149}
150
151QAbstractFileEngine::FileFlags QQmlPreviewFileEngine::fileFlags(
152 QAbstractFileEngine::FileFlags type) const
153{
154 if (m_fallback)
155 return m_fallback->fileFlags(type);
156
157 QAbstractFileEngine::FileFlags ret;
158
159 if (type & PermsMask) {
160 ret |= QAbstractFileEngine::FileFlags(
161 ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm);
162 }
163
164 if (type & TypesMask) {
165 if (m_result == QQmlPreviewFileLoader::Directory)
166 ret |= DirectoryType;
167 else
168 ret |= FileType;
169 }
170
171 if (type & FlagsMask) {
172 ret |= ExistsFlag;
173 if (isRootPath(m_name))
174 ret |= RootFlag;
175 }
176
177 return ret;
178}
179
180QString QQmlPreviewFileEngine::fileName(QAbstractFileEngine::FileName file) const
181{
182 if (m_fallback)
183 return m_fallback->fileName(file);
184
185 if (file == BaseName) {
186 int slashPos = m_name.lastIndexOf('/');
187 if (slashPos == -1)
188 return m_name;
189 return m_name.mid(slashPos + 1);
190 } else if (file == PathName || file == AbsolutePathName) {
191 const QString path = (file == AbsolutePathName) ? m_absolute : m_name;
192 const int slashPos = path.lastIndexOf('/');
193 if (slashPos == -1)
194 return QString();
195 else if (slashPos == 0)
196 return "/";
197 return path.left(slashPos);
198 } else if (file == CanonicalName || file == CanonicalPathName) {
199 if (file == CanonicalPathName) {
200 const int slashPos = m_absolute.lastIndexOf('/');
201 if (slashPos != -1)
202 return m_absolute.left(slashPos);
203 }
204 return m_absolute;
205 }
206 return m_name;
207}
208
209uint QQmlPreviewFileEngine::ownerId(QAbstractFileEngine::FileOwner owner) const
210{
211 return m_fallback ? m_fallback->ownerId(owner) : static_cast<uint>(-2);
212}
213
214QAbstractFileEngine::IteratorUniquePtr QQmlPreviewFileEngine::beginEntryList(
215 const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
216{
217 return m_fallback ? m_fallback->beginEntryList(path, filters, filterNames)
218 : std::make_unique<QQmlPreviewFileEngineIterator>(
219 path, filters, filterNames, m_entries);
220}
221
222QAbstractFileEngine::IteratorUniquePtr QQmlPreviewFileEngine::endEntryList()
223{
224 return m_fallback ? m_fallback->endEntryList() : nullptr;
225}
226
227bool QQmlPreviewFileEngine::flush()
228{
229 return m_fallback ? m_fallback->flush() : true;
230}
231
232bool QQmlPreviewFileEngine::syncToDisk()
233{
234 return m_fallback ? m_fallback->syncToDisk() : false;
235}
236
237bool QQmlPreviewFileEngine::isSequential() const
238{
239 return m_fallback ? m_fallback->isSequential() : m_contents.isSequential();
240}
241
242bool QQmlPreviewFileEngine::remove()
243{
244 return m_fallback ? m_fallback->remove() : false;
245}
246
247bool QQmlPreviewFileEngine::copy(const QString &newName)
248{
249 return m_fallback ? m_fallback->copy(newName) : false;
250}
251
252bool QQmlPreviewFileEngine::rename(const QString &newName)
253{
254 return m_fallback ? m_fallback->rename(newName) : false;
255}
256
257bool QQmlPreviewFileEngine::renameOverwrite(const QString &newName)
258{
259 return m_fallback ? m_fallback->renameOverwrite(newName) : false;
260}
261
262bool QQmlPreviewFileEngine::link(const QString &newName)
263{
264 return m_fallback ? m_fallback->link(newName) : false;
265}
266
267bool QQmlPreviewFileEngine::mkdir(const QString &dirName, bool createParentDirectories,
268 std::optional<QFile::Permissions> permissions) const
269{
270 return m_fallback ? m_fallback->mkdir(dirName, createParentDirectories, permissions) : false;
271}
272
273bool QQmlPreviewFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const
274{
275 return m_fallback ? m_fallback->rmdir(dirName, recurseParentDirectories) : false;
276}
277
278bool QQmlPreviewFileEngine::setSize(qint64 size)
279{
280 switch (m_result) {
281 case QQmlPreviewFileLoader::Fallback:
282 return m_fallback->setSize(size);
283 case QQmlPreviewFileLoader::File:
284 if (size < 0 || size > std::numeric_limits<int>::max())
285 return false;
286 m_contents.buffer().resize(static_cast<int>(size));
287 return true;
288 case QQmlPreviewFileLoader::Directory:
289 return false;
290 default:
291 Q_UNREACHABLE_RETURN(false);
292 }
293}
294
295bool QQmlPreviewFileEngine::caseSensitive() const
296{
297 return m_fallback ? m_fallback->caseSensitive() : true;
298}
299
300bool QQmlPreviewFileEngine::isRelativePath() const
301{
302 return m_fallback ? m_fallback->isRelativePath() : isRelative(m_name);
303}
304
305bool QQmlPreviewFileEngine::setPermissions(uint perms)
306{
307 return m_fallback ? m_fallback->setPermissions(perms) : false;
308}
309
310QByteArray QQmlPreviewFileEngine::id() const
311{
312 return m_fallback ? m_fallback->id() : QByteArray();
313}
314
315QString QQmlPreviewFileEngine::owner(FileOwner owner) const
316{
317 return m_fallback ? m_fallback->owner(owner) : QString();
318}
319
320QDateTime QQmlPreviewFileEngine::fileTime(QFile::FileTime time) const
321{
322 // Files we replace are always newer than the ones we had before. This makes the QML engine
323 // actually recompile them, rather than pick them from the cache.
324 return m_fallback ? m_fallback->fileTime(time) : QDateTime::currentDateTime();
325}
326
327int QQmlPreviewFileEngine::handle() const
328{
329 return m_fallback ? m_fallback->handle() : -1;
330}
331
332qint64 QQmlPreviewFileEngine::readLine(char *data, qint64 maxlen)
333{
334 return m_fallback ? m_fallback->readLine(data, maxlen) : m_contents.readLine(data, maxlen);
335}
336
337qint64 QQmlPreviewFileEngine::write(const char *data, qint64 len)
338{
339 return m_fallback ? m_fallback->write(data, len) : m_contents.write(data, len);
340}
341
342bool QQmlPreviewFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
343{
344 return m_fallback ? m_fallback->extension(extension, option, output) : false;
345}
346
347bool QQmlPreviewFileEngine::supportsExtension(Extension extension) const
348{
349 return m_fallback ? m_fallback->supportsExtension(extension) : false;
350}
351
352void QQmlPreviewFileEngine::load() const
353{
354 // We can get here from different threads on different instances of QQmlPreviewFileEngine.
355 // However, there is only one loader per QQmlPreviewFileEngineHandler and it is not thread-safe.
356 // Its content mutex doesn't help us here because we explicitly wait on it in load(), which
357 // causes it to be released. Therefore, lock the load mutex first.
358 // This doesn't cause any deadlocks because the only thread that wakes the loader on the content
359 // mutex never calls load(). It's the QML debug server thread that handles the debug protocol.
360 QMutexLocker loadLocker(m_loader->loadMutex());
361
362 m_result = m_loader->load(m_absolute);
363 switch (m_result) {
364 case QQmlPreviewFileLoader::File:
365 m_contents.setData(m_loader->contents());
366 break;
367 case QQmlPreviewFileLoader::Directory:
368 m_entries = m_loader->entries();
369 break;
370 case QQmlPreviewFileLoader::Fallback:
371 m_fallback = QAbstractFileEngine::create(m_name);
372 break;
373 case QQmlPreviewFileLoader::Unknown:
374 Q_UNREACHABLE();
375 break;
376 }
377}
378
379QQmlPreviewFileEngineHandler::QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader)
380 : m_loader(loader)
381{
382}
383
385 const QString &fileName) const
386{
387 using namespace Qt::StringLiterals;
388 static QList<QLatin1StringView> prohibitedSuffixes {
389 // Don't load compiled QML/JS over the network
390 ".qmlc"_L1, ".jsc"_L1, ".mjsc"_L1,
391
392 // Don't load plugins over the network
393 ".dll"_L1, ".so"_L1, ".dylib"_L1
394 };
395
396 for (QLatin1StringView suffix : prohibitedSuffixes) {
397 if (fileName.endsWith(suffix))
398 return nullptr;
399 }
400
401 if (isRootPath(fileName))
402 return nullptr;
403
404 QString relative = fileName;
405 while (relative.endsWith('/'))
406 relative.chop(1);
407
408 if (relative.isEmpty() || relative == ":")
409 return nullptr;
410
411 const QString absolute = relative.startsWith(':') ? relative : absolutePath(relative);
412
413 if (m_loader->isBlacklisted(absolute))
414 return {};
415
416 return std::make_unique<QQmlPreviewFileEngine>(relative, absolute, m_loader.data());
417}
418
419QT_END_NAMESPACE
std::unique_ptr< QAbstractFileEngine > create(const QString &fileName) const override
If this file handler can handle fileName, this method creates a file engine and returns it wrapped in...
QString currentFileName() const override
This pure virtual function returns the name of the current directory entry, excluding the path.
QQmlPreviewFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames, const QStringList &m_entries)
bool advance() override
This pure virtual function advances the iterator to the next directory entry; if the operation was su...
static QT_BEGIN_NAMESPACE bool isRelative(const QString &path)
static QString absolutePath(const QString &path)
bool isRootPath(const QString &path)