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