File indexing completed on 2024-05-19 04:59:16

0001 /* ============================================================
0002 * GreaseMonkey plugin for Falkon
0003 * Copyright (C) 2012-2018 David Rosca <nowrep@gmail.com>
0004 *
0005 * This program is free software: you can redistribute it and/or modify
0006 * it under the terms of the GNU General Public License as published by
0007 * the Free Software Foundation, either version 3 of the License, or
0008 * (at your option) any later version.
0009 *
0010 * This program is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 * GNU General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU General Public License
0016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017 * ============================================================ */
0018 #include "gm_manager.h"
0019 #include "gm_script.h"
0020 #include "gm_downloader.h"
0021 #include "gm_jsobject.h"
0022 #include "gm_icon.h"
0023 #include "gm_addscriptdialog.h"
0024 #include "settings/gm_settings.h"
0025 
0026 #include "browserwindow.h"
0027 #include "webpage.h"
0028 #include "qztools.h"
0029 #include "mainapplication.h"
0030 #include "networkmanager.h"
0031 #include "navigationbar.h"
0032 #include "desktopnotificationsfactory.h"
0033 #include "javascript/externaljsobject.h"
0034 #include "statusbar.h"
0035 
0036 #include <QTimer>
0037 #include <QDir>
0038 #include <QSettings>
0039 #include <QWebEngineProfile>
0040 #include <QWebEngineScriptCollection>
0041 
0042 GM_Manager::GM_Manager(const QString &sPath, QObject* parent)
0043     : QObject(parent)
0044     , m_settingsPath(sPath)
0045     , m_jsObject(new GM_JSObject(this))
0046 {
0047     load();
0048 }
0049 
0050 GM_Manager::~GM_Manager()
0051 {
0052     ExternalJsObject::unregisterExtraObject(m_jsObject);
0053 }
0054 
0055 void GM_Manager::showSettings(QWidget* parent)
0056 {
0057     if (!m_settings) {
0058         m_settings = new GM_Settings(this, parent);
0059     }
0060 
0061     m_settings.data()->show();
0062     m_settings.data()->raise();
0063 }
0064 
0065 void GM_Manager::downloadScript(const QUrl &url)
0066 {
0067     auto *downloader = new GM_Downloader(url, this);
0068     connect(downloader, &GM_Downloader::finished, this, [=](const QString &fileName) {
0069         bool deleteScript = true;
0070         auto *script = new GM_Script(this, fileName);
0071         if (script->isValid()) {
0072             if (!containsScript(script->fullName())) {
0073                 GM_AddScriptDialog dialog(this, script);
0074                 deleteScript = dialog.exec() != QDialog::Accepted;
0075             }
0076             else {
0077                 showNotification(tr("'%1' is already installed").arg(script->name()));
0078             }
0079         }
0080 
0081         if (deleteScript) {
0082             delete script;
0083             QFile(fileName).remove();
0084         }
0085     });
0086 }
0087 
0088 QString GM_Manager::settingsPath() const
0089 {
0090     return m_settingsPath;
0091 }
0092 
0093 QString GM_Manager::scriptsDirectory() const
0094 {
0095     return m_settingsPath + QL1S("/greasemonkey");
0096 }
0097 
0098 QString GM_Manager::requireScripts(const QStringList &urlList) const
0099 {
0100     QDir requiresDir(m_settingsPath + QL1S("/greasemonkey/requires"));
0101     if (!requiresDir.exists() || urlList.isEmpty()) {
0102         return {};
0103     }
0104 
0105     QSettings settings(m_settingsPath + QL1S("/greasemonkey/requires/requires.ini"), QSettings::IniFormat);
0106     settings.beginGroup(QSL("Files"));
0107 
0108     QString script;
0109 
0110     for (const QString &url : urlList) {
0111         if (settings.contains(url)) {
0112             QString fileName = settings.value(url).toString();
0113             if (!QFileInfo(fileName).isAbsolute()) {
0114                 fileName = m_settingsPath + QL1S("/greasemonkey/requires/") + fileName;
0115             }
0116             const QString data = QzTools::readAllFileContents(fileName).trimmed();
0117             if (!data.isEmpty()) {
0118                 script.append(data + QL1C('\n'));
0119             }
0120         }
0121     }
0122 
0123     return script;
0124 }
0125 
0126 QString GM_Manager::bootstrapScript() const
0127 {
0128     return m_bootstrapScript;
0129 }
0130 
0131 QString GM_Manager::valuesScript() const
0132 {
0133     return m_valuesScript;
0134 }
0135 
0136 void GM_Manager::unloadPlugin()
0137 {
0138     // Save settings
0139     QSettings settings(m_settingsPath + QSL("/extensions.ini"), QSettings::IniFormat);
0140     settings.beginGroup(QSL("GreaseMonkey"));
0141     settings.setValue(QSL("disabledScripts"), m_disabledScripts);
0142     settings.endGroup();
0143 
0144     delete m_settings.data();
0145 
0146     // Remove icons from all windows
0147     QHashIterator<BrowserWindow*, GM_Icon*> it(m_windows);
0148     while (it.hasNext()) {
0149         it.next();
0150         mainWindowDeleted(it.key());
0151     }
0152 }
0153 
0154 QList<GM_Script*> GM_Manager::allScripts() const
0155 {
0156     return m_scripts;
0157 }
0158 
0159 bool GM_Manager::containsScript(const QString &fullName) const
0160 {
0161     for (GM_Script* script : std::as_const(m_scripts)) {
0162         if (fullName == script->fullName()) {
0163             return true;
0164         }
0165     }
0166 
0167     return false;
0168 }
0169 
0170 void GM_Manager::enableScript(GM_Script* script)
0171 {
0172     script->setEnabled(true);
0173     m_disabledScripts.removeOne(script->fullName());
0174 
0175     QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
0176     collection->insert(script->webScript());
0177 }
0178 
0179 void GM_Manager::disableScript(GM_Script* script)
0180 {
0181     script->setEnabled(false);
0182     m_disabledScripts.append(script->fullName());
0183 
0184     QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
0185     for (const auto &script : collection->find(script->fullName())) {
0186         collection->remove(script);
0187     }
0188 }
0189 
0190 bool GM_Manager::addScript(GM_Script* script)
0191 {
0192     if (!script || !script->isValid()) {
0193         return false;
0194     }
0195 
0196     m_scripts.append(script);
0197     connect(script, &GM_Script::scriptChanged, this, &GM_Manager::scriptChanged);
0198 
0199     QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
0200     collection->insert(script->webScript());
0201 
0202     Q_EMIT scriptsChanged();
0203     return true;
0204 }
0205 
0206 bool GM_Manager::removeScript(GM_Script* script, bool removeFile)
0207 {
0208     if (!script) {
0209         return false;
0210     }
0211 
0212     m_scripts.removeOne(script);
0213 
0214     QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
0215     for (const auto &script : collection->find(script->fullName())) {
0216         collection->remove(script);
0217     }
0218 
0219     m_disabledScripts.removeOne(script->fullName());
0220 
0221     if (removeFile) {
0222         QFile::remove(script->fileName());
0223         delete script;
0224     }
0225 
0226     Q_EMIT scriptsChanged();
0227     return true;
0228 }
0229 
0230 void GM_Manager::showNotification(const QString &message, const QString &title)
0231 {
0232     QIcon icon(QSL(":gm/data/icon.svg"));
0233 
0234     mApp->desktopNotifications()->showNotification(icon.pixmap(48), title.isEmpty() ? tr("GreaseMonkey") : title, message);
0235 }
0236 
0237 void GM_Manager::load()
0238 {
0239     QDir gmDir(m_settingsPath + QL1S("/greasemonkey"));
0240     if (!gmDir.exists()) {
0241         gmDir.mkdir(m_settingsPath + QL1S("/greasemonkey"));
0242     }
0243 
0244     if (!gmDir.exists(QSL("requires"))) {
0245         gmDir.mkdir(QSL("requires"));
0246     }
0247 
0248     m_bootstrapScript = QzTools::readAllFileContents(QSL(":gm/data/bootstrap.min.js"));
0249     m_valuesScript = QzTools::readAllFileContents(QSL(":gm/data/values.min.js"));
0250 
0251     QSettings settings(m_settingsPath + QL1S("/extensions.ini"), QSettings::IniFormat);
0252     settings.beginGroup(QSL("GreaseMonkey"));
0253     m_disabledScripts = settings.value(QSL("disabledScripts"), QStringList()).toStringList();
0254 
0255     const auto fileNames = gmDir.entryList(QStringList(QSL("*.js")), QDir::Files);
0256     for (const QString &fileName : fileNames) {
0257         const QString absolutePath = gmDir.absoluteFilePath(fileName);
0258         auto* script = new GM_Script(this, absolutePath);
0259 
0260         if (!script->isValid()) {
0261             delete script;
0262             continue;
0263         }
0264 
0265         m_scripts.append(script);
0266 
0267         if (m_disabledScripts.contains(script->fullName())) {
0268             script->setEnabled(false);
0269         }
0270         else {
0271             mApp->webProfile()->scripts()->insert(script->webScript());
0272         }
0273     }
0274 
0275     m_jsObject->setSettingsFile(m_settingsPath + QSL("/greasemonkey/values.ini"));
0276     ExternalJsObject::registerExtraObject(QSL("greasemonkey"), m_jsObject);
0277 }
0278 
0279 void GM_Manager::scriptChanged()
0280 {
0281     auto *script = qobject_cast<GM_Script*>(sender());
0282     if (!script)
0283         return;
0284 
0285     QWebEngineScriptCollection *collection = mApp->webProfile()->scripts();
0286     for (const auto &script : collection->find(script->fullName())) {
0287         collection->remove(script);
0288     }
0289     collection->insert(script->webScript());
0290 }
0291 
0292 bool GM_Manager::canRunOnScheme(const QString &scheme)
0293 {
0294     return (scheme == QLatin1String("http") || scheme == QLatin1String("https")
0295             || scheme == QLatin1String("data") || scheme == QLatin1String("ftp"));
0296 }
0297 
0298 void GM_Manager::mainWindowCreated(BrowserWindow* window)
0299 {
0300     auto *icon = new GM_Icon(this);
0301     window->statusBar()->addButton(icon);
0302     window->navigationBar()->addToolButton(icon);
0303     m_windows[window] = icon;
0304 }
0305 
0306 void GM_Manager::mainWindowDeleted(BrowserWindow* window)
0307 {
0308     GM_Icon *icon = m_windows.take(window);
0309     window->statusBar()->removeButton(icon);
0310     window->navigationBar()->removeToolButton(icon);
0311     delete icon;
0312 }