/* * 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 "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 : m_plugins) delete info; qDeleteAll(m_windows); m_windows.clear(); } void Browser::about() { auto *dlg = new AboutDialog; dlg->exec(); } const QList> Browser::profileList() const { QList> profiles; for(const QString &id : m_profileManager->idList()) { profiles.append(qMakePair(id, m_profileManager->profile(id))); } return profiles; } QPair Browser::loadProfile(const QString &id, bool isOffTheRecord) { Configuration conf; const QString _id = [id]() { // if id contains a separator, it should be a path if(id.contains(QDir::separator())) { return QFileInfo(id).baseName(); } else { return id; } }(); auto *profile = m_profileManager->profile(/* id */ _id, /* path */ (_id == id) ? QString() : id, isOffTheRecord); connect(profile, &WebProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload); auto *interceptor = new UrlRequestInterceptor(profile, profile); for(UrlFilter *filter : 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(QLatin1Literal(":")); if(h.length() == 2) interceptor->addHttpHeader(h.at(0).toLatin1(), h.at(1).toLatin1()); } profile->setUrlRequestInterceptor(interceptor); spdlog::info("Added profile: {}{}", qUtf8Printable(_id), profile->isOffTheRecord() ? " (otr)" : ""); return QPair(_id, profile); } void Browser::removeProfile(const QString &id) { m_profileManager->deleteProfile(id); } WebProfileManager *Browser::getProfileManager() { return m_profileManager; } QPluginLoader *Browser::addPlugin(const QString &path) { if(path.isEmpty()) return nullptr; auto *loader = new PluginLoader(path, PluginLoader::SignatureMatchIfAvailable, this); const bool loaded = loader->load(); spdlog::info("Loading plugin [{}]: {}", qUtf8Printable(path), loaded ? "passed" : "failed"); if(!loaded) { delete loader; return nullptr; } auto *info = new PluginInfo(loader); m_plugins.append(info); for(MainWindow *window : m_windows) { addPluginTo(info, window); } return loader; } void Browser::setup(QVector plugins) { Configuration conf; for(QPluginLoader *loader : plugins) { m_plugins.append(new PluginInfo(loader)); } 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); for(const QString &profilePath : Util::files(conf.value("profile.path").value(), { "*.profile" })) { this->loadProfile(profilePath); } // set default profile { const QString id = conf.value("profile.default").value(); auto *profile = m_profileManager->profile(id); if(profile == nullptr) { profile = qobject_cast(loadProfile(id).second); } WebProfile::setDefaultProfile(profile); } // bookmarks m_bookmarks = std::make_shared(QString::fromStdString(conf.value("bookmarks.path").value())); connect(m_bookmarks.get(), &BookmarksWidget::showContextMenu, this, [this](const QUrl &url, const QPoint &pos) { auto *subwindow = m_windows.last()->currentSubWindow(); if(subwindow == nullptr) return; auto *menu = new QMenu(m_bookmarks.get()); menu->addAction(tr("Open link in current tab"), subwindow, [url, subwindow]() { subwindow->currentView()->load(url); }); auto *openInCurrentTabWithProfile = menu->addMenu(tr("Open link in current tab with profile")); m_profileManager->profileMenu(openInCurrentTabWithProfile, [url, subwindow](WebProfile *profile) { subwindow->currentView()->setProfile(profile); subwindow->currentView()->load(url); }); menu->addAction(tr("Open link in new tab"), subwindow, [url, subwindow]() { subwindow->addTab(url); }); auto *openInNewTabWithProfile = menu->addMenu(tr("Open link in new tab with profile")); m_profileManager->profileMenu(openInNewTabWithProfile, [url, subwindow](WebProfile *profile) { subwindow->addTab(url, profile); }); menu->exec(pos); }); 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); } MainWindow *Browser::createWindow() { // the window will delete itself when it closes, so we don't need to delete it auto *window = new MainWindow(); connect(window->addressBar, &AddressBar::complete, m_bookmarks.get(), &BookmarksWidget::search); for(auto *info : m_plugins) { addPluginTo(info, window); } m_windows.append(window); connect(window, &MainWindow::destroyed, this, [this, window]() { m_windows.removeOne(window); }); return 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; }); }