/* * 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 "builtins.h" #include "commandline.h" #include "configuration.h" #include "session/session.h" #include "session/sessiondialog.h" #include "util.h" #include "crashhandler.h" #include "version.h" #include #include #include #include #include #include #include #include "config.h" #if defined(CONFIG_USEPLASMA) && !defined(PLASMA) #error "You have enabled Plasma integration, but Frameworks was not found." #endif #if defined(CONFIG_USEBREAKPAD) && !defined(BREAKPAD) #error "You have enabled Breakpad integration, but Breakpad was not found." #endif int main(int argc, char **argv) { // change log pattern spdlog::set_pattern("[%^%l%$] [%P:%t] %v"); #ifdef QT_DEBUG spdlog::set_level(spdlog::level::debug); // Set global log level to debug #endif const std::unique_ptr cmd = std::make_unique(argc, argv); // --version if(cmd->exists("version")) { return builtins::version(); } // --build if(cmd->exists("build")) { return builtins::build(); } // Disable Chromium's crash handler so breakpad can capture crashes instead. // This has to be done before QtWebEngine gets initialized. const auto chromiumFlags = qgetenv("QTWEBENGINE_CHROMIUM_FLAGS"); if(!chromiumFlags.contains("disable-in-process-stack-traces")) { qputenv("QTWEBENGINE_CHROMIUM_FLAGS", chromiumFlags + " --disable-in-process-stack-traces"); } // create and load configuration std::unique_ptr config = std::make_unique(argc, argv, cmd->value("config").value()); QTranslator translator; if(config->exists("browser.translation")) { translator.load(config->value("browser.translation").value()); } QVector plugins; CommandHash_t pluginCommands; // Load plugins for(const QString &path : Util::files(config->value("plugins.path").value())) { auto *loader = new QPluginLoader(path); const bool loaded = loader->load(); spdlog::info("{} plugin {}", loaded ? "Loaded" : "Failed to load", qUtf8Printable(path)); if(loaded) { plugins.append(loader); auto *plugin = qobject_cast(loader->instance()); pluginCommands.unite(plugin->commands()); } else { spdlog::warn("{}", qUtf8Printable(loader->errorString())); delete loader; } } if(cmd->exists("help")) { return builtins::help(argv[0], cmd->description(), config->description(), pluginCommands, &translator); } // argc, argv, allowSecondary Browser app(argc, argv); // set this, otherwise the webview becomes black when using a stylesheet app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); app.installTranslator(&translator); if(config->exists("browser.locale")) { auto *locale = new QTranslator(&app); if(locale->load("qt_" + config->value("browser.locale").value(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) app.installTranslator(locale); else delete locale; } if(auto iconTheme = config->value("browser.iconTheme")) { QIcon::setThemeName(iconTheme.value()); } #ifdef CONFIG_USEBREAKPAD const std::string crashpath = config->value("browser.crash.path").value_or("/tmp"); assert(!crashpath.empty()); CrashHandler::BreakpadContext ctx; google_breakpad::MinidumpDescriptor descriptor(crashpath.c_str()); const auto crashHandler = config->value("browser.crash.handler"); if(crashHandler) { const int length = crashHandler.value().length() + 1; ctx.handler = new char[length]; crashHandler.value().copy(ctx.handler, length - 1); ctx.handler[length - 1] = '\0'; } // minidump descriptor, filter callback, minidump callback, callback_context, install handler, server_fd google_breakpad::ExceptionHandler eh(descriptor, nullptr, CrashHandler::dumpCallback, &ctx, true, -1); #ifdef QT_DEBUG spdlog::info("Installed breakpad exception handler (path {})", crashpath); #endif #endif // CONFIG_USEBREAKPAD const bool isStandalone = cmd->exists("no-remote"); const auto profile = config->value("profile.default"); app.setConfiguration(config); // app takes ownership of config app.setup(plugins); QStringList urls; if(const auto arguments = cmd->value>("args")) { for(const auto &u : arguments.value()) { if(pluginCommands.contains(QString::fromStdString(u))) { return pluginCommands.value(QString::fromStdString(u))(); } else { urls.append(QString::fromStdString(u)); } } } if(urls.isEmpty()) urls.append(QString()); // if app is primary, create new sessions from received messages if(app.isPrimary() && !isStandalone) { QObject::connect(&app, &Browser::receivedMessage, &app, [](quint32 instanceId, QByteArray message) { Q_UNUSED(instanceId); auto doc = QJsonDocument::fromJson(message); Session::restoreSession(doc.object()); }); } { QJsonObject sessionData; if(cmd->exists("pick-session")) { auto *dlg = new SessionDialog(); if(const auto pick = dlg->pickSession()) sessionData = pick.value(); else sessionData = Session::fromCommandLine(profile.value(), urls); } else if(const auto session = cmd->value("session")) { QFile sessionJson(session.value()); if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) { sessionData = QJsonDocument::fromJson(sessionJson.readAll()).object(); sessionJson.close(); } } else { sessionData = Session::fromCommandLine(profile.value(), urls); } if(app.isPrimary() || isStandalone) { Session::restoreSession(sessionData); } else { // app is secondary and not standalone return app.sendMessage(QJsonDocument(sessionData).toJson()); } } return app.exec(); }