diff --git a/spark-webapp-runtime.pro b/spark-webapp-runtime.pro
index 20e159b..a41f2b4 100644
--- a/spark-webapp-runtime.pro
+++ b/spark-webapp-runtime.pro
@@ -1,6 +1,6 @@
 TEMPLATE = subdirs
 
 SUBDIRS += \
-    spark-webapp-runtime
+        spark-webapp-runtime
 
 CONFIG += ordered
diff --git a/spark-webapp-runtime/application.cpp b/spark-webapp-runtime/application.cpp
new file mode 100644
index 0000000..9ac7b4d
--- /dev/null
+++ b/spark-webapp-runtime/application.cpp
@@ -0,0 +1,81 @@
+#include "application.h"
+#include "globaldefine.h"
+
+#include <DPlatformWindowHandle>
+#include <DAboutDialog>
+
+Application::Application(int &argc, char **argv)
+    : DApplication(argc, argv)
+{
+    loadTranslator();
+
+    setAttribute(Qt::AA_UseHighDpiPixmaps);
+    if (!DPlatformWindowHandle::pluginVersion().isEmpty()) {
+        setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
+    }
+
+    setApplicationVersion(QString(CURRENT_VER));
+    setOrganizationName(ORGANIZATION_NAME); // 添加组织名称,和商店主体的文件夹同在 ~/.local/share/spark-union 文件夹下
+    setApplicationName(APPLICATION_NAME); // 这里不要翻译,否则 ~/.local/share 中文件夹名也会被翻译
+    setProductName(DEFAULT_TITLE);
+    setApplicationDisplayName(DEFAULT_TITLE);
+    setApplicationLicense(" <a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPLv3</a> ");
+
+    initAboutDialog();
+}
+
+void Application::handleAboutAction()
+{
+    if (aboutDialog()) {
+        DApplication::handleAboutAction();
+        return;
+    }
+
+    initAboutDialog();
+    DApplication::handleAboutAction();
+}
+
+void Application::initAboutDialog()
+{
+    // Customized DAboutDialog
+    DAboutDialog *dialog = new DAboutDialog(activeWindow());
+    // WindowIcon
+    dialog->setWindowIcon(QIcon(":/images/spark-webapp-runtime.svg"));
+    // ProductIcon
+    dialog->setProductIcon(QIcon(":/images/spark-webapp-runtime.svg"));
+    // ProductName
+    dialog->setProductName(productName());
+    // Version
+    dialog->setVersion(translate("DAboutDialog", "Version: %1").arg(applicationVersion()));
+    // CompanyLogo
+    dialog->setCompanyLogo(QPixmap(":/images/Logo-Spark.png"));
+    // Description
+
+    QString szDefaultDesc = QString("<a href='https://gitee.com/deepin-community-store/spark-web-app-runtime'><span style='font-size:12pt;font-weight:500;'>%1</span></a><br/>"
+                                    "<span style='font-size:12pt;'>%2</span>")
+                                .arg(DEFAULT_TITLE)
+                                .arg(QObject::tr("Presented By Spark developers # HadesStudio"));
+
+    dialog->setDescription(szDefaultDesc);
+    // WebsiteName
+    dialog->setWebsiteName("Spark Project");
+    // WebsiteLink
+    dialog->setWebsiteLink("https://gitee.com/deepin-community-store/");
+    // License
+    dialog->setLicense(translate("DAboutDialog", "%1 is released under %2").arg(productName()).arg(applicationLicense()));
+
+    setAboutDialog(dialog);
+    connect(aboutDialog(), &DAboutDialog::destroyed, this, [=] {
+        setAboutDialog(nullptr);
+    });
+
+    dialog->hide();
+}
+
+void Application::slotMainWindowClose()
+{
+    if (aboutDialog()) {
+        aboutDialog()->close();
+        aboutDialog()->deleteLater();
+    }
+}
diff --git a/spark-webapp-runtime/application.h b/spark-webapp-runtime/application.h
new file mode 100644
index 0000000..30f7597
--- /dev/null
+++ b/spark-webapp-runtime/application.h
@@ -0,0 +1,26 @@
+#ifndef APPLICATION_H
+#define APPLICATION_H
+
+#include <DApplication>
+
+DWIDGET_USE_NAMESPACE
+
+class Application : public DApplication
+{
+    Q_OBJECT
+
+public:
+    Application(int &argc, char **argv);
+    void handleAboutAction() override;
+
+private:
+    void initAboutDialog();
+
+signals:
+    void sigQuit();
+
+public slots:
+    void slotMainWindowClose();
+};
+
+#endif // APPLICATION_H
diff --git a/spark-webapp-runtime/globaldefine.h b/spark-webapp-runtime/globaldefine.h
index 276638f..606903f 100644
--- a/spark-webapp-runtime/globaldefine.h
+++ b/spark-webapp-runtime/globaldefine.h
@@ -10,8 +10,8 @@
 #define DEFAULT_WIDTH (1024)
 #define DEFAULT_HEIGHT (768)
 
-#define DEFAULT_DESC QString()
 #define DEFAULT_ICON QString()
+#define DEFAULT_DESC QString()
 #define DEFAULT_CFG QString()
 
 #define DEFAULT_ROOT QString()
diff --git a/spark-webapp-runtime/main.cpp b/spark-webapp-runtime/main.cpp
index f1503a2..4eba78b 100644
--- a/spark-webapp-runtime/main.cpp
+++ b/spark-webapp-runtime/main.cpp
@@ -1,30 +1,28 @@
-/*
- *
+/**
+ * Spark WebApp Runtime
  * 星火网页应用运行环境
-*/
+ */
+#include "application.h"
 #include "mainwindow.h"
+#include "globaldefine.h"
+#include "httpd.h"
 
-#include <DApplication>
-#include <DPlatformWindowHandle>
+#include <DSysInfo>
 
 #include <QCommandLineParser>
 #include <QCommandLineOption>
 #include <QFileInfo>
 #include <QSettings>
-#include <QVector>
 
