From 166fe13747c8ac36c3c23739f7ee9d1e748447a4 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Wed, 19 Apr 2017 15:41:21 +0200 Subject: [PATCH] GreaseMonkey: Make sure to download all require scripts when userscript changes --- src/plugins/GreaseMonkey/gm_downloader.cpp | 153 ++++++++++----------- src/plugins/GreaseMonkey/gm_downloader.h | 12 +- src/plugins/GreaseMonkey/gm_manager.cpp | 5 +- src/plugins/GreaseMonkey/gm_script.cpp | 40 ++++-- src/plugins/GreaseMonkey/gm_script.h | 7 +- 5 files changed, 118 insertions(+), 99 deletions(-) diff --git a/src/plugins/GreaseMonkey/gm_downloader.cpp b/src/plugins/GreaseMonkey/gm_downloader.cpp index 4721f8da2..9dee07e8e 100644 --- a/src/plugins/GreaseMonkey/gm_downloader.cpp +++ b/src/plugins/GreaseMonkey/gm_downloader.cpp @@ -29,12 +29,16 @@ #include #include -GM_Downloader::GM_Downloader(const QUrl &url, GM_Manager* manager) +GM_Downloader::GM_Downloader(const QUrl &url, GM_Manager *manager, Mode mode) : QObject() , m_manager(manager) { m_reply = mApp->networkManager()->get(QNetworkRequest(url)); - connect(m_reply, &QNetworkReply::finished, this, &GM_Downloader::scriptDownloaded); + if (mode == DownloadMainScript) { + connect(m_reply, &QNetworkReply::finished, this, &GM_Downloader::scriptDownloaded); + } else { + connect(m_reply, &QNetworkReply::finished, this, &GM_Downloader::requireDownloaded); + } } void GM_Downloader::updateScript(const QString &fileName) @@ -44,72 +48,76 @@ void GM_Downloader::updateScript(const QString &fileName) void GM_Downloader::scriptDownloaded() { - if (m_reply != qobject_cast(sender())) { - emit error(); - deleteLater(); - return; - } + Q_ASSERT(m_reply == qobject_cast(sender())); + + deleteLater(); + m_reply->deleteLater(); if (m_reply->error() != QNetworkReply::NoError) { qWarning() << "GreaseMonkey: Cannot download script" << m_reply->errorString(); - } - else { - const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); - - if (response.contains(QByteArray("// ==UserScript=="))) { - if (m_fileName.isEmpty()) { - const QString filePath = QString("%1/%2").arg(m_manager->scriptsDirectory(), QzTools::getFileNameFromUrl(m_reply->url())); - m_fileName = QzTools::ensureUniqueFilename(filePath); - } - QFile file(m_fileName); - - if (!file.open(QFile::WriteOnly)) { - qWarning() << "GreaseMonkey: Cannot open file for writing" << m_fileName; - emit error(); - deleteLater(); - return; - } - - file.write(response); - file.close(); - - QSettings settings(m_manager->settinsPath() + QL1S("/greasemonkey/requires/requires.ini"), QSettings::IniFormat); - settings.beginGroup("Files"); - - QzRegExp rx("@require(.*)\\n"); - rx.setMinimal(true); - rx.indexIn(response); - - for (int i = 1; i <= rx.captureCount(); ++i) { - const QString url = rx.cap(i).trimmed(); - if (!url.isEmpty() && !settings.contains(url)) { - m_requireUrls.append(QUrl(url)); - } - } - } + emit error(); + return; } - m_reply->deleteLater(); - m_reply = 0; + const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); - downloadRequires(); + if (!response.contains(QByteArray("// ==UserScript=="))) { + qWarning() << "GreaseMonkey: Script does not contain UserScript header" << m_reply->request().url(); + emit error(); + return; + } + + if (m_fileName.isEmpty()) { + const QString filePath = QString("%1/%2").arg(m_manager->scriptsDirectory(), QzTools::getFileNameFromUrl(m_reply->url())); + m_fileName = QzTools::ensureUniqueFilename(filePath); + } + + QFile file(m_fileName); + + if (!file.open(QFile::WriteOnly)) { + qWarning() << "GreaseMonkey: Cannot open file for writing" << m_fileName; + emit error(); + return; + } + + file.write(response); + file.close(); + + emit finished(m_fileName); } void GM_Downloader::requireDownloaded() { + Q_ASSERT(m_reply == qobject_cast(sender())); + + deleteLater(); + m_reply->deleteLater(); + if (m_reply != qobject_cast(sender())) { emit error(); - deleteLater(); return; } if (m_reply->error() != QNetworkReply::NoError) { qWarning() << "GreaseMonkey: Cannot download require script" << m_reply->errorString(); + emit error(); + return; } - else { - const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); - if (!response.isEmpty()) { + const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); + + if (response.isEmpty()) { + qWarning() << "GreaseMonkey: Empty script downloaded" << m_reply->request().url(); + emit error(); + return; + } + + QSettings settings(m_manager->settinsPath() + QL1S("/greasemonkey/requires/requires.ini"), QSettings::IniFormat); + settings.beginGroup("Files"); + + if (m_fileName.isEmpty()) { + m_fileName = settings.value(m_reply->request().url().toString()).toString(); + if (m_fileName.isEmpty()) { QString name = QFileInfo(m_reply->request().url().path()).fileName(); if (name.isEmpty()) { name = QSL("require.js"); @@ -117,40 +125,25 @@ void GM_Downloader::requireDownloaded() name.append(QSL(".js")); } const QString filePath = m_manager->settinsPath() + QL1S("/greasemonkey/requires/") + name; - const QString fileName = QzTools::ensureUniqueFilename(filePath, "%1"); - - QFile file(fileName); - - if (!file.open(QFile::WriteOnly)) { - qWarning() << "GreaseMonkey: Cannot open file for writing" << fileName; - emit error(); - deleteLater(); - return; - } - - file.write(response); - file.close(); - - QSettings settings(m_manager->settinsPath() + QL1S("/greasemonkey/requires/requires.ini"), QSettings::IniFormat); - settings.beginGroup("Files"); - settings.setValue(m_reply->request().url().toString(), QFileInfo(fileName).fileName()); + m_fileName = QzTools::ensureUniqueFilename(filePath, "%1"); + } + if (!QFileInfo(m_fileName).isAbsolute()) { + m_fileName.prepend(m_manager->settinsPath() + QL1S("/greasemonkey/requires/")); } } - m_reply->deleteLater(); - m_reply = 0; + QFile file(m_fileName); - downloadRequires(); -} + if (!file.open(QFile::WriteOnly)) { + qWarning() << "GreaseMonkey: Cannot open file for writing" << m_fileName; + emit error(); + return; + } -void GM_Downloader::downloadRequires() -{ - if (!m_requireUrls.isEmpty()) { - m_reply = mApp->networkManager()->get(QNetworkRequest(m_requireUrls.takeFirst())); - connect(m_reply, &QNetworkReply::finished, this, &GM_Downloader::requireDownloaded); - } - else { - emit finished(m_fileName); - deleteLater(); - } + file.write(response); + file.close(); + + settings.setValue(m_reply->request().url().toString(), QFileInfo(m_fileName).fileName()); + + emit finished(m_fileName); } diff --git a/src/plugins/GreaseMonkey/gm_downloader.h b/src/plugins/GreaseMonkey/gm_downloader.h index 88e209b5f..ca3e9e84e 100644 --- a/src/plugins/GreaseMonkey/gm_downloader.h +++ b/src/plugins/GreaseMonkey/gm_downloader.h @@ -1,6 +1,6 @@ /* ============================================================ * GreaseMonkey plugin for QupZilla -* Copyright (C) 2012-2016 David Rosca +* Copyright (C) 2012-2017 David Rosca * * 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 @@ -30,7 +30,12 @@ class GM_Downloader : public QObject { Q_OBJECT public: - explicit GM_Downloader(const QUrl &url, GM_Manager* manager); + enum Mode { + DownloadMainScript, + DownloadRequireScript + }; + + explicit GM_Downloader(const QUrl &url, GM_Manager *manager, Mode mode = DownloadMainScript); void updateScript(const QString& fileName); @@ -47,10 +52,7 @@ private: GM_Manager* m_manager; QNetworkReply *m_reply; - QString m_fileName; - QList m_requireUrls; - }; #endif // GM_DOWNLOADER_H diff --git a/src/plugins/GreaseMonkey/gm_manager.cpp b/src/plugins/GreaseMonkey/gm_manager.cpp index bf67fa651..34178a9a5 100644 --- a/src/plugins/GreaseMonkey/gm_manager.cpp +++ b/src/plugins/GreaseMonkey/gm_manager.cpp @@ -86,7 +86,10 @@ QString GM_Manager::requireScripts(const QStringList &urlList) const if (!QFileInfo(fileName).isAbsolute()) { fileName = m_settingsPath + QL1S("/greasemonkey/requires/") + fileName; } - script.append(QzTools::readAllFileContents(fileName).trimmed() + '\n'); + const QString data = QzTools::readAllFileContents(fileName).trimmed(); + if (!data.isEmpty()) { + script.append(data + QL1C('\n')); + } } } diff --git a/src/plugins/GreaseMonkey/gm_script.cpp b/src/plugins/GreaseMonkey/gm_script.cpp index 2d3cee03d..c1beb0d4a 100644 --- a/src/plugins/GreaseMonkey/gm_script.cpp +++ b/src/plugins/GreaseMonkey/gm_script.cpp @@ -115,9 +115,9 @@ QStringList GM_Script::exclude() const return m_exclude; } -QString GM_Script::script() const +QStringList GM_Script::require() const { - return m_script; + return m_require; } QString GM_Script::fileName() const @@ -174,17 +174,13 @@ void GM_Script::updateScript() m_updating = false; emit updatingChanged(m_updating); }); + downloadRequires(); } void GM_Script::watchedFileChanged(const QString &file) { if (m_fileName == file) { - parseScript(); - - m_manager->removeScript(this, false); - m_manager->addScript(this); - - emit scriptChanged(); + reloadScript(); } } @@ -218,6 +214,7 @@ void GM_Script::parseScript() m_version.clear(); m_include.clear(); m_exclude.clear(); + m_require.clear(); m_downloadUrl.clear(); m_updateUrl.clear(); m_startAt = DocumentEnd; @@ -247,7 +244,6 @@ void GM_Script::parseScript() return; } - QStringList requireList; QzRegExp rxNL(QSL("(?:\\r\\n|[\\r\\n])")); const QStringList lines = metadataBlock.split(rxNL, QString::SkipEmptyParts); @@ -295,7 +291,7 @@ void GM_Script::parseScript() m_exclude.append(value); } else if (key == QLatin1String("@require")) { - requireList.append(value); + m_require.append(value); } else if (key == QLatin1String("@run-at")) { if (value == QLatin1String("document-end")) { @@ -335,6 +331,28 @@ void GM_Script::parseScript() "}" "delete __qz_includes;")).arg(toJavaScriptList(m_exclude), toJavaScriptList(m_include)); - m_script = QSL("(function(){%1\n%2\n%3\n%4\n})();").arg(runCheck, gmValues, m_manager->requireScripts(requireList), fileData); + m_script = QSL("(function(){%1\n%2\n%3\n%4\n})();").arg(runCheck, gmValues, m_manager->requireScripts(m_require), fileData); m_valid = true; + + downloadRequires(); +} + +void GM_Script::reloadScript() +{ + parseScript(); + + m_manager->removeScript(this, false); + m_manager->addScript(this); + + emit scriptChanged(); +} + +void GM_Script::downloadRequires() +{ + for (const QString &url : qAsConst(m_require)) { + if (m_manager->requireScripts({url}).isEmpty()) { + GM_Downloader *downloader = new GM_Downloader(QUrl(url), m_manager, GM_Downloader::DownloadRequireScript); + connect(downloader, &GM_Downloader::finished, this, &GM_Script::reloadScript); + } + } } diff --git a/src/plugins/GreaseMonkey/gm_script.h b/src/plugins/GreaseMonkey/gm_script.h index 2075e1340..013e9e904 100644 --- a/src/plugins/GreaseMonkey/gm_script.h +++ b/src/plugins/GreaseMonkey/gm_script.h @@ -1,6 +1,6 @@ /* ============================================================ * GreaseMonkey plugin for QupZilla -* Copyright (C) 2012-2016 David Rosca +* Copyright (C) 2012-2017 David Rosca * * 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 @@ -55,8 +55,8 @@ public: QStringList include() const; QStringList exclude() const; + QStringList require() const; - QString script() const; QString metaData() const; QString fileName() const; @@ -74,6 +74,8 @@ private slots: private: void parseScript(); + void reloadScript(); + void downloadRequires(); GM_Manager* m_manager; DelayedFileWatcher* m_fileWatcher; @@ -85,6 +87,7 @@ private: QStringList m_include; QStringList m_exclude; + QStringList m_require; QUrl m_downloadUrl; QUrl m_updateUrl;