From c1b23b6f6dd651feed9824678bf23b226052a0d2 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Thu, 27 Jul 2017 08:18:25 +0200 Subject: [PATCH] Added custom adblocked page. --- resources/skins/dark/html_adblocked.html | 11 + resources/skins/vergilius/html_adblocked.html | 11 + rssguard.pro | 8 +- src/definitions/definitions.h | 1 + src/gui/dialogs/formmain.cpp | 1 - src/miscellaneous/application.cpp | 5 + src/miscellaneous/skinfactory.cpp | 214 +++++++++--------- src/miscellaneous/skinfactory.h | 3 + src/network-web/adblock/adblockmanager.cpp | 16 +- .../adblock/adblockurlinterceptor.cpp | 4 +- src/network-web/rssguardschemehandler.cpp | 65 ++++++ src/network-web/rssguardschemehandler.h | 42 ++++ src/network-web/webpage.cpp | 1 - 13 files changed, 266 insertions(+), 116 deletions(-) create mode 100755 resources/skins/dark/html_adblocked.html create mode 100755 resources/skins/vergilius/html_adblocked.html create mode 100755 src/network-web/rssguardschemehandler.cpp create mode 100755 src/network-web/rssguardschemehandler.h diff --git a/resources/skins/dark/html_adblocked.html b/resources/skins/dark/html_adblocked.html new file mode 100755 index 000000000..3cb71403b --- /dev/null +++ b/resources/skins/dark/html_adblocked.html @@ -0,0 +1,11 @@ + +
+
+ + + %2 +
+
diff --git a/resources/skins/vergilius/html_adblocked.html b/resources/skins/vergilius/html_adblocked.html new file mode 100755 index 000000000..3cb71403b --- /dev/null +++ b/resources/skins/vergilius/html_adblocked.html @@ -0,0 +1,11 @@ + +
+
+ + + %2 +
+
diff --git a/rssguard.pro b/rssguard.pro index 4790468f6..d44382d29 100755 --- a/rssguard.pro +++ b/rssguard.pro @@ -498,16 +498,18 @@ equals(USE_WEBENGINE, true) { src/gui/webbrowser.h \ src/gui/discoverfeedsbutton.h \ src/network-web/googlesuggest.h \ - src/network-web/webpage.h + src/network-web/webpage.h \ + src/network-web/rssguardschemehandler.h SOURCES += src/gui/locationlineedit.cpp \ src/gui/webviewer.cpp \ src/gui/webbrowser.cpp \ src/gui/discoverfeedsbutton.cpp \ src/network-web/googlesuggest.cpp \ - src/network-web/webpage.cpp + src/network-web/webpage.cpp \ + src/network-web/rssguardschemehandler.cpp - # Add Ad-Block sources. + # Add AdBlock sources. HEADERS += src/network-web/adblock/adblockaddsubscriptiondialog.h \ src/network-web/adblock/adblockdialog.h \ src/network-web/adblock/adblockicon.h \ diff --git a/src/definitions/definitions.h b/src/definitions/definitions.h index 822ce60e3..b96d193c3 100755 --- a/src/definitions/definitions.h +++ b/src/definitions/definitions.h @@ -26,6 +26,7 @@ #define ARGUMENTS_LIST_SEPARATOR "\n" +#define ADBLOCK_ADBLOCKED_PAGE "adblockedpage" #define ADBLOCK_HOWTO_FILTERS "http://adblockplus.org/en/filters" #define ADBLOCK_UPDATE_DAYS_INTERVAL 5 #define ADBLOCK_ICON_ACTIVE "adblock" diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp index d1a2be5c7..9b27cf6d1 100755 --- a/src/gui/dialogs/formmain.cpp +++ b/src/gui/dialogs/formmain.cpp @@ -73,7 +73,6 @@ FormMain::FormMain(QWidget* parent, Qt::WindowFlags f) prepareMenus(); // Prepare tabs. - //m_ui->m_tabWidget->initializeTabs(); tabWidget()->feedMessageViewer()->feedsToolBar()->loadSavedActions(); tabWidget()->feedMessageViewer()->messagesToolBar()->loadSavedActions(); diff --git a/src/miscellaneous/application.cpp b/src/miscellaneous/application.cpp index 5b3eaea06..04328ac47 100755 --- a/src/miscellaneous/application.cpp +++ b/src/miscellaneous/application.cpp @@ -42,6 +42,7 @@ #include "network-web/networkurlinterceptor.h" #include "network-web/adblock/adblockicon.h" #include "network-web/adblock/adblockmanager.h" +#include "network-web/rssguardschemehandler.h" #include #include @@ -71,6 +72,10 @@ Application::Application(const QString& id, int& argc, char** argv) // TODO: Call load settings when saving app settings from dialog. // Will need add that if I add more settings in the future. m_urlInterceptor->loadSettings(); + + QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( + QByteArray(APP_LOW_NAME), + new RssGuardSchemeHandler(QWebEngineProfile::defaultProfile())); #endif } diff --git a/src/miscellaneous/skinfactory.cpp b/src/miscellaneous/skinfactory.cpp index e5c7945bf..2a08e51dd 100755 --- a/src/miscellaneous/skinfactory.cpp +++ b/src/miscellaneous/skinfactory.cpp @@ -32,140 +32,150 @@ SkinFactory::~SkinFactory() { } void SkinFactory::loadCurrentSkin() { - QList skin_names_to_try; - skin_names_to_try.append(selectedSkinName()); - skin_names_to_try.append(APP_SKIN_DEFAULT); - bool skin_parsed; - Skin skin_data; - QString skin_name; + QList skin_names_to_try; + skin_names_to_try.append(selectedSkinName()); + skin_names_to_try.append(APP_SKIN_DEFAULT); + bool skin_parsed; + Skin skin_data; + QString skin_name; - while (!skin_names_to_try.isEmpty()) { - skin_name = skin_names_to_try.takeFirst(); - skin_data = skinInfo(skin_name, &skin_parsed); + while (!skin_names_to_try.isEmpty()) { + skin_name = skin_names_to_try.takeFirst(); + skin_data = skinInfo(skin_name, &skin_parsed); - if (skin_parsed) { - loadSkinFromData(skin_data); - // Set this 'Skin' object as active one. - m_currentSkin = skin_data; - qDebug("Skin '%s' loaded.", qPrintable(skin_name)); - return; - } + if (skin_parsed) { + loadSkinFromData(skin_data); + // Set this 'Skin' object as active one. + m_currentSkin = skin_data; + qDebug("Skin '%s' loaded.", qPrintable(skin_name)); + return; + } - else { - qWarning("Failed to load skin '%s'.", qPrintable(skin_name)); - } - } + else { + qWarning("Failed to load skin '%s'.", qPrintable(skin_name)); + } + } - qCritical("Failed to load selected or default skin. Quitting!"); + qCritical("Failed to load selected or default skin. Quitting!"); } void SkinFactory::loadSkinFromData(const Skin& skin) { - if (!skin.m_rawData.isEmpty()) { - qApp->setStyleSheet(skin.m_rawData); - } + if (!skin.m_rawData.isEmpty()) { + qApp->setStyleSheet(skin.m_rawData); + } - qApp->setStyle(qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString()); + qApp->setStyle(qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString()); } void SkinFactory::setCurrentSkinName(const QString& skin_name) { - qApp->settings()->setValue(GROUP(GUI), GUI::Skin, skin_name); + qApp->settings()->setValue(GROUP(GUI), GUI::Skin, skin_name); } QString SkinFactory::getUserSkinBaseFolder() const { - return qApp->getUserDataPath() + QDir::separator() + APP_SKIN_USER_FOLDER; + return qApp->getUserDataPath() + QDir::separator() + APP_SKIN_USER_FOLDER; } QString SkinFactory::selectedSkinName() const { - return qApp->settings()->value(GROUP(GUI), SETTING(GUI::Skin)).toString(); + return qApp->settings()->value(GROUP(GUI), SETTING(GUI::Skin)).toString(); +} + +QString SkinFactory::adBlockedPage(const QString& subscription, const QString& rule) { + const QString& adblocked = currentSkin().m_adblocked.arg(tr("This page was blocked by AdBlock"), + tr("Blocked by set: \"%1\"
Blocked by filter: \"%2\"") + .arg(subscription, + rule)); + + return currentSkin().m_layoutMarkupWrapper.arg(tr("This page was blocked by AdBlock"), adblocked); } Skin SkinFactory::skinInfo(const QString& skin_name, bool* ok) const { - Skin skin; - QStringList base_skin_folders; - base_skin_folders.append(APP_SKIN_PATH); - base_skin_folders.append(getUserSkinBaseFolder()); + Skin skin; + QStringList base_skin_folders; + base_skin_folders.append(APP_SKIN_PATH); + base_skin_folders.append(getUserSkinBaseFolder()); - while (!base_skin_folders.isEmpty()) { - const QString skin_folder = base_skin_folders.takeAt(0) + QDir::separator() + skin_name + QDir::separator(); - const QString metadata_file = skin_folder + APP_SKIN_METADATA_FILE; + while (!base_skin_folders.isEmpty()) { + const QString skin_folder = base_skin_folders.takeAt(0) + QDir::separator() + skin_name + QDir::separator(); + const QString metadata_file = skin_folder + APP_SKIN_METADATA_FILE; - if (QFile::exists(metadata_file)) { - QFile skin_file(metadata_file); - QDomDocument dokument; + if (QFile::exists(metadata_file)) { + QFile skin_file(metadata_file); + QDomDocument dokument; - if (!skin_file.open(QIODevice::Text | QIODevice::ReadOnly) || !dokument.setContent(&skin_file, true)) { - if (ok) { - *ok = false; - } + if (!skin_file.open(QIODevice::Text | QIODevice::ReadOnly) || !dokument.setContent(&skin_file, true)) { + if (ok) { + *ok = false; + } - return skin; - } + return skin; + } - const QDomNode skin_node = dokument.namedItem(QSL("skin")); - // Obtain visible skin name. - skin.m_visibleName = skin_name; - // Obtain author. - skin.m_author = skin_node.namedItem(QSL("author")).namedItem(QSL("name")).toElement().text(); - // Obtain email. - skin.m_email = skin_node.namedItem(QSL("author")).namedItem(QSL("email")).toElement().text(); - // Obtain version. - skin.m_version = skin_node.attributes().namedItem(QSL("version")).toAttr().value(); - // Obtain other information. - skin.m_baseName = skin_name; - // Free resources. - skin_file.close(); - skin_file.deleteLater(); - // Here we use "/" instead of QDir::separator() because CSS2.1 url field - // accepts '/' as path elements separator. - // - // "##" is placeholder for the actual path to skin file. This is needed for using - // images within the QSS file. - // So if one uses "##/images/border.png" in QSS then it is - // replaced by fully absolute path and target file can - // be safely loaded. - skin.m_layoutMarkupWrapper = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_wrapper.html"))); - skin.m_layoutMarkupWrapper = skin.m_layoutMarkupWrapper.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - skin.m_enclosureImageMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_image.html"))); - skin.m_enclosureImageMarkup = skin.m_enclosureImageMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - skin.m_layoutMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_single_message.html"))); - skin.m_layoutMarkup = skin.m_layoutMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - skin.m_enclosureMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_every.html"))); - skin.m_enclosureMarkup = skin.m_enclosureMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - skin.m_rawData = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("theme.css"))); - skin.m_rawData = skin.m_rawData.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + const QDomNode skin_node = dokument.namedItem(QSL("skin")); + // Obtain visible skin name. + skin.m_visibleName = skin_name; + // Obtain author. + skin.m_author = skin_node.namedItem(QSL("author")).namedItem(QSL("name")).toElement().text(); + // Obtain email. + skin.m_email = skin_node.namedItem(QSL("author")).namedItem(QSL("email")).toElement().text(); + // Obtain version. + skin.m_version = skin_node.attributes().namedItem(QSL("version")).toAttr().value(); + // Obtain other information. + skin.m_baseName = skin_name; + // Free resources. + skin_file.close(); + skin_file.deleteLater(); + // Here we use "/" instead of QDir::separator() because CSS2.1 url field + // accepts '/' as path elements separator. + // + // "##" is placeholder for the actual path to skin file. This is needed for using + // images within the QSS file. + // So if one uses "##/images/border.png" in QSS then it is + // replaced by fully absolute path and target file can + // be safely loaded. + skin.m_layoutMarkupWrapper = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_wrapper.html"))); + skin.m_layoutMarkupWrapper = skin.m_layoutMarkupWrapper.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_enclosureImageMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_image.html"))); + skin.m_enclosureImageMarkup = skin.m_enclosureImageMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_layoutMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_single_message.html"))); + skin.m_layoutMarkup = skin.m_layoutMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_enclosureMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_every.html"))); + skin.m_enclosureMarkup = skin.m_enclosureMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_rawData = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("theme.css"))); + skin.m_rawData = skin.m_rawData.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_adblocked = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_adblocked.html"))); - if (ok != nullptr) { - *ok = !skin.m_author.isEmpty() && !skin.m_version.isEmpty() && - !skin.m_baseName.isEmpty() && !skin.m_email.isEmpty() && - !skin.m_layoutMarkup.isEmpty(); - } + if (ok != nullptr) { + *ok = !skin.m_author.isEmpty() && !skin.m_version.isEmpty() && + !skin.m_baseName.isEmpty() && !skin.m_email.isEmpty() && + !skin.m_layoutMarkup.isEmpty(); + } - break; - } - } + break; + } + } - return skin; + return skin; } QList SkinFactory::installedSkins() const { - QList skins; - bool skin_load_ok; - QStringList skin_directories = QDir(APP_SKIN_PATH).entryList(QDir::Dirs | - QDir::NoDotAndDotDot | - QDir::NoSymLinks | - QDir::Readable); - skin_directories.append(QDir(getUserSkinBaseFolder()).entryList(QDir::Dirs | - QDir::NoDotAndDotDot | - QDir::NoSymLinks | - QDir::Readable)); + QList skins; + bool skin_load_ok; + QStringList skin_directories = QDir(APP_SKIN_PATH).entryList(QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks | + QDir::Readable); + skin_directories.append(QDir(getUserSkinBaseFolder()).entryList(QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks | + QDir::Readable)); - foreach (const QString& base_directory, skin_directories) { - const Skin skin_info = skinInfo(base_directory, &skin_load_ok); + foreach (const QString& base_directory, skin_directories) { + const Skin skin_info = skinInfo(base_directory, &skin_load_ok); - if (skin_load_ok) { - skins.append(skin_info); - } - } + if (skin_load_ok) { + skins.append(skin_info); + } + } - return skins; + return skins; } diff --git a/src/miscellaneous/skinfactory.h b/src/miscellaneous/skinfactory.h index 8bb5f5261..7196d9b62 100755 --- a/src/miscellaneous/skinfactory.h +++ b/src/miscellaneous/skinfactory.h @@ -31,6 +31,7 @@ struct Skin { QString m_email; QString m_version; QString m_rawData; + QString m_adblocked; QString m_layoutMarkupWrapper; QString m_enclosureImageMarkup; QString m_layoutMarkup; @@ -60,6 +61,8 @@ class SkinFactory : public QObject { // after application restart. QString selectedSkinName() const; + QString adBlockedPage(const QString& subscription, const QString& rule); + // Gets skin about a particular skin. Skin skinInfo(const QString& skin_name, bool* ok = nullptr) const; diff --git a/src/network-web/adblock/adblockmanager.cpp b/src/network-web/adblock/adblockmanager.cpp index acd1835c4..01d5e816f 100755 --- a/src/network-web/adblock/adblockmanager.cpp +++ b/src/network-web/adblock/adblockmanager.cpp @@ -103,16 +103,20 @@ bool AdBlockManager::block(QWebEngineUrlRequestInfo& request) { const AdBlockRule* blockedRule = m_matcher->match(request, urlDomain, urlString); if (blockedRule) { - res = true; - if (request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) { - // NOTE: We are blocking main URL frame, we can display "AdBlock error page" or - // redirect to somewhere. - request.block(true); + QUrlQuery query; + QUrl url(QSL("rssguard:adblockedpage")); + + query.addQueryItem(QSL("rule"), blockedRule->filter()); + query.addQueryItem(QSL("subscription"), blockedRule->subscription()->title()); + url.setQuery(query); + + request.redirect(url); } else { - request.block(true); + res = true; + request.block(true); } } diff --git a/src/network-web/adblock/adblockurlinterceptor.cpp b/src/network-web/adblock/adblockurlinterceptor.cpp index d00fab6e7..3b8ff8867 100755 --- a/src/network-web/adblock/adblockurlinterceptor.cpp +++ b/src/network-web/adblock/adblockurlinterceptor.cpp @@ -26,7 +26,5 @@ AdBlockUrlInterceptor::AdBlockUrlInterceptor(AdBlockManager* manager) } void AdBlockUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { - if (m_manager->block(info)) { - info.block(true); - } + info.block(m_manager->block(info)); } diff --git a/src/network-web/rssguardschemehandler.cpp b/src/network-web/rssguardschemehandler.cpp new file mode 100755 index 000000000..6d37249f1 --- /dev/null +++ b/src/network-web/rssguardschemehandler.cpp @@ -0,0 +1,65 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// Copyright (C) 2010-2014 by David Rosca +// +// RSS Guard 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. +// +// RSS Guard 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 RSS Guard. If not, see . + +#include "network-web/rssguardschemehandler.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/skinfactory.h" + +#include +#include +#include + + +RssGuardSchemeHandler::RssGuardSchemeHandler(QObject* parent) : QWebEngineUrlSchemeHandler(parent) { +} + +RssGuardSchemeHandler::~RssGuardSchemeHandler() { +} + +void RssGuardSchemeHandler::requestStarted(QWebEngineUrlRequestJob* job) { + // Decide which data we want. + QByteArray data = targetData(job->requestUrl()); + + if (data.isEmpty()) { + job->fail(QWebEngineUrlRequestJob::UrlNotFound); + } + else { + QBuffer* buf = new QBuffer(job); + buf->setData(data); + + job->reply(QByteArray("text/html"), buf); + } +} + +QByteArray RssGuardSchemeHandler::targetData(const QUrl& url) { + const QString& url_string = url.toString(); + + if (url_string.contains(QSL(ADBLOCK_ADBLOCKED_PAGE))) { + QUrlQuery query(url); + + const QString& subscription = query.queryItemValue(QSL("subscription")); + const QString& rule = query.queryItemValue(QSL("rule")); + + return qApp->skins()->adBlockedPage(subscription, rule).toUtf8(); + } + else { + return QByteArray(); + } +} diff --git a/src/network-web/rssguardschemehandler.h b/src/network-web/rssguardschemehandler.h new file mode 100755 index 000000000..918bc3cd8 --- /dev/null +++ b/src/network-web/rssguardschemehandler.h @@ -0,0 +1,42 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// Copyright (C) 2010-2014 by David Rosca +// +// RSS Guard 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. +// +// RSS Guard 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 RSS Guard. If not, see . + +#ifndef RSSGUARDSCHEMEHANDLER_H +#define RSSGUARDSCHEMEHANDLER_H + +#include +#include + + +class QWebEngineUrlRequestJob; +class QBuffer; + +class RssGuardSchemeHandler : public QWebEngineUrlSchemeHandler { + Q_OBJECT + + public: + explicit RssGuardSchemeHandler(QObject* parent = nullptr); + virtual ~RssGuardSchemeHandler(); + + void requestStarted(QWebEngineUrlRequestJob* job); + + private: + QByteArray targetData(const QUrl& url); +}; + +#endif // RSSGUARDSCHEMEHANDLER_H diff --git a/src/network-web/webpage.cpp b/src/network-web/webpage.cpp index eebd64a88..ac4e32219 100755 --- a/src/network-web/webpage.cpp +++ b/src/network-web/webpage.cpp @@ -70,7 +70,6 @@ bool WebPage::acceptNavigationRequest(const QUrl& url, NavigationType type, bool setHtml(view()->messageContents(), QUrl(INTERNAL_URL_MESSAGE)); return true; } - else { return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); }