-#include "globaldefine.h"
-#include "httpd.h"
-
-DWIDGET_USE_NAMESPACE
+#include <unistd.h>
 
 int main(int argc, char *argv[])
 {
     if (!QString(qgetenv("XDG_CURRENT_DESKTOP")).toLower().startsWith("deepin")) {
-        setenv("XDG_CURRENT_DESKTOP", "Deepin", 1);
+        qputenv("XDG_CURRENT_DESKTOP", "Deepin");
     }
 
-    // 龙芯机器配置,使得DApplication能正确加载QTWEBENGINE
+    // 龙芯机器配置,使得 DApplication 能正确加载 QTWEBENGINE
     qputenv("DTK_FORCE_RASTER_WIDGETS", "FALSE");
 //    qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-features=UseModernMediaControls");
 //    qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-web-security");
@@ -32,60 +30,27 @@ int main(int argc, char *argv[])
     qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--no-sandbox");
 #endif
 
+    if (!Dtk::Core::DSysInfo::isDDE()) {
 #ifndef DSTORE_NO_DXCBs
-    Dtk::Widget::DApplication::loadDXcbPlugin();
+        DApplication::loadDXcbPlugin();
 #endif
-
-    // 强制使用DTK平台插件
-    QVector<char *> fakeArgv(argc + 2);
-    fakeArgv[0] = argv[0];
-    fakeArgv[1] = "-platformtheme";
-    fakeArgv[2] = "deepin";
-    for(int i = 1; i < argc; i++) fakeArgv[i + 2] = argv[i];
-    int fakeArgc = argc + 2;
-    DApplication a(fakeArgc, fakeArgv.data());
-
-    a.loadTranslator();
-    a.setAttribute(Qt::AA_UseHighDpiPixmaps);
-    if (!Dtk::Widget::DPlatformWindowHandle::pluginVersion().isEmpty()) {
-        a.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
     }
 
-    a.setApplicationVersion(QString(CURRENT_VER));
-    a.setOrganizationName(ORGANIZATION_NAME); // 添加组织名称,和商店主体的文件夹同在 ~/.local/share/spark-union 文件夹下
-    a.setApplicationName(APPLICATION_NAME); // 这里不要翻译,否则 ~/.local/share 中文件夹名也会被翻译
-    a.setProductName(DEFAULT_TITLE);
-    a.setApplicationDisplayName(DEFAULT_TITLE);
+    // 开启 HiDPI 缩放支持
+    DApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
 
-    // Customized DAboutDialog (Can't work on other distro like Ubuntu...)
-    DAboutDialog *dialog = new DAboutDialog;
-    a.setAboutDialog(dialog);
-
-    // WindowIcon
-    dialog->setWindowIcon(QIcon(":/images/spark-webapp-runtime.svg"));
-    // ProductIcon
-    dialog->setProductIcon(QIcon(":/images/spark-webapp-runtime.svg"));
-    // ProductName
-    dialog->setProductName(QString("<span>%1</span>").arg(DEFAULT_TITLE));
-    // Version
-    dialog->setVersion(QString("%1 %2").arg(QObject::tr("Version:")).arg(CURRENT_VER));
-    // CompanyLogo
-    dialog->setCompanyLogo(QPixmap(":/images/Logo-Spark.png"));
-    // Description
-
-    QString szDefaultDesc = QString("<a href='https://gitee.com/deepin-community-store/spark-web-app-runtime'><span style='font-size:12pt;font-weight:500;'>%1</span></a><br/>"
-                                    "<span style='font-size:12pt;'>%2</span>")
-                                .arg(DEFAULT_TITLE)
-                                .arg(QObject::tr("Presented By Spark developers # HadesStudio"));
-
-    dialog->setDescription(szDefaultDesc);
-    // WebsiteName
-    dialog->setWebsiteName("Spark Project");
-    // WebsiteLink
-    dialog->setWebsiteLink("https://gitee.com/deepin-community-store/");
-    // License
-    dialog->setLicense(QObject::tr("Published under GPLv3"));
+    // 强制使用 DTK 平台插件
+    int fakeArgc = argc + 2;
+    QVector<char *> fakeArgv(fakeArgc);
+    fakeArgv[0] = argv[0];
+    fakeArgv[1] = QString("-platformtheme").toUtf8().data();
+    fakeArgv[2] = QString("deepin").toUtf8().data();
+    for (int i = 1; i < argc; i++) {
+        fakeArgv[i + 2] = argv[i];
+    }
+    Application a(fakeArgc, fakeArgv.data());
 
+    // 解析命令行启动参数
     QCommandLineParser parser;
 
     parser.setApplicationDescription(QObject::tr("Description: %1").arg(DEFAULT_TITLE));
@@ -174,15 +139,14 @@ int main(int argc, char *argv[])
                                              << "port",
                                QObject::tr("The port number of the program web service."),
                                "port",
-                               DEFAULT_PORT);
+                               QString::number(DEFAULT_PORT));
     parser.addOption(optPort);
 
-
     QCommandLineOption useGPU(QStringList() << "G"
                                             << "GPU",
-                               QObject::tr("To use GPU instead of CPU to decoding. Default True."),
-                               "GPU",
-                               QString::number(DEFAULT_GPU));
+                              QObject::tr("To use GPU instead of CPU to decoding. Default True."),
+                              "GPU",
+                              QString::number(DEFAULT_GPU));
     parser.addOption(useGPU);
 
 #if SSL_SERVER
@@ -212,7 +176,12 @@ int main(int argc, char *argv[])
 #if SSL_SERVER
     quint16 u16sslPort = 0;
 #endif
+    QString szDefaultDesc = QString("<a href='https://gitee.com/deepin-community-store/spark-web-app-runtime'><span style='font-size:12pt;font-weight:500;'>%1</span></a><br/>"
+                                    "<span style='font-size:12pt;'>%2</span>")
+                                .arg(DEFAULT_TITLE)
+                                .arg(QObject::tr("Presented By Spark developers # HadesStudio"));
 
