diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index fe81689932..c4c6f41387 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -136,6 +136,7 @@ qtConfig(settings) { } else: darwin:!nacl { SOURCES += io/qsettings_mac.cpp } + wasm : SOURCES += io/qsettings_wasm.cpp } win32 { diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index fc7122d904..dcc9340473 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -76,10 +76,6 @@ # include #endif -#ifdef Q_OS_WASM -#include -#endif - #include #include @@ -295,7 +291,7 @@ after_loop: // see also qsettings_win.cpp, qsettings_winrt.cpp and qsettings_mac.cpp -#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) +#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) && !defined(Q_OS_WASM) QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope, const QString &organization, const QString &application) { @@ -1185,7 +1181,9 @@ QConfFileSettingsPrivate::QConfFileSettingsPrivate(QSettings::Format format, confFiles.append(QConfFile::fromName(systemPath.path + orgFile, false)); } +#ifndef Q_OS_WASM // wasm needs to delay access until after file sync initAccess(); +#endif } QConfFileSettingsPrivate::QConfFileSettingsPrivate(const QString &fileName, @@ -1548,13 +1546,6 @@ void QConfFileSettingsPrivate::syncConfFile(QConfFile *confFile) perms |= QFile::ReadGroup | QFile::ReadOther; QFile(confFile->name).setPermissions(perms); } -#ifdef Q_OS_WASM - EM_ASM( - // Sync sandbox filesystem to persistent database filesystem. See QTBUG-70002 - FS.syncfs(false, function(err) { - }); - ); -#endif } else { setStatus(QSettings::AccessError); } diff --git a/src/corelib/io/qsettings_p.h b/src/corelib/io/qsettings_p.h index d18c96a06c..c30f099a72 100644 --- a/src/corelib/io/qsettings_p.h +++ b/src/corelib/io/qsettings_p.h @@ -57,6 +57,10 @@ #include "QtCore/qiodevice.h" #include "QtCore/qstack.h" #include "QtCore/qstringlist.h" + +#include +#include "qsettings.h" + #ifndef QT_NO_QOBJECT #include "private/qobject_p.h" #endif @@ -253,6 +257,10 @@ protected: mutable QSettings::Status status; }; +#ifdef Q_OS_WASM +class QWasmSettingsPrivate; +#endif + class QConfFileSettingsPrivate : public QSettingsPrivate { public: @@ -281,7 +289,7 @@ public: private: void initFormat(); - void initAccess(); + virtual void initAccess(); void syncConfFile(QConfFile *confFile); bool writeIniFile(QIODevice &device, const ParsedSettingsMap &map); #ifdef Q_OS_MAC @@ -297,6 +305,9 @@ private: QString extension; Qt::CaseSensitivity caseSensitivity; int nextPosition; +#ifdef Q_OS_WASM + friend class QWasmSettingsPrivate; +#endif }; QT_END_NAMESPACE diff --git a/src/corelib/io/qsettings_wasm.cpp b/src/corelib/io/qsettings_wasm.cpp new file mode 100644 index 0000000000..8d8f4b505c --- /dev/null +++ b/src/corelib/io/qsettings_wasm.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsettings.h" +#ifndef QT_NO_SETTINGS + +#include "qsettings_p.h" +#ifndef QT_NO_QOBJECT +#include "qcoreapplication.h" +#include +#endif // QT_NO_QOBJECT +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static bool isReadReady = false; + +class QWasmSettingsPrivate : public QConfFileSettingsPrivate +{ +public: + QWasmSettingsPrivate(QSettings::Scope scope, const QString &organization, + const QString &application); + ~QWasmSettingsPrivate(); + + bool get(const QString &key, QVariant *value) const override; + QStringList children(const QString &prefix, ChildSpec spec) const override; + void clear() override; + 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; + +private: + QString databaseName; + QString id; +}; + +static void QWasmSettingsPrivate_onLoad(void *userData, void *dataPtr, int size) +{ + QWasmSettingsPrivate *wasm = reinterpret_cast(userData); + + QFile file(wasm->fileName()); + QFileInfo fileInfo(wasm->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(); + wasm->setReady(); + } +} + +static void QWasmSettingsPrivate_onError(void *userData) +{ + QWasmSettingsPrivate *wasm = reinterpret_cast(userData); + if (wasm) + wasm->setStatus(QSettings::AccessError); +} + +static void QWasmSettingsPrivate_onStore(void *userData) +{ + QWasmSettingsPrivate *wasm = reinterpret_cast(userData); + if (wasm) + wasm->setStatus(QSettings::NoError); +} + +static void QWasmSettingsPrivate_onCheck(void *userData, int exists) +{ + QWasmSettingsPrivate *wasm = reinterpret_cast(userData); + if (wasm) { + if (exists) + wasm->loadLocal(wasm->fileName().toLocal8Bit()); + else + wasm->setReady(); + } +} + +QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, + QSettings::Scope scope, + const QString &organization, + const QString &application) +{ + Q_UNUSED(format) + if (organization == QLatin1String("Qt")) + { + QString organizationDomain = QCoreApplication::organizationDomain(); + QString applicationName = QCoreApplication::applicationName(); + + QSettingsPrivate *newSettings; + newSettings = new QWasmSettingsPrivate(scope, organizationDomain, applicationName); + + newSettings->beginGroupOrArray(QSettingsGroup(normalizedKey(organization))); + if (!application.isEmpty()) + newSettings->beginGroupOrArray(QSettingsGroup(normalizedKey(application))); + + return newSettings; + } + return new QWasmSettingsPrivate(scope, organization, application); +} + +QWasmSettingsPrivate::QWasmSettingsPrivate(QSettings::Scope scope, const QString &organization, + const QString &application) + : QConfFileSettingsPrivate(QSettings::NativeFormat, scope, organization, application) +{ + 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), + QWasmSettingsPrivate_onCheck, + QWasmSettingsPrivate_onError); +} + +QWasmSettingsPrivate::~QWasmSettingsPrivate() +{ +} + + void QWasmSettingsPrivate::initAccess() +{ + if (isReadReady) + QConfFileSettingsPrivate::initAccess(); +} + +bool QWasmSettingsPrivate::get(const QString &key, QVariant *value) const +{ + if (isReadReady) + return QConfFileSettingsPrivate::get(key, value); + + return false; +} + +QStringList QWasmSettingsPrivate::children(const QString &prefix, ChildSpec spec) const +{ + return QConfFileSettingsPrivate::children(prefix, spec); +} + +void QWasmSettingsPrivate::clear() +{ + QConfFileSettingsPrivate::clear(); + emscripten_idb_async_delete("/home/web_user", + fileName().toLocal8Bit(), + reinterpret_cast(this), + QWasmSettingsPrivate_onStore, + QWasmSettingsPrivate_onError); +} + +void QWasmSettingsPrivate::sync() +{ + QConfFileSettingsPrivate::sync(); + + QFile file(fileName()); + 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), + QWasmSettingsPrivate_onStore, + QWasmSettingsPrivate_onError); + } +} + +void QWasmSettingsPrivate::flush() +{ + sync(); +} + +bool QWasmSettingsPrivate::isWritable() const +{ + return isReadReady && QConfFileSettingsPrivate::isWritable(); +} + +void QWasmSettingsPrivate::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), + QWasmSettingsPrivate_onStore, + QWasmSettingsPrivate_onError); + setReady(); + } +} + +void QWasmSettingsPrivate::loadLocal(const QByteArray &filename) +{ + emscripten_idb_async_load("/home/web_user", + filename.data(), + reinterpret_cast(this), + QWasmSettingsPrivate_onLoad, + QWasmSettingsPrivate_onError); +} + +void QWasmSettingsPrivate::setReady() +{ + isReadReady = true; + setStatus(QSettings::NoError); + QConfFileSettingsPrivate::initAccess(); +} + +QT_END_NAMESPACE +#endif // QT_NO_SETTINGS diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 6d95979f76..32efa727b8 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -121,7 +121,6 @@ #endif #ifdef Q_OS_WASM -#include #include #endif @@ -480,13 +479,6 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint QCoreApplicationPrivate::~QCoreApplicationPrivate() { -#ifdef Q_OS_WASM - EM_ASM( - // unmount persistent directory as IDBFS - // see also QTBUG-70002 - FS.unmount('/home/web_user'); - ); -#endif #ifndef QT_NO_QOBJECT cleanupThreadData(); #endif @@ -780,17 +772,8 @@ void QCoreApplicationPrivate::init() Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object"); QCoreApplication::self = q; -#ifdef Q_OS_WASM - EM_ASM( - // mount and sync persistent filesystem to sandbox - FS.mount(IDBFS, {}, '/home/web_user'); - FS.syncfs(true, function(err) { - if (err) - Module.print(err); - }); - ); - #if QT_CONFIG(thread) +#ifdef Q_OS_WASM QThreadPrivate::idealThreadCount = emscripten::val::global("navigator")["hardwareConcurrency"].as(); #endif #endif diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index f09f7e941b..455de52319 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1696,13 +1696,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate() qt_gl_set_global_share_context(0); } #endif -#ifdef Q_OS_WASM - EM_ASM( - // unmount persistent directory as IDBFS - // see QTBUG-70002 - FS.unmount('/home/web_user'); - ); -#endif + platform_integration->destroy(); delete platform_theme;