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 }