File indexing completed on 2024-05-12 04:57:53

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "mainapplication.h"
0019 #include "history.h"
0020 #include "qztools.h"
0021 #include "updater.h"
0022 #include "autofill.h"
0023 #include "settings.h"
0024 #include "autosaver.h"
0025 #include "datapaths.h"
0026 #include "tabwidget.h"
0027 #include "cookiejar.h"
0028 #include "bookmarks.h"
0029 #include "qzsettings.h"
0030 #include "proxystyle.h"
0031 #include "pluginproxy.h"
0032 #include "iconprovider.h"
0033 #include "browserwindow.h"
0034 #include "checkboxdialog.h"
0035 #include "networkmanager.h"
0036 #include "profilemanager.h"
0037 #include "browsinglibrary.h"
0038 #include "downloadmanager.h"
0039 #include "clearprivatedata.h"
0040 #include "useragentmanager.h"
0041 #include "commandlineoptions.h"
0042 #include "searchenginesmanager.h"
0043 #include "desktopnotificationsfactory.h"
0044 #include "html5permissions/html5permissionsmanager.h"
0045 #include "scripts.h"
0046 #include "sessionmanager.h"
0047 #include "closedwindowsmanager.h"
0048 #include "protocolhandlermanager.h"
0049 #include "../config.h"
0050 
0051 #include <QWebEngineSettings>
0052 #include <QDesktopServices>
0053 #include <QFontDatabase>
0054 #include <QSqlDatabase>
0055 #include <QLibraryInfo>
0056 #include <QMessageBox>
0057 #include <QTranslator>
0058 #include <QThreadPool>
0059 #include <QSettings>
0060 #include <QProcess>
0061 #include <QTimer>
0062 #include <QDir>
0063 #include <QStandardPaths>
0064 #include <QWebEngineProfile>
0065 #include <QWebEngineScriptCollection>
0066 #include <QRegularExpression>
0067 #include <QtWebEngineWidgetsVersion>
0068 #include <QtWebEngineCoreVersion>
0069 
0070 #include <QWebEngineNotification>
0071 #include <QWebEngineUrlScheme>
0072 
0073 #ifdef Q_OS_WIN
0074 #include <QtWin>
0075 #include <QWinJumpList>
0076 #include <QWinJumpListCategory>
0077 #endif
0078 
0079 #include <iostream>
0080 
0081 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
0082 #include "registerqappassociation.h"
0083 #endif
0084 
0085 static bool s_testMode = false;
0086 
0087 MainApplication::MainApplication(int &argc, char** argv)
0088     : QtSingleApplication(argc, argv)
0089     , m_isPrivate(false)
0090     , m_isPortable(false)
0091     , m_isClosing(false)
0092     , m_isStartingAfterCrash(false)
0093     , m_history(nullptr)
0094     , m_bookmarks(nullptr)
0095     , m_autoFill(nullptr)
0096     , m_cookieJar(nullptr)
0097     , m_plugins(nullptr)
0098     , m_browsingLibrary(nullptr)
0099     , m_networkManager(nullptr)
0100     , m_restoreManager(nullptr)
0101     , m_sessionManager(nullptr)
0102     , m_downloadManager(nullptr)
0103     , m_userAgentManager(nullptr)
0104     , m_searchEnginesManager(nullptr)
0105     , m_closedWindowsManager(nullptr)
0106     , m_protocolHandlerManager(nullptr)
0107     , m_html5PermissionsManager(nullptr)
0108     , m_desktopNotifications(nullptr)
0109     , m_webProfile(nullptr)
0110     , m_autoSaver(nullptr)
0111 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
0112     , m_registerQAppAssociation(0)
0113 #endif
0114 {
0115     setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
0116 
0117     setApplicationName(QStringLiteral("falkon"));
0118     setOrganizationDomain(QStringLiteral("org.kde"));
0119     setWindowIcon(QIcon::fromTheme(QSL("falkon"), QIcon(QSL(":icons/falkon.svg"))));
0120     setDesktopFileName(QSL("org.kde.falkon"));
0121 
0122 #ifdef GIT_REVISION
0123     setApplicationVersion(QSL("%1 (%2)").arg(QString::fromLatin1(Qz::VERSION), GIT_REVISION));
0124 #else
0125     setApplicationVersion(QString::fromLatin1(Qz::VERSION));
0126 #endif
0127 
0128     // Set fallback icon theme (eg. on Windows/Mac)
0129     if (QIcon::fromTheme(QSL("view-refresh")).isNull()) {
0130         QIcon::setThemeName(QSL("breeze-fallback"));
0131     }
0132 
0133     // QSQLITE database plugin is required
0134     if (!QSqlDatabase::isDriverAvailable(QSL("QSQLITE"))) {
0135         QMessageBox::critical(nullptr, QSL("Error"), QSL("Qt SQLite database plugin is not available. Please install it and restart the application."));
0136         m_isClosing = true;
0137         return;
0138     }
0139 
0140 #ifdef Q_OS_WIN
0141     // Set default app font (needed for N'ko)
0142     int fontId = QFontDatabase::addApplicationFont(QSL("font.ttf"));
0143     if (fontId != -1) {
0144         const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
0145         if (!families.empty())
0146             setFont(QFont(families.at(0)));
0147     }
0148 #endif
0149 
0150     QByteArray chromium_flags = qgetenv("QTWEBENGINE_CHROMIUM_FLAGS");
0151     chromium_flags.append(" --enable-features=WebRTCPipeWireCapturer");
0152     qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromium_flags);
0153 
0154     QUrl startUrl;
0155     QString startProfile;
0156     QStringList messages;
0157 
0158     bool noAddons = false;
0159     bool newInstance = false;
0160 
0161     if (argc > 1) {
0162         CommandLineOptions cmd;
0163         const auto actions = cmd.getActions();
0164         for (const CommandLineOptions::ActionPair &pair : actions) {
0165             switch (pair.action) {
0166             case Qz::CL_StartWithoutAddons:
0167                 noAddons = true;
0168                 break;
0169             case Qz::CL_StartWithProfile:
0170                 startProfile = pair.text;
0171                 break;
0172             case Qz::CL_StartPortable:
0173                 m_isPortable = true;
0174                 break;
0175             case Qz::CL_NewTab:
0176                 messages.append(QStringLiteral("ACTION:NewTab"));
0177                 m_postLaunchActions.append(OpenNewTab);
0178                 break;
0179             case Qz::CL_NewWindow:
0180                 messages.append(QStringLiteral("ACTION:NewWindow"));
0181                 break;
0182             case Qz::CL_ToggleFullScreen:
0183                 messages.append(QStringLiteral("ACTION:ToggleFullScreen"));
0184                 m_postLaunchActions.append(ToggleFullScreen);
0185                 break;
0186             case Qz::CL_ShowDownloadManager:
0187                 messages.append(QStringLiteral("ACTION:ShowDownloadManager"));
0188                 m_postLaunchActions.append(OpenDownloadManager);
0189                 break;
0190             case Qz::CL_StartPrivateBrowsing:
0191                 m_isPrivate = true;
0192                 break;
0193             case Qz::CL_StartNewInstance:
0194                 newInstance = true;
0195                 break;
0196             case Qz::CL_OpenUrlInCurrentTab:
0197                 startUrl = QUrl::fromUserInput(pair.text);
0198                 messages.append(QSL("ACTION:OpenUrlInCurrentTab") + pair.text);
0199                 break;
0200             case Qz::CL_OpenUrlInNewWindow:
0201                 startUrl = QUrl::fromUserInput(pair.text);
0202                 messages.append(QSL("ACTION:OpenUrlInNewWindow") + pair.text);
0203                 break;
0204             case Qz::CL_OpenUrl:
0205                 startUrl = QUrl::fromUserInput(pair.text);
0206                 messages.append(QSL("URL:") + pair.text);
0207                 break;
0208             case Qz::CL_ExitAction:
0209                 m_isClosing = true;
0210                 return;
0211             case Qz::CL_WMClass:
0212                 m_wmClass = pair.text.toUtf8();
0213                 break;
0214             default:
0215                 break;
0216             }
0217         }
0218     }
0219 
0220     if (!isPortable()) {
0221         QSettings falkonConf(QSL("%1/falkon.conf").arg(applicationDirPath()), QSettings::IniFormat);
0222         m_isPortable = falkonConf.value(QSL("Config/Portable")).toBool();
0223     }
0224 
0225     if (isPortable()) {
0226         std::cout << "Falkon: Running in Portable Mode." << std::endl;
0227         DataPaths::setPortableVersion();
0228     }
0229 
0230     // Don't start single application in private browsing
0231     if (!isPrivate()) {
0232         QString appId = QStringLiteral("org.kde.Falkon");
0233 
0234         if (isPortable()) {
0235             appId.append(QLatin1String(".Portable"));
0236         }
0237 
0238         if (isTestModeEnabled()) {
0239             appId.append(QSL(".TestMode"));
0240         }
0241 
0242         if (newInstance) {
0243             if (startProfile.isEmpty() || startProfile == QLatin1String("default")) {
0244                 std::cout << "New instance cannot be started with default profile!" << std::endl;
0245             }
0246             else {
0247                 // Generate unique appId so it is possible to start more separate instances
0248                 // of the same profile. It is dangerous to run more instances of the same profile,
0249                 // but if the user wants it, we should allow it.
0250                 appId.append(QLatin1Char('.') + startProfile + QString::number(QDateTime::currentMSecsSinceEpoch()));
0251             }
0252         }
0253 
0254         setAppId(appId);
0255     }
0256 
0257     // If there is nothing to tell other instance, we need to at least wake it
0258     if (messages.isEmpty()) {
0259         messages.append(QStringLiteral(" "));
0260     }
0261 
0262     if (isRunning()) {
0263         m_isClosing = true;
0264         for (const QString &message : std::as_const(messages)) {
0265             sendMessage(message);
0266         }
0267         return;
0268     }
0269 
0270 #ifdef Q_OS_MACOS
0271     setQuitOnLastWindowClosed(false);
0272     // disable tabbing issue#2261
0273     extern void disableWindowTabbing();
0274     disableWindowTabbing();
0275 #else
0276     setQuitOnLastWindowClosed(true);
0277 #endif
0278 
0279     QSettings::setDefaultFormat(QSettings::IniFormat);
0280     QDesktopServices::setUrlHandler(QSL("http"), this, "addNewTab");
0281     QDesktopServices::setUrlHandler(QSL("https"), this, "addNewTab");
0282     QDesktopServices::setUrlHandler(QSL("ftp"), this, "addNewTab");
0283 
0284     ProfileManager profileManager;
0285     profileManager.initConfigDir();
0286     profileManager.initCurrentProfile(startProfile);
0287 
0288     Settings::createSettings(DataPaths::currentProfilePath() + QLatin1String("/settings.ini"));
0289     if (Settings::globalSettings()->value("Web-Browser-Settings/hardwareAccel", false).toBool()) {
0290         chromium_flags.append(" --enable-oop-rasterization --enable-gpu-rasterization --enable-native-gpu-memory-buffers --use-gl=desktop");
0291         qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromium_flags);
0292     }
0293 
0294     NetworkManager::registerSchemes();
0295     registerAllowedSchemes();
0296 
0297     m_webProfile = isPrivate() ? new QWebEngineProfile() : new QWebEngineProfile(QSL("Default"));
0298     connect(m_webProfile, &QWebEngineProfile::downloadRequested, this, &MainApplication::downloadRequested);
0299 
0300     m_webProfile->setNotificationPresenter([&] (std::unique_ptr<QWebEngineNotification> notification) {
0301         auto notifications = desktopNotifications();
0302         notifications->showNotification(
0303             QPixmap::fromImage(notification->icon()), notification->title(), notification->message()
0304         );
0305     });
0306 
0307     m_networkManager = new NetworkManager(this);
0308 
0309     setupUserScripts();
0310 
0311     if (!isPrivate() && !isTestModeEnabled()) {
0312         m_sessionManager = new SessionManager(this);
0313         m_autoSaver = new AutoSaver(this);
0314         connect(m_autoSaver, &AutoSaver::save, m_sessionManager, &SessionManager::autoSaveLastSession);
0315 
0316         Settings settings;
0317         settings.beginGroup(QSL("SessionRestore"));
0318         const bool wasRunning = settings.value(QSL("isRunning"), false).toBool();
0319         const bool wasRestoring = settings.value(QSL("isRestoring"), false).toBool();
0320         settings.setValue(QSL("isRunning"), true);
0321         settings.setValue(QSL("isRestoring"), wasRunning);
0322         settings.endGroup();
0323         settings.sync();
0324 
0325         m_isStartingAfterCrash = wasRunning && wasRestoring;
0326 
0327         if (wasRunning) {
0328             QTimer::singleShot(60 * 1000, this, [this]() {
0329                 Settings().setValue(QSL("SessionRestore/isRestoring"), false);
0330             });
0331         }
0332 
0333         // we have to ask about startup session before creating main window
0334         if (!m_isStartingAfterCrash && afterLaunch() == SelectSession)
0335             m_restoreManager = new RestoreManager(sessionManager()->askSessionFromUser());
0336     }
0337 
0338     loadSettings();
0339 
0340     m_plugins = new PluginProxy(this);
0341     m_autoFill = new AutoFill(this);
0342     mApp->protocolHandlerManager();
0343 
0344     if (!noAddons)
0345         m_plugins->loadPlugins();
0346 
0347     BrowserWindow* window = createWindow(Qz::BW_FirstAppWindow, startUrl);
0348     connect(window, SIGNAL(startingCompleted()), this, SLOT(restoreOverrideCursor()));
0349 
0350     connect(this, &QApplication::focusChanged, this, &MainApplication::onFocusChanged);
0351 
0352     if (!isPrivate() && !isTestModeEnabled()) {
0353 #ifndef DISABLE_CHECK_UPDATES
0354         Settings settings;
0355         bool checkUpdates = settings.value("Web-Browser-Settings/CheckUpdates", true).toBool();
0356 
0357         if (checkUpdates) {
0358             new Updater(window);
0359         }
0360 #endif
0361 
0362         sessionManager()->backupSavedSessions();
0363 
0364         if (m_isStartingAfterCrash || afterLaunch() == RestoreSession) {
0365             m_restoreManager = new RestoreManager(sessionManager()->lastActiveSessionPath());
0366             if (!m_restoreManager->isValid()) {
0367                 destroyRestoreManager();
0368             }
0369         }
0370 
0371         if (!m_isStartingAfterCrash && m_restoreManager) {
0372             restoreSession(window, m_restoreManager->restoreData());
0373         }
0374     }
0375 
0376     QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, DataPaths::currentProfilePath());
0377 
0378     connect(this, SIGNAL(messageReceived(QString)), this, SLOT(messageReceived(QString)));
0379     connect(this, &QCoreApplication::aboutToQuit, this, &MainApplication::saveSettings);
0380 
0381     QTimer::singleShot(0, this, &MainApplication::postLaunch);
0382 }
0383 
0384 MainApplication::~MainApplication()
0385 {
0386     m_isClosing = true;
0387 
0388     QDesktopServices::unsetUrlHandler(QSL("http"));
0389     QDesktopServices::unsetUrlHandler(QSL("https"));
0390     QDesktopServices::unsetUrlHandler(QSL("ftp"));
0391 
0392     IconProvider::instance()->saveIconsToDatabase();
0393 
0394     // Wait for all QtConcurrent jobs to finish
0395     QThreadPool::globalInstance()->waitForDone();
0396 
0397     // Delete all classes that are saving data in destructor
0398     delete m_bookmarks;
0399     m_bookmarks = nullptr;
0400     delete m_cookieJar;
0401     m_cookieJar = nullptr;
0402 
0403     // On Qt 6, deleting the web profile is necessary in order to make sure cache, cookies, etc. are flushed to disk.
0404     delete m_webProfile;
0405     m_webProfile = nullptr;
0406 
0407     Settings::syncSettings();
0408 }
0409 
0410 bool MainApplication::isClosing() const
0411 {
0412     return m_isClosing;
0413 }
0414 
0415 bool MainApplication::isPrivate() const
0416 {
0417     return m_isPrivate;
0418 }
0419 
0420 bool MainApplication::isPortable() const
0421 {
0422 #ifdef PORTABLE_BUILD
0423     return true;
0424 #else
0425     return m_isPortable;
0426 #endif
0427 }
0428 
0429 bool MainApplication::isStartingAfterCrash() const
0430 {
0431     return m_isStartingAfterCrash;
0432 }
0433 
0434 int MainApplication::windowCount() const
0435 {
0436     return m_windows.count();
0437 }
0438 
0439 QList<BrowserWindow*> MainApplication::windows() const
0440 {
0441     return m_windows;
0442 }
0443 
0444 BrowserWindow* MainApplication::getWindow() const
0445 {
0446     if (m_lastActiveWindow) {
0447         return m_lastActiveWindow.data();
0448     }
0449 
0450     return m_windows.isEmpty() ? nullptr : m_windows.at(0);
0451 }
0452 
0453 BrowserWindow* MainApplication::createWindow(Qz::BrowserWindowType type, const QUrl &startUrl)
0454 {
0455     if (windowCount() == 0 && type != Qz::BW_MacFirstWindow) {
0456         type = Qz::BW_FirstAppWindow;
0457     }
0458 
0459     auto* window = new BrowserWindow(type, startUrl);
0460     connect(window, &QObject::destroyed, this, &MainApplication::windowDestroyed);
0461 
0462     m_windows.prepend(window);
0463     return window;
0464 }
0465 
0466 MainApplication::AfterLaunch MainApplication::afterLaunch() const
0467 {
0468     return static_cast<AfterLaunch>(Settings().value(QSL("Web-URL-Settings/afterLaunch"), RestoreSession).toInt());
0469 }
0470 
0471 void MainApplication::openSession(BrowserWindow* window, RestoreData &restoreData)
0472 {
0473     setOverrideCursor(Qt::BusyCursor);
0474 
0475     if (!window)
0476         window = createWindow(Qz::BW_OtherRestoredWindow);
0477 
0478     if (window->tabCount() != 0) {
0479         // This can only happen when recovering crashed session!
0480         // Don't restore tabs in current window as user already opened some new tabs.
0481         createWindow(Qz::BW_OtherRestoredWindow)->restoreWindow(restoreData.windows.takeAt(0));
0482     } else {
0483         window->restoreWindow(restoreData.windows.takeAt(0));
0484     }
0485 
0486     const auto restoreWindows = restoreData.windows;
0487     for (const BrowserWindow::SavedWindow &data : restoreWindows) {
0488         BrowserWindow* window = createWindow(Qz::BW_OtherRestoredWindow);
0489         window->restoreWindow(data);
0490     }
0491 
0492     m_closedWindowsManager->restoreState(restoreData.closedWindows);
0493 
0494     restoreOverrideCursor();
0495 }
0496 
0497 bool MainApplication::restoreSession(BrowserWindow* window, RestoreData restoreData)
0498 {
0499     if (m_isPrivate || !restoreData.isValid()) {
0500         return false;
0501     }
0502 
0503     openSession(window, restoreData);
0504 
0505     m_restoreManager->clearRestoreData();
0506     destroyRestoreManager();
0507 
0508     return true;
0509 }
0510 
0511 void MainApplication::destroyRestoreManager()
0512 {
0513     if (m_restoreManager && m_restoreManager->isValid()) {
0514         return;
0515     }
0516 
0517     delete m_restoreManager;
0518     m_restoreManager = nullptr;
0519 }
0520 
0521 void MainApplication::reloadSettings()
0522 {
0523     loadSettings();
0524     Q_EMIT settingsReloaded();
0525 }
0526 
0527 QString MainApplication::styleName() const
0528 {
0529     return m_proxyStyle ? m_proxyStyle->name() : QString();
0530 }
0531 
0532 void MainApplication::setProxyStyle(ProxyStyle *style)
0533 {
0534     m_proxyStyle = style;
0535     setStyle(style);
0536 }
0537 
0538 QByteArray MainApplication::wmClass() const
0539 {
0540     return m_wmClass;
0541 }
0542 
0543 History* MainApplication::history()
0544 {
0545     if (!m_history) {
0546         m_history = new History(this);
0547     }
0548     return m_history;
0549 }
0550 
0551 Bookmarks* MainApplication::bookmarks()
0552 {
0553     if (!m_bookmarks) {
0554         m_bookmarks = new Bookmarks(this);
0555     }
0556     return m_bookmarks;
0557 }
0558 
0559 AutoFill* MainApplication::autoFill()
0560 {
0561     return m_autoFill;
0562 }
0563 
0564 CookieJar* MainApplication::cookieJar()
0565 {
0566     if (!m_cookieJar) {
0567         m_cookieJar = new CookieJar(this);
0568     }
0569     return m_cookieJar;
0570 }
0571 
0572 PluginProxy* MainApplication::plugins()
0573 {
0574     return m_plugins;
0575 }
0576 
0577 BrowsingLibrary* MainApplication::browsingLibrary()
0578 {
0579     if (!m_browsingLibrary) {
0580         m_browsingLibrary = new BrowsingLibrary(getWindow());
0581     }
0582     return m_browsingLibrary;
0583 }
0584 
0585 NetworkManager *MainApplication::networkManager()
0586 {
0587     return m_networkManager;
0588 }
0589 
0590 RestoreManager* MainApplication::restoreManager()
0591 {
0592     return m_restoreManager;
0593 }
0594 
0595 SessionManager* MainApplication::sessionManager()
0596 {
0597     return m_sessionManager;
0598 }
0599 
0600 DownloadManager* MainApplication::downloadManager()
0601 {
0602     if (!m_downloadManager) {
0603         m_downloadManager = new DownloadManager();
0604     }
0605     return m_downloadManager;
0606 }
0607 
0608 UserAgentManager* MainApplication::userAgentManager()
0609 {
0610     if (!m_userAgentManager) {
0611         m_userAgentManager = new UserAgentManager(this);
0612     }
0613     return m_userAgentManager;
0614 }
0615 
0616 SearchEnginesManager* MainApplication::searchEnginesManager()
0617 {
0618     if (!m_searchEnginesManager) {
0619         m_searchEnginesManager = new SearchEnginesManager(this);
0620     }
0621     return m_searchEnginesManager;
0622 }
0623 
0624 ClosedWindowsManager* MainApplication::closedWindowsManager()
0625 {
0626     if (!m_closedWindowsManager) {
0627         m_closedWindowsManager = new ClosedWindowsManager(this);
0628     }
0629     return m_closedWindowsManager;
0630 }
0631 
0632 ProtocolHandlerManager *MainApplication::protocolHandlerManager()
0633 {
0634     if (!m_protocolHandlerManager) {
0635         m_protocolHandlerManager = new ProtocolHandlerManager(this);
0636     }
0637     return m_protocolHandlerManager;
0638 }
0639 
0640 HTML5PermissionsManager* MainApplication::html5PermissionsManager()
0641 {
0642     if (!m_html5PermissionsManager) {
0643         m_html5PermissionsManager = new HTML5PermissionsManager(this);
0644     }
0645     return m_html5PermissionsManager;
0646 }
0647 
0648 DesktopNotificationsFactory* MainApplication::desktopNotifications()
0649 {
0650     if (!m_desktopNotifications) {
0651         m_desktopNotifications = new DesktopNotificationsFactory(this);
0652     }
0653     return m_desktopNotifications;
0654 }
0655 
0656 QWebEngineProfile *MainApplication::webProfile() const
0657 {
0658     return m_webProfile;
0659 }
0660 
0661 QWebEngineSettings *MainApplication::webSettings() const
0662 {
0663     return m_webProfile->settings();
0664 }
0665 
0666 // static
0667 MainApplication* MainApplication::instance()
0668 {
0669     return static_cast<MainApplication*>(QCoreApplication::instance());
0670 }
0671 
0672 // static
0673 bool MainApplication::isTestModeEnabled()
0674 {
0675     return s_testMode;
0676 }
0677 
0678 // static
0679 void MainApplication::setTestModeEnabled(bool enabled)
0680 {
0681     s_testMode = enabled;
0682 }
0683 
0684 void MainApplication::addNewTab(const QUrl &url)
0685 {
0686     BrowserWindow* window = getWindow();
0687 
0688     if (window) {
0689         window->tabWidget()->addView(url, url.isEmpty() ? Qz::NT_SelectedNewEmptyTab : Qz::NT_SelectedTabAtTheEnd);
0690     }
0691 }
0692 
0693 void MainApplication::startPrivateBrowsing(const QUrl &startUrl)
0694 {
0695     QUrl url = startUrl;
0696     if (auto* act = qobject_cast<QAction*>(sender())) {
0697         url = act->data().toUrl();
0698     }
0699 
0700     QStringList args;
0701     args.append(QSL("--private-browsing"));
0702     args.append(QSL("--profile=") + ProfileManager::currentProfile());
0703 
0704     if (!url.isEmpty()) {
0705         args << QString::fromUtf8(url.toEncoded());
0706     }
0707 
0708     if (!QProcess::startDetached(applicationFilePath(), args)) {
0709         qWarning() << "MainApplication: Cannot start new browser process for private browsing!" << applicationFilePath() << args;
0710     }
0711 }
0712 
0713 void MainApplication::reloadUserStyleSheet()
0714 {
0715     const QString userCssFile = Settings().value(QSL("Web-Browser-Settings/userStyleSheet"), QString()).toString();
0716     setUserStyleSheet(userCssFile);
0717 }
0718 
0719 void MainApplication::restoreOverrideCursor()
0720 {
0721     QApplication::restoreOverrideCursor();
0722 }
0723 
0724 void MainApplication::changeOccurred()
0725 {
0726     if (m_autoSaver)
0727         m_autoSaver->changeOccurred();
0728 }
0729 
0730 void MainApplication::quitApplication()
0731 {
0732     if (m_downloadManager && !m_downloadManager->canClose()) {
0733         m_downloadManager->show();
0734         return;
0735     }
0736 
0737     for (BrowserWindow *window : std::as_const(m_windows)) {
0738         Q_EMIT window->aboutToClose();
0739     }
0740 
0741     if (m_sessionManager && m_windows.count() > 0) {
0742         m_sessionManager->autoSaveLastSession();
0743     }
0744 
0745     m_isClosing = true;
0746 
0747     for (BrowserWindow *window : std::as_const(m_windows)) {
0748         window->close();
0749     }
0750 
0751     // Saving settings in saveSettings() slot called from quit() so
0752     // everything gets saved also when quitting application in other
0753     // way than clicking Quit action in File menu or closing last window
0754     // eg. on Mac (#157)
0755 
0756     if (!isPrivate()) {
0757         removeLockFile();
0758     }
0759 
0760     quit();
0761 }
0762 
0763 void MainApplication::postLaunch()
0764 {
0765     if (m_postLaunchActions.contains(OpenDownloadManager)) {
0766         downloadManager()->show();
0767     }
0768 
0769     if (m_postLaunchActions.contains(OpenNewTab)) {
0770         getWindow()->tabWidget()->addView(QUrl(), Qz::NT_SelectedNewEmptyTab);
0771     }
0772 
0773     if (m_postLaunchActions.contains(ToggleFullScreen)) {
0774         getWindow()->toggleFullScreen();
0775     }
0776 
0777     createJumpList();
0778     initPulseSupport();
0779 
0780     QTimer::singleShot(5000, this, &MainApplication::runDeferredPostLaunchActions);
0781 }
0782 
0783 QByteArray MainApplication::saveState() const
0784 {
0785     RestoreData restoreData;
0786     restoreData.windows.reserve(m_windows.count());
0787     for (BrowserWindow *window : std::as_const(m_windows)) {
0788         restoreData.windows.append(BrowserWindow::SavedWindow(window));
0789     }
0790 
0791     if (m_restoreManager && m_restoreManager->isValid()) {
0792         QDataStream stream(&restoreData.crashedSession, QIODevice::WriteOnly);
0793         stream << m_restoreManager->restoreData();
0794     }
0795 
0796     restoreData.closedWindows = m_closedWindowsManager->saveState();
0797 
0798     QByteArray data;
0799     QDataStream stream(&data, QIODevice::WriteOnly);
0800 
0801     stream << Qz::sessionVersion;
0802     stream << restoreData;
0803 
0804     return data;
0805 }
0806 
0807 void MainApplication::saveSettings()
0808 {
0809     if (isPrivate()) {
0810         return;
0811     }
0812 
0813     m_isClosing = true;
0814 
0815     Settings settings;
0816     settings.beginGroup(QSL("SessionRestore"));
0817     settings.setValue(QSL("isRunning"), false);
0818     settings.setValue(QSL("isRestoring"), false);
0819     settings.endGroup();
0820 
0821     settings.beginGroup(QSL("Web-Browser-Settings"));
0822     bool deleteCache = settings.value(QSL("deleteCacheOnClose"), false).toBool();
0823     bool deleteHistory = settings.value(QSL("deleteHistoryOnClose"), false).toBool();
0824     bool deleteHtml5Storage = settings.value(QSL("deleteHTML5StorageOnClose"), false).toBool();
0825     settings.endGroup();
0826 
0827     settings.beginGroup(QSL("Cookie-Settings"));
0828     bool deleteCookies = settings.value(QSL("deleteCookiesOnClose"), false).toBool();
0829     settings.endGroup();
0830 
0831     if (deleteHistory) {
0832         m_history->clearHistory();
0833     }
0834     if (deleteHtml5Storage) {
0835         ClearPrivateData::clearLocalStorage();
0836     }
0837     if (deleteCookies) {
0838         m_cookieJar->deleteAllCookies(false);
0839     }
0840     if (deleteCache) {
0841         QzTools::removeRecursively(mApp->webProfile()->cachePath());
0842     }
0843 
0844     m_searchEnginesManager->saveSettings();
0845     m_plugins->shutdown();
0846     m_networkManager->shutdown();
0847 
0848     qzSettings->saveSettings();
0849     QFile::remove(DataPaths::currentProfilePath() + QLatin1String("/WebpageIcons.db"));
0850 
0851     sessionManager()->saveSettings();
0852 }
0853 
0854 void MainApplication::messageReceived(const QString &message)
0855 {
0856     QWidget* actWin = getWindow();
0857     QUrl actUrl;
0858 
0859     if (message.startsWith(QLatin1String("URL:"))) {
0860         const QUrl url = QUrl::fromUserInput(message.mid(4));
0861         addNewTab(url);
0862         actWin = getWindow();
0863     }
0864     else if (message.startsWith(QLatin1String("ACTION:"))) {
0865         const QString text = message.mid(7);
0866         if (text == QLatin1String("NewTab")) {
0867             addNewTab();
0868         }
0869         else if (text == QLatin1String("NewWindow")) {
0870             actWin = createWindow(Qz::BW_NewWindow);
0871         }
0872         else if (text == QLatin1String("ShowDownloadManager")) {
0873             downloadManager()->show();
0874             actWin = downloadManager();
0875         }
0876         else if (text == QLatin1String("ToggleFullScreen") && actWin) {
0877             auto* qz = static_cast<BrowserWindow*>(actWin);
0878             qz->toggleFullScreen();
0879         }
0880         else if (text.startsWith(QLatin1String("OpenUrlInCurrentTab"))) {
0881             actUrl = QUrl::fromUserInput(text.mid(19));
0882         }
0883         else if (text.startsWith(QLatin1String("OpenUrlInNewWindow"))) {
0884             createWindow(Qz::BW_NewWindow, QUrl::fromUserInput(text.mid(18)));
0885             return;
0886         }
0887     }
0888     else {
0889         // User attempted to start another instance, let's open a new window
0890         actWin = createWindow(Qz::BW_NewWindow);
0891     }
0892 
0893     if (!actWin) {
0894         if (!isClosing()) {
0895             // It can only occur if download manager window was still opened
0896             createWindow(Qz::BW_NewWindow, actUrl);
0897         }
0898         return;
0899     }
0900 
0901     actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
0902     actWin->raise();
0903     actWin->activateWindow();
0904     actWin->setFocus();
0905 
0906     auto* win = qobject_cast<BrowserWindow*>(actWin);
0907 
0908     if (win && !actUrl.isEmpty()) {
0909         win->loadAddress(actUrl);
0910     }
0911 }
0912 
0913 void MainApplication::windowDestroyed(QObject* window)
0914 {
0915     // qobject_cast doesn't work because QObject::destroyed is emitted from destructor
0916     Q_ASSERT(static_cast<BrowserWindow*>(window));
0917     Q_ASSERT(m_windows.contains(static_cast<BrowserWindow*>(window)));
0918 
0919     m_windows.removeOne(static_cast<BrowserWindow*>(window));
0920 }
0921 
0922 void MainApplication::onFocusChanged()
0923 {
0924     auto* activeBrowserWindow = qobject_cast<BrowserWindow*>(activeWindow());
0925 
0926     if (activeBrowserWindow) {
0927         m_lastActiveWindow = activeBrowserWindow;
0928 
0929         Q_EMIT activeWindowChanged(m_lastActiveWindow);
0930     }
0931 }
0932 
0933 void MainApplication::runDeferredPostLaunchActions()
0934 {
0935     checkDefaultWebBrowser();
0936     checkOptimizeDatabase();
0937 }
0938 
0939 void MainApplication::downloadRequested(QWebEngineDownloadRequest *download)
0940 {
0941     downloadManager()->download(download);
0942 }
0943 
0944 void MainApplication::loadSettings()
0945 {
0946     Settings settings;
0947     settings.beginGroup(QSL("Themes"));
0948     QString activeTheme = settings.value(QSL("activeTheme"), DEFAULT_THEME_NAME).toString();
0949     settings.endGroup();
0950 
0951     loadTheme(activeTheme);
0952 
0953     QWebEngineSettings* webSettings = m_webProfile->settings();
0954 
0955     // Web browsing settings
0956     settings.beginGroup(QSL("Web-Browser-Settings"));
0957 
0958     webSettings->setAttribute(QWebEngineSettings::LocalStorageEnabled, settings.value(QSL("HTML5StorageEnabled"), true).toBool());
0959     webSettings->setAttribute(QWebEngineSettings::PluginsEnabled, settings.value(QSL("allowPlugins"), true).toBool());
0960     webSettings->setAttribute(QWebEngineSettings::JavascriptEnabled, settings.value(QSL("allowJavaScript"), true).toBool());
0961     webSettings->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, settings.value(QSL("allowJavaScriptOpenWindow"), false).toBool());
0962     webSettings->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, settings.value(QSL("allowJavaScriptAccessClipboard"), true).toBool());
0963     webSettings->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, settings.value(QSL("IncludeLinkInFocusChain"), false).toBool());
0964     webSettings->setAttribute(QWebEngineSettings::XSSAuditingEnabled, settings.value(QSL("XSSAuditing"), false).toBool());
0965     webSettings->setAttribute(QWebEngineSettings::PrintElementBackgrounds, settings.value(QSL("PrintElementBackground"), true).toBool());
0966     webSettings->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, settings.value(QSL("SpatialNavigation"), false).toBool());
0967     webSettings->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, settings.value(QSL("AnimateScrolling"), true).toBool());
0968     webSettings->setAttribute(QWebEngineSettings::HyperlinkAuditingEnabled, false);
0969     webSettings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
0970     webSettings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
0971     webSettings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
0972 
0973     webSettings->setAttribute(QWebEngineSettings::AllowWindowActivationFromJavaScript, settings.value(QSL("allowJavaScriptActivateWindow"), false).toBool());
0974 
0975     webSettings->setAttribute(QWebEngineSettings::JavascriptCanPaste, settings.value(QSL("allowJavaScriptPaste"), true).toBool());
0976     webSettings->setAttribute(QWebEngineSettings::PlaybackRequiresUserGesture, settings.value(QSL("DisableVideoAutoPlay"), false).toBool());
0977     webSettings->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, settings.value(QSL("WebRTCPublicIpOnly"), true).toBool());
0978     webSettings->setUnknownUrlSchemePolicy(QWebEngineSettings::AllowAllUnknownUrlSchemes);
0979     webSettings->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, settings.value(QSL("DNSPrefetch"), true).toBool());
0980     webSettings->setAttribute(QWebEngineSettings::PdfViewerEnabled, settings.value(QSL("intPDFViewer"), false).toBool());
0981     webSettings->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, settings.value(QSL("screenCaptureEnabled"), false).toBool());
0982 
0983     webSettings->setDefaultTextEncoding(settings.value(QSL("DefaultEncoding"), webSettings->defaultTextEncoding()).toString());
0984 
0985     setWheelScrollLines(settings.value(QSL("wheelScrollLines"), wheelScrollLines()).toInt());
0986 
0987     const QString userCss = settings.value(QSL("userStyleSheet"), QString()).toString();
0988     settings.endGroup();
0989 
0990     setUserStyleSheet(userCss);
0991 
0992     settings.beginGroup(QSL("Browser-Fonts"));
0993     webSettings->setFontFamily(QWebEngineSettings::StandardFont, settings.value(QSL("StandardFont"), webSettings->fontFamily(QWebEngineSettings::StandardFont)).toString());
0994     webSettings->setFontFamily(QWebEngineSettings::CursiveFont, settings.value(QSL("CursiveFont"), webSettings->fontFamily(QWebEngineSettings::CursiveFont)).toString());
0995     webSettings->setFontFamily(QWebEngineSettings::FantasyFont, settings.value(QSL("FantasyFont"), webSettings->fontFamily(QWebEngineSettings::FantasyFont)).toString());
0996     webSettings->setFontFamily(QWebEngineSettings::FixedFont, settings.value(QSL("FixedFont"), webSettings->fontFamily(QWebEngineSettings::FixedFont)).toString());
0997     webSettings->setFontFamily(QWebEngineSettings::SansSerifFont, settings.value(QSL("SansSerifFont"), webSettings->fontFamily(QWebEngineSettings::SansSerifFont)).toString());
0998     webSettings->setFontFamily(QWebEngineSettings::SerifFont, settings.value(QSL("SerifFont"), webSettings->fontFamily(QWebEngineSettings::SerifFont)).toString());
0999     webSettings->setFontSize(QWebEngineSettings::DefaultFontSize, settings.value(QSL("DefaultFontSize"), 15).toInt());
1000     webSettings->setFontSize(QWebEngineSettings::DefaultFixedFontSize, settings.value(QSL("FixedFontSize"), 14).toInt());
1001     webSettings->setFontSize(QWebEngineSettings::MinimumFontSize, settings.value(QSL("MinimumFontSize"), 3).toInt());
1002     webSettings->setFontSize(QWebEngineSettings::MinimumLogicalFontSize, settings.value(QSL("MinimumLogicalFontSize"), 5).toInt());
1003     settings.endGroup();
1004 
1005     QWebEngineProfile* profile = m_webProfile;
1006     profile->setPersistentCookiesPolicy(QWebEngineProfile::AllowPersistentCookies);
1007     profile->setPersistentStoragePath(DataPaths::currentProfilePath());
1008 
1009     QString defaultPath = DataPaths::path(DataPaths::Cache);
1010     if (!defaultPath.startsWith(DataPaths::currentProfilePath()))
1011         defaultPath.append(QLatin1Char('/') + ProfileManager::currentProfile());
1012     const QString &cachePath = settings.value(QSL("Web-Browser-Settings/CachePath"), defaultPath).toString();
1013     profile->setCachePath(cachePath);
1014 
1015     const bool allowCache = settings.value(QSL("Web-Browser-Settings/AllowLocalCache"), true).toBool();
1016     profile->setHttpCacheType(allowCache ? QWebEngineProfile::DiskHttpCache : QWebEngineProfile::MemoryHttpCache);
1017 
1018     const int cacheSize = settings.value(QSL("Web-Browser-Settings/LocalCacheSize"), 50).toInt() * 1000 * 1000;
1019     profile->setHttpCacheMaximumSize(cacheSize);
1020 
1021     settings.beginGroup(QSL("SpellCheck"));
1022     profile->setSpellCheckEnabled(settings.value(QSL("Enabled"), false).toBool());
1023     profile->setSpellCheckLanguages(settings.value(QSL("Languages")).toStringList());
1024     settings.endGroup();
1025 
1026     if (isPrivate()) {
1027         profile->setPersistentStoragePath(DataPaths::path(DataPaths::Temp) + QLatin1String("/private-storage"));
1028         history()->setSaving(false);
1029     }
1030 
1031     if (m_downloadManager) {
1032         m_downloadManager->loadSettings();
1033     }
1034 
1035     qzSettings->loadSettings();
1036     userAgentManager()->loadSettings();
1037     networkManager()->loadSettings();
1038 }
1039 
1040 void MainApplication::loadTheme(const QString &name)
1041 {
1042     QString activeThemePath = DataPaths::locate(DataPaths::Themes, name);
1043 
1044     if (activeThemePath.isEmpty()) {
1045         qWarning() << "Cannot load theme " << name;
1046         activeThemePath = QSL("%1/%2").arg(DataPaths::path(DataPaths::Themes), DEFAULT_THEME_NAME);
1047     }
1048 
1049     QString qss = QzTools::readAllFileContents(activeThemePath + QLatin1String("/main.css"));
1050 
1051 #if defined(Q_OS_MACOS)
1052     qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/mac.css")));
1053 #elif defined(Q_OS_UNIX)
1054     qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/linux.css")));
1055 #elif defined(Q_OS_WIN) || defined(Q_OS_OS2)
1056     qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/windows.css")));
1057 #endif
1058 
1059     if (isRightToLeft()) {
1060         qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/rtl.css")));
1061     }
1062 
1063     if (isPrivate()) {
1064         qss.append(QzTools::readAllFileContents(activeThemePath + QLatin1String("/private.css")));
1065     }
1066 
1067     qss.append(QzTools::readAllFileContents(DataPaths::currentProfilePath() + QL1S("/userChrome.css")));
1068 
1069     QString relativePath = QDir::current().relativeFilePath(activeThemePath);
1070     qss.replace(QRegularExpression(QSL("url\\s*\\(\\s*([^\\*:\\);]+)\\s*\\)")), QSL("url(%1/\\1)").arg(relativePath));
1071     setStyleSheet(qss);
1072 }
1073 
1074 void MainApplication::checkDefaultWebBrowser()
1075 {
1076     if (isPortable()) {
1077         return;
1078     }
1079 
1080 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
1081     Settings settings;
1082     bool checkNow = settings.value(QSL("Web-Browser-Settings/CheckDefaultBrowser"), DEFAULT_CHECK_DEFAULTBROWSER).toBool();
1083 
1084     if (!checkNow) {
1085         return;
1086     }
1087 
1088     bool checkAgain = true;
1089 
1090     if (!associationManager()->isDefaultForAllCapabilities()) {
1091         CheckBoxDialog dialog(QMessageBox::Yes | QMessageBox::No, getWindow());
1092         dialog.setDefaultButton(QMessageBox::Yes);
1093         dialog.setText(tr("Falkon is not currently your default browser. Would you like to make it your default browser?"));
1094         dialog.setCheckBoxText(tr("Always perform this check when starting Falkon."));
1095         dialog.setDefaultCheckState(Qt::Checked);
1096         dialog.setWindowTitle(tr("Default Browser"));
1097         dialog.setIcon(QMessageBox::Warning);
1098 
1099         if (dialog.exec() == QMessageBox::Yes) {
1100             if (!mApp->associationManager()->showNativeDefaultAppSettingsUi())
1101                 mApp->associationManager()->registerAllAssociation();
1102         }
1103 
1104         checkAgain = dialog.isChecked();
1105     }
1106 
1107     settings.setValue(QSL("Web-Browser-Settings/CheckDefaultBrowser"), checkAgain);
1108 #endif
1109 }
1110 
1111 void MainApplication::checkOptimizeDatabase()
1112 {
1113     Settings settings;
1114     settings.beginGroup(QSL("Browser"));
1115     const int numberOfRuns = settings.value(QSL("RunsWithoutOptimizeDb"), 0).toInt();
1116     settings.setValue(QSL("RunsWithoutOptimizeDb"), numberOfRuns + 1);
1117 
1118     if (numberOfRuns > 20) {
1119         std::cout << "Optimizing database..." << std::endl;
1120         IconProvider::instance()->clearOldIconsInDatabase();
1121         settings.setValue(QSL("RunsWithoutOptimizeDb"), 0);
1122     }
1123 
1124     settings.endGroup();
1125 }
1126 
1127 void MainApplication::registerAllowedSchemes()
1128 {
1129     for (const QString &schemeName : std::as_const(qzSettings->allowedSchemes)) {
1130         if (qzSettings->blockedSchemes.contains(schemeName)) {
1131             continue;
1132         }
1133         QWebEngineUrlScheme scheme(schemeName.toUtf8());
1134         scheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::ContentSecurityPolicyIgnored);
1135         scheme.setSyntax(QWebEngineUrlScheme::Syntax::Path);
1136         QWebEngineUrlScheme::registerScheme(scheme);
1137     }
1138 }
1139 
1140 void MainApplication::setupUserScripts()
1141 {
1142     // WebChannel for SafeJsWorld
1143     QWebEngineScript script;
1144     script.setName(QSL("_falkon_webchannel"));
1145     script.setInjectionPoint(QWebEngineScript::DocumentCreation);
1146     script.setWorldId(WebPage::SafeJsWorld);
1147     script.setRunsOnSubFrames(true);
1148     script.setSourceCode(Scripts::setupWebChannel());
1149     m_webProfile->scripts()->insert(script);
1150 
1151     // falkon:restore
1152     QWebEngineScript falkonRestore;
1153     falkonRestore.setWorldId(WebPage::SafeJsWorld);
1154     falkonRestore.setSourceCode(QzTools::readAllFileContents(QSL(":html/restore.user.js")));
1155     m_webProfile->scripts()->insert(falkonRestore);
1156 
1157     // falkon:speeddial
1158     QWebEngineScript falkonSpeedDial;
1159     falkonSpeedDial.setWorldId(WebPage::SafeJsWorld);
1160     falkonSpeedDial.setSourceCode(Scripts::setupSpeedDial());
1161     m_webProfile->scripts()->insert(falkonSpeedDial);
1162 
1163     // document.window object addons
1164     QWebEngineScript documentWindowAddons;
1165     documentWindowAddons.setName(QSL("_falkon_window_object"));
1166     documentWindowAddons.setInjectionPoint(QWebEngineScript::DocumentCreation);
1167     documentWindowAddons.setWorldId(WebPage::UnsafeJsWorld);
1168     documentWindowAddons.setRunsOnSubFrames(true);
1169     documentWindowAddons.setSourceCode(Scripts::setupWindowObject());
1170     m_webProfile->scripts()->insert(documentWindowAddons);
1171 }
1172 
1173 void MainApplication::setUserStyleSheet(const QString &filePath)
1174 {
1175     QString userCss;
1176 
1177 #if !defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
1178     // Don't grey out selection on losing focus (to prevent graying out found text)
1179     QString highlightColor;
1180     QString highlightedTextColor;
1181 #ifdef Q_OS_MACOS
1182     highlightColor = QLatin1String("#b6d6fc");
1183     highlightedTextColor = QLatin1String("#000");
1184 #else
1185     QPalette pal = style()->standardPalette();
1186     highlightColor = pal.color(QPalette::Highlight).name();
1187     highlightedTextColor = pal.color(QPalette::HighlightedText).name();
1188 #endif
1189     userCss += QString("::selection {background: %1; color: %2;} ").arg(highlightColor, highlightedTextColor);
1190 #endif
1191 
1192     userCss += QzTools::readAllFileContents(filePath).remove(QLatin1Char('\n'));
1193 
1194     const QString name = QStringLiteral("_falkon_userstylesheet");
1195     for (const QWebEngineScript &oldScript : m_webProfile->scripts()->find(name)) {
1196         m_webProfile->scripts()->remove(oldScript);
1197     }
1198 
1199     if (userCss.isEmpty())
1200         return;
1201 
1202     QWebEngineScript script;
1203     script.setName(name);
1204     script.setInjectionPoint(QWebEngineScript::DocumentReady);
1205     script.setWorldId(WebPage::SafeJsWorld);
1206     script.setRunsOnSubFrames(true);
1207     script.setSourceCode(Scripts::setCss(userCss));
1208     m_webProfile->scripts()->insert(script);
1209 }
1210 
1211 void MainApplication::createJumpList()
1212 {
1213 #ifdef Q_OS_WIN
1214     QWinJumpList *jumpList = new QWinJumpList(this);
1215     jumpList->clear();
1216 
1217     // Frequent
1218     QWinJumpListCategory *frequent = jumpList->frequent();
1219     frequent->setVisible(true);
1220     const QVector<HistoryEntry> mostList = m_history->mostVisited(7);
1221     for (const HistoryEntry &entry : mostList) {
1222         frequent->addLink(IconProvider::iconForUrl(entry.url), entry.title, applicationFilePath(), QStringList{(QString::fromUtf8entry.url.toEncoded())});
1223     }
1224 
1225     // Tasks
1226     QWinJumpListCategory *tasks = jumpList->tasks();
1227     tasks->setVisible(true);
1228     tasks->addLink(IconProvider::newTabIcon(), tr("Open new tab"), applicationFilePath(), {QSL("--new-tab")});
1229     tasks->addLink(IconProvider::newWindowIcon(), tr("Open new window"), applicationFilePath(), {QSL("--new-window")});
1230     tasks->addLink(IconProvider::privateBrowsingIcon(), tr("Open new private window"), applicationFilePath(), {QSL("--private-browsing")});
1231 #endif
1232 }
1233 
1234 void MainApplication::initPulseSupport()
1235 {
1236     qputenv("PULSE_PROP_OVERRIDE_application.name", "Falkon");
1237     qputenv("PULSE_PROP_OVERRIDE_application.icon_name", "falkon");
1238     qputenv("PULSE_PROP_OVERRIDE_media.icon_name", "falkon");
1239 }
1240 
1241 #if defined(Q_OS_WIN) && !defined(Q_OS_OS2)
1242 RegisterQAppAssociation* MainApplication::associationManager()
1243 {
1244     if (!m_registerQAppAssociation) {
1245         QString desc = tr("Falkon is a new and very fast Qt web browser. Falkon is licensed under GPL version 3 or (at your option) any later version. It is based on QtWebEngine and Qt Framework.");
1246         QString fileIconPath = QApplication::applicationFilePath() + QSL(",1");
1247         QString appIconPath = QApplication::applicationFilePath() + QSL(",0");
1248         m_registerQAppAssociation = new RegisterQAppAssociation(QSL("Falkon"), QApplication::applicationFilePath(), appIconPath, desc, this);
1249         m_registerQAppAssociation->addCapability(QSL(".html"), QSL("FalkonHTML"), QSL("Falkon HTML Document"), fileIconPath, RegisterQAppAssociation::FileAssociation);
1250         m_registerQAppAssociation->addCapability(QSL(".htm"), QSL("FalkonHTML"), QSL("Falkon HTML Document"), fileIconPath, RegisterQAppAssociation::FileAssociation);
1251         m_registerQAppAssociation->addCapability(QSL("http"), QSL("FalkonURL"), QSL("Falkon URL"), appIconPath, RegisterQAppAssociation::UrlAssociation);
1252         m_registerQAppAssociation->addCapability(QSL("https"), QSL("FalkonURL"), QSL("Falkon URL"), appIconPath, RegisterQAppAssociation::UrlAssociation);
1253         m_registerQAppAssociation->addCapability(QSL("ftp"), QSL("FalkonURL"), QSL("Falkon URL"), appIconPath, RegisterQAppAssociation::UrlAssociation);
1254     }
1255     return m_registerQAppAssociation;
1256 }
1257 #endif
1258 
1259 #ifdef Q_OS_MACOS
1260 #include <QFileOpenEvent>
1261 
1262 bool MainApplication::event(QEvent* e)
1263 {
1264     switch (e->type()) {
1265     case QEvent::FileOpen: {
1266         QFileOpenEvent *ev = static_cast<QFileOpenEvent*>(e);
1267         if (!ev->url().isEmpty()) {
1268             addNewTab(ev->url());
1269         } else if (!ev->file().isEmpty()) {
1270             addNewTab(QUrl::fromLocalFile(ev->file()));
1271         } else {
1272             return false;
1273         }
1274         return true;
1275     }
1276 
1277     case QEvent::ApplicationActivate:
1278         if (!activeWindow() && m_windows.isEmpty())
1279             createWindow(Qz::BW_NewWindow);
1280         break;
1281 
1282     default:
1283         break;
1284     }
1285 
1286     return QtSingleApplication::event(e);
1287 }
1288 #endif