diff --git a/src/lib/adblock/adblockmanager.cpp b/src/lib/adblock/adblockmanager.cpp index 00d12553..f88822bb 100644 --- a/src/lib/adblock/adblockmanager.cpp +++ b/src/lib/adblock/adblockmanager.cpp @@ -1,459 +1,459 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 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 * 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 "adblockmanager.h" #include "adblockdialog.h" #include "adblockmatcher.h" #include "adblocksubscription.h" #include "adblockurlinterceptor.h" #include "datapaths.h" #include "mainapplication.h" #include "webpage.h" #include "qztools.h" #include "browserwindow.h" #include "settings.h" #include "networkmanager.h" #include #include #include #include #include #include #include #include #include //#define ADBLOCK_DEBUG #ifdef ADBLOCK_DEBUG #include #endif Q_GLOBAL_STATIC(AdBlockManager, qz_adblock_manager) AdBlockManager::AdBlockManager(QObject* parent) : QObject(parent) , m_loaded(false) , m_enabled(true) , m_matcher(new AdBlockMatcher(this)) , m_interceptor(new AdBlockUrlInterceptor(this)) { qRegisterMetaType(); load(); } AdBlockManager::~AdBlockManager() { qDeleteAll(m_subscriptions); } AdBlockManager* AdBlockManager::instance() { return qz_adblock_manager(); } void AdBlockManager::setEnabled(bool enabled) { if (m_enabled == enabled) { return; } m_enabled = enabled; emit enabledChanged(enabled); Settings settings; settings.beginGroup("AdBlock"); settings.setValue("enabled", m_enabled); settings.endGroup(); load(); mApp->reloadUserStyleSheet(); QMutexLocker locker(&m_mutex); if (m_enabled) { m_matcher->update(); } else { m_matcher->clear(); } } QList AdBlockManager::subscriptions() const { return m_subscriptions; } bool AdBlockManager::block(QWebEngineUrlRequestInfo &request, QString &ruleFilter, QString &ruleSubscription) { QMutexLocker locker(&m_mutex); if (!isEnabled()) { return false; } #ifdef ADBLOCK_DEBUG QElapsedTimer timer; timer.start(); #endif const QString urlString = request.requestUrl().toEncoded().toLower(); const QString urlDomain = request.requestUrl().host().toLower(); const QString urlScheme = request.requestUrl().scheme().toLower(); if (!canRunOnScheme(urlScheme) || !canBeBlocked(request.firstPartyUrl())) { return false; } const AdBlockRule* blockedRule = m_matcher->match(request, urlDomain, urlString); if (blockedRule) { ruleFilter = blockedRule->filter(); ruleSubscription = blockedRule->subscription()->title(); #ifdef ADBLOCK_DEBUG qDebug() << "BLOCKED: " << timer.elapsed() << blockedRule->filter() << request.requestUrl(); #endif } #ifdef ADBLOCK_DEBUG qDebug() << timer.elapsed() << request.requestUrl(); #endif return blockedRule; } QVector AdBlockManager::blockedRequestsForUrl(const QUrl &url) const { return m_blockedRequests.value(url); } void AdBlockManager::clearBlockedRequestsForUrl(const QUrl &url) { if (m_blockedRequests.remove(url)) { emit blockedRequestsChanged(url); } } QStringList AdBlockManager::disabledRules() const { return m_disabledRules; } void AdBlockManager::addDisabledRule(const QString &filter) { m_disabledRules.append(filter); } void AdBlockManager::removeDisabledRule(const QString &filter) { m_disabledRules.removeOne(filter); } bool AdBlockManager::addSubscriptionFromUrl(const QUrl &url) { const QList > queryItems = QUrlQuery(url).queryItems(QUrl::FullyDecoded); QString subscriptionTitle; QString subscriptionUrl; for (int i = 0; i < queryItems.count(); ++i) { QPair pair = queryItems.at(i); if (pair.first == QL1S("location")) subscriptionUrl = pair.second; else if (pair.first == QL1S("title")) subscriptionTitle = pair.second; } if (subscriptionTitle.isEmpty() || subscriptionUrl.isEmpty()) return false; const QString message = AdBlockManager::tr("Do you want to add %1 subscription?").arg(subscriptionTitle); QMessageBox::StandardButton result = QMessageBox::question(0, AdBlockManager::tr("AdBlock Subscription"), message, QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::Yes) { AdBlockManager::instance()->addSubscription(subscriptionTitle, subscriptionUrl); AdBlockManager::instance()->showDialog(); } return true; } AdBlockSubscription* AdBlockManager::addSubscription(const QString &title, const QString &url) { if (title.isEmpty() || url.isEmpty()) { return 0; } QString fileName = QzTools::filterCharsFromFilename(title.toLower()) + ".txt"; QString filePath = QzTools::ensureUniqueFilename(DataPaths::currentProfilePath() + "/adblock/" + fileName); QByteArray data = QString("Title: %1\nUrl: %2\n[Adblock Plus 1.1.1]").arg(title, url).toLatin1(); QSaveFile file(filePath); if (!file.open(QFile::WriteOnly)) { qWarning() << "AdBlockManager: Cannot write to file" << filePath; return 0; } file.write(data); file.commit(); AdBlockSubscription* subscription = new AdBlockSubscription(title, this); subscription->setUrl(QUrl(url)); subscription->setFilePath(filePath); subscription->loadSubscription(m_disabledRules); m_subscriptions.insert(m_subscriptions.count() - 1, subscription); connect(subscription, SIGNAL(subscriptionUpdated()), mApp, SLOT(reloadUserStyleSheet())); connect(subscription, SIGNAL(subscriptionChanged()), this, SLOT(updateMatcher())); return subscription; } bool AdBlockManager::removeSubscription(AdBlockSubscription* subscription) { QMutexLocker locker(&m_mutex); if (!m_subscriptions.contains(subscription) || !subscription->canBeRemoved()) { return false; } QFile(subscription->filePath()).remove(); m_subscriptions.removeOne(subscription); m_matcher->update(); delete subscription; return true; } AdBlockCustomList* AdBlockManager::customList() const { foreach (AdBlockSubscription* subscription, m_subscriptions) { AdBlockCustomList* list = qobject_cast(subscription); if (list) { return list; } } return 0; } void AdBlockManager::load() { QMutexLocker locker(&m_mutex); if (m_loaded) { return; } #ifdef ADBLOCK_DEBUG QElapsedTimer timer; timer.start(); #endif Settings settings; settings.beginGroup("AdBlock"); m_enabled = settings.value("enabled", m_enabled).toBool(); m_disabledRules = settings.value("disabledRules", QStringList()).toStringList(); QDateTime lastUpdate = settings.value("lastUpdate", QDateTime()).toDateTime(); settings.endGroup(); if (!m_enabled) { return; } QDir adblockDir(DataPaths::currentProfilePath() + "/adblock"); // Create if neccessary if (!adblockDir.exists()) { QDir(DataPaths::currentProfilePath()).mkdir("adblock"); } foreach (const QString &fileName, adblockDir.entryList(QStringList("*.txt"), QDir::Files)) { if (fileName == QLatin1String("customlist.txt")) { continue; } const QString absolutePath = adblockDir.absoluteFilePath(fileName); QFile file(absolutePath); if (!file.open(QFile::ReadOnly)) { continue; } QTextStream textStream(&file); textStream.setCodec("UTF-8"); QString title = textStream.readLine(1024).remove(QLatin1String("Title: ")); QUrl url = QUrl(textStream.readLine(1024).remove(QLatin1String("Url: "))); if (title.isEmpty() || !url.isValid()) { qWarning() << "AdBlockManager: Invalid subscription file" << absolutePath; continue; } AdBlockSubscription* subscription = new AdBlockSubscription(title, this); subscription->setUrl(url); subscription->setFilePath(absolutePath); m_subscriptions.append(subscription); } // Add EasyList + NoCoinList if subscriptions are empty if (m_subscriptions.isEmpty()) { AdBlockSubscription *easyList = new AdBlockSubscription(tr("EasyList"), this); easyList->setUrl(QUrl(ADBLOCK_EASYLIST_URL)); easyList->setFilePath(DataPaths::currentProfilePath() + QLatin1String("/adblock/easylist.txt")); m_subscriptions.append(easyList); AdBlockSubscription *noCoinList = new AdBlockSubscription(tr("NoCoin List"), this); noCoinList->setUrl(QUrl(ADBLOCK_NOCOINLIST_URL)); noCoinList->setFilePath(DataPaths::currentProfilePath() + QLatin1String("/adblock/nocoinlist.txt")); m_subscriptions.append(noCoinList); } // Append CustomList AdBlockCustomList* customList = new AdBlockCustomList(this); m_subscriptions.append(customList); // Load all subscriptions foreach (AdBlockSubscription* subscription, m_subscriptions) { subscription->loadSubscription(m_disabledRules); connect(subscription, SIGNAL(subscriptionUpdated()), mApp, SLOT(reloadUserStyleSheet())); connect(subscription, SIGNAL(subscriptionChanged()), this, SLOT(updateMatcher())); } if (lastUpdate.addDays(5) < QDateTime::currentDateTime()) { QTimer::singleShot(1000 * 60, this, SLOT(updateAllSubscriptions())); } #ifdef ADBLOCK_DEBUG qDebug() << "AdBlock loaded in" << timer.elapsed(); #endif m_matcher->update(); m_loaded = true; connect(m_interceptor, &AdBlockUrlInterceptor::requestBlocked, this, [this](const AdBlockedRequest &request) { m_blockedRequests[request.firstPartyUrl].append(request); emit blockedRequestsChanged(request.firstPartyUrl); }); mApp->networkManager()->installUrlInterceptor(m_interceptor); } void AdBlockManager::updateMatcher() { QMutexLocker locker(&m_mutex); m_matcher->update(); } void AdBlockManager::updateAllSubscriptions() { foreach (AdBlockSubscription* subscription, m_subscriptions) { subscription->updateSubscription(); } Settings settings; settings.beginGroup("AdBlock"); settings.setValue("lastUpdate", QDateTime::currentDateTime()); settings.endGroup(); } void AdBlockManager::save() { if (!m_loaded) { return; } foreach (AdBlockSubscription* subscription, m_subscriptions) { subscription->saveSubscription(); } Settings settings; settings.beginGroup("AdBlock"); settings.setValue("enabled", m_enabled); settings.setValue("disabledRules", m_disabledRules); settings.endGroup(); } bool AdBlockManager::isEnabled() const { return m_enabled; } bool AdBlockManager::canRunOnScheme(const QString &scheme) const { return !(scheme == QL1S("file") || scheme == QL1S("qrc") || scheme == QL1S("view-source") || scheme == QL1S("falkon") || scheme == QL1S("data") || scheme == QL1S("abp")); } bool AdBlockManager::canBeBlocked(const QUrl &url) const { return !m_matcher->adBlockDisabledForUrl(url); } QString AdBlockManager::elementHidingRules(const QUrl &url) const { if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) return QString(); return m_matcher->elementHidingRules(); } QString AdBlockManager::elementHidingRulesForDomain(const QUrl &url) const { if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) return QString(); return m_matcher->elementHidingRulesForDomain(url.host()); } AdBlockSubscription* AdBlockManager::subscriptionByName(const QString &name) const { foreach (AdBlockSubscription* subscription, m_subscriptions) { if (subscription->title() == name) { return subscription; } } return 0; } -AdBlockDialog* AdBlockManager::showDialog() +AdBlockDialog *AdBlockManager::showDialog(QWidget *parent) { if (!m_adBlockDialog) { - m_adBlockDialog = new AdBlockDialog(mApp->getWindow()); + m_adBlockDialog = new AdBlockDialog(parent ? parent : mApp->getWindow()); } m_adBlockDialog.data()->show(); m_adBlockDialog.data()->raise(); m_adBlockDialog.data()->activateWindow(); return m_adBlockDialog.data(); } void AdBlockManager::showRule() { if (QAction* action = qobject_cast(sender())) { const AdBlockRule* rule = static_cast(action->data().value()); if (rule) { showDialog()->showRule(rule); } } } diff --git a/src/lib/adblock/adblockmanager.h b/src/lib/adblock/adblockmanager.h index f05e4750..3ee4a936 100644 --- a/src/lib/adblock/adblockmanager.h +++ b/src/lib/adblock/adblockmanager.h @@ -1,118 +1,118 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 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 * 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 . * ============================================================ */ #ifndef ADBLOCKMANAGER_H #define ADBLOCKMANAGER_H #include #include #include #include #include #include #include "qzcommon.h" #define ADBLOCK_EASYLIST_URL "https://easylist-downloads.adblockplus.org/easylist.txt" #define ADBLOCK_NOCOINLIST_URL "https://raw.githubusercontent.com/hoshsadiq/adblock-nocoin-list/master/nocoin.txt" class AdBlockRule; class AdBlockDialog; class AdBlockMatcher; class AdBlockCustomList; class AdBlockSubscription; class AdBlockUrlInterceptor; struct AdBlockedRequest { QUrl requestUrl; QUrl firstPartyUrl; QByteArray requestMethod; QWebEngineUrlRequestInfo::ResourceType resourceType; QWebEngineUrlRequestInfo::NavigationType navigationType; QString rule; }; Q_DECLARE_METATYPE(AdBlockedRequest) class FALKON_EXPORT AdBlockManager : public QObject { Q_OBJECT public: AdBlockManager(QObject* parent = 0); ~AdBlockManager(); void load(); void save(); bool isEnabled() const; bool canRunOnScheme(const QString &scheme) const; bool canBeBlocked(const QUrl &url) const; QString elementHidingRules(const QUrl &url) const; QString elementHidingRulesForDomain(const QUrl &url) const; AdBlockSubscription* subscriptionByName(const QString &name) const; QList subscriptions() const; bool block(QWebEngineUrlRequestInfo &request, QString &ruleFilter, QString &ruleSubscription); QVector blockedRequestsForUrl(const QUrl &url) const; void clearBlockedRequestsForUrl(const QUrl &url); QStringList disabledRules() const; void addDisabledRule(const QString &filter); void removeDisabledRule(const QString &filter); bool addSubscriptionFromUrl(const QUrl &url); AdBlockSubscription* addSubscription(const QString &title, const QString &url); bool removeSubscription(AdBlockSubscription* subscription); AdBlockCustomList* customList() const; static AdBlockManager* instance(); Q_SIGNALS: void enabledChanged(bool enabled); void blockedRequestsChanged(const QUrl &url); public Q_SLOTS: void setEnabled(bool enabled); void showRule(); void updateMatcher(); void updateAllSubscriptions(); - AdBlockDialog* showDialog(); + AdBlockDialog *showDialog(QWidget *parent = nullptr); private: bool m_loaded; bool m_enabled; QList m_subscriptions; AdBlockMatcher* m_matcher; QStringList m_disabledRules; AdBlockUrlInterceptor *m_interceptor; QPointer m_adBlockDialog; QMutex m_mutex; QHash> m_blockedRequests; }; #endif // ADBLOCKMANAGER_H diff --git a/src/lib/adblock/adblockplugin.cpp b/src/lib/adblock/adblockplugin.cpp index 66435a1c..0c6ec4d9 100644 --- a/src/lib/adblock/adblockplugin.cpp +++ b/src/lib/adblock/adblockplugin.cpp @@ -1,126 +1,131 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2018 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 * 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 "adblockplugin.h" #include "adblockmanager.h" #include "adblockicon.h" #include "scripts.h" #include "webpage.h" #include "pluginproxy.h" #include "browserwindow.h" #include "navigationbar.h" #include "mainapplication.h" #include "statusbar.h" #include "desktopfile.h" AdBlockPlugin::AdBlockPlugin() : QObject() { } DesktopFile AdBlockPlugin::metaData() const { return DesktopFile(QSL(":adblock/metadata.desktop")); } void AdBlockPlugin::init(InitState state, const QString &settingsPath) { Q_UNUSED(settingsPath) connect(mApp, &MainApplication::aboutToQuit, AdBlockManager::instance(), &AdBlockManager::save); connect(mApp->plugins(), &PluginProxy::webPageCreated, this, &AdBlockPlugin::webPageCreated); connect(mApp->plugins(), &PluginProxy::webPageDeleted, this, &AdBlockPlugin::webPageDeleted); connect(mApp->plugins(), &PluginProxy::mainWindowCreated, this, &AdBlockPlugin::mainWindowCreated); connect(mApp->plugins(), &PluginProxy::mainWindowDeleted, this, &AdBlockPlugin::mainWindowDeleted); if (state == LateInitState) { const auto windows = mApp->windows(); for (BrowserWindow *window : windows) { mainWindowCreated(window); } } } void AdBlockPlugin::unload() { const auto windows = mApp->windows(); for (BrowserWindow *window : windows) { mainWindowDeleted(window); } } bool AdBlockPlugin::testPlugin() { return true; } +void AdBlockPlugin::showSettings(QWidget *parent) +{ + AdBlockManager::instance()->showDialog(parent); +} + void AdBlockPlugin::webPageCreated(WebPage *page) { connect(page, &WebPage::loadFinished, this, [=]() { AdBlockManager *manager = AdBlockManager::instance(); if (!manager->isEnabled()) { return; } // Apply global element hiding rules const QString elementHiding = manager->elementHidingRules(page->url()); if (!elementHiding.isEmpty()) { page->runJavaScript(Scripts::setCss(elementHiding), WebPage::SafeJsWorld); } // Apply domain-specific element hiding rules const QString siteElementHiding = manager->elementHidingRulesForDomain(page->url()); if (!siteElementHiding.isEmpty()) { page->runJavaScript(Scripts::setCss(siteElementHiding), WebPage::SafeJsWorld); } }); } void AdBlockPlugin::webPageDeleted(WebPage *page) { AdBlockManager::instance()->clearBlockedRequestsForUrl(page->url()); } void AdBlockPlugin::mainWindowCreated(BrowserWindow *window) { AdBlockIcon *icon = new AdBlockIcon(window); m_icons[window] = icon; window->statusBar()->addButton(icon); window->navigationBar()->addToolButton(icon); } void AdBlockPlugin::mainWindowDeleted(BrowserWindow *window) { AdBlockIcon *icon = m_icons.take(window); window->statusBar()->removeButton(icon); window->navigationBar()->removeToolButton(icon); delete icon; } bool AdBlockPlugin::acceptNavigationRequest(WebPage *page, const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) { Q_UNUSED(type) AdBlockManager *manager = AdBlockManager::instance(); if (isMainFrame) { manager->clearBlockedRequestsForUrl(page->url()); } if (url.scheme() == QL1S("abp") && AdBlockManager::instance()->addSubscriptionFromUrl(url)) { return false; } return true; } diff --git a/src/lib/adblock/adblockplugin.h b/src/lib/adblock/adblockplugin.h index f0bc52e5..533c309e 100644 --- a/src/lib/adblock/adblockplugin.h +++ b/src/lib/adblock/adblockplugin.h @@ -1,48 +1,49 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2018 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 * 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 "plugininterface.h" class WebPage; class BrowserWindow; class AdBlockIcon; class AdBlockPlugin : public QObject, public PluginInterface { Q_OBJECT Q_INTERFACES(PluginInterface) Q_PLUGIN_METADATA(IID "Falkon.Browser.plugin.AdBlock") public: explicit AdBlockPlugin(); DesktopFile metaData() const override; void init(InitState state, const QString &settingsPath) override; void unload() override; bool testPlugin() override; + void showSettings(QWidget *parent = nullptr) override; private: void webPageCreated(WebPage *page); void webPageDeleted(WebPage *page); void mainWindowCreated(BrowserWindow *window); void mainWindowDeleted(BrowserWindow *window); bool acceptNavigationRequest(WebPage *page, const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) override; QHash m_icons; }; diff --git a/src/lib/adblock/metadata.desktop b/src/lib/adblock/metadata.desktop index 51ee9efc..2d35651a 100644 --- a/src/lib/adblock/metadata.desktop +++ b/src/lib/adblock/metadata.desktop @@ -1,32 +1,32 @@ [Desktop Entry] Name=AdBlock Name[ca]=AdBlock Name[ca@valencia]=AdBlock Name[cs]=AdBlock Name[de]=AdBlock Name[en_GB]=AdBlock Name[nl]=AdBlock Name[pt]=AdBlock Name[pt_BR]=AdBlock Name[sv]=Reklamblockering Name[uk]=Блокування реклами Name[x-test]=xxAdBlockxx Comment=Blocks unwanted web content Comment[ca]=Bloqueja el contingut web no desitjat Comment[ca@valencia]=Bloqueja el contingut web no desitjat Comment[cs]=Blokovat nevyžádaný obsah Comment[de]=Blockiert unerwünschte Web-Inhalte Comment[en_GB]=Blocks unwanted web content Comment[nl]=Blokkeert niet-gewilde webinhoud Comment[pt]=Bloqueia o conteúdo Web indesejado Comment[sv]=Blockerar oönskat webbinnehåll Comment[uk]=Блокує небажані інтернет-дані Comment[x-test]=xxBlocks unwanted web contentxx Icon=:adblock/data/adblock.png Type=Service X-Falkon-Author=David Rosca X-Falkon-Email=nowrep@gmail.com X-Falkon-Version=1.1.0 -X-Falkon-Settings=false +X-Falkon-Settings=true