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