/* * This file is part of smolbote. It's copyrighted by the contributors recorded * in the version control history of the file, available from its original * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote * * SPDX-License-Identifier: GPL-3.0 */ #include "browser.h" #include "aboutdialog.h" #include "aboutplugin.h" #include "adblock/adblocklist.h" #include "bookmarks/bookmarkswidget.h" #include "configuration.h" #include "downloadswidget.h" #include "hostlist/hostlist.h" #include "mainwindow/addressbar.h" #include "mainwindow/mainwindow.h" #include "mainwindow/menubar.h" #include "subwindow/subwindow.h" #include "urlfilter.h" #include "util.h" #include "webengine/urlinterceptor.h" #include "webengine/webprofile.h" #include "webengine/webprofilemanager.h" #include "webengine/webview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Browser::Browser(int &argc, char *argv[], bool allowSecondary) : SingleApplication(argc, argv, allowSecondary, SingleApplication::User | SingleApplication::SecondaryNotification | SingleApplication::ExcludeAppVersion) { Configuration conf; setApplicationName(conf.value("poi.name").value()); setWindowIcon(QIcon(conf.value("poi.icon").value())); setApplicationVersion(QVersionNumber::fromString(QLatin1String(poi_Version)).toString()); if(const auto _translation = conf.value("browser.translation")) { auto *translator = new QTranslator(this); if(translator->load(_translation.value())) installTranslator(translator); else delete translator; } if(const auto _locale = conf.value("browser.locale")) { auto *locale = new QTranslator(this); if(locale->load("qt_" + _locale.value(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) installTranslator(locale); else delete locale; } if(auto iconTheme = conf.value("browser.iconTheme")) { QIcon::setThemeName(iconTheme.value()); } } Browser::~Browser() { if(m_bookmarks) m_bookmarks->save(); for(auto *info : qAsConst(m_plugins)) delete info; qDeleteAll(m_windows); m_windows.clear(); } void Browser::about() { auto *dlg = new AboutDialog; dlg->exec(); } void Browser::loadProfiles(const QStringList &profilePaths) { Configuration conf; for(const QString &path : profilePaths) { const QString id = QFileInfo(path).baseName(); auto *profile = m_profileManager->add(id, path); connect(profile, &WebProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload); auto *interceptor = new UrlRequestInterceptor(profile, profile); for(UrlFilter *filter : qAsConst(m_filters)) { interceptor->addFilter(filter); } const auto headers = conf.value("filter.header").value_or(QStringList()); for(const QString &header : headers) { const auto h = header.split(QLatin1String(":")); if(h.length() == 2) interceptor->addHttpHeader(h.at(0).toLatin1(), h.at(1).toLatin1()); } profile->setUrlRequestInterceptor(interceptor); spdlog::info("Added profile\t{}{}\t{}", qUtf8Printable(id), profile->isOffTheRecord() ? "*" : "", qUtf8Printable(profile->name())); } } void Browser::loadPlugins(const QStringList &paths, const std::function &callback) { if(paths.isEmpty()) return; Configuration conf; const PluginLoader::SignatureState state( conf.value("plugins.signature.ignored").value(), conf.value("plugins.signature.checked").value(), conf.value("plugins.signature.enforced").value()); for(const auto &path : paths) { auto *loader = new PluginLoader(path, state, this); const bool loaded = loader->load(); callback(loader); if(!loaded) { delete loader; } else { auto *info = new PluginInfo(loader); m_plugins.append(info); for(MainWindow *window : qAsConst(m_windows)) { addPluginTo(info, window); } } } } void Browser::setup() { Configuration conf; if(auto stylesheet = conf.value("browser.stylesheet")) { QFile f(stylesheet.value()); if(f.open(QIODevice::ReadOnly)) { setStyleSheet(f.readAll()); f.close(); } } // downloads m_downloads = std::make_unique(conf.value("downloads.path").value()); // url request filter for(const QString &hostlist : Util::files(conf.value("filter.hosts").value_or(QString()))) { QFile f(hostlist); if(f.open(QIODevice::ReadOnly | QIODevice::Text)) { m_filters.append(new HostList(&f)); f.close(); } } for(const QString &adblock : Util::files(conf.value("filter.adblock").value_or(QString()))) { QFile f(adblock); if(f.open(QIODevice::ReadOnly | QIODevice::Text)) { m_filters.append(new AdBlockList(&f)); f.close(); } } // cookie request filter // load profiles m_profileManager = new WebProfileManager(this); WebProfileManager::setInstance(m_profileManager); loadProfiles(Util::files(conf.value("profile.path").value(), { "*.profile" })); // set default profile { const QString id = conf.value("profile.default").value(); auto *profile = m_profileManager->profile(id); if(profile == nullptr) { spdlog::error("Unknown default profile, conjuring one up..."); profile = m_profileManager->add("default", QString(), true); } spdlog::info("Default profile\t{}{}\t{}", qUtf8Printable(id), profile->isOffTheRecord() ? "*" : "", qUtf8Printable(profile->name())); WebProfile::setDefaultProfile(profile); } // bookmarks m_bookmarks = std::make_shared(QString::fromStdString(conf.value("bookmarks.path").value())); connect(m_bookmarks.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) { m_windows.last()->createTab(url); }); auto *timer = new QTimer(this); connect(timer, &QTimer::timeout, m_bookmarks.get(), &BookmarksWidget::save); // 5min * 60sec * 1000ms timer->start(5 * 60 * 1000); } void Browser::showWidget(QWidget *widget, MainWindow *where) const { bool wasVisible = widget->isVisible(); for(MainWindow *w : qAsConst(m_windows)) w->removeDockWidget(widget); if(!wasVisible) where->addDockWidget(Qt::RightDockWidgetArea, widget); } void Browser::open(const QVector &data, bool merge) { if(data.count() == 0 && merge) { m_windows.at(0)->createTab(QUrl()); } if(data.count() == 1 && m_windows.count() >= 1 && merge) { const auto windowData = data.at(0); auto *window = m_windows.at(0); if(windowData.subwindows.count() == 1) { const auto subwindowData = windowData.subwindows.at(0); for(const auto &tab : subwindowData.tabs) { window->createTab(tab); } } else { for(const auto &data : windowData.subwindows) { window->createSubWindow(data); } } return; } for(const auto &windowData : data) { // the window will delete itself when it closes, so we don't need to delete it auto *window = new MainWindow(windowData); connect(window->addressBar, &AddressBar::complete, m_bookmarks.get(), &BookmarksWidget::search); for(auto *info : qAsConst(m_plugins)) { addPluginTo(info, window); } m_windows.append(window); connect(window, &MainWindow::destroyed, this, [this, window]() { m_windows.removeOne(window); }); } } void Browser::addPluginTo(PluginInfo *info, MainWindow *window) { QPluginLoader *loader = info->loader; auto *pluginMenu = new QMenu(loader->metaData().value("MetaData").toObject().value("name").toString()); window->m_menuBar->insertPlugin(pluginMenu); info->menus.append(pluginMenu); auto *aboutAction = pluginMenu->addAction(tr("About")); connect(aboutAction, &QAction::triggered, this, [loader, window]() { auto *dlg = new AboutPluginDialog(loader, window); dlg->exec(); }); auto *runAction = pluginMenu->addAction(tr("Run")); runAction->setShortcut(QKeySequence::fromString(loader->metaData().value("MetaData").toObject().value("shortcut").toString())); connect(runAction, &QAction::triggered, window, [loader, window]() { if(loader->isLoaded()) { const auto *interface = qobject_cast(loader->instance()); Q_CHECK_PTR(interface); interface->createWidget(window)->exec(); } }); auto *removeAction = pluginMenu->addAction(tr("Remove")); connect(removeAction, &QAction::triggered, this, [this, info]() { ; m_plugins.removeOne(info); delete info; }); }