+    // 解析可能存在的配置文件
     QString szCfgFile = DEFAULT_CFG;
     if (parser.isSet(optCfgFile)) {
         szCfgFile = parser.value(optCfgFile);
@@ -232,7 +201,7 @@ int main(int argc, char *argv[])
                              .arg(settings.value("SparkWebAppRuntime/Desc", QString()).toString())
                              .arg(szDefaultDesc);
                 szRootPath = settings.value("SparkWebAppRuntime/RootPath", QString()).toString();
-                u16Port = settings.value("SparkWebAppRuntime/Port", 0).toUInt();
+                u16Port = static_cast<quint16>(settings.value("SparkWebAppRuntime/Port", 0).toUInt());
 #if SSL_SERVER
                 u16sslPort = settings.value("SparkWebAppRuntime/SSLPort", 0).toUInt();
 #endif
@@ -267,6 +236,9 @@ int main(int argc, char *argv[])
         hideButtons = true;
     }
 
+    if (parser.isSet(optIcon)) {
+        szIcon = parser.value(optIcon);
+    }
     if (parser.isSet(optDesc)) {
         szDesc = QString("%1<br/><br/>%2").arg(parser.value(optDesc)).arg(szDefaultDesc);
     }
@@ -276,17 +248,17 @@ int main(int argc, char *argv[])
     }
 
     if (parser.isSet(optPort)) {
-        u16Port = parser.value(optPort).toUInt();
+        u16Port = static_cast<quint16>(parser.value(optPort).toUInt());
     }
 
     if (parser.isSet(useGPU)) {
         toUseGPU = parser.value(useGPU).toUInt();
     }
-    if (toUseGPU == true){
-        qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--ignore-gpu-blocklist --enable-gpu-rasterization --enable-native-gpu-memory-buffers --enable-accelerated-video-decode");
-        #ifdef __sw_64__
-            qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--ignore-gpu-blocklist --enable-gpu-rasterization --enable-native-gpu-memory-buffers --enable-accelerated-video-decode --no-sandbox");
-        #endif
+    if (toUseGPU == true) {
+        qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--ignore-gpu-blocklist --enable-gpu-rasterization --enable-native-gpu-memory-buffers --enable-accelerated-video-decode --blink-settings=darkMode=4,darkModeImagePolicy=2");
+#ifdef __sw_64__
+        qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--ignore-gpu-blocklist --enable-gpu-rasterization --enable-native-gpu-memory-buffers --enable-accelerated-video-decode --blink-settings=darkMode=4,darkModeImagePolicy=2 --no-sandbox");
+#endif
         qDebug() << "Setting GPU to True.";
     }
 
@@ -335,7 +307,7 @@ int main(int argc, char *argv[])
             szRootPath = QString(argv[11]);
         }
         if (argc > 12) {
-            u16Port = QString(argv[12]).toUInt();
+            u16Port = static_cast<quint16>(QString(argv[12]).toUInt());
         }
 #if SSL_SERVER
         if (argc > 13) {
@@ -348,10 +320,13 @@ int main(int argc, char *argv[])
         fullScreen = false; // 固定窗口大小时禁用全屏模式,避免标题栏按钮 BUG
     }
 
+    // DApplication 单例运行(标题名称_当前登录用户 id)
+    if (!a.setSingleInstance(szTitle + "_" + QString::number(getuid()))) {
+        qInfo() << "Another instance has already started, now exit...";
+        exit(0);
+    }
     a.setQuitOnLastWindowClosed(!tray); // 启用托盘时,退出程序后服务不终止
 
-    MainWindow w(szTitle, szUrl, width, height, tray, fullScreen, fixSize, hideButtons, dialog);
-
 #if SSL_SERVER
     if (!szRootPath.isEmpty() && u16Port > 0 && u16sslPort > 0) {
         HttpD httpd(szRootPath, u16Port, u16sslPort);
@@ -360,24 +335,23 @@ int main(int argc, char *argv[])
 #else
     if (!szRootPath.isEmpty() && u16Port > 0) {
         static HttpD httpd(szRootPath, u16Port);
-        QObject::connect(&w, &MainWindow::sigQuit, &httpd, &HttpD::stop);
+        QObject::connect(&a, &Application::sigQuit, &httpd, &HttpD::stop);
         httpd.start();
     }
 #endif
 
-    if (parser.isSet(optIcon)) {
-        szIcon = parser.value(optIcon);
-    }
+    MainWindow w(szTitle, szUrl, width, height, tray, fullScreen, fixSize, hideButtons);
+    QObject::connect(&a, &Application::newInstanceStarted, &w, &MainWindow::slotNewInstanceStarted);
+    QObject::connect(&w, &MainWindow::sigClose, &a, &Application::slotMainWindowClose);
 
     if (!szIcon.isEmpty()) {
-        dialog->setWindowIcon(QIcon(szIcon));
-        dialog->setProductIcon(QIcon(szIcon));
         w.setIcon(szIcon);
     }
     if (!szDesc.isEmpty()) {
-        dialog->setDescription(szDesc);
+        w.setDescription(szDesc);
     }
 
     w.show();
+
     return a.exec();
 }
diff --git a/spark-webapp-runtime/mainwindow.cpp b/spark-webapp-runtime/mainwindow.cpp
index c46d810..883620a 100644
--- a/spark-webapp-runtime/mainwindow.cpp
+++ b/spark-webapp-runtime/mainwindow.cpp
@@ -1,16 +1,19 @@
 #include "mainwindow.h"
+#include "application.h"
+#include "webengineview.h"
+#include "webenginepage.h"
 
 #include <DWidgetUtil>
 #include <DTitlebar>
 #include <DMessageManager>
 #include <DDesktopServices>
 
+#include <QKeyEvent>
+#include <QWebEngineProfile>
 #include <QFileInfo>
 #include <QFileDialog>
 #include <QDir>
