13#include <QtCore/private/qstdweb_p.h>
14#include <QtCore/private/qwasmglobal_p.h>
21#include <emscripten.h>
22# include <emscripten/proxying.h>
23# include <emscripten/threading.h>
24# include <emscripten/val.h>
29using namespace Qt::StringLiterals;
32QStringView keyNameFromPrefixedStorageName(QStringView prefix, QStringView prefixedStorageName)
35 if (!prefixedStorageName.startsWith(prefix))
37 return prefixedStorageName.sliced(prefix.length());
46class QWasmLocalStorageSettingsPrivate
final :
public QSettingsPrivate
53 void remove(
const QString &key)
final;
54 void set(
const QString &key,
const QVariant &value)
final;
64 QStringList m_keyPrefixes;
67QWasmLocalStorageSettingsPrivate::QWasmLocalStorageSettingsPrivate(QSettings::Scope scope,
68 const QString &organization,
69 const QString &application)
70 : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
72 if (organization.isEmpty()) {
73 setStatus(QSettings::AccessError);
89 const QString allAppsSetting = QStringLiteral(
"all-apps");
90 const QString systemSetting = QStringLiteral(
"sys-tem");
92 const QLatin1String separator(
"-");
93 const QLatin1String doubleSeparator(
"--");
94 const QString escapedOrganization = QString(organization).replace(separator, doubleSeparator);
95 const QString escapedApplication = QString(application).replace(separator, doubleSeparator);
96 const QString prefix =
"qt-v0-" + escapedOrganization + separator;
97 if (scope == QSettings::Scope::UserScope) {
98 if (!escapedApplication.isEmpty())
99 m_keyPrefixes.push_back(prefix + escapedApplication + separator);
100 m_keyPrefixes.push_back(prefix + allAppsSetting + separator);
102 if (!escapedApplication.isEmpty()) {
103 m_keyPrefixes.push_back(prefix + escapedApplication + separator + systemSetting
106 m_keyPrefixes.push_back(prefix + allAppsSetting + separator + systemSetting + separator);
109void QWasmLocalStorageSettingsPrivate::
remove(
const QString &key)
111 const std::string removed = QString(m_keyPrefixes.first() + key).toStdString();
113 qwasmglobal::runTaskOnMainThread<
void>([
this, &removed, &key]() {
114 std::vector<std::string> children = { removed };
115 const int length = val::global(
"window")[
"localStorage"][
"length"].as<
int>();
116 for (
int i = 0; i < length; ++i) {
117 const QString storedKeyWithPrefix = QString::fromStdString(
118 val::global(
"window")[
"localStorage"].call<val>(
"key", i).as<std::string>());
120 const QStringView storedKey = keyNameFromPrefixedStorageName(
121 m_keyPrefixes.first(), QStringView(storedKeyWithPrefix));
122 if (storedKey.isEmpty() || !storedKey.startsWith(key))
125 children.push_back(storedKeyWithPrefix.toStdString());
128 for (
const auto &child : children)
129 val::global(
"window")[
"localStorage"].call<val>(
"removeItem", child);
133void QWasmLocalStorageSettingsPrivate::
set(
const QString &key,
const QVariant &value)
135 qwasmglobal::runTaskOnMainThread<
void>([
this, &key, &value]() {
136 const std::string keyString = QString(m_keyPrefixes.first() + key).toStdString();
137 const std::string valueString = QSettingsPrivate::variantToString(value).toStdString();
138 val::global(
"window")[
"localStorage"].call<
void>(
"setItem", keyString, valueString);
144 return qwasmglobal::runTaskOnMainThread<std::optional<QVariant>>(
145 [
this, &key]() -> std::optional<QVariant> {
146 for (
const auto &prefix : m_keyPrefixes) {
147 const std::string keyString = QString(prefix + key).toStdString();
148 const emscripten::val value =
149 val::global(
"window")[
"localStorage"].call<val>(
"getItem", keyString);
150 if (!value.isNull()) {
151 return QSettingsPrivate::stringToVariant(
152 QString::fromStdString(value.as<std::string>()));
164 return qwasmglobal::runTaskOnMainThread<QStringList>([
this, &prefix, &spec]() -> QStringList {
168 QStringList children;
169 const int length = val::global(
"window")[
"localStorage"][
"length"].as<
int>();
170 for (
int i = 0; i < length; ++i) {
171 for (
const auto &storagePrefix : m_keyPrefixes) {
172 const QString keyString =
173 QString::fromStdString(val::global(
"window")[
"localStorage"]
177 const QStringView key =
178 keyNameFromPrefixedStorageName(storagePrefix, QStringView(keyString));
179 if (!key.isEmpty() && key.startsWith(prefix)) {
180 QStringList children;
181 QSettingsPrivate::processChild(key.sliced(prefix.length()), spec, children);
182 if (!children.isEmpty())
183 nodes.insert(children.first());
190 return QStringList(nodes.begin(), nodes.end());
194void QWasmLocalStorageSettingsPrivate::
clear()
196 qwasmglobal::runTaskOnMainThread<
void>([
this]() {
198 const int length = val::global(
"window")[
"localStorage"][
"length"].as<
int>();
200 keys.reserve(length);
201 for (
int i = 0; i < length; ++i)
202 keys.append(QString::fromStdString(
203 (val::global(
"window")[
"localStorage"].call<val>(
"key", i).as<std::string>())));
208 for (
const QString &key : keys) {
209 if (!keyNameFromPrefixedStorageName(m_keyPrefixes.first(), key).isEmpty())
210 val::global(
"window")[
"localStorage"].call<val>(
"removeItem", key.toStdString());
215void QWasmLocalStorageSettingsPrivate::
sync() { }
217void QWasmLocalStorageSettingsPrivate::
flush() { }
244 bool writeSettingsToTemporaryFile(
const QString &fileName,
void *dataPtr,
int size);
245 void loadIndexedDBFiles();
248 QString databaseName;
252constexpr char DbName[] =
"/home/web_user";
255 const QString &organization,
256 const QString &application)
257 : QConfFileSettingsPrivate(QSettings::WebIndexedDBFormat, scope, organization, application)
259 Q_ASSERT_X(qstdweb::haveJspi(), Q_FUNC_INFO,
"QWasmIDBSettingsPrivate needs JSPI to work");
261 if (organization.isEmpty()) {
262 setStatus(QSettings::AccessError);
266 databaseName = organization;
269 loadIndexedDBFiles();
279 QFile file(fileName);
280 QFileInfo fileInfo(fileName);
281 QDir dir(fileInfo.path());
283 dir.mkpath(fileInfo.path());
285 if (!file.open(QFile::WriteOnly))
288 return size == file.write(
reinterpret_cast<
char *>(dataPtr), size);
296 emscripten_idb_delete(DbName, fileName().toLocal8Bit(), &error);
297 setStatus(!!error ? QSettings::AccessError : QSettings::NoError);
304 loadIndexedDBFiles();
308 QFile file(fileName());
309 if (file.open(QFile::ReadOnly)) {
310 QByteArray dataPointer = file.readAll();
313 emscripten_idb_store(DbName, fileName().toLocal8Bit(),
314 reinterpret_cast<
void *>(dataPointer.data()), dataPointer.length(),
316 setStatus(!!error ? QSettings::AccessError : QSettings::NoError);
322 for (
const auto *confFile : getConfFiles()) {
325 emscripten_idb_exists(DbName, confFile->name.toLocal8Bit(), &exists, &error);
327 setStatus(QSettings::AccessError);
333 emscripten_idb_load(DbName, confFile->name.toLocal8Bit(), &contents, &size, &error);
334 if (error || !writeSettingsToTemporaryFile(confFile->name, contents, size)) {
335 setStatus(QSettings::AccessError);
342QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
343 const QString &organization,
const QString &application)
346 if (format == QSettings::NativeFormat)
347 format = QSettings::WebLocalStorageFormat;
351 const bool cookiesEnabled = qwasmglobal::runTaskOnMainThread<
bool>(
352 []() {
return val::global(
"navigator")[
"cookieEnabled"].as<
bool>(); });
354 constexpr QLatin1StringView cookiesWarningMessage(
355 "QSettings::%1 requires cookies, falling back to IniFormat with temporary file");
356 if (!cookiesEnabled) {
357 if (format == QSettings::WebLocalStorageFormat) {
358 qWarning() << cookiesWarningMessage.arg(
"WebLocalStorageFormat");
359 format = QSettings::IniFormat;
360 }
else if (format == QSettings::WebIndexedDBFormat) {
361 qWarning() << cookiesWarningMessage.arg(
"WebIndexedDBFormat");
362 format = QSettings::IniFormat;
365 if (format == QSettings::WebIndexedDBFormat && !qstdweb::haveJspi()) {
366 qWarning() <<
"QSettings::WebIndexedDBFormat requires JSPI, falling back to IniFormat with "
368 format = QSettings::IniFormat;
373 case QSettings::Format::WebLocalStorageFormat:
374 return new QWasmLocalStorageSettingsPrivate(scope, organization, application);
375 case QSettings::Format::WebIndexedDBFormat:
376 return new QWasmIDBSettingsPrivate(scope, organization, application);
377 case QSettings::Format::IniFormat:
378 case QSettings::Format::CustomFormat1:
379 case QSettings::Format::CustomFormat2:
380 case QSettings::Format::CustomFormat3:
381 case QSettings::Format::CustomFormat4:
382 case QSettings::Format::CustomFormat5:
383 case QSettings::Format::CustomFormat6:
384 case QSettings::Format::CustomFormat7:
385 case QSettings::Format::CustomFormat8:
386 case QSettings::Format::CustomFormat9:
387 case QSettings::Format::CustomFormat10:
388 case QSettings::Format::CustomFormat11:
389 case QSettings::Format::CustomFormat12:
390 case QSettings::Format::CustomFormat13:
391 case QSettings::Format::CustomFormat14:
392 case QSettings::Format::CustomFormat15:
393 case QSettings::Format::CustomFormat16:
394 return new QConfFileSettingsPrivate(format, scope, organization, application);
395 case QSettings::Format::InvalidFormat:
397 case QSettings::Format::NativeFormat:
virtual void initAccess()
~QWasmIDBSettingsPrivate()
QWasmIDBSettingsPrivate(QSettings::Scope scope, const QString &organization, const QString &application)
std::optional< QVariant > get(const QString &key) const final
bool isWritable() const final
~QWasmLocalStorageSettingsPrivate() final=default
QWasmLocalStorageSettingsPrivate(QSettings::Scope scope, const QString &organization, const QString &application)
QStringList children(const QString &prefix, ChildSpec spec) const final
void remove(const QString &key) final
void set(const QString &key, const QVariant &value) final
QString fileName() const final
Combined button and popup list for selecting options.