/* * 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/smolbote.hg * * SPDX-License-Identifier: GPL-3.0 */ #include "browser.h" #include "addressbar/addressbar.h" #include "mainwindow/mainwindow.h" #include "mainwindow/subwindow.h" #include "webengine/urlinterceptor.h" #include #include #include #include #include #include #include #include #include #include #include inline Plugin loadPluginFromPath(const QString &path) { Plugin p; QPluginLoader loader(path); if(loader.load()) { #ifdef QT_DEBUG qDebug("Loading plugin: %s [ok]", qUtf8Printable(path)); #endif auto meta = loader.metaData().value("MetaData").toObject(); p.name = meta.value("name").toString(); p.author = meta.value("author").toString(); p.shortcut = QKeySequence::fromString(meta.value("shortcut").toString()); p.instance = std::shared_ptr(loader.instance()); #ifdef QT_DEBUG } else { qDebug("Loading pluing: %s [failed]", qUtf8Printable(path)); #endif } return p; } inline QVector loadPlugins(const QString &path) { QVector list; QFileInfo location(path); if(!location.exists()) { return list; } if(location.isFile()) { // only load this one plugin auto p = loadPluginFromPath(location.absoluteFilePath()); if(p.instance) list.append(p); } else if(location.isDir()) { // load all profiles from this directory const auto entries = QDir(location.absoluteFilePath()).entryInfoList(QDir::Files | QDir::Readable); for(const auto &f : entries) { auto p = loadPluginFromPath(f.absoluteFilePath()); if(p.instance) list.append(p); } } else { #ifdef QT_DEBUG qDebug("Path is neither file nor folder: %s", qUtf8Printable(path)); #endif } return list; } Browser::Browser(int &argc, char *argv[]) : SingleApplication(argc, argv) { setApplicationName("smolbote"); setWindowIcon(QIcon(":/icon.svg")); setApplicationVersion(SMOLBOTE_VERSION); } Browser::~Browser() { if(m_bookmarks) m_bookmarks->save(); qDeleteAll(m_windows); m_windows.clear(); //qDeleteAll(m_plugins); m_plugins.clear(); qDeleteAll(m_profiles); } void Browser::setConfiguration(std::shared_ptr &config) { Q_ASSERT(config); m_config = config; } void Browser::setup(const QString &defaultProfile) { Q_ASSERT_X(m_config, "Browser::setup", "Configuration not set"); // load profiles { const auto defaults = m_config->section("profile"); const QDir profilesDir(m_config->value("profile.path").value()); if(profilesDir.exists()) { const auto entries = profilesDir.entryInfoList({"*.profile"}, QDir::Files | QDir::Readable); for(const QFileInfo &f : entries) { auto *profile = loadProfile(f.baseName(), defaults, f.absoluteFilePath()); m_profiles.insert(f.baseName(), profile); } } // set default profile if(!m_profiles.contains(defaultProfile)) { // if this profile has not been added, it doesn't have a path m_profiles.insert(defaultProfile, loadProfile(defaultProfile, defaults)); } WebProfile::setDefaultProfile(m_profiles.value(defaultProfile)); } // url request filter m_urlFilter = std::make_shared(QString::fromStdString(m_config->value("filter.path").value())); WebProfile::defaultProfile()->setRequestInterceptor(m_urlFilter.get()); // cookie request filter // bookmarks m_bookmarks = std::make_shared(QString::fromStdString(m_config->value("bookmarks.path").value())); connect(m_bookmarks.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) { m_windows.last()->createTab(url); }); connect(WebProfile::defaultProfile(), &WebProfile::addBookmarkRequested, m_bookmarks.get(), &BookmarksWidget::addBookmark); // downloads m_downloads = std::make_shared(QString::fromStdString(m_config->value("downloads.path").value())); connect(WebProfile::defaultProfile(), &WebProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload); // load plugins last m_plugins.append(loadPlugins(QString::fromStdString(m_config->value("plugins.path").value()))); // register commands for(const Plugin &p : qAsConst(m_plugins)) { if(p.instance->inherits("ProfileInterface")) { auto *profileEditor = qobject_cast(p.instance.get()); Q_ASSERT_X(profileEditor != nullptr, "Browser::setup", "profile interface cast failed"); profileEditor->setProfiles(&m_profiles); } auto *plugin = qobject_cast(p.instance.get()); if(plugin) { m_commands.unite(plugin->commands()); } } } WebProfile *Browser::profile(const QString &name) const { if(m_profiles.contains(name)) return m_profiles.value(name); else return nullptr; } int Browser::command(const QString &command) { if(m_commands.contains(command)) { return m_commands.value(command)(); } else { qWarning("No such command: %s", qUtf8Printable(command)); return -1; } } void Browser::createSession(const QString &profileName, bool newWindow, const QStringList &urls) { if(m_windows.isEmpty()) { createWindow(); } auto *mainwindow = m_windows.last(); if(newWindow) { QString firstUrl;// = WebProfile::defaultProfile()->homepage(); if(!urls.isEmpty()) firstUrl = urls.at(0); auto *w = mainwindow->createSubWindow(firstUrl); for(int i = 1; i < urls.count() - 1; i++) { w->addTab(QUrl::fromUserInput(urls.at(i))); } } else { if(urls.isEmpty()) mainwindow->createTab(WebProfile::defaultProfile()->homepage()); else { for(const QString &url : urls) { mainwindow->createTab(QUrl::fromUserInput(url)); } } } } MainWindow *Browser::createWindow() { // the window will delete itself when it closes, so we don't need to delete it MainWindow *window = new MainWindow(m_config); connect(window->addressBar, &AddressBar::complete, m_bookmarks.get(), &BookmarksWidget::search); auto *bookmarksAction = new QAction(tr("Bookmarks"), window); bookmarksAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value("bookmarks.shortcut").value()))); connect(bookmarksAction, &QAction::triggered, window, [this, window]() { bool wasVisible = m_bookmarks->isVisible(); for(MainWindow *w : m_windows) { w->removeDockWidget(m_bookmarks.get()); } if(!wasVisible) { window->addDockWidget(Qt::RightDockWidgetArea, m_bookmarks.get()); } }); window->addAction(MainWindow::ToolsMenu, bookmarksAction); auto *downloadsAction = new QAction(tr("Downloads"), window); downloadsAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value("downloads.shortcut").value()))); connect(downloadsAction, &QAction::triggered, window, [this, window]() { bool wasVisible = m_downloads->isVisible(); for(MainWindow *w : m_windows) { w->removeDockWidget(m_downloads.get()); } if(!wasVisible) { window->addDockWidget(Qt::RightDockWidgetArea, m_downloads.get()); } }); window->addAction(MainWindow::ToolsMenu, downloadsAction); for(const Plugin &p : qAsConst(m_plugins)) { if(p.instance->inherits("ProfileInterface")) { auto *profileEditor = qobject_cast(p.instance.get()); auto *profileAction = new QAction(tr("Profile"), window); connect(profileAction, &QAction::triggered, window, [profileEditor]() { profileEditor->createWidget(nullptr)->show(); }); window->addAction(MainWindow::ToolsMenu, profileAction); } } m_windows.append(window); connect(window, &MainWindow::destroyed, this, [this, window]() { m_windows.removeOne(window); }); return window; }