-#include <QCloseEvent>
-
-#include "webengineview.h"
+#include <QStandardPaths>
 
 MainWindow::MainWindow(QString szTitle,
                        QString szUrl,
@@ -20,7 +23,6 @@ MainWindow::MainWindow(QString szTitle,
                        bool nFullScreen,
                        bool nFixSize,
                        bool nHideButtons,
-                       QDialog *dialog,
                        QWidget *parent)
     : DMainWindow(parent)
     , m_title(szTitle)
@@ -32,27 +34,26 @@ MainWindow::MainWindow(QString szTitle,
     , m_isFixedSize(nFixSize)
     , m_isHideButton(nHideButtons)
     , m_widget(new Widget(m_url, this))
-    , m_dialog(dynamic_cast<DAboutDialog *>(dialog))
     , m_tray(new QSystemTrayIcon(this))
     , btnBack(new DToolButton(titlebar()))
     , btnForward(new DToolButton(titlebar()))
     , btnRefresh(new DToolButton(titlebar()))
     , m_menu(new QMenu(titlebar()))
-    , m_fullScreen(new QAction(tr("Full Screen"), this))
-    , m_fixSize(new QAction(tr("Fix Size"), this))
-    , m_hideButtons(new QAction(tr("Hide Buttons"), this))
-    , m_clearCache(new QAction(tr("Clear Cache"), this))
+    , m_fullScreen(new QAction(QObject::tr("Full Screen"), this))
+    , m_fixSize(new QAction(QObject::tr("Fix Size"), this))
+    , m_hideButtons(new QAction(QObject::tr("Hide Buttons"), this))
+    , m_clearCache(new QAction(QObject::tr("Clear Cache"), this))
     , t_menu(new QMenu(this))
-    , t_show(new QAction(tr("Show MainWindow"), this))
-    , t_about(new QAction(tr("About"), this))
-    , t_exit(new QAction(tr("Exit"), this))
+    , t_show(new QAction(QObject::tr("Show MainWindow"), this))
+    , t_about(new QAction(qApp->translate("TitleBarMenu", "About"), this))
+    , t_exit(new QAction(qApp->translate("TitleBarMenu", "Exit"), this))
     , downloadMessage(new DFloatingMessage(DFloatingMessage::ResidentType, this))
     , downloadProgressWidget(new QWidget(downloadMessage))
     , progressBarLayout(new QHBoxLayout(downloadProgressWidget))
     , downloadProgressBar(new DProgressBar(downloadProgressWidget))
-    , btnPause(new DPushButton(tr("Pause"), downloadProgressWidget))
-    , btnResume(new DPushButton(tr("Resume"), downloadProgressWidget))
-    , btnCancel(new DPushButton(tr("Cancel"), downloadProgressWidget))
+    , btnPause(new DPushButton(QObject::tr("Pause"), downloadProgressWidget))
+    , btnResume(new DPushButton(QObject::tr("Resume"), downloadProgressWidget))
+    , btnCancel(new DPushButton(QObject::tr("Cancel"), downloadProgressWidget))
     , isCanceled(false)
 {
     initUI();
@@ -62,16 +63,30 @@ MainWindow::MainWindow(QString szTitle,
 
 MainWindow::~MainWindow()
 {
-    emit sigQuit();
-    delete m_dialog;
 }
 
 void MainWindow::setIcon(QString szIconPath)
 {
-    if (QFileInfo(szIconPath).exists()) {
-        titlebar()->setIcon(QIcon(szIconPath));
-        setWindowIcon(QIcon(szIconPath));
-        m_tray->setIcon(QIcon(szIconPath));
+    if (!QFileInfo(szIconPath).exists()) {
+        return;
+    }
+
+    titlebar()->setIcon(QIcon(szIconPath));
+    setWindowIcon(QIcon(szIconPath));
+    m_tray->setIcon(QIcon(szIconPath));
+
+    DAboutDialog *aboutDialog = qobject_cast<Application *>(qApp)->aboutDialog();
+    if (aboutDialog) {
+        aboutDialog->setWindowIcon(QIcon::fromTheme(szIconPath));
+        aboutDialog->setProductIcon(QIcon::fromTheme(szIconPath));
+    }
+}
+
+void MainWindow::setDescription(const QString &desc)
+{
+    DAboutDialog *aboutDialog = qobject_cast<Application *>(qApp)->aboutDialog();
+    if (aboutDialog) {
+        aboutDialog->setDescription(desc);
     }
 }
 
@@ -85,7 +100,8 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
             m_menu->update();
         }
     }
-    event->accept();
+
+    DMainWindow::keyPressEvent(event);
 }
 
 void MainWindow::resizeEvent(QResizeEvent *event)
@@ -98,15 +114,17 @@ void MainWindow::resizeEvent(QResizeEvent *event)
             m_fixSize->setEnabled(true); // 命令行参数没有固定窗口大小时,窗口模式下允许手动选择固定窗口大小
         }
     }
+
     DMainWindow::resizeEvent(event);
 }
 
 void MainWindow::closeEvent(QCloseEvent *event)
 {
     if (!m_isTrayEnabled) {
-        m_dialog->close(); // 不启用托盘时,关闭主窗口则关闭关于窗口
+        emit sigClose(); // 不启用托盘时,关闭主窗口则关闭关于窗口
     }
-    event->accept();
+
+    DMainWindow::closeEvent(event);
 }
 
 void MainWindow::initUI()
@@ -240,8 +258,7 @@ void MainWindow::initConnections()
         fixSize();
     });
     connect(t_about, &QAction::triggered, this, [=]() {
-        m_dialog->activateWindow();
-        m_dialog->show();
+        qobject_cast<Application *>(qApp)->handleAboutAction();
     });
     connect(t_exit, &QAction::triggered, this, [=]() {
         exit(0);
@@ -262,14 +279,14 @@ void MainWindow::fullScreen()
         m_fixSize->setDisabled(true);
         m_menu->update();
         showFullScreen();
-        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(tr("%1Fullscreen Mode")).arg("    "));
+        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(QObject::tr("%1Fullscreen Mode")).arg("    "));
     } else {
         if (!m_isFixedSize) {
             m_fixSize->setDisabled(false); // 命令行参数没有固定窗口大小时,窗口模式下允许手动选择固定窗口大小
         }
         m_menu->update();
         showNormal();
-        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(tr("%1Windowed Mode")).arg("    "));
+        // DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-information").pixmap(64, 64), QString(QObject::tr("%1Windowed Mode")).arg("    "));
     }
 }
 
