From cdaf8efbce09acb1f5d5ee6f1f56c65b64ecb8b0 Mon Sep 17 00:00:00 2001 From: Anmol Gautam Date: Wed, 25 Jul 2018 12:46:28 +0530 Subject: [PATCH] Created falkon:extensions page --- src/lib/CMakeLists.txt | 2 + src/lib/app/mainapplication.cpp | 6 + src/lib/data/html.qrc | 4 + src/lib/data/html/extensions.html | 65 ++++++++ src/lib/data/html/extensions.list.html | 24 +++ src/lib/data/html/extensions.user.js | 103 +++++++++++++ src/lib/data/html/themes.list.html | 21 +++ .../schemehandlers/falkonschemehandler.cpp | 99 +++++++++++- .../schemehandlers/falkonschemehandler.h | 1 + src/lib/plugins/extensions.cpp | 105 +++++++++++++ src/lib/plugins/extensions.h | 41 +++++ src/lib/plugins/plugins.cpp | 28 ++++ src/lib/plugins/plugins.h | 7 + src/lib/plugins/themes.cpp | 145 ++++++++++++++++++ src/lib/plugins/themes.h | 47 ++++++ src/lib/preferences/pluginsmanager.cpp | 1 + src/lib/tools/scripts.cpp | 8 + src/lib/tools/scripts.h | 1 + .../webengine/javascript/externaljsobject.cpp | 19 +++ .../webengine/javascript/externaljsobject.h | 6 + 20 files changed, 732 insertions(+), 1 deletion(-) create mode 100644 src/lib/data/html/extensions.html create mode 100644 src/lib/data/html/extensions.list.html create mode 100644 src/lib/data/html/extensions.user.js create mode 100644 src/lib/data/html/themes.list.html create mode 100644 src/lib/plugins/extensions.cpp create mode 100644 src/lib/plugins/extensions.h create mode 100644 src/lib/plugins/themes.cpp create mode 100644 src/lib/plugins/themes.h diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 30091246f..ad9cce7cc 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -189,6 +189,8 @@ set(SRCS ${SRCS} plugins/qml/api/extensionscheme/qmlextensionscheme.cpp plugins/qml/api/extensionscheme/qmlwebengineurlrequestjob.cpp plugins/qml/api/fileutils/qmlfileutils.cpp + plugins/extensions.cpp + plugins/themes.cpp popupwindow/popuplocationbar.cpp popupwindow/popupstatusbarmessage.cpp popupwindow/popupwebview.cpp diff --git a/src/lib/app/mainapplication.cpp b/src/lib/app/mainapplication.cpp index 884122df7..206789b01 100644 --- a/src/lib/app/mainapplication.cpp +++ b/src/lib/app/mainapplication.cpp @@ -1107,6 +1107,12 @@ void MainApplication::setupUserScripts() falkonSpeedDial.setSourceCode(Scripts::setupSpeedDial()); m_webProfile->scripts()->insert(falkonSpeedDial); + // falkon:extensions + QWebEngineScript falkonExtensions; + falkonExtensions.setWorldId(WebPage::SafeJsWorld); + falkonExtensions.setSourceCode(Scripts::setupExtensions()); + m_webProfile->scripts()->insert(falkonExtensions); + // document.window object addons QWebEngineScript documentWindowAddons; documentWindowAddons.setName(QSL("_falkon_window_object")); diff --git a/src/lib/data/html.qrc b/src/lib/data/html.qrc index 34f890a2e..eab7e41bd 100644 --- a/src/lib/data/html.qrc +++ b/src/lib/data/html.qrc @@ -22,5 +22,9 @@ html/edit_active.svg html/sd_bg.svg html/broken-page.svg + html/extensions.html + html/extensions.user.js + html/extensions.list.html + html/themes.list.html diff --git a/src/lib/data/html/extensions.html b/src/lib/data/html/extensions.html new file mode 100644 index 000000000..2f3f0f8a1 --- /dev/null +++ b/src/lib/data/html/extensions.html @@ -0,0 +1,65 @@ + + + + +%TITLE% + + + + + +
+
+
+
+ +
+ %EXTENSIONS-LIST% +
+ +
+ %THEMES-LIST% +
+ + + + + diff --git a/src/lib/data/html/extensions.list.html b/src/lib/data/html/extensions.list.html new file mode 100644 index 000000000..faee93cca --- /dev/null +++ b/src/lib/data/html/extensions.list.html @@ -0,0 +1,24 @@ +
+
+
%EXTENSION-NAME%
+
+
+ %EXTENSION-DESCRIPTION-SHORT% + more +
+
+ %EXTENSION-DESCRIPTION% + + + +
Author%EXTENSION-AUTHOR%
Version%EXTENSION-VERSION%
+ +
+
+ + + +
+
+
+
diff --git a/src/lib/data/html/extensions.user.js b/src/lib/data/html/extensions.user.js new file mode 100644 index 000000000..347f45061 --- /dev/null +++ b/src/lib/data/html/extensions.user.js @@ -0,0 +1,103 @@ +// ==UserScript== +// @name _falkon_extensions +// @run-at document-end +// @include falkon:extensions +// ==/UserScript== + +(function() { + + function createNavigation() { + + $("#extensions-button").click(function() { + display("extensions", this); + }); + + $("#themes-button").click(function() { + display("themes", this); + }); + + function display(id, button) { + var content = $("#" + id).html(); + $("#content").html(content); + $(".sidebar-menu").removeClass("active"); + $("#" + button.id).addClass("active"); + } + } + + function initToggleDescription() { + $(document).on("click", ".extension-description-state-button", function() { + $(".extension-description-short").addClass("visible"); + $(".extension-description").removeClass("visible"); + var extensionId = this.id.substring(0, this.id.length - "-description-state-button-id".length); + document.getElementById(extensionId + "-description-short-id").className = document.getElementById(extensionId + "-description-short-id").className.replace("visible", ""); + document.getElementById(extensionId + "-description-id").className += " visible"; + }); + + $(document).on("click", ".theme-description-state-button", function() { + $(".theme-description-short").addClass("visible"); + $(".theme-description").removeClass("visible"); + var themeId = this.id.substring(0, this.id.length - "-description-state-button-id".length); + document.getElementById(themeId + "-description-short-id").className = document.getElementById(themeId + "-description-short-id").className.replace("visible", ""); + document.getElementById(themeId + "-description-id").className += " visible"; + }); + } + + function initControlButtonsAndCheckbox() { + $(document).on("click", ".extension-control-button.enable", function() { + var extensionId = this.id.substring(0, this.id.length - "-enable-button-id".length); + external.extensions.pluginStateChanged(extensionId); + if (this.html() == "Enable") { + document.getElementById(extensionId + "-settings-button-id").disabled = true; + this.html("Disable"); + } else { + document.getElementById(extensionId + "-settings-button-id").disabled = false; + this.html("Enable"); + } + }); + + $(document).on("click", ".extension-control-button.settings", function() { + var extensionId = this.id.substring(0, this.id.length - "-settings-button-id".length); + external.extensions.showSettings(extensionId); + }); + + $(document).on("click", ".extension-control-button.remove", function() { + var extensionId = this.id.substring(0, this.id.length - "-remove-button-id".length); + external.extensions.removeExtension(extensionId); + }); + + $(document).on("click", ".allow-in-incognito-button-checkbox", function() { + var extensionId = this.id.substring(0, this.id.length - "-allow-in-incognito-id".length); + external.extensions.allowInIncognito(extensionId, this.checked); + }); + + $(document).on("click", ".theme-control-button.enable", function() { + $(".theme-control-button.enable").prop('disabled', false); + this.disabled = true; + var themeId = this.id.substring(0, this.id.length - "-enable-button-id".length); + external.themes.makeCurrent(themeId); + }); + + $(document).on("click", ".theme-control-button.license", function() { + var themeId = this.id.substring(0, this.id.length - "-license-button-id".length); + external.themes.showLicense(themeId); + }); + } + + function init() { + createNavigation(); + initToggleDescription(); + initControlButtonsAndCheckbox(); + + external.extensions.requestSync(); + external.extensions.reload.connect(function() { location.reload() }); + } + + %JQUERY% + %JQUERY-UI% + + if (window._falkon_external) { + init(); + } else { + document.addEventListener("_falkon_external_created", init); + } +})(); diff --git a/src/lib/data/html/themes.list.html b/src/lib/data/html/themes.list.html new file mode 100644 index 000000000..75ca1681a --- /dev/null +++ b/src/lib/data/html/themes.list.html @@ -0,0 +1,21 @@ +
+
+
%THEME-NAME%
+
+
+ %THEME-DESCRIPTION-SHORT% + more +
+
+ %THEME-DESCRIPTION% + + +
Author%THEME-AUTHOR%
+
+
+ + +
+
+
+
diff --git a/src/lib/network/schemehandlers/falkonschemehandler.cpp b/src/lib/network/schemehandlers/falkonschemehandler.cpp index 4e01897db..b6e8aa270 100644 --- a/src/lib/network/schemehandlers/falkonschemehandler.cpp +++ b/src/lib/network/schemehandlers/falkonschemehandler.cpp @@ -28,6 +28,8 @@ #include "sessionmanager.h" #include "restoremanager.h" #include "../config.h" +#include "sqldatabase.h" +#include "themes.h" #include #include @@ -52,7 +54,12 @@ void FalkonSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) } QStringList knownPages; - knownPages << "about" << "start" << "speeddial" << "config" << "restore"; + knownPages << "about" << "start" << "speeddial" << "config" << "restore" << "extensions"; + + if (job->requestUrl().path().contains("config") || + job->requestUrl().path().contains("extensions")) { + mApp->plugins()->getAvailablePlugins(); + } if (knownPages.contains(job->requestUrl().path())) job->reply(QByteArrayLiteral("text/html"), new FalkonSchemeReply(job)); @@ -109,6 +116,8 @@ void FalkonSchemeReply::loadPage() contents = configPage(); } else if (m_pageName == QLatin1String("restore")) { contents = restorePage(); + } else if (m_pageName == QLatin1String("extensions")) { + contents = extensionsPage(); } QMutexLocker lock(&m_mutex); @@ -416,3 +425,91 @@ QString FalkonSchemeReply::configPage() return page; } + +QString FalkonSchemeReply::extensionsPage() +{ + static QString ePage; + + if (ePage.isEmpty()) { + ePage.append(QzTools::readAllFileContents(":html/extensions.html")); + ePage.replace(QLatin1String("%TITLE%"), tr("Falkon Extensions")); + ePage.replace(QLatin1String("%SIDEBAR-WIDTH%"), "200px"); + ePage = QzTools::applyDirectionToPage(ePage); + } + + QString page = ePage; + + // Plugins + QSet allowedPluginsInPrivateMode; + QSqlQuery query(SqlDatabase::instance()->database()); + query.exec(QSL("SELECT * FROM allowed_plugins WHERE allowInPrivateMode=1")); + while (query.next()) { + allowedPluginsInPrivateMode.insert(query.value(0).toString()); + } + + QString pluginsString; + QString pluginsList = QzTools::readAllFileByteContents(":html/extensions.list.html"); + const QList &availablePlugins = mApp->plugins()->getAvailablePlugins(); + int charsInShortDescription = 75; + foreach (const Plugins::Plugin &plugin, availablePlugins) { + QString pluginListItem = pluginsList; + PluginSpec spec = plugin.pluginSpec; + QImage extensionImage = spec.icon.toImage(); + QByteArray byteArray; + QBuffer buffer(&byteArray); + extensionImage.save(&buffer, "PNG"); + pluginListItem.replace(QLatin1String("%EXTENSION-ICON%"), QString::fromLatin1(byteArray.toBase64().data())); + pluginListItem.replace(QLatin1String("%EXTENSION-NAME%"), spec.name); + pluginListItem.replace(QLatin1String("%EXTENSION-DESCRIPTION-SHORT%"), QString("%1%2").arg(spec.description.left(charsInShortDescription), spec.description.length() > charsInShortDescription ? "..." : "")); + pluginListItem.replace(QLatin1String("%EXTENSION-DESCRIPTION%"), spec.description); + pluginListItem.replace(QLatin1String("%EXTENSION-DESCRIPTION-SHORT-ID%"), QString("%1-description-short-id").arg(plugin.pluginId)); + pluginListItem.replace(QLatin1String("%EXTENSION-DESCRIPTION-ID%"), QString("%1-description-id").arg(plugin.pluginId)); + pluginListItem.replace(QLatin1String("%EXTENSION-DESCRIPTION-STATE-BUTTON-ID%"), QString("%1-description-state-button-id").arg(plugin.pluginId)); + pluginListItem.replace(QLatin1String("%EXTENSION-AUTHOR%"), spec.author); + pluginListItem.replace(QLatin1String("%EXTENSION-VERSION%"), spec.version); + pluginListItem.replace(QLatin1String("%EXTENSION-ENABLE-BUTTON-ID%"), QString("%1-enable-button-id").arg(plugin.pluginId)); + pluginListItem.replace(QLatin1String("%EXTENSION-SETTINGS-BUTTON-ID%"), QString("%1-settings-button-id").arg(plugin.pluginId)); + pluginListItem.replace(QLatin1String("%EXTENSION-REMOVE-BUTTON-ID%"), QString("%1-remove-button-id").arg(plugin.pluginId)); + pluginListItem.replace(QLatin1String("%EXTENSION-STATE%"), plugin.isLoaded() ? tr("Disable") : tr("Enable")); + pluginListItem.replace(QLatin1String("%DISABLE-SETTINGS%"), plugin.isLoaded() && spec.hasSettings ? tr("") : tr("disabled")); + pluginListItem.replace(QLatin1String("%DISABLE-REMOVE%"), plugin.type == Plugins::Plugin::QmlPlugin ? tr("") : tr("disabled")); + pluginListItem.replace(QLatin1String("%DISABLE-ALLOW-IN-INCOGNITO%"), plugin.isLoaded() ? tr("") : tr("disabled")); + pluginListItem.replace(QLatin1String("%EXTENSION-ALLOW-IN-INCOGNITO-ID%"), QString("%1-allow-in-incognito-id").arg(plugin.pluginId)); + pluginListItem.replace(QLatin1String("%EXTENSION-ALLOWED-IN-INCOGNITO%"), allowedPluginsInPrivateMode.contains(plugin.pluginId) ? "checked" : ""); + pluginsString.append(pluginListItem); + } + page.replace(QLatin1String("%EXTENSIONS-LIST%"), pluginsString); + + // Themes + const QString activeTheme = Themes::getActiveTheme(); + const QList &availableThemes = Themes::getAvailableThemes(); + QString themesString; + QString themesList = QzTools::readAllFileByteContents(":html/themes.list.html"); + foreach (const Themes::Theme &theme, availableThemes) { + QString themeListItem = themesList; + QImage themeImage = theme.icon.pixmap(32, 32).toImage(); + QByteArray byteArray; + QBuffer buffer(&byteArray); + themeImage.save(&buffer, "PNG"); + themeListItem.replace(QLatin1String("%THEME-ICON%"), QString::fromLatin1(byteArray.toBase64().data())); + themeListItem.replace(QLatin1String("%THEME-NAME%"), theme.name); + themeListItem.replace(QLatin1String("%THEME-DESCRIPTION-SHORT%"), QString("%1%2").arg(theme.description.left(charsInShortDescription), theme.description.length() > charsInShortDescription ? "..." : "")); + themeListItem.replace(QLatin1String("%THEME-DESCRIPTION%"), theme.description); + themeListItem.replace(QLatin1String("%THEME-DESCRIPTION-SHORT-ID%"), QString("%1-description-short-id").arg(theme.name)); + themeListItem.replace(QLatin1String("%THEME-DESCRIPTION-ID%"), QString("%1-description-id").arg(theme.name)); + themeListItem.replace(QLatin1String("%THEME-DESCRIPTION-STATE-BUTTON-ID%"), QString("%1-description-state-button-id").arg(theme.name)); + themeListItem.replace(QLatin1String("%THEME-AUTHOR%"), theme.author); + themeListItem.replace(QLatin1String("%THEME-ENABLE-BUTTON-ID%"), QString("%1-enable-button-id").arg(theme.name)); + themeListItem.replace(QLatin1String("%THEME-LICENSE-BUTTON-ID%"), QString("%1-license-button-id").arg(theme.name)); + themeListItem.replace(QLatin1String("%THEME-REMOVE-BUTTON-ID%"), QString("%1-remove-button-id").arg(theme.name)); + themeListItem.replace(QLatin1String("%DISABLE-LICENSE%"), theme.license.isEmpty() ? "disabled" : ""); + if (activeTheme == theme.dirName) { + themeListItem.replace(QLatin1String("%DISABLE-ENABLE%"), QLatin1String("disabled")); + } else { + themeListItem.replace(QLatin1String("%DISABLE-ENABLE%"), QLatin1String("")); + } + themesString.append(themeListItem); + } + page.replace(QLatin1String("%THEMES-LIST%"), themesString); + return page; +} diff --git a/src/lib/network/schemehandlers/falkonschemehandler.h b/src/lib/network/schemehandlers/falkonschemehandler.h index b92969dfd..09c5bcd96 100644 --- a/src/lib/network/schemehandlers/falkonschemehandler.h +++ b/src/lib/network/schemehandlers/falkonschemehandler.h @@ -56,6 +56,7 @@ private: QString speeddialPage(); QString restorePage(); QString configPage(); + QString extensionsPage(); bool m_loaded; QBuffer m_buffer; diff --git a/src/lib/plugins/extensions.cpp b/src/lib/plugins/extensions.cpp new file mode 100644 index 000000000..877a6e203 --- /dev/null +++ b/src/lib/plugins/extensions.cpp @@ -0,0 +1,105 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#include "extensions.h" +#include "mainapplication.h" +#include "pluginproxy.h" +#include "sqldatabase.h" +#include +#include + +Extensions::Extensions(QObject *parent) + : QObject(parent) +{ +} + +void Extensions::requestSync() +{ + m_availablePlugins = mApp->plugins()->getAvailablePlugins(); +} + +void Extensions::requestReload() +{ + emit reload(); +} + +void Extensions::pluginStateChanged(const QString &pluginId) +{ + foreach (Plugins::Plugin plugin, m_availablePlugins) { + if (plugin.pluginId == pluginId) { + if (plugin.isLoaded()) { + mApp->plugins()->unloadPlugin(&plugin); + auto job = new SqlQueryJob(QSL("DELETE FROM allowed_plugins WHERE pluginId = ?"), this); + job->addBindValue(plugin.pluginId); + job->start(); + } else { + mApp->plugins()->loadPlugin(&plugin); + auto job = new SqlQueryJob(QSL("INSERT OR REPLACE INTO allowed_plugins (pluginId, allowInPrivateMode) VALUES (?, ?)"), this); + job->addBindValue(plugin.pluginId); + job->addBindValue(0); + job->start(); + } + return; + } + } +} + +void Extensions::showSettings(const QString &pluginId) +{ + foreach (const Plugins::Plugin &plugin, m_availablePlugins) { + if (plugin.pluginId == pluginId && plugin.pluginSpec.hasSettings) { + plugin.instance->showSettings(); + return; + } + } +} + +void Extensions::removeExtension(const QString &pluginId) +{ + Plugins::Plugin requiredPlugin; + foreach (const Plugins::Plugin &plugin, m_availablePlugins) { + if (plugin.pluginId == pluginId) { + requiredPlugin = plugin; + break; + } + } + if (requiredPlugin.type == Plugins::Plugin::Invalid) { + return; + } + QMessageBox dialog; + dialog.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + dialog.setText(QString(tr("Are you sure to permanently remove %1 plugin").arg(requiredPlugin.pluginSpec.name))); + dialog.setIcon(QMessageBox::Question); + if (dialog.exec() != QMessageBox::Yes) { + return; + } + + mApp->plugins()->removePlugin(&requiredPlugin); +} + +void Extensions::allowInIncognito(const QString &pluginId, bool allowed) +{ + foreach (const Plugins::Plugin &plugin, m_availablePlugins) { + if (plugin.pluginId == pluginId && plugin.isLoaded()) { + auto job = new SqlQueryJob(QSL("UPDATE allowed_plugins SET allowInPrivateMode=? WHERE pluginId=?"), this); + job->addBindValue(allowed ? 1 : 0); + job->addBindValue(plugin.pluginId); + job->start(); + break; + } + } +} diff --git a/src/lib/plugins/extensions.h b/src/lib/plugins/extensions.h new file mode 100644 index 000000000..636c2f2dd --- /dev/null +++ b/src/lib/plugins/extensions.h @@ -0,0 +1,41 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#pragma once +#include +#include "plugins.h" +#include "mainapplication.h" +#include "pluginproxy.h" +#include "qzcommon.h" + +class FALKON_EXPORT Extensions : public QObject +{ + Q_OBJECT +public: + explicit Extensions(QObject *parent = nullptr); +Q_SIGNALS: + void reload(); +public Q_SLOTS: + void requestSync(); + void requestReload(); + void pluginStateChanged(const QString &pluginId); + void showSettings(const QString &pluginId); + void removeExtension(const QString &pluginId); + void allowInIncognito(const QString &pluginId, bool allowed); +private: + QList m_availablePlugins; +}; diff --git a/src/lib/plugins/plugins.cpp b/src/lib/plugins/plugins.cpp index dac2653bd..cd43e9e62 100644 --- a/src/lib/plugins/plugins.cpp +++ b/src/lib/plugins/plugins.cpp @@ -19,6 +19,7 @@ #include "plugininterface.h" #include "mainapplication.h" #include "speeddial.h" +#include "extensions.h" #include "settings.h" #include "datapaths.h" #include "adblock/adblockplugin.h" @@ -37,6 +38,7 @@ Plugins::Plugins(QObject* parent) : QObject(parent) , m_pluginsLoaded(false) , m_speedDial(new SpeedDial(this)) + , m_extensions(new Extensions(this)) { loadSettings(); @@ -45,6 +47,8 @@ Plugins::Plugins(QObject* parent) } loadQmlSupport(); + + connect(this, &Plugins::refreshedLoadedPlugins, m_extensions, &Extensions::requestReload); } QList Plugins::getAvailablePlugins() @@ -88,6 +92,28 @@ void Plugins::unloadPlugin(Plugins::Plugin* plugin) refreshLoadedPlugins(); } +void Plugins::removePlugin(Plugins::Plugin *plugin) +{ + if (plugin->type != Plugin::QmlPlugin) { + return; + } + if (plugin->isLoaded()) { + unloadPlugin(plugin); + } + + // For QML plugins, pluginId is qml: + const QString dirName = plugin->pluginId.remove(0, QLatin1String("qml:").size()); + const QString dirPath = DataPaths::locate(DataPaths::Plugins, QSL("qml/") + dirName); + bool result = QDir(dirPath).removeRecursively(); + if (!result) { + qWarning() << "Unable to remove" << plugin->pluginSpec.name; + return; + } + + m_availablePlugins.removeOne(*plugin); + refreshedLoadedPlugins(); +} + void Plugins::loadSettings() { QSqlQuery query(SqlDatabase::instance()->database()); @@ -243,6 +269,8 @@ void Plugins::refreshLoadedPlugins() m_loadedPlugins.append(plugin.instance); } } + + emit refreshedLoadedPlugins(); } void Plugins::loadPythonSupport() diff --git a/src/lib/plugins/plugins.h b/src/lib/plugins/plugins.h index d7aa42653..cc54727f5 100644 --- a/src/lib/plugins/plugins.h +++ b/src/lib/plugins/plugins.h @@ -30,6 +30,7 @@ class QLibrary; class QPluginLoader; class SpeedDial; +class Extensions; class QmlPluginLoader; struct PluginSpec { @@ -95,12 +96,16 @@ public: bool loadPlugin(Plugin* plugin); void unloadPlugin(Plugin* plugin); + void removePlugin(Plugin *plugin); void shutdown(); // SpeedDial SpeedDial* speedDial() { return m_speedDial; } + // Extensions + Extensions *extensions() { return m_extensions; } + static PluginSpec createSpec(const DesktopFile &metaData); static QStringList getDefaultAllowedPlugins(); @@ -115,6 +120,7 @@ protected: Q_SIGNALS: void pluginUnloaded(PluginInterface* plugin); + void refreshedLoadedPlugins(); private: void loadPythonSupport(); @@ -141,6 +147,7 @@ private: bool m_pluginsLoaded; SpeedDial* m_speedDial; + Extensions *m_extensions; QList m_internalPlugins; QLibrary *m_pythonPlugin = nullptr; diff --git a/src/lib/plugins/themes.cpp b/src/lib/plugins/themes.cpp new file mode 100644 index 000000000..5091ccef4 --- /dev/null +++ b/src/lib/plugins/themes.cpp @@ -0,0 +1,145 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2010-2014 David Rosca +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#include "themes.h" +#include "datapaths.h" +#include "desktopfile.h" +#include "qztools.h" +#include "settings.h" +#include "licenseviewer.h" +#include "mainapplication.h" +#include + +Themes::Themes(QObject *parent) + : QObject(parent) +{ +} + +// static +QList Themes::getAvailableThemes() +{ + QList availableThemes; + const QStringList themePaths = DataPaths::allPaths(DataPaths::Themes); + foreach (const QString &path, themePaths) { + QDir dir(path); + QStringList list = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + foreach (const QString &name, list) { + Theme themeInfo = parseTheme(dir.absoluteFilePath(name) + QLatin1Char('/'), name); + if (!themeInfo.isValid) { + continue; + } + availableThemes.append(themeInfo); + } + } + return availableThemes; +} + +QString Themes::getActiveTheme() +{ + Settings settings; + settings.beginGroup("Themes"); + const QString activeTheme = settings.value("activeTheme", DEFAULT_THEME_NAME).toString(); + settings.endGroup(); + return activeTheme; +} + +void Themes::showLicense(const QString &themeName) +{ + Theme theme = getThemeByName(themeName); + if (!theme.isValid) { + return; + } + LicenseViewer *licenseViewer = new LicenseViewer; + licenseViewer->setText(theme.license); + licenseViewer->show(); + QzTools::centerWidgetOnScreen(licenseViewer); +} + +void Themes::makeCurrent(const QString &themeName) +{ + Theme theme = getThemeByName(themeName); + if (!theme.isValid) { + return; + } + Settings settings; + settings.beginGroup("Themes"); + settings.setValue("activeTheme", theme.dirName); + settings.endGroup(); + + mApp->reloadSettings(); +} + +// static +Themes::Theme Themes::parseTheme(const QString &path, const QString &name) +{ + Theme info; + info.isValid = false; + + if (!QFile(path + "main.css").exists() || !QFile(path + "metadata.desktop").exists()) { + info.isValid = false; + return info; + } + + DesktopFile metadata(path + QSL("metadata.desktop")); + info.name = metadata.name(); + info.dirName = name; + info.description = metadata.comment(); + info.author = metadata.value(QSL("X-Falkon-Author")).toString(); + + const QString iconName = metadata.icon(); + if (!iconName.isEmpty()) { + if (QFileInfo::exists(path + iconName)) { + info.icon = QIcon(path + iconName); + } else { + info.icon = QIcon::fromTheme(iconName); + } + } + + const QString licensePath = metadata.value(QSL("X-Falkon-License")).toString(); + if (!licensePath.isEmpty() && QFileInfo::exists(path + licensePath)) { + info.license = QzTools::readAllFileContents(path + licensePath); + } + + if (info.name.isEmpty()) { + return info; + } + + info.isValid = true; + return info; +} + +Themes::Theme Themes::getThemeByName(const QString &themeName) const +{ + const QStringList themePaths = DataPaths::allPaths(DataPaths::Themes); + foreach (const QString &path, themePaths) { + QDir dir(path); + QStringList list = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + foreach (const QString &name, list) { + Theme themeInfo = parseTheme(dir.absoluteFilePath(name) + QLatin1Char('/'), name); + if (!themeInfo.isValid) { + continue; + } + if (themeInfo.name == themeName) { + return themeInfo; + } + } + } + Theme info; + info.isValid = false; + return info; +} diff --git a/src/lib/plugins/themes.h b/src/lib/plugins/themes.h new file mode 100644 index 000000000..fc05c5a98 --- /dev/null +++ b/src/lib/plugins/themes.h @@ -0,0 +1,47 @@ +/* ============================================================ +* Falkon - Qt web browser +* Copyright (C) 2010-2014 David Rosca +* Copyright (C) 2018 Anmol Gautam +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#pragma once +#include +#include +#include "qzcommon.h" + +class FALKON_EXPORT Themes : public QObject +{ + Q_OBJECT +public: + struct Theme { + bool isValid; + QIcon icon; + QString name; + QString dirName; + QString author; + QString description; + QString license; + }; + explicit Themes(QObject *parent = nullptr); + + static QList getAvailableThemes(); + static QString getActiveTheme(); +public Q_SLOTS: + void showLicense(const QString &themeName); + void makeCurrent(const QString &themeName); +private: + static Theme parseTheme(const QString &path, const QString &name); + Theme getThemeByName(const QString &themeName) const; +}; diff --git a/src/lib/preferences/pluginsmanager.cpp b/src/lib/preferences/pluginsmanager.cpp index 38ccee150..48e4b12a8 100644 --- a/src/lib/preferences/pluginsmanager.cpp +++ b/src/lib/preferences/pluginsmanager.cpp @@ -25,6 +25,7 @@ #include "iconprovider.h" #include "../config.h" #include "sqldatabase.h" +#include "extensions.h" #include #include diff --git a/src/lib/tools/scripts.cpp b/src/lib/tools/scripts.cpp index 3f535718d..542315126 100644 --- a/src/lib/tools/scripts.cpp +++ b/src/lib/tools/scripts.cpp @@ -162,6 +162,14 @@ QString Scripts::setupSpeedDial() return source; } +QString Scripts::setupExtensions() +{ + QString source = QzTools::readAllFileContents(QSL(":html/extensions.user.js")); + source.replace(QL1S("%JQUERY%"), QzTools::readAllFileContents(QSL(":html/jquery.js"))); + source.replace(QL1S("%JQUERY-UI%"), QzTools::readAllFileContents(QSL(":html/jquery-ui.js"))); + return source; +} + QString Scripts::setCss(const QString &css) { QString source = QL1S("(function() {" diff --git a/src/lib/tools/scripts.h b/src/lib/tools/scripts.h index 0d34f7684..c6f8690c1 100644 --- a/src/lib/tools/scripts.h +++ b/src/lib/tools/scripts.h @@ -32,6 +32,7 @@ public: static QString setupFormObserver(); static QString setupWindowObject(); static QString setupSpeedDial(); + static QString setupExtensions(); static QString setCss(const QString &css); static QString sendPostData(const QUrl &url, const QByteArray &data); diff --git a/src/lib/webengine/javascript/externaljsobject.cpp b/src/lib/webengine/javascript/externaljsobject.cpp index 3a387c280..fad2e4682 100644 --- a/src/lib/webengine/javascript/externaljsobject.cpp +++ b/src/lib/webengine/javascript/externaljsobject.cpp @@ -19,9 +19,11 @@ #include "mainapplication.h" #include "pluginproxy.h" #include "speeddial.h" +#include "extensions.h" #include "webpage.h" #include "autofilljsobject.h" #include "restoremanager.h" +#include "themes.h" #include @@ -31,6 +33,7 @@ ExternalJsObject::ExternalJsObject(WebPage *page) : QObject(page) , m_page(page) , m_autoFill(new AutoFillJsObject(this)) + , m_themes(new Themes(this)) { } @@ -81,3 +84,19 @@ QObject *ExternalJsObject::recovery() const return mApp->restoreManager()->recoveryObject(m_page); } + +QObject *ExternalJsObject::extensions() const +{ + if (m_page->url().toString() != QL1S("falkon:extensions")) + return Q_NULLPTR; + + return mApp->plugins()->extensions(); +} + +QObject *ExternalJsObject::themes() const +{ + if (m_page->url().toString() != QL1S("falkon:extensions")) + return Q_NULLPTR; + + return m_themes; +} diff --git a/src/lib/webengine/javascript/externaljsobject.h b/src/lib/webengine/javascript/externaljsobject.h index c59e72e32..e1a7263c1 100644 --- a/src/lib/webengine/javascript/externaljsobject.h +++ b/src/lib/webengine/javascript/externaljsobject.h @@ -24,6 +24,7 @@ class WebPage; class AutoFillJsObject; +class Themes; class QWebChannel; @@ -33,6 +34,8 @@ class FALKON_EXPORT ExternalJsObject : public QObject Q_PROPERTY(QObject* speedDial READ speedDial CONSTANT) Q_PROPERTY(QObject* autoFill READ autoFill CONSTANT) Q_PROPERTY(QObject* recovery READ recovery CONSTANT) + Q_PROPERTY(QObject* extensions READ extensions CONSTANT) + Q_PROPERTY(QObject* themes READ themes CONSTANT) public: explicit ExternalJsObject(WebPage *page); @@ -48,9 +51,12 @@ private: QObject *speedDial() const; QObject *autoFill() const; QObject *recovery() const; + QObject *extensions() const; + QObject *themes() const; WebPage *m_page; AutoFillJsObject *m_autoFill; + Themes *m_themes; }; #endif // EXTERNALJSOBJECT_H