File indexing completed on 2024-05-12 16:23:45

0001 /*
0002     SPDX-FileCopyrightText: 2019 Jonah BrĂ¼chert <jbb.prv@gmx.de>
0003     SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <QApplication>
0009 #include <QCommandLineOption>
0010 #include <QCommandLineParser>
0011 #include <QQmlApplicationEngine>
0012 #include <QQuickStyle>
0013 #include <QQuickWindow>
0014 #include <QUrl>
0015 #include <QtQml>
0016 #include <QtWebEngineQuick>
0017 
0018 #include <KDBusService>
0019 #include <KLocalizedContext>
0020 #include <KLocalizedString>
0021 #include <KWindowSystem>
0022 
0023 #include <csignal>
0024 #include <ranges>
0025 
0026 #include "adblockfilterlistsmanager.h"
0027 #include "adblockfilterlistsmodel.h"
0028 #include "adblockurlinterceptor.h"
0029 #include "angelfishsettings.h"
0030 #include "angelfishwebprofile.h"
0031 #include "bookmarkshistorymodel.h"
0032 #include "browsermanager.h"
0033 #include "domdistiller.h"
0034 #include "downloadsmodel.h"
0035 #include "iconimageprovider.h"
0036 #include "tabsmodel.h"
0037 #include "urlobserver.h"
0038 #include "urlutils.h"
0039 #include "useragent.h"
0040 #include "version.h"
0041 #include "webappcreator.h"
0042 #include "webappmanagermodel.h"
0043 
0044 namespace ranges = std::ranges;
0045 
0046 constexpr auto APPLICATION_ID = "org.kde.angelfish";
0047 
0048 Q_DECL_EXPORT int main(int argc, char *argv[])
0049 {
0050     // set default style and icon theme
0051     QIcon::setFallbackThemeName(QStringLiteral("breeze"));
0052     if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE") && QQuickStyle::name().isEmpty()) {
0053         QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
0054     }
0055 
0056     QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
0057 
0058     // Setup QtWebEngine
0059     qputenv("QTWEBENGINE_DIALOG_SET", "QtQuickControls2");
0060     QString chromiumFlags;
0061     if (AngelfishSettings::self()->webSmoothScrollingEnabled()) {
0062         chromiumFlags.append(QStringLiteral(" --enable-smooth-scrolling"));
0063     }
0064     if (AngelfishSettings::self()->webDarkModeEnabled()) {
0065         chromiumFlags.append(QStringLiteral(" --blink-settings=forceDarkModeEnabled=true"));
0066     }
0067     qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromiumFlags.toUtf8());
0068 
0069     QtWebEngineQuick::initialize();
0070 
0071     QApplication app(argc, argv);
0072     app.setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.angelfish"), app.windowIcon()));
0073     QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
0074     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0075     QCoreApplication::setApplicationName(QStringLiteral("angelfish"));
0076     QCoreApplication::setApplicationVersion(QStringLiteral(ANGELFISH_VERSION_STRING));
0077     QGuiApplication::setDesktopFileName(QStringLiteral("org.kde.angelfish"));
0078     KLocalizedString::setApplicationDomain("angelfish");
0079 
0080     // Command line parser
0081     QCommandLineParser parser;
0082     parser.addPositionalArgument(QStringLiteral("url"), i18n("URL to open"), QStringLiteral("[url]"));
0083     parser.addHelpOption();
0084     parser.addVersionOption();
0085     parser.process(app);
0086 
0087     // QML loading
0088     QQmlApplicationEngine engine;
0089 
0090     // Open links in the already running window when e.g clicked on in another application.
0091     KDBusService service(KDBusService::Unique, &app);
0092     QObject::connect(&service, &KDBusService::activateRequested, &app, [&parser, &engine](const QStringList &arguments) {
0093         parser.parse(arguments);
0094 
0095         auto *webbrowserWindow = qobject_cast<QQuickWindow *>(engine.rootObjects().constFirst());
0096         if (!webbrowserWindow) {
0097             qWarning() << "No webbrowser window is open, can't activate";
0098             return;
0099         }
0100 
0101         if (!parser.positionalArguments().isEmpty()) {
0102             const QUrl initialUrl = QUrl::fromUserInput(parser.positionalArguments().constFirst());
0103             const auto *pageStack = webbrowserWindow->property("pageStack").value<QObject *>();
0104             const auto *initialPage = pageStack->property("initialPage").value<QObject *>();
0105 
0106             // This should be initialPage->findChild<TabsModel *>(QStringLiteral("regularTabsObject")), for some reason
0107             // it doesn't find our tabsModel.
0108             const auto children = initialPage->children();
0109             const auto *regularTabs = *ranges::find_if(children, [](const QObject *child) {
0110                 return child->objectName() == QStringLiteral("regularTabsObject");
0111             });
0112 
0113             auto *tabsModel = regularTabs->property("tabsModel").value<TabsModel *>();
0114             // Open new tab with requested url
0115             tabsModel->newTab(initialUrl);
0116         }
0117 
0118         // Move window to the front
0119         KWindowSystem::updateStartupId(webbrowserWindow);
0120         KWindowSystem::activateWindow(webbrowserWindow);
0121     });
0122 
0123     // register as early possible so it has time to load, constructor doesn't block
0124     qmlRegisterSingletonInstance<AdblockUrlInterceptor>(APPLICATION_ID, 1, 0, "AdblockUrlInterceptor", &AdblockUrlInterceptor::instance());
0125 
0126     engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
0127 
0128     engine.addImageProvider(IconImageProvider::providerId(), new IconImageProvider());
0129 
0130     // initial url command line parameter
0131     if (!parser.positionalArguments().isEmpty()) {
0132         const auto initialUrl = QUrl::fromUserInput(parser.positionalArguments().constFirst());
0133         if (initialUrl.isValid()) {
0134             BrowserManager::instance()->setInitialUrl(initialUrl);
0135         }
0136         BrowserManager::instance()->setInitialUrl(initialUrl);
0137     }
0138 
0139     // Exported types
0140     qmlRegisterType<BookmarksHistoryModel>(APPLICATION_ID, 1, 0, "BookmarksHistoryModel");
0141     qmlRegisterType<UrlObserver>(APPLICATION_ID, 1, 0, "UrlObserver");
0142     qmlRegisterType<UserAgent>(APPLICATION_ID, 1, 0, "UserAgentGenerator");
0143     qmlRegisterType<TabsModel>(APPLICATION_ID, 1, 0, "TabsModel");
0144     qmlRegisterType<AngelfishWebProfile>(APPLICATION_ID, 1, 0, "AngelfishWebProfile");
0145     qmlRegisterSingletonInstance<AngelfishSettings>(APPLICATION_ID, 1, 0, "Settings", AngelfishSettings::self());
0146     qmlRegisterType<AdblockFilterListsModel>(APPLICATION_ID, 1, 0, "AdblockFilterListsModel");
0147     qmlRegisterType<DownloadsModel>(APPLICATION_ID, 1, 0, "DownloadsModel");
0148     qmlRegisterType<WebAppManagerModel>(APPLICATION_ID, 1, 0, "WebAppManagerModel");
0149     qmlRegisterType<WebAppCreator>(APPLICATION_ID, 1, 0, "WebAppCreator");
0150     qmlRegisterAnonymousType<QWebEngineUrlRequestInterceptor>(APPLICATION_ID, 1);
0151 
0152     // Dom Distiller
0153     qmlRegisterSingletonType<DomDistiller>(APPLICATION_ID, 1, 0, "DomDistiller", [](QQmlEngine *, QJSEngine *) -> QObject * {
0154         return new DomDistiller();
0155     });
0156 
0157     // URL utils
0158     qmlRegisterSingletonType<UrlUtils>(APPLICATION_ID, 1, 0, "UrlUtils", [](QQmlEngine *, QJSEngine *) -> QObject * {
0159         return new UrlUtils();
0160     });
0161 
0162     // Browser Manager
0163     qmlRegisterSingletonType<BrowserManager>(APPLICATION_ID, 1, 0, "BrowserManager", [](QQmlEngine *, QJSEngine *) -> QObject * {
0164         return BrowserManager::instance();
0165     });
0166 
0167     Q_INIT_RESOURCE(resources_angelfish_generated);
0168 
0169     QObject::connect(QApplication::instance(), &QCoreApplication::aboutToQuit, QApplication::instance(), [] {
0170         AngelfishSettings::self()->save();
0171     });
0172 
0173     // Setup Unix signal handlers
0174     const auto unixExitHandler = [](int /*sig*/) -> void {
0175         QCoreApplication::quit();
0176     };
0177 
0178     const std::array<int, 4> quitSignals = {SIGQUIT, SIGINT, SIGTERM, SIGHUP};
0179 
0180     sigset_t blockingMask;
0181     sigemptyset(&blockingMask);
0182     for (const auto sig : quitSignals) {
0183         sigaddset(&blockingMask, sig);
0184     }
0185 
0186     struct sigaction sa;
0187     sa.sa_handler = unixExitHandler;
0188     sa.sa_mask = blockingMask;
0189     sa.sa_flags = 0;
0190 
0191     for (auto sig : quitSignals) {
0192         sigaction(sig, &sa, nullptr);
0193     }
0194 
0195     // Load QML
0196 
0197     const QUrl url(SettingsHelper::isMobile()
0198                    ? QStringLiteral("qrc:/mobile.qml")
0199                    : QStringLiteral("qrc:/desktop.qml"));
0200     QObject::connect(
0201         &engine,
0202         &QQmlApplicationEngine::objectCreated,
0203         &app,
0204         [url](QObject* obj, const QUrl& objUrl) {
0205             if ((obj == nullptr) && url == objUrl) {
0206                 QCoreApplication::exit(-1);
0207             }
0208         },
0209         Qt::QueuedConnection);
0210     engine.load(url);
0211 
0212     // Error handling
0213     if (engine.rootObjects().isEmpty()) {
0214         return -1;
0215     }
0216 
0217     const auto window = qobject_cast<QQuickWindow *>(engine.rootObjects().constFirst());
0218     QObject::connect(window, &QQuickWindow::widthChanged, AngelfishSettings::self(), [window] {
0219         AngelfishSettings::setWindowWidth(window->width());
0220     });
0221     QObject::connect(window, &QQuickWindow::heightChanged, AngelfishSettings::self(), [window] {
0222         AngelfishSettings::setWindowHeight(window->height());
0223     });
0224     QObject::connect(window, &QQuickWindow::xChanged, AngelfishSettings::self(), [window] {
0225         AngelfishSettings::setWindowX(window->x());
0226     });
0227     QObject::connect(window, &QQuickWindow::yChanged, AngelfishSettings::self(), [window] {
0228         AngelfishSettings::setWindowY(window->y());
0229     });
0230 
0231     return app.exec();
0232 }