@@ -307,7 +324,7 @@ void MainWindow::hideButtons()
 void MainWindow::clearCache()
 {
     // 清除缓存文件夹并刷新页面
-    QDir dir(QDir::homePath() + "/.local/share/" + ORGANIZATION_NAME + "/" + APPLICATION_NAME);
+    QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
     if (dir.exists()) {
         dir.removeRecursively();
     }
@@ -317,7 +334,9 @@ void MainWindow::clearCache()
 
 QString MainWindow::saveAs(QString fileName)
 {
-    QString saveFile = QFileDialog::getSaveFileName(this, tr("Save As"), QDir::homePath() + "/Downloads/" + fileName);
+    QString saveFile = QFileDialog::getSaveFileName(this,
+                                                    QObject::tr("Save As"),
+                                                    QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + fileName);
     if (!saveFile.isEmpty()) {
         // 判断上层目录是否可写入
         if (QFileInfo(QFileInfo(saveFile).absolutePath()).isWritable()) {
@@ -329,11 +348,19 @@ QString MainWindow::saveAs(QString fileName)
     return nullptr;
 }
 
+void MainWindow::slotNewInstanceStarted()
+{
+    this->setWindowState(Qt::WindowActive);
+    this->activateWindow();
+    this->show();
+}
+
 void MainWindow::on_trayIconActivated(QSystemTrayIcon::ActivationReason reason)
 {
     switch (reason) {
     /* 响应托盘点击事件 */
     case QSystemTrayIcon::Trigger:
+        this->setWindowState(Qt::WindowActive);
         this->activateWindow();
         fixSize();
         break;
@@ -372,7 +399,7 @@ void MainWindow::on_downloadStart(QWebEngineDownloadItem *item)
 
         DFloatingMessage *message = new DFloatingMessage(DFloatingMessage::TransientType);
         message->setIcon(QIcon::fromTheme("dialog-information").pixmap(64, 64));
-        message->setMessage(QString(tr("%1Start downloading %2")).arg("    ").arg(fileName));
+        message->setMessage(QString(QObject::tr("%1Start downloading %2")).arg("    ").arg(fileName));
         DMessageManager::instance()->sendMessage(this, message);
 
         item->accept();
@@ -383,7 +410,7 @@ void MainWindow::on_downloadStart(QWebEngineDownloadItem *item)
         btnPause->show();
         this->downloadMessage->show(); // 上一次下载完成后隐藏了进度条,这里要重新显示
     } else {
-        DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-cancel").pixmap(64, 64), QString(tr("%1Wait for previous download to complete!")).arg("    "));
+        DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-cancel").pixmap(64, 64), QString(QObject::tr("%1Wait for previous download to complete!")).arg("    "));
     }
 }
 
@@ -405,17 +432,18 @@ void MainWindow::on_downloadFinish(QString filePath)
 
     // 下载完成显示提示信息
     if (!isCanceled) {
-        DPushButton *button = new DPushButton(tr("Open"));
+        DPushButton *button = new DPushButton(QObject::tr("Open"));
 
         DFloatingMessage *message = new DFloatingMessage(DFloatingMessage::ResidentType);
         message->setIcon(QIcon::fromTheme("dialog-ok").pixmap(64, 64));
-        message->setMessage(QString("    %1 %2 %3").arg(QFileInfo(filePath).fileName()).arg(tr("download finished.")).arg(tr("Show in file manager?")));
+        message->setMessage(QString("    %1 %2 %3").arg(QFileInfo(filePath).fileName()).arg(QObject::tr("download finished.")).arg(QObject::tr("Show in file manager?")));
         message->setWidget(button);
         DMessageManager::instance()->sendMessage(this, message);
 
         connect(button, &DPushButton::clicked, this, [=]() {
             DDesktopServices::showFileItem(filePath);
             message->hide();
+            message->deleteLater();
         });
     }
 }
@@ -446,5 +474,5 @@ void MainWindow::on_downloadCancel(QWebEngineDownloadItem *item)
     mutex.unlock();
 
     downloadMessage->hide();
-    DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-error").pixmap(64, 64), QString(tr("%1Download canceled!")).arg("    "));
+    DMessageManager::instance()->sendMessage(this, QIcon::fromTheme("dialog-error").pixmap(64, 64), QString(QObject::tr("%1Download canceled!")).arg("    "));
 }
diff --git a/spark-webapp-runtime/mainwindow.h b/spark-webapp-runtime/mainwindow.h
index c060638..935cfa9 100644
--- a/spark-webapp-runtime/mainwindow.h
+++ b/spark-webapp-runtime/mainwindow.h
@@ -30,11 +30,11 @@ public:
                bool nFullScreen = false,
                bool nFixSize = false,
                bool nHideButtons = false,
-               QDialog *dialog = nullptr,
                QWidget *parent = nullptr);
     ~MainWindow();
 
     void setIcon(QString szIconPath);
+    void setDescription(const QString &desc);
 
 protected:
     void keyPressEvent(QKeyEvent *event);
@@ -56,7 +56,10 @@ private:
     QString saveAs(QString fileName);
 
 signals:
-    void sigQuit();
+    void sigClose();
+
+public slots:
+    void slotNewInstanceStarted();
 
 private slots:
     void on_trayIconActivated(QSystemTrayIcon::ActivationReason reason);
@@ -74,7 +77,6 @@ private:
     bool m_isTrayEnabled, m_isFullScreen, m_isFixedSize, m_isHideButton;
 
     Widget *m_widget;
-    DAboutDialog *m_dialog;
     QSystemTrayIcon *m_tray;
 
     DToolButton *btnBack;
diff --git a/spark-webapp-runtime/spark-webapp-runtime.pro b/spark-webapp-runtime/spark-webapp-runtime.pro
index a76bd94..87016fa 100644
--- a/spark-webapp-runtime/spark-webapp-runtime.pro
+++ b/spark-webapp-runtime/spark-webapp-runtime.pro
@@ -7,7 +7,7 @@ TEMPLATE = app
 DEFINES += QT_DEPRECATED_WARNINGS
 
 CONFIG += c++11 link_pkgconfig
-PKGCONFIG += dtkwidget
+PKGCONFIG += dtkcore dtkgui dtkwidget
 
 HEADERS += \
         mainwindow.h \
@@ -15,14 +15,20 @@ HEADERS += \
         httpd.h \
         httplib.h \
         widget.h \
