14#include <QtCore/private/qstdweb_p.h>
15#include <QtCore/private/qwasmglobal_p.h>
22#include <emscripten.h>
23# include <emscripten/proxying.h>
24# include <emscripten/threading.h>
25# include <emscripten/val.h>
30using namespace Qt::StringLiterals;
33QStringView keyNameFromPrefixedStorageName(QStringView prefix, QStringView prefixedStorageName)
36 if (!prefixedStorageName.startsWith(prefix))
38 return prefixedStorageName.sliced(prefix.length());
47class QWasmLocalStorageSettingsPrivate
final :
public QSettingsPrivate
54 void remove(
const QString &key)
final;
55 void set(
const QString &key,
const QVariant &value)
final;
65 QStringList m_keyPrefixes;
68QWasmLocalStorageSettingsPrivate::QWasmLocalStorageSettingsPrivate(QSettings::Scope scope,
69 const QString &organization,
70 const QString &application)
71 : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
73 if (organization.isEmpty()) {
74 setStatus(QSettings::AccessError);
90 const QString allAppsSetting = QStringLiteral(
"all-apps");
91 const QString systemSetting = QStringLiteral(
"sys-tem");
93 const QLatin1String separator(
"-");
94 const QLatin1String doubleSeparator(
"--");
95 const QString escapedOrganization = QString(organization).replace(separator, doubleSeparator);
96 const QString escapedApplication = QString(application).replace(separator, doubleSeparator);
97 const QString prefix =
"qt-v0-" + escapedOrganization + separator;
98 if (scope == QSettings::Scope::UserScope) {
99 if (!escapedApplication.isEmpty())
100 m_keyPrefixes.push_back(prefix + escapedApplication + separator);
101 m_keyPrefixes.push_back(prefix + allAppsSetting + separator);
103 if (!escapedApplication.isEmpty()) {
104 m_keyPrefixes.push_back(prefix + escapedApplication + separator + systemSetting
107 m_keyPrefixes.push_back(prefix + allAppsSetting + separator + systemSetting + separator);
110void QWasmLocalStorageSettingsPrivate::
remove(
const QString &key)
112 const std::string removed = QString(m_keyPrefixes.first() + key).toStdString();
114 qwasmglobal::runTaskOnMainThread<
void>([
this, &removed, &key]() {
115 std::vector<std::string> children = { removed };
116 const int length = val::global(
"window")[
"localStorage"][
"length"].as<
int>();
117 for (
int i = 0; i < length; ++i) {
118 const QString storedKeyWithPrefix = QString::fromStdString(
119 val::global(
"window")[
"localStorage"].call<val>(
"key", i).as<std::string>());
121 const QStringView storedKey = keyNameFromPrefixedStorageName(
122 m_keyPrefixes.first(), QStringView(storedKeyWithPrefix));
123 if (storedKey.isEmpty() || !storedKey.startsWith(key))
126 children.push_back(storedKeyWithPrefix.toStdString());
129 for (
const auto &child : children)
130 val::global(
"window")[
"localStorage"].call<val>(
"removeItem", child);
134void QWasmLocalStorageSettingsPrivate::
set(
const QString &key,
const QVariant &value)
136 qwasmglobal::runTaskOnMainThread<
void>([
this, &key, &value]() {
137 const std::string keyString = QString(m_keyPrefixes.first() + key).toStdString();
138 const std::string valueString = QSettingsPrivate::variantToString(value).toStdString();
139 val::global(
"window")[
"localStorage"].call<
void>(
"setItem", keyString, valueString);
145 return qwasmglobal::runTaskOnMainThread<std::optional<QVariant>>(
146 [
this, &key]() -> std::optional<QVariant> {
147 for (
const auto &prefix : m_keyPrefixes) {
148 const std::string keyString = QString(prefix + key).toStdString();
149 const emscripten::val value =
150 val::global(
"window")[
"localStorage"].call<val>(
"getItem", keyString);
151 if (!value.isNull()) {
152 return QSettingsPrivate::stringToVariant(
153 QString::fromStdString(value.as<std::string>()));
165 return qwasmglobal::runTaskOnMainThread<QStringList>([
this, &prefix, &spec]() -> QStringList {
169 QStringList children;
170 const int length = val::global(
"window")[
"localStorage"][
"length"].as<
int>();
171 for (
int i = 0; i < length; ++i) {
172 for (
const auto &storagePrefix : m_keyPrefixes) {
173 const QString keyString =
174 QString::fromStdString(val::global(
"window")[
"localStorage"]
178 const QStringView key =
179 keyNameFromPrefixedStorageName(storagePrefix, QStringView(keyString));
180 if (!key.isEmpty() && key.startsWith(prefix)) {
181 QStringList children;
182 QSettingsPrivate::processChild(key.sliced(prefix.length()), spec, children);
183 if (!children.isEmpty())
184 nodes.insert(children.first());
191 return QStringList(nodes.begin(), nodes.end());
195void QWasmLocalStorageSettingsPrivate::
clear()
197 qwasmglobal::runTaskOnMainThread<
void>([
this]() {
199 const int length = val::global(
"window")[
"localStorage"][
"length"].as<
int>();
201 keys.reserve(length);
202 for (
int i = 0; i < length; ++i)
203 keys.append(QString::fromStdString(
204 (val::global(
"window")[
"localStorage"].call<val>(
"key", i).as<std::string>())));
209 for (
const QString &key : keys) {
210 if (!keyNameFromPrefixedStorageName(m_keyPrefixes.first(), key).isEmpty())
211 val::global(
"window")[
"localStorage"].call<val>(
"removeItem", key.toStdString());
216void QWasmLocalStorageSettingsPrivate::
sync() { }
218void QWasmLocalStorageSettingsPrivate::
flush() { }
245 bool writeSettingsToTemporaryFile(
const QString &fileName,
void *dataPtr,
int size);
246 void loadIndexedDBFiles();
249 QString databaseName;
253constexpr char DbName[] =
"/home/web_user";
256 const QString &organization,
257 const QString &application)
258 : QConfFileSettingsPrivate(QSettings::WebIndexedDBFormat, scope, organization, application)
260 Q_ASSERT_X(qstdweb::haveJspi(), Q_FUNC_INFO,
"QWasmIDBSettingsPrivate needs JSPI to work");
262 if (organization.isEmpty()) {
263 setStatus(QSettings::AccessError);
267 databaseName = organization;
270 loadIndexedDBFiles();
280 QFile file(fileName);
281 QFileInfo fileInfo(fileName);
282 QDir dir(fileInfo.path());
284 dir.mkpath(fileInfo.path());
286 if (!file.open(QFile::WriteOnly))
289 return size == file.write(
reinterpret_cast<
char *>(dataPtr), size);
297 emscripten_idb_delete(DbName, fileName().toLocal8Bit(), &error);
298 setStatus(!!error ? QSettings::AccessError : QSettings::NoError);
305 loadIndexedDBFiles();
309 QFile file(fileName());
310 if (file.open(QFile::ReadOnly)) {
311 QByteArray dataPointer = file.readAll();
314 emscripten_idb_store(DbName, fileName().toLocal8Bit(),
315 reinterpret_cast<
void *>(dataPointer.data()), dataPointer.length(),
317 setStatus(!!error ? QSettings::AccessError : QSettings::NoError);
323 for (
const auto *confFile : getConfFiles()) {
326 emscripten_idb_exists(DbName, confFile->name.toLocal8Bit(), &exists, &error);
328 setStatus(QSettings::AccessError);
334 emscripten_idb_load(DbName, confFile->name.toLocal8Bit(), &contents, &size, &error);
335 if (error || !writeSettingsToTemporaryFile(confFile->name, contents, size)) {
336 setStatus(QSettings::AccessError);
343QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
344 const QString &organization,
const QString &application)
347 if (format == QSettings::NativeFormat)
348 format = QSettings::WebLocalStorageFormat;
352 const bool cookiesEnabled = qwasmglobal::runTaskOnMainThread<
bool>(
353 []() {
return val::global(
"navigator")[
"cookieEnabled"].as<
bool>(); });
355 constexpr QLatin1StringView cookiesWarningMessage(
356 "QSettings::%1 requires cookies, falling back to IniFormat with temporary file");
357 if (!cookiesEnabled) {
358 if (format == QSettings::WebLocalStorageFormat) {
359 qWarning() << cookiesWarningMessage.arg(
"WebLocalStorageFormat");
360 format = QSettings::IniFormat;
361 }
else if (format == QSettings::WebIndexedDBFormat) {
362 qWarning() << cookiesWarningMessage.arg(
"WebIndexedDBFormat");
363 format = QSettings::IniFormat;
366 if (format == QSettings::WebIndexedDBFormat && !qstdweb::haveJspi()) {
367 qWarning() <<
"QSettings::WebIndexedDBFormat requires JSPI, falling back to IniFormat with "
369 format = QSettings::IniFormat;
374 case QSettings::Format::WebLocalStorageFormat:
375 return new QWasmLocalStorageSettingsPrivate(scope, organization, application);
376 case QSettings::Format::WebIndexedDBFormat:
377 return new QWasmIDBSettingsPrivate(scope, organization, application);
378 case QSettings::Format::IniFormat:
379 case QSettings::Format::CustomFormat1:
380 case QSettings::Format::CustomFormat2:
381 case QSettings::Format::CustomFormat3:
382 case QSettings::Format::CustomFormat4:
383 case QSettings::Format::CustomFormat5:
384 case QSettings::Format::CustomFormat6:
385 case QSettings::Format::CustomFormat7:
386 case QSettings::Format::CustomFormat8:
387 case QSettings::Format::CustomFormat9:
388 case QSettings::Format::CustomFormat10:
389 case QSettings::Format::CustomFormat11:
390 case QSettings::Format::CustomFormat12:
391 case QSettings::Format::CustomFormat13:
392 case QSettings::Format::CustomFormat14:
393 case QSettings::Format::CustomFormat15:
394 case QSettings::Format::CustomFormat16:
395 return new QConfFileSettingsPrivate(format, scope, organization, application);
396 case QSettings::Format::InvalidFormat:
398 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