1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QML preview debug service.
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 "qqmlpreviewfileloader.h"
41 #include "qqmlpreviewservice.h"
42
43 #include <QtQml/qqmlfile.h>
44
45 #include <QtCore/qlibraryinfo.h>
46 #include <QtCore/qstandardpaths.h>
47
48 QT_BEGIN_NAMESPACE
49
QQmlPreviewFileLoader(QQmlPreviewServiceImpl * service)50 QQmlPreviewFileLoader::QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service) : m_service(service)
51 {
52 // Exclude some resource paths used by Qt itself. There is no point in loading those from the
53 // client as the client will not have the files (or even worse, it may have different ones).
54 m_blacklist.blacklist(":/qt-project.org");
55 m_blacklist.blacklist(":/QtQuick/Controls/Styles");
56 m_blacklist.blacklist(":/ExtrasImports/QtQuick/Controls/Styles");
57 m_blacklist.blacklist(":/qgradient");
58
59 // Target specific configuration should not replaced with files from the host.
60 m_blacklist.blacklist("/etc");
61
62 for (int loc = QLibraryInfo::PrefixPath; loc < QLibraryInfo::TestsPath; ++loc) {
63 m_blacklist.blacklist(QLibraryInfo::location(
64 static_cast<QLibraryInfo::LibraryLocation>(loc)));
65 }
66 m_blacklist.blacklist(QLibraryInfo::location(QLibraryInfo::SettingsPath));
67
68 static const QStandardPaths::StandardLocation blackListLocations[] = {
69 QStandardPaths::DataLocation,
70 QStandardPaths::CacheLocation,
71 QStandardPaths::GenericDataLocation,
72 QStandardPaths::ConfigLocation,
73 QStandardPaths::GenericCacheLocation,
74 QStandardPaths::GenericConfigLocation,
75 QStandardPaths::AppDataLocation,
76 QStandardPaths::AppConfigLocation
77 };
78
79 for (auto locationType : blackListLocations) {
80 const QStringList locations = QStandardPaths::standardLocations(locationType);
81 for (const QString &location : locations)
82 m_blacklist.blacklist(location);
83 }
84
85 m_blacklist.whitelist(QLibraryInfo::location(QLibraryInfo::TestsPath));
86
87 connect(this, &QQmlPreviewFileLoader::request, service, &QQmlPreviewServiceImpl::forwardRequest,
88 Qt::DirectConnection);
89 connect(service, &QQmlPreviewServiceImpl::directory, this, &QQmlPreviewFileLoader::directory);
90 connect(service, &QQmlPreviewServiceImpl::file, this, &QQmlPreviewFileLoader::file);
91 connect(service, &QQmlPreviewServiceImpl::error, this, &QQmlPreviewFileLoader::error);
92 connect(service, &QQmlPreviewServiceImpl::clearCache, this, &QQmlPreviewFileLoader::clearCache);
93 moveToThread(&m_thread);
94 m_thread.start();
95 }
96
~QQmlPreviewFileLoader()97 QQmlPreviewFileLoader::~QQmlPreviewFileLoader() {
98 m_thread.quit();
99 m_thread.wait();
100 }
101
load(const QString & path)102 QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path)
103 {
104 QMutexLocker locker(&m_mutex);
105 m_path = path;
106
107 auto fileIterator = m_fileCache.constFind(path);
108 if (fileIterator != m_fileCache.constEnd()) {
109 m_result = File;
110 m_contents = *fileIterator;
111 m_entries.clear();
112 return m_result;
113 }
114
115 auto dirIterator = m_directoryCache.constFind(path);
116 if (dirIterator != m_directoryCache.constEnd()) {
117 m_result = Directory;
118 m_contents.clear();
119 m_entries = *dirIterator;
120 return m_result;
121 }
122
123 m_result = Unknown;
124 m_entries.clear();
125 m_contents.clear();
126 emit request(path);
127 m_waitCondition.wait(&m_mutex);
128 return m_result;
129 }
130
contents()131 QByteArray QQmlPreviewFileLoader::contents()
132 {
133 QMutexLocker locker(&m_mutex);
134 return m_contents;
135 }
136
entries()137 QStringList QQmlPreviewFileLoader::entries()
138 {
139 QMutexLocker locker(&m_mutex);
140 return m_entries;
141 }
142
whitelist(const QUrl & url)143 void QQmlPreviewFileLoader::whitelist(const QUrl &url)
144 {
145 const QString path = QQmlFile::urlToLocalFileOrQrc(url);
146 if (!path.isEmpty()) {
147 QMutexLocker locker(&m_mutex);
148 m_blacklist.whitelist(path);
149 }
150 }
151
isBlacklisted(const QString & path)152 bool QQmlPreviewFileLoader::isBlacklisted(const QString &path)
153 {
154 QMutexLocker locker(&m_mutex);
155 return m_blacklist.isBlacklisted(path);
156 }
157
file(const QString & path,const QByteArray & contents)158 void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents)
159 {
160 QMutexLocker locker(&m_mutex);
161 m_blacklist.whitelist(path);
162 m_fileCache[path] = contents;
163 if (path == m_path) {
164 m_contents = contents;
165 m_result = File;
166 m_waitCondition.wakeOne();
167 }
168 }
169
directory(const QString & path,const QStringList & entries)170 void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &entries)
171 {
172 QMutexLocker locker(&m_mutex);
173 m_blacklist.whitelist(path);
174 m_directoryCache[path] = entries;
175 if (path == m_path) {
176 m_entries = entries;
177 m_result = Directory;
178 m_waitCondition.wakeOne();
179 }
180 }
181
error(const QString & path)182 void QQmlPreviewFileLoader::error(const QString &path)
183 {
184 QMutexLocker locker(&m_mutex);
185 m_blacklist.blacklist(path);
186 if (path == m_path) {
187 m_result = Fallback;
188 m_waitCondition.wakeOne();
189 }
190 }
191
clearCache()192 void QQmlPreviewFileLoader::clearCache()
193 {
194 QMutexLocker locker(&m_mutex);
195 m_fileCache.clear();
196 m_directoryCache.clear();
197 }
198
199 QT_END_NAMESPACE
200