-        webengineview.h
+        webengineview.h \
+        webenginepage.h \
+        application.h \
+        webengineurlrequestinterceptor.h
 
 SOURCES += \
         main.cpp \
         httpd.cpp \
         mainwindow.cpp \
         widget.cpp \
-        webengineview.cpp
+        webengineview.cpp \
+        webenginepage.cpp \
+        application.cpp \
+        webengineurlrequestinterceptor.cpp
 
 RESOURCES += \
           imgs.qrc
diff --git a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.qm b/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.qm
index b87ba60..aabae4c 100644
Binary files a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.qm and b/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.qm differ
diff --git a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts b/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
index 8849e49..96a00f5 100644
--- a/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
+++ b/spark-webapp-runtime/translations/spark-webapp-runtime_zh_CN.ts
@@ -2,200 +2,108 @@
 <!DOCTYPE TS>
 <TS version="2.1" language="zh_CN">
 <context>
-    <name>MainWindow</name>
+    <name>DAboutDialog</name>
     <message>
-        <location filename="../mainwindow.cpp" line="41"/>
-        <source>Full Screen</source>
-        <translation>全屏显示</translation>
+        <location filename="../application.cpp" line="49"/>
+        <source>Version: %1</source>
+        <translation>版本:%1</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="42"/>
-        <source>Fix Size</source>
-        <translation>固定大小</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="43"/>
-        <source>Hide Buttons</source>
-        <translation>隐藏按钮</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="44"/>
-        <source>Clear Cache</source>
-        <translation>清理缓存</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="46"/>
-        <source>Show MainWindow</source>
-        <translation>显示主界面</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="47"/>
-        <source>About</source>
-        <translation>关于</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="48"/>
-        <source>Exit</source>
-        <translation>退出</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="53"/>
-        <source>Pause</source>
-        <translation>暂停</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="54"/>
-        <source>Resume</source>
-        <translation>继续</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="55"/>
-        <source>Cancel</source>
-        <translation>取消</translation>
-    </message>
-    <message>
-        <source>%1Fullscreen Mode</source>
-        <translation type="vanished">%1全屏模式</translation>
-    </message>
-    <message>
-        <source>%1Windowed Mode</source>
-        <translation type="vanished">%1窗口模式</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="321"/>
-        <source>Save As</source>
-        <translation>另存为</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="376"/>
-        <source>%1Start downloading %2</source>
-        <translation>%1开始下载 %2</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="387"/>
-        <source>%1Wait for previous download to complete!</source>
-        <translation>%1请等待上一个下载任务完成!</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="409"/>
-        <source>Open</source>
-        <translation>打开</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="413"/>
-        <source>download finished.</source>
-        <translation>下载完成。</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="413"/>
-        <source>Show in file manager?</source>
-        <translation>是否在文件管理器中显示?</translation>
-    </message>
-    <message>
-        <location filename="../mainwindow.cpp" line="450"/>
-        <source>%1Download canceled!</source>
-        <translation>%1下载取消!</translation>
+        <location filename="../application.cpp" line="65"/>
+        <source>%1 is released under %2</source>
+        <translation>%1遵循%2协议发布</translation>
     </message>
 </context>
 <context>
     <name>QObject</name>
     <message>
-        <location filename="../main.cpp" line="71"/>
+        <location filename="../application.cpp" line="57"/>
+        <location filename="../main.cpp" line="182"/>
         <source>Presented By Spark developers # HadesStudio</source>
         <translation>由 星火开发者联盟 @ 花心胡萝卜 提供</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="63"/>
-        <source>Version:</source>
-        <translation>版本:</translation>
-    </message>
-    <message>
-        <location filename="../main.cpp" line="79"/>
-        <source>Published under GPLv3</source>
-        <translation>遵循 GPLv3 协议发布</translation>
-    </message>
-    <message>
-        <location filename="../main.cpp" line="83"/>
+        <location filename="../main.cpp" line="56"/>
         <source>Description: %1</source>
         <translation>描述:%1</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="89"/>
+        <location filename="../main.cpp" line="62"/>
         <source>Enable CommandLineParser. Default is false.</source>
         <translation>启用参数解析方式。默认顺序解析方式。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="94"/>
+        <location filename="../main.cpp" line="67"/>
         <source>The Title of Application. Default is %1.</source>
         <translation>设置程序的运行标题。默认是 %1。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="101"/>
+        <location filename="../main.cpp" line="74"/>
         <source>The target URL. Default is Blank.</source>
         <translation>设置要打开的目标 URL。默认是空。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="108"/>
+        <location filename="../main.cpp" line="81"/>
         <source>The Width of Application. Default is %1.</source>
         <translation>设置应用的窗口宽度。默认是 %1。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="115"/>
+        <location filename="../main.cpp" line="88"/>
         <source>The Height of Application. Default is %1.</source>
         <translation>设置应用的窗口高度。默认是 %1。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="122"/>
+        <location filename="../main.cpp" line="95"/>
         <source>Enable Tray Icon. Default is false.</source>
         <translation>启用托盘图标。默认不启用。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="126"/>
+        <location filename="../main.cpp" line="99"/>
         <source>Run in Fullscreen Mode. Default is false.</source>
         <translation>以全屏模式运行。默认关闭该功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="130"/>
+        <location filename="../main.cpp" line="103"/>
         <source>Fix Window Size. Default is false.</source>
         <translation>固定窗口大小。默认关闭该功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="134"/>
+        <location filename="../main.cpp" line="107"/>
         <source>Hide Control Buttons. Default is false.</source>
         <translation>隐藏控制按钮。默认关闭该此功能。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="147"/>
+        <location filename="../main.cpp" line="112"/>
         <source>The ICON of Application.</source>
         <translation>设置应用的图标。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="154"/>
+        <location filename="../main.cpp" line="119"/>
         <source>The Description of Application.</source>
         <translation>设置应用的描述信息。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="161"/>
+        <location filename="../main.cpp" line="126"/>
         <source>The Configuration file of Application.</source>
         <translation>设置应用的配置文件。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="168"/>
+        <location filename="../main.cpp" line="133"/>
         <source>The root path of the program web service.</source>
         <translation>设置内置 WebServer 的根路径。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="175"/>
