31#include <QPluginLoader>
34#include <QQmlComponent>
45 return !pluginPath.isEmpty() && QFileInfo(pluginPath).isWritable();
55 , m_pluginsLoaded(false)
67 loadAvailablePlugins();
68 return m_availablePlugins;
81 m_availablePlugins.removeOne(*plugin);
82 m_availablePlugins.prepend(*plugin);
84 refreshLoadedPlugins();
99 m_availablePlugins.removeOne(*plugin);
100 m_availablePlugins.append(*plugin);
102 refreshLoadedPlugins();
119 result = QDir(plugin->
pluginPath).removeRecursively();
120 }
else if (info.isFile()) {
129 m_availablePlugins.removeOne(*plugin);
140 qWarning() <<
"Invalid plugin spec of" <<
id <<
"plugin";
143 registerAvailablePlugin(plugin);
150 QStringList defaultAllowedPlugins = {
151 QSL(
"internal:adblock")
155 if (qgetenv(
"KDE_FULL_SESSION") == QByteArray(
"true")) {
156 defaultAllowedPlugins.append(
QSL(
"lib:KDEFrameworksIntegration.so"));
161 m_allowedPlugins = settings.
value(
QSL(
"AllowedPlugins"), defaultAllowedPlugins).toStringList();
176 QFile::remove(tempIcon);
177 QFile::remove(tempMetadata);
178 QSettings settings(tempMetadata, QSettings::IniFormat);
179 settings.beginGroup(
QSL(
"Desktop Entry"));
180 for (
auto it = metaData.begin(); it != metaData.end(); ++it) {
181 const QString
value = it.value().toString();
182 if (it.key() ==
QL1S(
"Icon") &&
value.startsWith(
QL1S(
"base64:"))) {
183 QFile file(tempIcon);
184 if (file.open(QFile::WriteOnly)) {
185 file.write(QByteArray::fromBase64(
value.mid(7).toUtf8()));
186 settings.setValue(it.key(), tempIcon);
189 settings.setValue(it.key(), it.value().toString());
202 spec.
author =
QSL(
"%1 <%2>").arg(metaData.
value(
QSL(
"X-Falkon-Author")).toString(), metaData.
value(
QSL(
"X-Falkon-Email")).toString());
205 const QString iconName = metaData.
icon();
206 if (!iconName.isEmpty()) {
207 if (QFileInfo::exists(iconName)) {
208 spec.
icon = QIcon(iconName).pixmap(32);
210 const QString relativeFile = QFileInfo(metaData.
fileName()).dir().absoluteFilePath(iconName);
211 if (QFileInfo::exists(relativeFile)) {
212 spec.
icon = QIcon(relativeFile).pixmap(32);
214 spec.
icon = QIcon::fromTheme(iconName).pixmap(32);
225 if (!settingsDir.exists()) {
226 settingsDir.mkdir(settingsDir.absolutePath());
229 for (
const QString &pluginId : std::as_const(m_allowedPlugins)) {
235 qWarning() <<
"Invalid plugin spec of" << pluginId <<
"plugin";
239 qWarning() <<
"Failed to init" << pluginId <<
"plugin";
242 registerAvailablePlugin(plugin);
245 refreshLoadedPlugins();
247 std::cout <<
"Falkon: " <<
m_loadedPlugins.count() <<
" extensions loaded" << std::endl;
250void Plugins::loadAvailablePlugins()
252 if (m_pluginsLoaded) {
256 m_pluginsLoaded =
true;
261 registerAvailablePlugin(loadInternalPlugin(
QSL(
"adblock")));
263 for (
const QString &dir : dirs) {
264 const auto files = QDir(dir).entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
265 for (
const QFileInfo &info : files) {
267 const QString pluginPath = info.absoluteFilePath();
268 if (info.isFile() && QLibrary::isLibrary(pluginPath)) {
270 if (info.baseName() !=
QL1S(
"PyFalkon")) {
271 plugin = loadSharedLibraryPlugin(pluginPath);
273 }
else if (info.isDir()) {
274 const DesktopFile metaData(QDir(pluginPath).filePath(
QSL(
"metadata.desktop")));
275 const QString type = metaData.value(
QSL(
"X-Falkon-Type")).toString();
276 if (type ==
QL1S(
"Extension/Python")) {
278 plugin = loadPythonPlugin(pluginPath);
279 }
else if (type ==
QL1S(
"Extension/Qml")) {
283 qWarning() <<
"Invalid type" << type <<
"of" << pluginPath <<
"plugin";
289 if (plugin.pluginSpec.name.isEmpty()) {
290 qWarning() <<
"Invalid plugin spec of" << pluginPath <<
"plugin";
293 registerAvailablePlugin(plugin);
298void Plugins::registerAvailablePlugin(
const Plugin &plugin)
300 if (!m_availablePlugins.contains(plugin)) {
301 m_availablePlugins.append(plugin);
305void Plugins::refreshLoadedPlugins()
309 for (
const Plugin &plugin : std::as_const(m_availablePlugins)) {
310 if (plugin.isLoaded()) {
318void Plugins::loadPythonSupport()
321 for (
const QString &dir : dirs) {
322 const auto files = QDir(dir).entryInfoList({
QSL(
"PyFalkon*")}, QDir::Files);
323 for (
const QFileInfo &info : files) {
324 m_pythonPlugin =
new QLibrary(info.absoluteFilePath(),
this);
325 m_pythonPlugin->setLoadHints(QLibrary::ExportExternalSymbolsHint);
326 if (!m_pythonPlugin->load()) {
327 qWarning() <<
"Failed to load python support plugin" << m_pythonPlugin->errorString();
328 delete m_pythonPlugin;
329 m_pythonPlugin =
nullptr;
331 std::cout <<
"Falkon: Python plugin support initialized" << std::endl;
343 const int colon =
id.indexOf(
QL1C(
':'));
345 const auto t = QStringView{
id}.left(colon);
346 if (
t ==
QL1S(
"internal")) {
348 }
else if (
t ==
QL1S(
"lib")) {
350 }
else if (
t ==
QL1S(
"python")) {
352 }
else if (
t ==
QL1S(
"qml")) {
355 name =
id.mid(colon + 1);
363 return loadInternalPlugin(name);
366 return loadSharedLibraryPlugin(name);
369 return loadPythonPlugin(name);
381 if (name ==
QL1S(
"adblock")) {
384 plugin.pluginId =
QSL(
"internal:adblock");
396 if (QFileInfo(name).isAbsolute()) {
400 if (fullPath.isEmpty()) {
401 qWarning() <<
"Library plugin" << name <<
"not found";
408 plugin.pluginId =
QSL(
"lib:%1").arg(QFileInfo(fullPath).fileName());
409 plugin.pluginPath = fullPath;
410 plugin.pluginLoader =
new QPluginLoader(fullPath);
411 plugin.pluginSpec =
createSpec(plugin.pluginLoader->metaData().value(
QSL(
"MetaData")).toObject());
419 if (!m_pythonPlugin) {
420 qWarning() <<
"Python support plugin is not loaded";
424 auto f = (Plugin*(*)(
const QString &)) m_pythonPlugin->resolve(
"pyfalkon_load_plugin");
426 qWarning() <<
"Failed to resolve" <<
"pyfalkon_load_plugin";
445 switch (plugin->type) {
447 initInternalPlugin(plugin);
451 initSharedLibraryPlugin(plugin);
455 initPythonPlugin(plugin);
466 if (!plugin->instance) {
474 if (!plugin->instance->testPlugin()) {
476 plugin->instance =
nullptr;
483void Plugins::initInternalPlugin(Plugin *plugin)
487 plugin->instance = plugin->internalInstance;
490void Plugins::initSharedLibraryPlugin(Plugin *plugin)
494 plugin->instance = qobject_cast<PluginInterface*>(plugin->pluginLoader->instance());
496 if (!plugin->instance) {
497 qWarning() <<
"Loading" << plugin->pluginPath <<
"failed:" << plugin->pluginLoader->errorString();
501void Plugins::initPythonPlugin(Plugin *plugin)
505 if (!m_pythonPlugin) {
506 qWarning() <<
"Python support plugin is not loaded";
510 auto f = (void(*)(Plugin *)) m_pythonPlugin->resolve(
"pyfalkon_init_plugin");
512 qWarning() <<
"Failed to resolve" <<
"pyfalkon_init_plugin";
static QString path(Path type)
static QStringList allPaths(Path type)
static QString currentProfilePath()
static QString locate(Path type, const QString &file)
QVariant value(const QString &key, bool localized=false) const
static bool isTestModeEnabled()
void removePlugin(Plugin *plugin)
Plugins(QObject *parent=nullptr)
QList< Plugin > availablePlugins()
void pluginUnloaded(PluginInterface *plugin)
void unloadPlugin(Plugin *plugin)
bool addPlugin(const QString &id)
bool loadPlugin(Plugin *plugin)
void availablePluginsChanged()
static PluginSpec createSpec(const QJsonObject &metaData)
QList< PluginInterface * > m_loadedPlugins
static void initPlugin(Plugins::Plugin *plugin)
static Plugins::Plugin loadPlugin(const QString &name)
void beginGroup(const QString &prefix)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant())
int value(const QColor &c)
PluginInterface * instance
bool operator==(const Plugin &other) const