diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index 20dc590483e..be0f2536dc0 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -878,7 +878,13 @@ QStringList QSettingsPrivate::splitArgs(const QString &s, qsizetype idx) void QConfFileSettingsPrivate::initFormat() { +#if defined(Q_OS_WASM) + extension = (format == QSettings::NativeFormat || format == QSettings::WebIndexedDBFormat) + ? ".conf"_L1 + : ".ini"_L1; +#else extension = (format == QSettings::NativeFormat) ? ".conf"_L1 : ".ini"_L1; +#endif readFunc = nullptr; writeFunc = nullptr; #if defined(Q_OS_DARWIN) @@ -887,7 +893,11 @@ void QConfFileSettingsPrivate::initFormat() caseSensitivity = IniCaseSensitivity; #endif +#if defined Q_OS_WASM + if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) { +#else if (format > QSettings::IniFormat) { +#endif const auto locker = qt_scoped_lock(settingsGlobalMutex); const CustomFormatVector *customFormatVector = customFormatVectorFunc(); @@ -905,7 +915,11 @@ void QConfFileSettingsPrivate::initFormat() void QConfFileSettingsPrivate::initAccess() { if (!confFiles.isEmpty()) { +#if defined Q_OS_WASM + if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat) { +#else if (format > QSettings::IniFormat) { +#endif if (!readFunc) setStatus(QSettings::AccessError); } @@ -1307,7 +1321,11 @@ QString QConfFileSettingsPrivate::fileName() const bool QConfFileSettingsPrivate::isWritable() const { +#if defined(Q_OS_WASM) + if (format > QSettings::IniFormat && format != QSettings::WebIndexedDBFormat && !writeFunc) +#else if (format > QSettings::IniFormat && !writeFunc) +#endif return false; if (confFiles.isEmpty()) @@ -1386,6 +1404,13 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) */ if (file.isReadable() && file.size() != 0) { bool ok = false; + +#ifdef Q_OS_WASM + if (format == QSettings::WebIndexedDBFormat) { + QByteArray data = file.readAll(); + ok = readIniFile(data, &confFile->unparsedIniSections); + } else +#endif #ifdef Q_OS_DARWIN if (format == QSettings::NativeFormat) { QByteArray data = file.readAll(); @@ -1442,6 +1467,11 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) return; } +#ifdef Q_OS_WASM + if (format == QSettings::WebIndexedDBFormat) { + ok = writeIniFile(sf, mergedKeys); + } else +#endif #ifdef Q_OS_DARWIN if (format == QSettings::NativeFormat) { ok = writePlistFile(sf, mergedKeys); diff --git a/src/corelib/io/qsettings_wasm.cpp b/src/corelib/io/qsettings_wasm.cpp index 8329124804a..9b206e26d53 100644 --- a/src/corelib/io/qsettings_wasm.cpp +++ b/src/corelib/io/qsettings_wasm.cpp @@ -10,6 +10,7 @@ #include #endif // QT_NO_QOBJECT #include +#include #include #include @@ -216,7 +217,6 @@ public: QWasmIDBSettingsPrivate(QSettings::Scope scope, const QString &organization, const QString &application); ~QWasmIDBSettingsPrivate(); - static QWasmIDBSettingsPrivate *get(void *userData); std::optional get(const QString &key) const override; QStringList children(const QString &prefix, ChildSpec spec) const override; @@ -224,91 +224,59 @@ public: void sync() override; void flush() override; bool isWritable() const override; - - void syncToLocal(const char *data, int size); - void loadLocal(const QByteArray &filename); - void setReady(); void initAccess() override; + void loadLocal(); + void setReady(); + private: + bool writeSettingsToTemporaryFile(void *dataPtr, int size); + QString databaseName; QString id; - static QList liveSettings; }; -QList QWasmIDBSettingsPrivate::liveSettings; static bool isReadReady = false; -static void QWasmIDBSettingsPrivate_onLoad(void *userData, void *dataPtr, int size) -{ - QWasmIDBSettingsPrivate *settings = QWasmIDBSettingsPrivate::get(userData); - if (!settings) - return; - - QFile file(settings->fileName()); - QFileInfo fileInfo(settings->fileName()); - QDir dir(fileInfo.path()); - if (!dir.exists()) - dir.mkpath(fileInfo.path()); - - if (file.open(QFile::WriteOnly)) { - file.write(reinterpret_cast(dataPtr), size); - file.close(); - settings->setReady(); - } -} - -static void QWasmIDBSettingsPrivate_onError(void *userData) -{ - if (QWasmIDBSettingsPrivate *settings = QWasmIDBSettingsPrivate::get(userData)) - settings->setStatus(QSettings::AccessError); -} - -static void QWasmIDBSettingsPrivate_onStore(void *userData) -{ - if (QWasmIDBSettingsPrivate *settings = QWasmIDBSettingsPrivate::get(userData)) - settings->setStatus(QSettings::NoError); -} - -static void QWasmIDBSettingsPrivate_onCheck(void *userData, int exists) -{ - if (QWasmIDBSettingsPrivate *settings = QWasmIDBSettingsPrivate::get(userData)) { - if (exists) - settings->loadLocal(settings->fileName().toLocal8Bit()); - else - settings->setReady(); - } -} +constexpr char DbName[] = "/home/web_user"; QWasmIDBSettingsPrivate::QWasmIDBSettingsPrivate(QSettings::Scope scope, const QString &organization, const QString &application) - : QConfFileSettingsPrivate(QSettings::NativeFormat, scope, organization, application) + : QConfFileSettingsPrivate(QSettings::WebIndexedDBFormat, scope, organization, application) { - liveSettings.push_back(this); + Q_ASSERT_X(qstdweb::haveJspi(), Q_FUNC_INFO, "QWasmIDBSettingsPrivate needs JSPI to work"); + + if (organization.isEmpty()) { + setStatus(QSettings::AccessError); + return; + } setStatus(QSettings::AccessError); // access error until sandbox gets loaded databaseName = organization; id = application; - emscripten_idb_async_exists("/home/web_user", - fileName().toLocal8Bit(), - reinterpret_cast(this), - QWasmIDBSettingsPrivate_onCheck, - QWasmIDBSettingsPrivate_onError); + int exists = 0; + int error = 0; + emscripten_idb_exists(DbName, fileName().toLocal8Bit(), &exists, &error); + if (error) { + setStatus(QSettings::AccessError); + return; + } + if (exists) { + void *contents; + int size; + emscripten_idb_load(DbName, fileName().toLocal8Bit(), &contents, &size, &error); + if (error || !writeSettingsToTemporaryFile(contents, size)) { + setStatus(QSettings::AccessError); + return; + } + } + + setReady(); } -QWasmIDBSettingsPrivate::~QWasmIDBSettingsPrivate() -{ - liveSettings.removeAll(this); -} - -QWasmIDBSettingsPrivate *QWasmIDBSettingsPrivate::get(void *userData) -{ - if (QWasmIDBSettingsPrivate::liveSettings.contains(userData)) - return reinterpret_cast(userData); - return nullptr; -} +QWasmIDBSettingsPrivate::~QWasmIDBSettingsPrivate() = default; void QWasmIDBSettingsPrivate::initAccess() { @@ -324,6 +292,20 @@ std::optional QWasmIDBSettingsPrivate::get(const QString &key) const return std::nullopt; } +bool QWasmIDBSettingsPrivate::writeSettingsToTemporaryFile(void *dataPtr, int size) +{ + QFile file(fileName()); + QFileInfo fileInfo(fileName()); + QDir dir(fileInfo.path()); + if (!dir.exists()) + dir.mkpath(fileInfo.path()); + + if (!file.open(QFile::WriteOnly)) + return false; + + return size == file.write(reinterpret_cast(dataPtr), size); +} + QStringList QWasmIDBSettingsPrivate::children(const QString &prefix, ChildSpec spec) const { return QConfFileSettingsPrivate::children(prefix, spec); @@ -332,11 +314,10 @@ QStringList QWasmIDBSettingsPrivate::children(const QString &prefix, ChildSpec s void QWasmIDBSettingsPrivate::clear() { QConfFileSettingsPrivate::clear(); - emscripten_idb_async_delete("/home/web_user", - fileName().toLocal8Bit(), - reinterpret_cast(this), - QWasmIDBSettingsPrivate_onStore, - QWasmIDBSettingsPrivate_onError); + + int error = 0; + emscripten_idb_delete(DbName, fileName().toLocal8Bit(), &error); + setStatus(!!error ? QSettings::AccessError : QSettings::NoError); } void QWasmIDBSettingsPrivate::sync() @@ -347,13 +328,11 @@ void QWasmIDBSettingsPrivate::sync() if (file.open(QFile::ReadOnly)) { QByteArray dataPointer = file.readAll(); - emscripten_idb_async_store("/home/web_user", - fileName().toLocal8Bit(), - reinterpret_cast(dataPointer.data()), - dataPointer.length(), - reinterpret_cast(this), - QWasmIDBSettingsPrivate_onStore, - QWasmIDBSettingsPrivate_onError); + int error = 0; + emscripten_idb_store(DbName, fileName().toLocal8Bit(), + reinterpret_cast(dataPointer.data()), dataPointer.length(), + &error); + setStatus(!!error ? QSettings::AccessError : QSettings::NoError); } } @@ -367,34 +346,6 @@ bool QWasmIDBSettingsPrivate::isWritable() const return isReadReady && QConfFileSettingsPrivate::isWritable(); } -void QWasmIDBSettingsPrivate::syncToLocal(const char *data, int size) -{ - QFile file(fileName()); - - if (file.open(QFile::WriteOnly)) { - file.write(data, size + 1); - QByteArray data = file.readAll(); - - emscripten_idb_async_store("/home/web_user", - fileName().toLocal8Bit(), - reinterpret_cast(data.data()), - data.length(), - reinterpret_cast(this), - QWasmIDBSettingsPrivate_onStore, - QWasmIDBSettingsPrivate_onError); - setReady(); - } -} - -void QWasmIDBSettingsPrivate::loadLocal(const QByteArray &filename) -{ - emscripten_idb_async_load("/home/web_user", - filename.data(), - reinterpret_cast(this), - QWasmIDBSettingsPrivate_onLoad, - QWasmIDBSettingsPrivate_onError); -} - void QWasmIDBSettingsPrivate::setReady() { isReadReady = true; @@ -422,6 +373,11 @@ QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings:: format = QSettings::IniFormat; } } + if (format == QSettings::WebIndexedDBFormat && !qstdweb::haveJspi()) { + qWarning() << "QSettings::WebIndexedDBFormat requires JSPI, falling back to IniFormat with " + "temporary file"; + format = QSettings::IniFormat; + } // Create settings backend according to selected format switch (format) { diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index 337428d01a3..0661ac17ca7 100644 --- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp +++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp @@ -42,9 +42,9 @@ #endif #if defined(Q_OS_WASM) -# include +#include -# include "emscripten/val.h" +#include "emscripten/val.h" #endif Q_DECLARE_METATYPE(QSettings::Format) @@ -89,6 +89,10 @@ static void populateWithFormats() QTest::addColumn("format"); QTest::newRow("native") << QSettings::NativeFormat; +#if defined(Q_OS_WASM) + if (qstdweb::haveJspi()) + QTest::newRow("idb") << QSettings::WebIndexedDBFormat; +#endif // defined(Q_OS_WASM) QTest::newRow("ini") << QSettings::IniFormat; QTest::newRow("custom1") << QSettings::CustomFormat1; QTest::newRow("custom2") << QSettings::CustomFormat2;