+        <location filename="../main.cpp" line="140"/>
         <source>The port number of the program web service.</source>
         <translation>设置内置 WebServer 的监听端口号。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="183"/>
+        <location filename="../main.cpp" line="147"/>
         <source>To use GPU instead of CPU to decoding. Default True.</source>
         <translation>启用GPU渲染,默认开启。</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="191"/>
+        <location filename="../main.cpp" line="155"/>
         <source>The ssl port number of the program web service.</source>
         <translation>设置内置 WebServer 的 SSL 协议的监听端口号。</translation>
     </message>
@@ -204,5 +112,93 @@
         <source>SparkWebAppRuntime</source>
         <translation>星火网页应用运行环境</translation>
     </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="43"/>
+        <source>Full Screen</source>
+        <translation>全屏显示</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="44"/>
+        <source>Fix Size</source>
+        <translation>固定大小</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="45"/>
+        <source>Hide Buttons</source>
+        <translation>隐藏按钮</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="46"/>
+        <source>Clear Cache</source>
+        <translation>清理缓存</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="48"/>
+        <source>Show MainWindow</source>
+        <translation>显示主界面</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="55"/>
+        <source>Pause</source>
+        <translation>暂停</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="56"/>
+        <source>Resume</source>
+        <translation>继续</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="57"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="339"/>
+        <source>Save As</source>
+        <translation>另存为</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="403"/>
+        <source>%1Start downloading %2</source>
+        <translation>%1开始下载 %2</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="414"/>
+        <source>%1Wait for previous download to complete!</source>
+        <translation>%1请等待上一个下载任务完成!</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="436"/>
+        <source>Open</source>
+        <translation>打开</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="440"/>
+        <source>download finished.</source>
+        <translation>下载完成。</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="440"/>
+        <source>Show in file manager?</source>
+        <translation>是否在文件管理器中显示?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="478"/>
+        <source>%1Download canceled!</source>
+        <translation>%1下载取消!</translation>
+    </message>
+</context>
+<context>
+    <name>TitleBarMenu</name>
+    <message>
+        <location filename="../mainwindow.cpp" line="49"/>
+        <source>About</source>
+        <translation>关于</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="50"/>
+        <source>Exit</source>
+        <translation>退出</translation>
+    </message>
 </context>
 </TS>
diff --git a/spark-webapp-runtime/webenginepage.cpp b/spark-webapp-runtime/webenginepage.cpp
new file mode 100644
index 0000000..756758e
--- /dev/null
+++ b/spark-webapp-runtime/webenginepage.cpp
@@ -0,0 +1,44 @@
+#include "webenginepage.h"
+
+#include <QDesktopServices>
+
+WebEnginePage::WebEnginePage(QObject *parent)
+    : QWebEnginePage(parent)
+{
+}
+
+WebEnginePage::~WebEnginePage()
+{
+}
+
+void WebEnginePage::setUrl(const QUrl &url)
+{
+    if (m_currentUrl == url) {
+        return;
+    }
+
+    m_currentUrl = url;
+    QWebEnginePage::setUrl(url);
+}
+
+QWebEnginePage *WebEnginePage::createWindow(QWebEnginePage::WebWindowType type)
+{
+    qDebug() << Q_FUNC_INFO << type;
+
+    WebEnginePage *page = new WebEnginePage(parent());
+    connect(page, &WebEnginePage::urlChanged, this, &WebEnginePage::slotUrlChanged);
+    return page;
+}
+
+void WebEnginePage::slotUrlChanged(const QUrl &url)
+{
+    if (m_currentUrl == url) {
+        sender()->deleteLater();
+        return;
+    }
+
+    qDebug() << Q_FUNC_INFO << m_currentUrl << url;
+
+    QDesktopServices::openUrl(url);
+    sender()->deleteLater();
+}
diff --git a/spark-webapp-runtime/webenginepage.h b/spark-webapp-runtime/webenginepage.h
new file mode 100644
index 0000000..6ebfb19
--- /dev/null
+++ b/spark-webapp-runtime/webenginepage.h
@@ -0,0 +1,26 @@
+#ifndef WEBENGINEPAGE_H
+#define WEBENGINEPAGE_H
+
+#include <QWebEnginePage>
+
+class WebEnginePage : public QWebEnginePage
+{
+    Q_OBJECT
+
+public:
+    explicit WebEnginePage(QObject *parent = nullptr);
+    ~WebEnginePage() override;
+
+    void setUrl(const QUrl &url);
+
+protected:
+    QWebEnginePage *createWindow(WebWindowType type) override;
+
+private slots:
+    void slotUrlChanged(const QUrl &url);
+
+private:
+    QUrl m_currentUrl;
+};
+
+#endif // WEBENGINEPAGE_H
diff --git a/spark-webapp-runtime/webengineurlrequestinterceptor.cpp b/spark-webapp-runtime/webengineurlrequestinterceptor.cpp
new file mode 100644
index 0000000..b87a05d
--- /dev/null
+++ b/spark-webapp-runtime/webengineurlrequestinterceptor.cpp
@@ -0,0 +1,13 @@
+#include "webengineurlrequestinterceptor.h"
+
+#include <QLocale>
+
+WebEngineUrlRequestInterceptor::WebEngineUrlRequestInterceptor(QObject *parent)
+    : QWebEngineUrlRequestInterceptor(parent)
+{
+}
+
+void WebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info)
+{
+    info.setHttpHeader("Accept-Language", QLocale::system().name().toUtf8());
+}
diff --git a/spark-webapp-runtime/webengineurlrequestinterceptor.h b/spark-webapp-runtime/webengineurlrequestinterceptor.h
new file mode 100644
index 0000000..73d3a92
--- /dev/null
+++ b/spark-webapp-runtime/webengineurlrequestinterceptor.h
@@ -0,0 +1,15 @@
+#ifndef WEBENGINEURLREQUESTINTERCEPTOR_H
+#define WEBENGINEURLREQUESTINTERCEPTOR_H
+
+#include <QWebEngineUrlRequestInterceptor>
+
+class WebEngineUrlRequestInterceptor : public QWebEngineUrlRequestInterceptor
+{
+    Q_OBJECT
+
+public:
+    explicit WebEngineUrlRequestInterceptor(QObject *parent = nullptr);
+    void interceptRequest(QWebEngineUrlRequestInfo &info) override;
+};
+
+#endif // WEBENGINEURLREQUESTINTERCEPTOR_H
diff --git a/spark-webapp-runtime/webengineview.cpp b/spark-webapp-runtime/webengineview.cpp
index acae105..dc25354 100644
--- a/spark-webapp-runtime/webengineview.cpp
+++ b/spark-webapp-runtime/webengineview.cpp
@@ -1,22 +1,20 @@
 #include "webengineview.h"
+//#include "webengineurlrequestinterceptor.h"
+
+#include <QWebEngineSettings>
+#include <QWebEngineProfile>
+#include <QLocale>
 
 WebEngineView::WebEngineView(QWidget *parent)
     : QWebEngineView(parent)
+//    , interceptor(new WebEngineUrlRequestInterceptor(this))
 {
-}
-
-QWebEngineView *WebEngineView::createWindow(QWebEnginePage::WebWindowType type)
-{
-    Q_UNUSED(type)
-
-    QWebEngineView *view = new QWebEngineView;
-    connect(view, &QWebEngineView::urlChanged, this, &WebEngineView::on_urlChanged);
-
-    return view;
-}
-
-void WebEngineView::on_urlChanged(QUrl url)
-{
-    setUrl(url);
-    sender()->deleteLater();
+    //    page()->profile()->setHttpUserAgent(page()->profile()->httpUserAgent() + " MediaFeature/prefers-color-scheme:dark");
+
+    connect(this, &WebEngineView::urlChanged, this, [=]() {
+        //        page()->setUrlRequestInterceptor(interceptor);
+        //        page()->settings()->setAttribute(QWebEngineSettings::WebAttribute::LocalContentCanAccessRemoteUrls, true);
+        page()->profile()->setHttpAcceptLanguage(QLocale::system().name());
+        //        qInfo() << "User Agent:" << page()->profile()->httpUserAgent();
+    });
 }
diff --git a/spark-webapp-runtime/webengineview.h b/spark-webapp-runtime/webengineview.h
index 8102441..4ca48b2 100644
--- a/spark-webapp-runtime/webengineview.h
+++ b/spark-webapp-runtime/webengineview.h
@@ -3,6 +3,7 @@
 
 #include <QWebEngineView>
 
+// class WebEngineUrlRequestInterceptor;
 class WebEngineView : public QWebEngineView
 {
     Q_OBJECT
@@ -10,12 +11,8 @@ class WebEngineView : public QWebEngineView
 public:
     explicit WebEngineView(QWidget *parent = nullptr);
 
-protected:
-    QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override;
-
-private slots:
-    void on_urlChanged(QUrl url);
-
+private:
+    //    WebEngineUrlRequestInterceptor *interceptor = nullptr;
 };
 
 #endif // WEBENGINEVIEW_H
diff --git a/spark-webapp-runtime/widget.cpp b/spark-webapp-runtime/widget.cpp
index b6e1dd8..482d457 100644
--- a/spark-webapp-runtime/widget.cpp
+++ b/spark-webapp-runtime/widget.cpp
@@ -1,4 +1,10 @@
 #include "widget.h"
+#include "webengineview.h"
+#include "webenginepage.h"
+
+#include <DApplication>
+
+DWIDGET_USE_NAMESPACE
 
 Widget::Widget(QString szUrl, QWidget *parent)
     : QWidget(parent)
@@ -12,14 +18,16 @@ Widget::Widget(QString szUrl, QWidget *parent)
     m_webEngineView->setObjectName(QStringLiteral("webEngineView"));
     m_webEngineView->setEnabled(true);
     m_webEngineView->setAutoFillBackground(false);
-    m_webEngineView->setZoomFactor(1.0);
 
-    QWebEnginePage *page = new QWebEnginePage(m_webEngineView);
+    DApplication *dApp = qobject_cast<DApplication *>(qApp);
+    m_webEngineView->setZoomFactor(dApp->devicePixelRatio());
+
+    WebEnginePage *page = new WebEnginePage(m_webEngineView);
     m_webEngineView->setPage(page);
 
-    m_webEngineView->setUrl(QUrl(nullptr));
+    page->setUrl(QUrl());
     if (!m_szUrl.isEmpty()) {
-        m_webEngineView->setUrl(QUrl(m_szUrl));
+        page->setUrl(QUrl(m_szUrl));
     }
 
     QWidget *spinnerWidget = new QWidget(this);
@@ -40,8 +48,6 @@ Widget::Widget(QString szUrl, QWidget *parent)
 
 Widget::~Widget()
 {
-    delete m_webEngineView;
-    delete m_spinner;
 }
 
 QWebEnginePage *Widget::getPage()
diff --git a/spark-webapp-runtime/widget.h b/spark-webapp-runtime/widget.h
index ec2c2f9..c128dbc 100644
--- a/spark-webapp-runtime/widget.h
+++ b/spark-webapp-runtime/widget.h
@@ -1,18 +1,15 @@
 #ifndef WIDGET_H
 #define WIDGET_H
 
-#include <QWidget>
-#include <QWebEngineProfile>
-#include <QWebEngineView>
-#include <QLayout>
-#include <QStackedLayout>
-
 #include <DSpinner>
 
-#include "webengineview.h"
+#include <QWidget>
+#include <QWebEnginePage>
+#include <QStackedLayout>
 
 DWIDGET_USE_NAMESPACE
 
+class WebEngineView;
 class Widget : public QWidget
 {
     Q_OBJECT
@@ -26,16 +23,16 @@ public:
     void goForward();
     void refresh();
 
-private:
-    WebEngineView *m_webEngineView;
-    DSpinner *m_spinner;
-    QStackedLayout *mainLayout;
-
-    QString m_szUrl;
-
 private slots:
     void on_loadStarted();
     void on_loadFinished();
+
+private:
+    WebEngineView *m_webEngineView = nullptr;
+    DSpinner *m_spinner = nullptr;
+    QStackedLayout *mainLayout = nullptr;
+
+    QString m_szUrl;
 };
 
 #endif // WIDGET_H