From e6080a5289d0ec3a2a0e0a93bc826abcb3ba5840 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Wed, 19 Jul 2017 11:15:32 +0200 Subject: [PATCH] Add new adblock sources. Taken from Qupzilla. Will be mentioned in changelog and readme. --- resources/text/CHANGELOG | 6 + rssguard.pro | 33 +- .../adblock/adblockaddsubscriptiondialog.cpp | 91 +++ .../adblock/adblockaddsubscriptiondialog.h | 63 ++ .../adblock/adblockaddsubscriptiondialog.ui | 94 +++ src/network-web/adblock/adblockdialog.cpp | 185 +++++ src/network-web/adblock/adblockdialog.h | 71 ++ src/network-web/adblock/adblockdialog.ui | 163 ++++ src/network-web/adblock/adblockicon.cpp | 217 +++++ src/network-web/adblock/adblockicon.h | 62 ++ src/network-web/adblock/adblockmanager.cpp | 449 +++++++++++ src/network-web/adblock/adblockmanager.h | 101 +++ src/network-web/adblock/adblockmatcher.cpp | 219 +++++ src/network-web/adblock/adblockmatcher.h | 66 ++ src/network-web/adblock/adblockrule.cpp | 757 ++++++++++++++++++ src/network-web/adblock/adblockrule.h | 187 +++++ src/network-web/adblock/adblocksearchtree.cpp | 137 ++++ src/network-web/adblock/adblocksearchtree.h | 58 ++ .../adblock/adblocksubscription.cpp | 426 ++++++++++ src/network-web/adblock/adblocksubscription.h | 136 ++++ src/network-web/adblock/adblocktreewidget.cpp | 266 ++++++ src/network-web/adblock/adblocktreewidget.h | 61 ++ .../adblock/adblockurlinterceptor.cpp | 32 + .../adblock/adblockurlinterceptor.h | 38 + src/network-web/networkurlinterceptor.cpp | 59 ++ src/network-web/networkurlinterceptor.h | 45 ++ src/network-web/urlinterceptor.h | 32 + 27 files changed, 4052 insertions(+), 2 deletions(-) create mode 100755 src/network-web/adblock/adblockaddsubscriptiondialog.cpp create mode 100755 src/network-web/adblock/adblockaddsubscriptiondialog.h create mode 100755 src/network-web/adblock/adblockaddsubscriptiondialog.ui create mode 100755 src/network-web/adblock/adblockdialog.cpp create mode 100755 src/network-web/adblock/adblockdialog.h create mode 100755 src/network-web/adblock/adblockdialog.ui create mode 100755 src/network-web/adblock/adblockicon.cpp create mode 100755 src/network-web/adblock/adblockicon.h create mode 100755 src/network-web/adblock/adblockmanager.cpp create mode 100755 src/network-web/adblock/adblockmanager.h create mode 100755 src/network-web/adblock/adblockmatcher.cpp create mode 100755 src/network-web/adblock/adblockmatcher.h create mode 100755 src/network-web/adblock/adblockrule.cpp create mode 100755 src/network-web/adblock/adblockrule.h create mode 100755 src/network-web/adblock/adblocksearchtree.cpp create mode 100755 src/network-web/adblock/adblocksearchtree.h create mode 100755 src/network-web/adblock/adblocksubscription.cpp create mode 100755 src/network-web/adblock/adblocksubscription.h create mode 100755 src/network-web/adblock/adblocktreewidget.cpp create mode 100755 src/network-web/adblock/adblocktreewidget.h create mode 100755 src/network-web/adblock/adblockurlinterceptor.cpp create mode 100755 src/network-web/adblock/adblockurlinterceptor.h create mode 100755 src/network-web/networkurlinterceptor.cpp create mode 100755 src/network-web/networkurlinterceptor.h create mode 100755 src/network-web/urlinterceptor.h diff --git a/resources/text/CHANGELOG b/resources/text/CHANGELOG index 9b417b2a0..ac503ece0 100644 --- a/resources/text/CHANGELOG +++ b/resources/text/CHANGELOG @@ -1,3 +1,9 @@ +3.4.2 +————— + +Added: +▪ New Ad-Block feature for internal web browser. Based on Qt examples and QupZilla. Original license GNU GPLv3 is retained. + 3.4.1 ————— diff --git a/rssguard.pro b/rssguard.pro index d2d6bc4fe..e76de2efd 100755 --- a/rssguard.pro +++ b/rssguard.pro @@ -333,8 +333,8 @@ HEADERS += src/core/feeddownloader.h \ src/core/messagesmodelcache.h \ src/core/messagesmodelsqllayer.h \ src/gui/treeviewcolumnsmenu.h \ - src/services/abstract/labelsrootitem.h \ - src/services/abstract/label.h + src/services/abstract/labelsrootitem.h \ + src/services/abstract/label.h SOURCES += src/core/feeddownloader.cpp \ src/core/feedsmodel.cpp \ @@ -503,6 +503,35 @@ equals(USE_WEBENGINE, true) { src/gui/discoverfeedsbutton.cpp \ src/network-web/googlesuggest.cpp \ src/network-web/webpage.cpp + + # Add Ad-Block sources. + HEADERS += src/network-web/adblock/adblockaddsubscriptiondialog.h \ + src/network-web/adblock/adblockdialog.h \ + src/network-web/adblock/adblockicon.h \ + src/network-web/adblock/adblockmanager.h \ + src/network-web/adblock/adblockmatcher.h \ + src/network-web/adblock/adblockrule.h \ + src/network-web/adblock/adblocksearchtree.h \ + src/network-web/adblock/adblocksubscription.h \ + src/network-web/adblock/adblocktreewidget.h \ + src/network-web/adblock/adblockurlinterceptor.h \ + src/network-web/urlinterceptor.h \ + src/network-web/networkurlinterceptor.h + + SOURCES += src/network-web/adblock/adblockaddsubscriptiondialog.cpp \ + src/network-web/adblock/adblockdialog.cpp \ + src/network-web/adblock/adblockicon.cpp \ + src/network-web/adblock/adblockmanager.cpp \ + src/network-web/adblock/adblockmatcher.cpp \ + src/network-web/adblock/adblockrule.cpp \ + src/network-web/adblock/adblocksearchtree.cpp \ + src/network-web/adblock/adblocksubscription.cpp \ + src/network-web/adblock/adblocktreewidget.cpp \ + src/network-web/adblock/adblockurlinterceptor.cpp \ + src/network-web/networkurlinterceptor.cpp + + FORMS += src/network-web/adblock/adblockaddsubscriptiondialog.ui \ + src/network-web/adblock/adblockdialog.ui } else { HEADERS += src/gui/messagepreviewer.h \ diff --git a/src/network-web/adblock/adblockaddsubscriptiondialog.cpp b/src/network-web/adblock/adblockaddsubscriptiondialog.cpp new file mode 100755 index 000000000..7571eb7ec --- /dev/null +++ b/src/network-web/adblock/adblockaddsubscriptiondialog.cpp @@ -0,0 +1,91 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2010-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 +* 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 "adblockaddsubscriptiondialog.h" +#include "ui_adblockaddsubscriptiondialog.h" + +AdBlockAddSubscriptionDialog::AdBlockAddSubscriptionDialog(QWidget* parent) + : QDialog(parent) + , ui(new Ui::AdBlockAddSubscriptionDialog) +{ + ui->setupUi(this); + + m_knownSubscriptions << Subscription("EasyList (English)", ADBLOCK_EASYLIST_URL) + << Subscription("BSI Lista Polska (Polish)", "http://www.bsi.info.pl/filtrABP.txt") + << Subscription("Czech List (Czech)", "http://adblock.dajbych.net/adblock.txt") + << Subscription("dutchblock (Dutch)", "http://groenewoudt.net/dutchblock/list.txt") + << Subscription("Filtros Nauscopicos (Spanish)", "http://abp.mozilla-hispano.org/nauscopio/filtros.txt") + << Subscription("IsraelList (Hebrew)", "http://secure.fanboy.co.nz/israelilist/IsraelList.txt") + << Subscription("NLBlock (Dutch)", "http://www.verzijlbergh.com/adblock/nlblock.txt") + << Subscription("Peter Lowe's list (English)", "http://pgl.yoyo.org/adservers/serverlist.php?hostformat=adblockplus&mimetype=plaintext") + << Subscription("PLgeneral (Polish)", "http://www.niecko.pl/adblock/adblock.txt") + << Subscription("Schacks Adblock Plus liste (Danish)", "http://adblock.schack.dk/block.txt") + << Subscription("Xfiles (Italian)", "http://mozilla.gfsolone.com/filtri.txt") + << Subscription("EasyPrivacy (English)", "http://easylist-downloads.adblockplus.org/easyprivacy.txt") + << Subscription("RU Adlist (Russian)", "https://easylist-downloads.adblockplus.org/advblock.txt") + << Subscription("ABPindo (Indonesian)", "https://raw.githubusercontent.com/heradhis/indonesianadblockrules/master/subscriptions/abpindo.txt") + << Subscription("Easylist China (Chinese)", "https://easylist-downloads.adblockplus.org/easylistchina.txt") + << Subscription("Anti-Adblock Killer", "https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt") + << Subscription(tr("Other..."), QString()); + + foreach (const Subscription &subscription, m_knownSubscriptions) { + ui->comboBox->addItem(subscription.title); + } + + connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(indexChanged(int))); + indexChanged(0); +} + +QString AdBlockAddSubscriptionDialog::title() const +{ + return ui->title->text(); +} + +QString AdBlockAddSubscriptionDialog::url() const +{ + return ui->url->text(); +} + +void AdBlockAddSubscriptionDialog::indexChanged(int index) +{ + const Subscription subscription = m_knownSubscriptions.at(index); + + // "Other..." entry + if (subscription.url.isEmpty()) { + ui->title->clear(); + ui->url->clear(); + } + else { + int pos = subscription.title.indexOf(QLatin1Char('(')); + QString title = subscription.title; + + if (pos > 0) { + title = title.left(pos).trimmed(); + } + + ui->title->setText(title); + ui->title->setCursorPosition(0); + + ui->url->setText(subscription.url); + ui->url->setCursorPosition(0); + } +} + +AdBlockAddSubscriptionDialog::~AdBlockAddSubscriptionDialog() +{ + delete ui; +} diff --git a/src/network-web/adblock/adblockaddsubscriptiondialog.h b/src/network-web/adblock/adblockaddsubscriptiondialog.h new file mode 100755 index 000000000..f8125d676 --- /dev/null +++ b/src/network-web/adblock/adblockaddsubscriptiondialog.h @@ -0,0 +1,63 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2014 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 ADBLOCKADDSUBSCRIPTIONDIALOG_H +#define ADBLOCKADDSUBSCRIPTIONDIALOG_H + +#include +#include + +#include "qzcommon.h" + +namespace Ui +{ +class AdBlockAddSubscriptionDialog; +} + +class QUPZILLA_EXPORT AdBlockAddSubscriptionDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AdBlockAddSubscriptionDialog(QWidget* parent = 0); + ~AdBlockAddSubscriptionDialog(); + + QString title() const; + QString url() const; + +private slots: + void indexChanged(int index); + +private: + Ui::AdBlockAddSubscriptionDialog* ui; + + struct Subscription { + QString title; + QString url; + + Subscription() {} + + Subscription(const QString &t, const QString &u) { + title = t; + url = u; + } + }; + + QVector m_knownSubscriptions; +}; + +#endif // ADBLOCKADDSUBSCRIPTIONDIALOG_H diff --git a/src/network-web/adblock/adblockaddsubscriptiondialog.ui b/src/network-web/adblock/adblockaddsubscriptiondialog.ui new file mode 100755 index 000000000..88b77db8a --- /dev/null +++ b/src/network-web/adblock/adblockaddsubscriptiondialog.ui @@ -0,0 +1,94 @@ + + + AdBlockAddSubscriptionDialog + + + + 0 + 0 + 557 + 162 + + + + Add Subscription + + + + + + + + + Title: + + + + + + + + + + Address: + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Add new subscription to AdBlock: + + + + + + + + + buttonBox + accepted() + AdBlockAddSubscriptionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AdBlockAddSubscriptionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/network-web/adblock/adblockdialog.cpp b/src/network-web/adblock/adblockdialog.cpp new file mode 100755 index 000000000..c05bda164 --- /dev/null +++ b/src/network-web/adblock/adblockdialog.cpp @@ -0,0 +1,185 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2010-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 +* 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 "adblockdialog.h" +#include "adblockmanager.h" +#include "adblocksubscription.h" +#include "adblocktreewidget.h" +#include "adblockaddsubscriptiondialog.h" +#include "mainapplication.h" +#include "qztools.h" + +#include +#include +#include +#include + +AdBlockDialog::AdBlockDialog(QWidget* parent) + : QWidget(parent) + , m_manager(AdBlockManager::instance()) + , m_currentTreeWidget(0) + , m_currentSubscription(0) + , m_loaded(false) +{ + setAttribute(Qt::WA_DeleteOnClose); + setupUi(this); + + QzTools::centerWidgetOnScreen(this); + +#ifdef Q_OS_MACOS + tabWidget->setDocumentMode(false); +#endif + adblockCheckBox->setChecked(m_manager->isEnabled()); + + QMenu* menu = new QMenu(buttonOptions); + m_actionAddRule = menu->addAction(tr("Add Rule"), this, SLOT(addRule())); + m_actionRemoveRule = menu->addAction(tr("Remove Rule"), this, SLOT(removeRule())); + menu->addSeparator(); + m_actionAddSubscription = menu->addAction(tr("Add Subscription"), this, SLOT(addSubscription())); + m_actionRemoveSubscription = menu->addAction(tr("Remove Subscription"), this, SLOT(removeSubscription())); + menu->addAction(tr("Update Subscriptions"), m_manager, SLOT(updateAllSubscriptions())); + menu->addSeparator(); + menu->addAction(tr("Learn about writing rules..."), this, SLOT(learnAboutRules())); + + buttonOptions->setMenu(menu); + connect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowMenu())); + + connect(adblockCheckBox, SIGNAL(toggled(bool)), this, SLOT(enableAdBlock(bool))); + connect(search, SIGNAL(textChanged(QString)), this, SLOT(filterString(QString))); + connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int))); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(close())); + + load(); + + buttonBox->setFocus(); +} + +void AdBlockDialog::showRule(const AdBlockRule* rule) const +{ + AdBlockSubscription* subscription = rule->subscription(); + if (!subscription) { + return; + } + + for (int i = 0; i < tabWidget->count(); ++i) { + AdBlockTreeWidget* treeWidget = qobject_cast(tabWidget->widget(i)); + + if (subscription == treeWidget->subscription()) { + treeWidget->showRule(rule); + tabWidget->setCurrentIndex(i); + break; + } + } +} + +void AdBlockDialog::addRule() +{ + m_currentTreeWidget->addRule(); +} + +void AdBlockDialog::removeRule() +{ + m_currentTreeWidget->removeRule(); +} + +void AdBlockDialog::addSubscription() +{ + AdBlockAddSubscriptionDialog dialog(this); + if (dialog.exec() != QDialog::Accepted) { + return; + } + + QString title = dialog.title(); + QString url = dialog.url(); + + if (AdBlockSubscription* subscription = m_manager->addSubscription(title, url)) { + AdBlockTreeWidget* tree = new AdBlockTreeWidget(subscription, tabWidget); + int index = tabWidget->insertTab(tabWidget->count() - 1, tree, subscription->title()); + + tabWidget->setCurrentIndex(index); + } +} + +void AdBlockDialog::removeSubscription() +{ + if (m_manager->removeSubscription(m_currentSubscription)) { + delete m_currentTreeWidget; + } +} + +void AdBlockDialog::currentChanged(int index) +{ + if (index != -1) { + m_currentTreeWidget = qobject_cast(tabWidget->widget(index)); + m_currentSubscription = m_currentTreeWidget->subscription(); + } +} + +void AdBlockDialog::filterString(const QString &string) +{ + if (m_currentTreeWidget && adblockCheckBox->isChecked()) { + m_currentTreeWidget->filterString(string); + } +} + +void AdBlockDialog::enableAdBlock(bool state) +{ + m_manager->setEnabled(state); + + if (state) { + load(); + } +} + +void AdBlockDialog::aboutToShowMenu() +{ + bool subscriptionEditable = m_currentSubscription && m_currentSubscription->canEditRules(); + bool subscriptionRemovable = m_currentSubscription && m_currentSubscription->canBeRemoved(); + + m_actionAddRule->setEnabled(subscriptionEditable); + m_actionRemoveRule->setEnabled(subscriptionEditable); + m_actionRemoveSubscription->setEnabled(subscriptionRemovable); +} + +void AdBlockDialog::learnAboutRules() +{ + mApp->addNewTab(QUrl("http://adblockplus.org/en/filters")); +} + +void AdBlockDialog::loadSubscriptions() +{ + for (int i = 0; i < tabWidget->count(); ++i) { + AdBlockTreeWidget* treeWidget = qobject_cast(tabWidget->widget(i)); + treeWidget->refresh(); + } +} + +void AdBlockDialog::load() +{ + if (m_loaded || !adblockCheckBox->isChecked()) { + return; + } + + foreach (AdBlockSubscription* subscription, m_manager->subscriptions()) { + AdBlockTreeWidget* tree = new AdBlockTreeWidget(subscription, tabWidget); + tabWidget->addTab(tree, subscription->title()); + } + + m_loaded = true; + + QTimer::singleShot(50, this, SLOT(loadSubscriptions())); +} diff --git a/src/network-web/adblock/adblockdialog.h b/src/network-web/adblock/adblockdialog.h new file mode 100755 index 000000000..ce1725de0 --- /dev/null +++ b/src/network-web/adblock/adblockdialog.h @@ -0,0 +1,71 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2010-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 +* 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 ADBLOCKDIALOG_H +#define ADBLOCKDIALOG_H + +#include + +#include "qzcommon.h" +#include "ui_adblockdialog.h" + +class AdBlockSubscription; +class AdBlockTreeWidget; +class AdBlockManager; +class AdBlockRule; + +class QUPZILLA_EXPORT AdBlockDialog : public QWidget, public Ui_AdBlockDialog +{ + Q_OBJECT + +public: + explicit AdBlockDialog(QWidget* parent = 0); + + void showRule(const AdBlockRule* rule) const; + +private slots: + void addRule(); + void removeRule(); + + void addSubscription(); + void removeSubscription(); + + void currentChanged(int index); + void filterString(const QString &string); + void enableAdBlock(bool state); + + void aboutToShowMenu(); + void learnAboutRules(); + + void loadSubscriptions(); + void load(); + +private: + AdBlockManager* m_manager; + AdBlockTreeWidget* m_currentTreeWidget; + AdBlockSubscription* m_currentSubscription; + + QAction* m_actionAddRule; + QAction* m_actionRemoveRule; + QAction* m_actionAddSubscription; + QAction* m_actionRemoveSubscription; + + bool m_loaded; +}; + +#endif // ADBLOCKDIALOG_H + diff --git a/src/network-web/adblock/adblockdialog.ui b/src/network-web/adblock/adblockdialog.ui new file mode 100755 index 000000000..8435061b8 --- /dev/null +++ b/src/network-web/adblock/adblockdialog.ui @@ -0,0 +1,163 @@ + + + AdBlockDialog + + + + 0 + 0 + 546 + 462 + + + + AdBlock Configuration + + + + + + Enable AdBlock + + + true + + + + + + + + + Search... + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 50 + 20 + + + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Options + + + + :/icons/other/adblock.png:/icons/other/adblock.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + AdBlock + + + + + + + + + -1 + + + true + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + adblockCheckBox + toggled(bool) + adblockWidget + setEnabled(bool) + + + 106 + 39 + + + 349 + 74 + + + + + diff --git a/src/network-web/adblock/adblockicon.cpp b/src/network-web/adblock/adblockicon.cpp new file mode 100755 index 000000000..26be32687 --- /dev/null +++ b/src/network-web/adblock/adblockicon.cpp @@ -0,0 +1,217 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2014 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 "adblockicon.h" +#include "adblockrule.h" +#include "adblockmanager.h" +#include "adblocksubscription.h" +#include "mainapplication.h" +#include "browserwindow.h" +#include "webpage.h" +#include "tabbedwebview.h" +#include "tabwidget.h" +#include "desktopnotificationsfactory.h" +#include "qztools.h" + +#include +#include + +AdBlockIcon::AdBlockIcon(BrowserWindow* window, QWidget* parent) + : ClickableLabel(parent) + , m_window(window) + , m_menuAction(0) + , m_flashTimer(0) + , m_timerTicks(0) + , m_enabled(false) +{ + setCursor(Qt::PointingHandCursor); + setToolTip(tr("AdBlock lets you block unwanted content on web pages")); + setFixedSize(16, 16); + + connect(this, SIGNAL(clicked(QPoint)), this, SLOT(showMenu(QPoint))); + connect(AdBlockManager::instance(), SIGNAL(enabledChanged(bool)), this, SLOT(setEnabled(bool))); +} + +AdBlockIcon::~AdBlockIcon() +{ + for (int i = 0; i < m_blockedPopups.count(); ++i) + delete m_blockedPopups.at(i).first; +} + +void AdBlockIcon::popupBlocked(const QString &ruleString, const QUrl &url) +{ + int index = ruleString.lastIndexOf(QLatin1String(" (")); + + const QString subscriptionName = ruleString.left(index); + const QString filter = ruleString.mid(index + 2, ruleString.size() - index - 3); + AdBlockSubscription* subscription = AdBlockManager::instance()->subscriptionByName(subscriptionName); + if (filter.isEmpty() || !subscription) { + return; + } + + QPair pair; + pair.first = new AdBlockRule(filter, subscription); + pair.second = url; + m_blockedPopups.append(pair); + + mApp->desktopNotifications()->showNotification(QPixmap(":html/adblock_big.png"), tr("Blocked popup window"), tr("AdBlock blocked unwanted popup window.")); + + if (!m_flashTimer) { + m_flashTimer = new QTimer(this); + } + + if (m_flashTimer->isActive()) { + stopAnimation(); + } + + m_flashTimer->setInterval(500); + m_flashTimer->start(); + + connect(m_flashTimer, SIGNAL(timeout()), this, SLOT(animateIcon())); +} + +QAction* AdBlockIcon::menuAction() +{ + if (!m_menuAction) { + m_menuAction = new QAction(tr("AdBlock"), this); + m_menuAction->setMenu(new QMenu); + connect(m_menuAction->menu(), SIGNAL(aboutToShow()), this, SLOT(createMenu())); + } + + m_menuAction->setIcon(QIcon(m_enabled ? ":icons/other/adblock.png" : ":icons/other/adblock-disabled.png")); + + return m_menuAction; +} + +void AdBlockIcon::createMenu(QMenu* menu) +{ + if (!menu) { + menu = qobject_cast(sender()); + if (!menu) { + return; + } + } + + menu->clear(); + + AdBlockManager* manager = AdBlockManager::instance(); + AdBlockCustomList* customList = manager->customList(); + + WebPage* page = m_window->weView()->page(); + const QUrl pageUrl = page->url(); + + menu->addAction(tr("Show AdBlock &Settings"), manager, SLOT(showDialog())); + menu->addSeparator(); + + if (!pageUrl.host().isEmpty() && m_enabled && manager->canRunOnScheme(pageUrl.scheme())) { + const QString host = page->url().host().contains(QLatin1String("www.")) ? pageUrl.host().mid(4) : pageUrl.host(); + const QString hostFilter = QString("@@||%1^$document").arg(host); + const QString pageFilter = QString("@@|%1|$document").arg(pageUrl.toString()); + + QAction* act = menu->addAction(tr("Disable on %1").arg(host)); + act->setCheckable(true); + act->setChecked(customList->containsFilter(hostFilter)); + act->setData(hostFilter); + connect(act, SIGNAL(triggered()), this, SLOT(toggleCustomFilter())); + + act = menu->addAction(tr("Disable only on this page")); + act->setCheckable(true); + act->setChecked(customList->containsFilter(pageFilter)); + act->setData(pageFilter); + connect(act, SIGNAL(triggered()), this, SLOT(toggleCustomFilter())); + + menu->addSeparator(); + } + + if (!m_blockedPopups.isEmpty()) { + menu->addAction(tr("Blocked Popup Windows"))->setEnabled(false); + for (int i = 0; i < m_blockedPopups.count(); i++) { + const QPair &pair = m_blockedPopups.at(i); + + QString address = pair.second.toString().right(55); + QString actionText = tr("%1 with (%2)").arg(address, pair.first->filter()).replace(QLatin1Char('&'), QLatin1String("&&")); + + QAction* action = menu->addAction(actionText, manager, SLOT(showRule())); + action->setData(QVariant::fromValue((void*)pair.first)); + } + } +} + +void AdBlockIcon::showMenu(const QPoint &pos) +{ + QMenu menu; + createMenu(&menu); + + menu.exec(pos); +} + +void AdBlockIcon::toggleCustomFilter() +{ + QAction* action = qobject_cast(sender()); + if (!action) { + return; + } + + const QString filter = action->data().toString(); + AdBlockManager* manager = AdBlockManager::instance(); + AdBlockCustomList* customList = manager->customList(); + + if (customList->containsFilter(filter)) { + customList->removeFilter(filter); + } + else { + AdBlockRule* rule = new AdBlockRule(filter, customList); + customList->addRule(rule); + } +} + +void AdBlockIcon::animateIcon() +{ + ++m_timerTicks; + if (m_timerTicks > 10) { + stopAnimation(); + return; + } + + if (pixmap()->isNull()) { + setPixmap(QIcon(QSL(":icons/other/adblock.png")).pixmap(16)); + } + else { + setPixmap(QPixmap()); + } +} + +void AdBlockIcon::stopAnimation() +{ + m_timerTicks = 0; + m_flashTimer->stop(); + disconnect(m_flashTimer, SIGNAL(timeout()), this, SLOT(animateIcon())); + + setEnabled(m_enabled); +} + +void AdBlockIcon::setEnabled(bool enabled) +{ + if (enabled) { + setPixmap(QIcon(QSL(":icons/other/adblock.png")).pixmap(16)); + } + else { + setPixmap(QIcon(QSL(":icons/other/adblock-disabled.png")).pixmap(16)); + } + + m_enabled = enabled; +} diff --git a/src/network-web/adblock/adblockicon.h b/src/network-web/adblock/adblockicon.h new file mode 100755 index 000000000..2b1897c28 --- /dev/null +++ b/src/network-web/adblock/adblockicon.h @@ -0,0 +1,62 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2014 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 ADBLOCKICON_H +#define ADBLOCKICON_H + +#include "qzcommon.h" +#include "clickablelabel.h" +#include "adblockrule.h" + +class QMenu; +class QUrl; + +class BrowserWindow; + +class QUPZILLA_EXPORT AdBlockIcon : public ClickableLabel +{ + Q_OBJECT +public: + explicit AdBlockIcon(BrowserWindow* window, QWidget* parent = 0); + ~AdBlockIcon(); + + void popupBlocked(const QString &ruleString, const QUrl &url); + QAction* menuAction(); + +public slots: + void setEnabled(bool enabled); + void createMenu(QMenu* menu = 0); + +private slots: + void showMenu(const QPoint &pos); + void toggleCustomFilter(); + + void animateIcon(); + void stopAnimation(); + +private: + BrowserWindow* m_window; + QAction* m_menuAction; + + QVector > m_blockedPopups; + QTimer* m_flashTimer; + + int m_timerTicks; + bool m_enabled; +}; + +#endif // ADBLOCKICON_H diff --git a/src/network-web/adblock/adblockmanager.cpp b/src/network-web/adblock/adblockmanager.cpp new file mode 100755 index 000000000..df3a9e575 --- /dev/null +++ b/src/network-web/adblock/adblockmanager.cpp @@ -0,0 +1,449 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2010-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 +* 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 + +//#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)) +{ + 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) +{ + 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; + } + + bool res = false; + const AdBlockRule* blockedRule = m_matcher->match(request, urlDomain, urlString); + + if (blockedRule) { + res = true; + + if (request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) { + QUrl url(QSL("qupzilla:adblock")); + QUrlQuery query; + query.addQueryItem(QSL("rule"), blockedRule->filter()); + query.addQueryItem(QSL("subscription"), blockedRule->subscription()->title()); + url.setQuery(query); + request.redirect(url); + } + else { + request.block(true); + } + +#ifdef ADBLOCK_DEBUG + qDebug() << "BLOCKED: " << timer.elapsed() << blockedRule->filter() << request.requestUrl(); +#endif + } + +#ifdef ADBLOCK_DEBUG + qDebug() << timer.elapsed() << request.requestUrl(); +#endif + + return res; +} + +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); + } + + // Prepend EasyList 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.prepend(easyList); + } + + // 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; + + 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 == QLatin1String("file") || scheme == QLatin1String("qrc") + || scheme == QLatin1String("qupzilla") || scheme == QLatin1String("data") + || scheme == QLatin1String("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() +{ + if (!m_adBlockDialog) { + m_adBlockDialog = new AdBlockDialog; + } + + 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/network-web/adblock/adblockmanager.h b/src/network-web/adblock/adblockmanager.h new file mode 100755 index 000000000..bd2e83bea --- /dev/null +++ b/src/network-web/adblock/adblockmanager.h @@ -0,0 +1,101 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2010-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 +* 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 "qzcommon.h" + +class QUrl; +class QWebEngineUrlRequestInfo; + +class AdBlockRule; +class AdBlockDialog; +class AdBlockMatcher; +class AdBlockCustomList; +class AdBlockSubscription; +class AdBlockUrlInterceptor; + +class QUPZILLA_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; + + 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); + + 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(); + +signals: + void enabledChanged(bool enabled); + +public slots: + void setEnabled(bool enabled); + void showRule(); + + void updateMatcher(); + void updateAllSubscriptions(); + + AdBlockDialog* showDialog(); + +private: + inline bool canBeBlocked(const QUrl &url) const; + + bool m_loaded; + bool m_enabled; + + QList m_subscriptions; + AdBlockMatcher* m_matcher; + QStringList m_disabledRules; + + AdBlockUrlInterceptor *m_interceptor; + QPointer m_adBlockDialog; + QMutex m_mutex; +}; + +#endif // ADBLOCKMANAGER_H + diff --git a/src/network-web/adblock/adblockmatcher.cpp b/src/network-web/adblock/adblockmatcher.cpp new file mode 100755 index 000000000..98581d906 --- /dev/null +++ b/src/network-web/adblock/adblockmatcher.cpp @@ -0,0 +1,219 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2014-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 +* 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 "adblockmatcher.h" +#include "adblockmanager.h" +#include "adblockrule.h" +#include "adblocksubscription.h" + +AdBlockMatcher::AdBlockMatcher(AdBlockManager* manager) + : QObject(manager) + , m_manager(manager) +{ +} + +AdBlockMatcher::~AdBlockMatcher() +{ + clear(); +} + +const AdBlockRule* AdBlockMatcher::match(const QWebEngineUrlRequestInfo &request, const QString &urlDomain, const QString &urlString) const +{ + // Exception rules + if (m_networkExceptionTree.find(request, urlDomain, urlString)) + return 0; + + int count = m_networkExceptionRules.count(); + for (int i = 0; i < count; ++i) { + const AdBlockRule* rule = m_networkExceptionRules.at(i); + if (rule->networkMatch(request, urlDomain, urlString)) + return 0; + } + + // Block rules + if (const AdBlockRule* rule = m_networkBlockTree.find(request, urlDomain, urlString)) + return rule; + + count = m_networkBlockRules.count(); + for (int i = 0; i < count; ++i) { + const AdBlockRule* rule = m_networkBlockRules.at(i); + if (rule->networkMatch(request, urlDomain, urlString)) + return rule; + } + + return 0; +} + +bool AdBlockMatcher::adBlockDisabledForUrl(const QUrl &url) const +{ + int count = m_documentRules.count(); + + for (int i = 0; i < count; ++i) + if (m_documentRules.at(i)->urlMatch(url)) + return true; + + return false; +} + +bool AdBlockMatcher::elemHideDisabledForUrl(const QUrl &url) const +{ + if (adBlockDisabledForUrl(url)) + return true; + + int count = m_elemhideRules.count(); + + for (int i = 0; i < count; ++i) + if (m_elemhideRules.at(i)->urlMatch(url)) + return true; + + return false; +} + +QString AdBlockMatcher::elementHidingRules() const +{ + return m_elementHidingRules; +} + +QString AdBlockMatcher::elementHidingRulesForDomain(const QString &domain) const +{ + QString rules; + int addedRulesCount = 0; + int count = m_domainRestrictedCssRules.count(); + + for (int i = 0; i < count; ++i) { + const AdBlockRule* rule = m_domainRestrictedCssRules.at(i); + if (!rule->matchDomain(domain)) + continue; + + if (Q_UNLIKELY(addedRulesCount == 1000)) { + rules.append(rule->cssSelector()); + rules.append(QL1S("{display:none !important;}\n")); + addedRulesCount = 0; + } + else { + rules.append(rule->cssSelector() + QLatin1Char(',')); + addedRulesCount++; + } + } + + if (addedRulesCount != 0) { + rules = rules.left(rules.size() - 1); + rules.append(QL1S("{display:none !important;}\n")); + } + + return rules; +} + +void AdBlockMatcher::update() +{ + clear(); + + QHash cssRulesHash; + QVector exceptionCssRules; + + foreach (AdBlockSubscription* subscription, m_manager->subscriptions()) { + foreach (const AdBlockRule* rule, subscription->allRules()) { + // Don't add internally disabled rules to cache + if (rule->isInternalDisabled()) + continue; + + if (rule->isCssRule()) { + // We will add only enabled css rules to cache, because there is no enabled/disabled + // check on match. They are directly embedded to pages. + if (!rule->isEnabled()) + continue; + + if (rule->isException()) + exceptionCssRules.append(rule); + else + cssRulesHash.insert(rule->cssSelector(), rule); + } + else if (rule->isDocument()) { + m_documentRules.append(rule); + } + else if (rule->isElemhide()) { + m_elemhideRules.append(rule); + } + else if (rule->isException()) { + if (!m_networkExceptionTree.add(rule)) + m_networkExceptionRules.append(rule); + } + else { + if (!m_networkBlockTree.add(rule)) + m_networkBlockRules.append(rule); + } + } + } + + foreach (const AdBlockRule* rule, exceptionCssRules) { + const AdBlockRule* originalRule = cssRulesHash.value(rule->cssSelector()); + + // If we don't have this selector, the exception does nothing + if (!originalRule) + continue; + + AdBlockRule* copiedRule = originalRule->copy(); + copiedRule->m_options |= AdBlockRule::DomainRestrictedOption; + copiedRule->m_blockedDomains.append(rule->m_allowedDomains); + + cssRulesHash[rule->cssSelector()] = copiedRule; + m_createdRules.append(copiedRule); + } + + // Apparently, excessive amount of selectors for one CSS rule is not what WebKit likes. + // (In my testings, 4931 is the number that makes it crash) + // So let's split it by 1000 selectors... + int hidingRulesCount = 0; + + QHashIterator it(cssRulesHash); + while (it.hasNext()) { + it.next(); + const AdBlockRule* rule = it.value(); + + if (rule->isDomainRestricted()) { + m_domainRestrictedCssRules.append(rule); + } + else if (Q_UNLIKELY(hidingRulesCount == 1000)) { + m_elementHidingRules.append(rule->cssSelector()); + m_elementHidingRules.append(QL1S("{display:none !important;} ")); + hidingRulesCount = 0; + } + else { + m_elementHidingRules.append(rule->cssSelector() + QLatin1Char(',')); + hidingRulesCount++; + } + } + + if (hidingRulesCount != 0) { + m_elementHidingRules = m_elementHidingRules.left(m_elementHidingRules.size() - 1); + m_elementHidingRules.append(QL1S("{display:none !important;} ")); + } +} + +void AdBlockMatcher::clear() +{ + m_networkExceptionTree.clear(); + m_networkExceptionRules.clear(); + m_networkBlockTree.clear(); + m_networkBlockRules.clear(); + m_domainRestrictedCssRules.clear(); + m_elementHidingRules.clear(); + m_documentRules.clear(); + m_elemhideRules.clear(); + qDeleteAll(m_createdRules); + m_createdRules.clear(); +} diff --git a/src/network-web/adblock/adblockmatcher.h b/src/network-web/adblock/adblockmatcher.h new file mode 100755 index 000000000..12ff2318d --- /dev/null +++ b/src/network-web/adblock/adblockmatcher.h @@ -0,0 +1,66 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2014-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 +* 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 ADBLOCKMATCHER_H +#define ADBLOCKMATCHER_H + +#include +#include + +#include "qzcommon.h" +#include "adblocksearchtree.h" + +class QWebEngineUrlRequestInfo; + +class AdBlockManager; + +class QUPZILLA_EXPORT AdBlockMatcher : public QObject +{ + Q_OBJECT + +public: + explicit AdBlockMatcher(AdBlockManager* manager); + ~AdBlockMatcher(); + + const AdBlockRule* match(const QWebEngineUrlRequestInfo &request, const QString &urlDomain, const QString &urlString) const; + + bool adBlockDisabledForUrl(const QUrl &url) const; + bool elemHideDisabledForUrl(const QUrl &url) const; + + QString elementHidingRules() const; + QString elementHidingRulesForDomain(const QString &domain) const; + +public slots: + void update(); + void clear(); + +private: + AdBlockManager* m_manager; + + QVector m_createdRules; + QVector m_networkExceptionRules; + QVector m_networkBlockRules; + QVector m_domainRestrictedCssRules; + QVector m_documentRules; + QVector m_elemhideRules; + + QString m_elementHidingRules; + AdBlockSearchTree m_networkBlockTree; + AdBlockSearchTree m_networkExceptionTree; +}; + +#endif // ADBLOCKMATCHER_H diff --git a/src/network-web/adblock/adblockrule.cpp b/src/network-web/adblock/adblockrule.cpp new file mode 100755 index 000000000..163500f93 --- /dev/null +++ b/src/network-web/adblock/adblockrule.cpp @@ -0,0 +1,757 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2014 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 . +* ============================================================ */ +/** + * Copyright (c) 2009, Zsombor Gegesy + * Copyright (c) 2009, Benjamin C. Meyer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Benjamin Meyer nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "adblockrule.h" +#include "adblocksubscription.h" +#include "qztools.h" +#include "qzregexp.h" + +#include +#include +#include +#include +#include + +static QString toSecondLevelDomain(const QUrl &url) +{ + const QString topLevelDomain = url.topLevelDomain(); + const QString urlHost = url.host(); + + if (topLevelDomain.isEmpty() || urlHost.isEmpty()) { + return QString(); + } + + QString domain = urlHost.left(urlHost.size() - topLevelDomain.size()); + + if (domain.count(QL1C('.')) == 0) { + return urlHost; + } + + while (domain.count(QL1C('.')) != 0) { + domain = domain.mid(domain.indexOf(QL1C('.')) + 1); + } + + return domain + topLevelDomain; +} + +AdBlockRule::AdBlockRule(const QString &filter, AdBlockSubscription* subscription) + : m_subscription(subscription) + , m_type(StringContainsMatchRule) + , m_caseSensitivity(Qt::CaseInsensitive) + , m_isEnabled(true) + , m_isException(false) + , m_isInternalDisabled(false) + , m_regExp(0) +{ + setFilter(filter); +} + +AdBlockRule::~AdBlockRule() +{ + delete m_regExp; +} + +AdBlockRule* AdBlockRule::copy() const +{ + AdBlockRule* rule = new AdBlockRule(); + rule->m_subscription = m_subscription; + rule->m_type = m_type; + rule->m_options = m_options; + rule->m_exceptions = m_exceptions; + rule->m_filter = m_filter; + rule->m_matchString = m_matchString; + rule->m_caseSensitivity = m_caseSensitivity; + rule->m_isEnabled = m_isEnabled; + rule->m_isException = m_isException; + rule->m_isInternalDisabled = m_isInternalDisabled; + rule->m_allowedDomains = m_allowedDomains; + rule->m_blockedDomains = m_blockedDomains; + + if (m_regExp) { + rule->m_regExp = new RegExp; + rule->m_regExp->regExp = m_regExp->regExp; + rule->m_regExp->matchers = m_regExp->matchers; + } + + return rule; +} + +AdBlockSubscription* AdBlockRule::subscription() const +{ + return m_subscription; +} + +void AdBlockRule::setSubscription(AdBlockSubscription* subscription) +{ + m_subscription = subscription; +} + +QString AdBlockRule::filter() const +{ + return m_filter; +} + +void AdBlockRule::setFilter(const QString &filter) +{ + m_filter = filter; + parseFilter(); +} + +bool AdBlockRule::isCssRule() const +{ + return m_type == CssRule; +} + +QString AdBlockRule::cssSelector() const +{ + return m_matchString; +} + +bool AdBlockRule::isDocument() const +{ + return hasOption(DocumentOption); +} + +bool AdBlockRule::isElemhide() const +{ + return hasOption(ElementHideOption); +} + +bool AdBlockRule::isDomainRestricted() const +{ + return hasOption(DomainRestrictedOption); +} + +bool AdBlockRule::isException() const +{ + return m_isException; +} + +bool AdBlockRule::isComment() const +{ + return m_filter.startsWith(QL1C('!')); +} + +bool AdBlockRule::isEnabled() const +{ + return m_isEnabled; +} + +void AdBlockRule::setEnabled(bool enabled) +{ + m_isEnabled = enabled; +} + +bool AdBlockRule::isSlow() const +{ + return m_regExp != 0; +} + +bool AdBlockRule::isInternalDisabled() const +{ + return m_isInternalDisabled; +} + +bool AdBlockRule::urlMatch(const QUrl &url) const +{ + if (!hasOption(DocumentOption) && !hasOption(ElementHideOption)) { + return false; + } + + + const QString encodedUrl = url.toEncoded(); + const QString domain = url.host(); + + return stringMatch(domain, encodedUrl); +} + +bool AdBlockRule::networkMatch(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &encodedUrl) const +{ + if (m_type == CssRule || !m_isEnabled || m_isInternalDisabled) { + return false; + } + + bool matched = stringMatch(domain, encodedUrl); + + if (matched) { + // Check domain restrictions + if (hasOption(DomainRestrictedOption) && !matchDomain(request.firstPartyUrl().host())) { + return false; + } + + // Check third-party restriction + if (hasOption(ThirdPartyOption) && !matchThirdParty(request)) { + return false; + } + + // Check object restrictions + if (hasOption(ObjectOption) && !matchObject(request)) { + return false; + } + + // Check subdocument restriction + if (hasOption(SubdocumentOption) && !matchSubdocument(request)) { + return false; + } + + // Check xmlhttprequest restriction + if (hasOption(XMLHttpRequestOption) && !matchXmlHttpRequest(request)) { + return false; + } + + // Check image restriction + if (hasOption(ImageOption) && !matchImage(request)) { + return false; + } + + // Check script restriction + if (hasOption(ScriptOption) && !matchScript(request)) { + return false; + } + + // Check stylesheet restriction + if (hasOption(StyleSheetOption) && !matchStyleSheet(request)) { + return false; + } + + // Check object-subrequest restriction + if (hasOption(ObjectSubrequestOption) && !matchObjectSubrequest(request)) { + return false; + } + } + + return matched; +} + +bool AdBlockRule::matchDomain(const QString &domain) const +{ + if (!m_isEnabled) { + return false; + } + + if (!hasOption(DomainRestrictedOption)) { + return true; + } + + if (m_blockedDomains.isEmpty()) { + foreach (const QString &d, m_allowedDomains) { + if (isMatchingDomain(domain, d)) { + return true; + } + } + } + else if (m_allowedDomains.isEmpty()) { + foreach (const QString &d, m_blockedDomains) { + if (isMatchingDomain(domain, d)) { + return false; + } + } + return true; + } + else { + foreach (const QString &d, m_blockedDomains) { + if (isMatchingDomain(domain, d)) { + return false; + } + } + + foreach (const QString &d, m_allowedDomains) { + if (isMatchingDomain(domain, d)) { + return true; + } + } + } + + return false; +} + +bool AdBlockRule::matchThirdParty(const QWebEngineUrlRequestInfo &request) const +{ + // Third-party matching should be performed on second-level domains + const QString firstPartyHost = toSecondLevelDomain(request.firstPartyUrl()); + const QString host = toSecondLevelDomain(request.requestUrl()); + + bool match = firstPartyHost != host; + + return hasException(ThirdPartyOption) ? !match : match; +} + +bool AdBlockRule::matchObject(const QWebEngineUrlRequestInfo &request) const +{ + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeObject; + + return hasException(ObjectOption) ? !match : match; +} + +bool AdBlockRule::matchSubdocument(const QWebEngineUrlRequestInfo &request) const +{ + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubFrame; + + return hasException(SubdocumentOption) ? !match : match; +} + +bool AdBlockRule::matchXmlHttpRequest(const QWebEngineUrlRequestInfo &request) const +{ + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeXhr; + + return hasException(XMLHttpRequestOption) ? !match : match; +} + +bool AdBlockRule::matchImage(const QWebEngineUrlRequestInfo &request) const +{ + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeImage; + + return hasException(ImageOption) ? !match : match; +} + +bool AdBlockRule::matchScript(const QWebEngineUrlRequestInfo &request) const +{ + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeScript; + + return hasException(ScriptOption) ? !match : match; +} + +bool AdBlockRule::matchStyleSheet(const QWebEngineUrlRequestInfo &request) const +{ + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeStylesheet; + + return hasException(StyleSheetOption) ? !match : match; +} + +bool AdBlockRule::matchObjectSubrequest(const QWebEngineUrlRequestInfo &request) const +{ + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubResource; + + return hasException(ObjectSubrequestOption) ? !match : match; +} + +void AdBlockRule::parseFilter() +{ + QString parsedLine = m_filter; + + // Empty rule or just comment + if (m_filter.trimmed().isEmpty() || m_filter.startsWith(QL1C('!'))) { + // We want to differentiate rule disabled by user and rule disabled in subscription file + // m_isInternalDisabled is also used when rule is disabled due to all options not being supported + m_isEnabled = false; + m_isInternalDisabled = true; + m_type = Invalid; + return; + } + + // CSS Element hiding rule + if (parsedLine.contains(QL1S("##")) || parsedLine.contains(QL1S("#@#"))) { + m_type = CssRule; + int pos = parsedLine.indexOf(QL1C('#')); + + // Domain restricted rule + if (!parsedLine.startsWith(QL1S("##"))) { + QString domains = parsedLine.left(pos); + parseDomains(domains, QL1C(',')); + } + + m_isException = parsedLine.at(pos + 1) == QL1C('@'); + m_matchString = parsedLine.mid(m_isException ? pos + 3 : pos + 2); + + // CSS rule cannot have more options -> stop parsing + return; + } + + // Exception always starts with @@ + if (parsedLine.startsWith(QL1S("@@"))) { + m_isException = true; + parsedLine = parsedLine.mid(2); + } + + // Parse all options following $ char + int optionsIndex = parsedLine.indexOf(QL1C('$')); + if (optionsIndex >= 0) { + const QStringList options = parsedLine.mid(optionsIndex + 1).split(QL1C(','), QString::SkipEmptyParts); + + int handledOptions = 0; + foreach (const QString &option, options) { + if (option.startsWith(QL1S("domain="))) { + parseDomains(option.mid(7), QL1C('|')); + ++handledOptions; + } + else if (option == QL1S("match-case")) { + m_caseSensitivity = Qt::CaseSensitive; + ++handledOptions; + } + else if (option.endsWith(QL1S("third-party"))) { + setOption(ThirdPartyOption); + setException(ThirdPartyOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option.endsWith(QL1S("object"))) { + setOption(ObjectOption); + setException(ObjectOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option.endsWith(QL1S("subdocument"))) { + setOption(SubdocumentOption); + setException(SubdocumentOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option.endsWith(QL1S("xmlhttprequest"))) { + setOption(XMLHttpRequestOption); + setException(XMLHttpRequestOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option.endsWith(QL1S("image"))) { + setOption(ImageOption); + setException(ImageOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option.endsWith(QL1S("script"))) { + setOption(ScriptOption); + setException(ScriptOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option.endsWith(QL1S("stylesheet"))) { + setOption(StyleSheetOption); + setException(StyleSheetOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option.endsWith(QL1S("object-subrequest"))) { + setOption(ObjectSubrequestOption); + setException(ObjectSubrequestOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } + else if (option == QL1S("document") && m_isException) { + setOption(DocumentOption); + ++handledOptions; + } + else if (option == QL1S("elemhide") && m_isException) { + setOption(ElementHideOption); + ++handledOptions; + } + else if (option == QL1S("collapse")) { + // Hiding placeholders of blocked elements is enabled by default + ++handledOptions; + } + } + + // If we don't handle all options, it's safer to just disable this rule + if (handledOptions != options.count()) { + m_isInternalDisabled = true; + m_type = Invalid; + return; + } + + parsedLine = parsedLine.left(optionsIndex); + } + + // Rule is classic regexp + if (parsedLine.startsWith(QL1C('/')) && parsedLine.endsWith(QL1C('/'))) { + parsedLine = parsedLine.mid(1); + parsedLine = parsedLine.left(parsedLine.size() - 1); + + m_type = RegExpMatchRule; + m_regExp = new RegExp; + m_regExp->regExp = QzRegExp(parsedLine, m_caseSensitivity); + m_regExp->matchers = createStringMatchers(parseRegExpFilter(parsedLine)); + return; + } + + // Remove starting and ending wildcards (*) + if (parsedLine.startsWith(QL1C('*'))) { + parsedLine = parsedLine.mid(1); + } + + if (parsedLine.endsWith(QL1C('*'))) { + parsedLine = parsedLine.left(parsedLine.size() - 1); + } + + // We can use fast string matching for domain here + if (filterIsOnlyDomain(parsedLine)) { + parsedLine = parsedLine.mid(2); + parsedLine = parsedLine.left(parsedLine.size() - 1); + + m_type = DomainMatchRule; + m_matchString = parsedLine; + return; + } + + // If rule contains only | at end, we can also use string matching + if (filterIsOnlyEndsMatch(parsedLine)) { + parsedLine = parsedLine.left(parsedLine.size() - 1); + + m_type = StringEndsMatchRule; + m_matchString = parsedLine; + return; + } + + // If we still find a wildcard (*) or separator (^) or (|) + // we must modify parsedLine to comply with QzRegExp + if (parsedLine.contains(QL1C('*')) || + parsedLine.contains(QL1C('^')) || + parsedLine.contains(QL1C('|')) + ) { + m_type = RegExpMatchRule; + m_regExp = new RegExp; + m_regExp->regExp = QzRegExp(createRegExpFromFilter(parsedLine), m_caseSensitivity); + m_regExp->matchers = createStringMatchers(parseRegExpFilter(parsedLine)); + return; + } + + // We haven't found anything that needs use of regexp, yay! + m_type = StringContainsMatchRule; + m_matchString = parsedLine; +} + +void AdBlockRule::parseDomains(const QString &domains, const QChar &separator) +{ + QStringList domainsList = domains.split(separator, QString::SkipEmptyParts); + + foreach (const QString domain, domainsList) { + if (domain.isEmpty()) { + continue; + } + if (domain.startsWith(QL1C('~'))) { + m_blockedDomains.append(domain.mid(1)); + } + else { + m_allowedDomains.append(domain); + } + } + + if (!m_blockedDomains.isEmpty() || !m_allowedDomains.isEmpty()) { + setOption(DomainRestrictedOption); + } +} + +bool AdBlockRule::filterIsOnlyDomain(const QString &filter) const +{ + if (!filter.endsWith(QL1C('^')) || !filter.startsWith(QL1S("||"))) + return false; + + for (int i = 0; i < filter.size(); ++i) { + switch (filter.at(i).toLatin1()) { + case '/': + case ':': + case '?': + case '=': + case '&': + case '*': + return false; + default: + break; + } + } + + return true; +} + +bool AdBlockRule::filterIsOnlyEndsMatch(const QString &filter) const +{ + for (int i = 0; i < filter.size(); ++i) { + switch (filter.at(i).toLatin1()) { + case '^': + case '*': + return false; + case '|': + return i == filter.size() - 1; + default: + break; + } + } + + return false; +} + +static bool wordCharacter(const QChar &c) +{ + return c.isLetterOrNumber() || c.isMark() || c == QL1C('_'); +} + +QString AdBlockRule::createRegExpFromFilter(const QString &filter) const +{ + QString parsed; + parsed.reserve(filter.size()); + + bool hadWildcard = false; // Filter multiple wildcards + + for (int i = 0; i < filter.size(); ++i) { + const QChar c = filter.at(i); + switch (c.toLatin1()) { + case '^': + parsed.append(QL1S("(?:[^\\w\\d\\-.%]|$)")); + break; + + case '*': + if (!hadWildcard) + parsed.append(QL1S(".*")); + break; + + case '|': + if (i == 0) { + if (filter.size() > 1 && filter.at(1) == QL1C('|')) { + parsed.append(QL1S("^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?")); + i++; + } + else { + parsed.append('^'); + } + break; + } + else if (i == filter.size() - 1) { + parsed.append(QL1C('$')); + break; + } + // fallthrough + + default: + if (!wordCharacter(c)) + parsed.append(QL1C('\\') + c); + else + parsed.append(c); + } + + hadWildcard = c == QL1C('*'); + } + + return parsed; +} + +QList AdBlockRule::createStringMatchers(const QStringList &filters) const +{ + QList matchers; + matchers.reserve(filters.size()); + + foreach (const QString &filter, filters) { + matchers.append(QStringMatcher(filter, m_caseSensitivity)); + } + + return matchers; +} + +bool AdBlockRule::stringMatch(const QString &domain, const QString &encodedUrl) const +{ + if (m_type == StringContainsMatchRule) { + return encodedUrl.contains(m_matchString, m_caseSensitivity); + } + else if (m_type == DomainMatchRule) { + return isMatchingDomain(domain, m_matchString); + } + else if (m_type == StringEndsMatchRule) { + return encodedUrl.endsWith(m_matchString, m_caseSensitivity); + } + else if (m_type == RegExpMatchRule) { + if (!isMatchingRegExpStrings(encodedUrl)) { + return false; + } + return (m_regExp->regExp.indexIn(encodedUrl) != -1); + } + + return false; +} + +bool AdBlockRule::isMatchingDomain(const QString &domain, const QString &filter) const +{ + return QzTools::matchDomain(filter, domain); +} + +bool AdBlockRule::isMatchingRegExpStrings(const QString &url) const +{ + Q_ASSERT(m_regExp); + + foreach (const QStringMatcher &matcher, m_regExp->matchers) { + if (matcher.indexIn(url) == -1) + return false; + } + + return true; +} + +// Split regexp filter into strings that can be used with QString::contains +// Don't use parts that contains only 1 char and duplicated parts +QStringList AdBlockRule::parseRegExpFilter(const QString &filter) const +{ + QStringList list; + int startPos = -1; + + for (int i = 0; i < filter.size(); ++i) { + const QChar c = filter.at(i); + // Meta characters in AdBlock rules are | * ^ + if (c == QL1C('|') || c == QL1C('*') || c == QL1C('^')) { + const QString sub = filter.mid(startPos, i - startPos); + if (sub.size() > 1) + list.append(sub); + startPos = i + 1; + } + } + + const QString sub = filter.mid(startPos); + if (sub.size() > 1) + list.append(sub); + + list.removeDuplicates(); + + return list; +} + +bool AdBlockRule::hasOption(const AdBlockRule::RuleOption &opt) const +{ + return (m_options & opt); +} + +bool AdBlockRule::hasException(const AdBlockRule::RuleOption &opt) const +{ + return (m_exceptions & opt); +} + +void AdBlockRule::setOption(const AdBlockRule::RuleOption &opt) +{ + m_options |= opt; +} + +void AdBlockRule::setException(const AdBlockRule::RuleOption &opt, bool on) +{ + if (on) { + m_exceptions |= opt; + } +} diff --git a/src/network-web/adblock/adblockrule.h b/src/network-web/adblock/adblockrule.h new file mode 100755 index 000000000..84be0814a --- /dev/null +++ b/src/network-web/adblock/adblockrule.h @@ -0,0 +1,187 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2014 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 . +* ============================================================ */ +/** + * Copyright (c) 2009, Benjamin C. Meyer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Benjamin Meyer nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ADBLOCKRULE_H +#define ADBLOCKRULE_H + +#include +#include +#include + +#include "qzcommon.h" +#include "qzregexp.h" + +class QUrl; +class QWebEngineUrlRequestInfo; + +class AdBlockSubscription; + +class QUPZILLA_EXPORT AdBlockRule +{ + Q_DISABLE_COPY(AdBlockRule) + +public: + AdBlockRule(const QString &filter = QString(), AdBlockSubscription* subscription = 0); + ~AdBlockRule(); + + AdBlockRule* copy() const; + + AdBlockSubscription* subscription() const; + void setSubscription(AdBlockSubscription* subscription); + + QString filter() const; + void setFilter(const QString &filter); + + bool isCssRule() const; + QString cssSelector() const; + + bool isDocument() const; + bool isElemhide() const; + + bool isDomainRestricted() const; + bool isException() const; + + bool isComment() const; + bool isEnabled() const; + void setEnabled(bool enabled); + + bool isSlow() const; + bool isInternalDisabled() const; + + bool urlMatch(const QUrl &url) const; + bool networkMatch(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &encodedUrl) const; + + bool matchDomain(const QString &domain) const; + bool matchThirdParty(const QWebEngineUrlRequestInfo &request) const; + bool matchObject(const QWebEngineUrlRequestInfo &request) const; + bool matchSubdocument(const QWebEngineUrlRequestInfo &request) const; + bool matchXmlHttpRequest(const QWebEngineUrlRequestInfo &request) const; + bool matchImage(const QWebEngineUrlRequestInfo &request) const; + bool matchScript(const QWebEngineUrlRequestInfo &request) const; + bool matchStyleSheet(const QWebEngineUrlRequestInfo &request) const; + bool matchObjectSubrequest(const QWebEngineUrlRequestInfo &request) const; + +protected: + bool stringMatch(const QString &domain, const QString &encodedUrl) const; + bool isMatchingDomain(const QString &domain, const QString &filter) const; + bool isMatchingRegExpStrings(const QString &url) const; + QStringList parseRegExpFilter(const QString &filter) const; + +private: + enum RuleType { + CssRule = 0, + DomainMatchRule = 1, + RegExpMatchRule = 2, + StringEndsMatchRule = 3, + StringContainsMatchRule = 4, + Invalid = 5 + }; + + enum RuleOption { + DomainRestrictedOption = 1, + ThirdPartyOption = 2, + ObjectOption = 4, + SubdocumentOption = 8, + XMLHttpRequestOption = 16, + ImageOption = 32, + ScriptOption = 64, + StyleSheetOption = 128, + ObjectSubrequestOption = 256, + + // Exception only options + DocumentOption = 1024, + ElementHideOption = 2048 + }; + + Q_DECLARE_FLAGS(RuleOptions, RuleOption) + + inline bool hasOption(const RuleOption &opt) const; + inline bool hasException(const RuleOption &opt) const; + + inline void setOption(const RuleOption &opt); + inline void setException(const RuleOption &opt, bool on); + + void parseFilter(); + void parseDomains(const QString &domains, const QChar &separator); + bool filterIsOnlyDomain(const QString &filter) const; + bool filterIsOnlyEndsMatch(const QString &filter) const; + QString createRegExpFromFilter(const QString &filter) const; + QList createStringMatchers(const QStringList &filters) const; + + AdBlockSubscription* m_subscription; + + RuleType m_type; + RuleOptions m_options; + RuleOptions m_exceptions; + + // Original rule filter + QString m_filter; + // Parsed rule for string matching (CSS Selector for CSS rules) + QString m_matchString; + // Case sensitivity for string matching + Qt::CaseSensitivity m_caseSensitivity; + + bool m_isEnabled; + bool m_isException; + bool m_isInternalDisabled; + + QStringList m_allowedDomains; + QStringList m_blockedDomains; + + struct RegExp { + QzRegExp regExp; + QList matchers; + }; + + // Use dynamic allocation to save memory + RegExp* m_regExp; + + friend class AdBlockMatcher; + friend class AdBlockSearchTree; + friend class AdBlockSubscription; +}; + +#endif // ADBLOCKRULE_H + diff --git a/src/network-web/adblock/adblocksearchtree.cpp b/src/network-web/adblock/adblocksearchtree.cpp new file mode 100755 index 000000000..de6317292 --- /dev/null +++ b/src/network-web/adblock/adblocksearchtree.cpp @@ -0,0 +1,137 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2013-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 +* 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 "adblocksearchtree.h" +#include "adblockrule.h" + +#include + +AdBlockSearchTree::AdBlockSearchTree() + : m_root(new Node) +{ +} + +AdBlockSearchTree::~AdBlockSearchTree() +{ + deleteNode(m_root); +} + +void AdBlockSearchTree::clear() +{ + deleteNode(m_root); + m_root = new Node; +} + +bool AdBlockSearchTree::add(const AdBlockRule* rule) +{ + if (rule->m_type != AdBlockRule::StringContainsMatchRule) { + return false; + } + + const QString filter = rule->m_matchString; + int len = filter.size(); + + if (len <= 0) { + qDebug() << "AdBlockSearchTree: Inserting rule with filter len <= 0!"; + return false; + } + + Node* node = m_root; + + for (int i = 0; i < len; ++i) { + const QChar c = filter.at(i); + Node *next = node->children.value(c); + if (!next) { + next = new Node; + next->c = c; + node->children[c] = next; + } + node = next; + } + + node->rule = rule; + + return true; +} + +const AdBlockRule* AdBlockSearchTree::find(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &urlString) const +{ + int len = urlString.size(); + + if (len <= 0) { + return 0; + } + + const QChar* string = urlString.constData(); + + for (int i = 0; i < len; ++i) { + const AdBlockRule* rule = prefixSearch(request, domain, urlString, string++, len - i); + if (rule) { + return rule; + } + } + + return 0; +} + +const AdBlockRule* AdBlockSearchTree::prefixSearch(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &urlString, const QChar* string, int len) const +{ + if (len <= 0) { + return 0; + } + + QChar c = string[0]; + + Node* node = m_root->children.value(c); + if (!node) { + return nullptr; + } + + for (int i = 1; i < len; ++i) { + const QChar c = (++string)[0]; + + if (node->rule && node->rule->networkMatch(request, domain, urlString)) { + return node->rule; + } + + node = node->children.value(c); + if (!node) { + return nullptr; + } + } + + if (node->rule && node->rule->networkMatch(request, domain, urlString)) { + return node->rule; + } + + return nullptr; +} + +void AdBlockSearchTree::deleteNode(AdBlockSearchTree::Node* node) +{ + if (!node) { + return; + } + + QHashIterator i(node->children); + while (i.hasNext()) { + i.next(); + deleteNode(i.value()); + } + + delete node; +} diff --git a/src/network-web/adblock/adblocksearchtree.h b/src/network-web/adblock/adblocksearchtree.h new file mode 100755 index 000000000..878a588f9 --- /dev/null +++ b/src/network-web/adblock/adblocksearchtree.h @@ -0,0 +1,58 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2013-2014 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 ADBLOCKSEARCHTREE_H +#define ADBLOCKSEARCHTREE_H + +#include +#include + +#include "qzcommon.h" + +class QWebEngineUrlRequestInfo; + +class AdBlockRule; + +class QUPZILLA_EXPORT AdBlockSearchTree +{ +public: + explicit AdBlockSearchTree(); + ~AdBlockSearchTree(); + + void clear(); + + bool add(const AdBlockRule* rule); + const AdBlockRule* find(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &urlString) const; + +private: + struct Node { + QChar c; + const AdBlockRule* rule; + QHash children; + + Node() : c(0) , rule(0) { } + }; + + const AdBlockRule* prefixSearch(const QWebEngineUrlRequestInfo &request, const QString &domain, + const QString &urlString, const QChar* string, int len) const; + + void deleteNode(Node* node); + + Node* m_root; +}; + +#endif // ADBLOCKSEARCHTREE_H diff --git a/src/network-web/adblock/adblocksubscription.cpp b/src/network-web/adblock/adblocksubscription.cpp new file mode 100755 index 000000000..b4b964fb0 --- /dev/null +++ b/src/network-web/adblock/adblocksubscription.cpp @@ -0,0 +1,426 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2010-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 +* 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 . +* ============================================================ */ +/** + * Copyright (c) 2009, Benjamin C. Meyer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Benjamin Meyer nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "adblocksubscription.h" +#include "adblockmanager.h" +#include "adblocksearchtree.h" +#include "mainapplication.h" +#include "networkmanager.h" +#include "datapaths.h" +#include "qztools.h" + +#include +#include +#include +#include + +AdBlockSubscription::AdBlockSubscription(const QString &title, QObject* parent) + : QObject(parent) + , m_reply(0) + , m_title(title) + , m_updated(false) +{ +} + +QString AdBlockSubscription::title() const +{ + return m_title; +} + +QString AdBlockSubscription::filePath() const +{ + return m_filePath; +} + +void AdBlockSubscription::setFilePath(const QString &path) +{ + m_filePath = path; +} + +QUrl AdBlockSubscription::url() const +{ + return m_url; +} + +void AdBlockSubscription::setUrl(const QUrl &url) +{ + m_url = url; +} + +void AdBlockSubscription::loadSubscription(const QStringList &disabledRules) +{ + QFile file(m_filePath); + + if (!file.exists()) { + QTimer::singleShot(0, this, SLOT(updateSubscription())); + return; + } + + if (!file.open(QFile::ReadOnly)) { + qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for reading" << m_filePath; + QTimer::singleShot(0, this, SLOT(updateSubscription())); + return; + } + + QTextStream textStream(&file); + textStream.setCodec("UTF-8"); + // Header is on 3rd line + textStream.readLine(1024); + textStream.readLine(1024); + QString header = textStream.readLine(1024); + + if (!header.startsWith(QLatin1String("[Adblock")) || m_title.isEmpty()) { + qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "invalid format of adblock file" << m_filePath; + QTimer::singleShot(0, this, SLOT(updateSubscription())); + return; + } + + m_rules.clear(); + + while (!textStream.atEnd()) { + AdBlockRule* rule = new AdBlockRule(textStream.readLine(), this); + + if (disabledRules.contains(rule->filter())) { + rule->setEnabled(false); + } + + m_rules.append(rule); + } + + // Initial update + if (m_rules.isEmpty() && !m_updated) { + QTimer::singleShot(0, this, SLOT(updateSubscription())); + } +} + +void AdBlockSubscription::saveSubscription() +{ +} + +void AdBlockSubscription::updateSubscription() +{ + if (m_reply || !m_url.isValid()) { + return; + } + + m_reply = mApp->networkManager()->get(QNetworkRequest(m_url)); + connect(m_reply, &QNetworkReply::finished, this, &AdBlockSubscription::subscriptionDownloaded); +} + +void AdBlockSubscription::subscriptionDownloaded() +{ + if (m_reply != qobject_cast(sender())) { + return; + } + + bool error = false; + const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); + + if (m_reply->error() != QNetworkReply::NoError || + !response.startsWith(QByteArray("[Adblock")) || + !saveDownloadedData(response) + ) { + error = true; + } + + m_reply->deleteLater(); + m_reply = 0; + + if (error) { + emit subscriptionError(tr("Cannot load subscription!")); + return; + } + + loadSubscription(AdBlockManager::instance()->disabledRules()); + + emit subscriptionUpdated(); + emit subscriptionChanged(); +} + +bool AdBlockSubscription::saveDownloadedData(const QByteArray &data) +{ + QSaveFile file(m_filePath); + + if (!file.open(QFile::WriteOnly)) { + qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for writing:" << m_filePath; + return false; + } + + // Write subscription header + file.write(QString("Title: %1\nUrl: %2\n").arg(title(), url().toString()).toUtf8()); + file.write(data); + file.commit(); + return true; +} + +const AdBlockRule* AdBlockSubscription::rule(int offset) const +{ + if (!QzTools::containsIndex(m_rules, offset)) { + return 0; + } + + return m_rules[offset]; +} + +QVector AdBlockSubscription::allRules() const +{ + return m_rules; +} + +const AdBlockRule* AdBlockSubscription::enableRule(int offset) +{ + if (!QzTools::containsIndex(m_rules, offset)) { + return 0; + } + + AdBlockRule* rule = m_rules[offset]; + rule->setEnabled(true); + AdBlockManager::instance()->removeDisabledRule(rule->filter()); + + emit subscriptionChanged(); + + if (rule->isCssRule()) + mApp->reloadUserStyleSheet(); + + return rule; +} + +const AdBlockRule* AdBlockSubscription::disableRule(int offset) +{ + if (!QzTools::containsIndex(m_rules, offset)) { + return 0; + } + + AdBlockRule* rule = m_rules[offset]; + rule->setEnabled(false); + AdBlockManager::instance()->addDisabledRule(rule->filter()); + + emit subscriptionChanged(); + + if (rule->isCssRule()) + mApp->reloadUserStyleSheet(); + + return rule; +} + +bool AdBlockSubscription::canEditRules() const +{ + return false; +} + +bool AdBlockSubscription::canBeRemoved() const +{ + return true; +} + +int AdBlockSubscription::addRule(AdBlockRule* rule) +{ + Q_UNUSED(rule) + return -1; +} + +bool AdBlockSubscription::removeRule(int offset) +{ + Q_UNUSED(offset) + return false; +} + +const AdBlockRule* AdBlockSubscription::replaceRule(AdBlockRule* rule, int offset) +{ + Q_UNUSED(rule) + Q_UNUSED(offset) + return 0; +} + +AdBlockSubscription::~AdBlockSubscription() +{ + qDeleteAll(m_rules); +} + +// AdBlockCustomList + +AdBlockCustomList::AdBlockCustomList(QObject* parent) + : AdBlockSubscription(tr("Custom Rules"), parent) +{ + setFilePath(DataPaths::currentProfilePath() + QLatin1String("/adblock/customlist.txt")); +} + +void AdBlockCustomList::loadSubscription(const QStringList &disabledRules) +{ + // DuckDuckGo ad whitelist rules + // They cannot be removed, but can be disabled. + // Please consider not disabling them. Thanks! + + const QString ddg1 = QSL("@@||duckduckgo.com^$document"); + const QString ddg2 = QSL("duckduckgo.com#@#.has-ad"); + + const QString rules = QzTools::readAllFileContents(filePath()); + + QFile file(filePath()); + if (!file.exists()) { + saveSubscription(); + } + + if (file.open(QFile::WriteOnly | QFile::Append)) { + QTextStream stream(&file); + stream.setCodec("UTF-8"); + + if (!rules.contains(ddg1 + QL1S("\n"))) + stream << ddg1 << endl; + + if (!rules.contains(QL1S("\n") + ddg2)) + stream << ddg2 << endl; + } + file.close(); + + AdBlockSubscription::loadSubscription(disabledRules); +} + +void AdBlockCustomList::saveSubscription() +{ + QFile file(filePath()); + + if (!file.open(QFile::ReadWrite | QFile::Truncate)) { + qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for writing:" << filePath(); + return; + } + + QTextStream textStream(&file); + textStream.setCodec("UTF-8"); + textStream << "Title: " << title() << endl; + textStream << "Url: " << url().toString() << endl; + textStream << "[Adblock Plus 1.1.1]" << endl; + + foreach (const AdBlockRule* rule, m_rules) { + textStream << rule->filter() << endl; + } + + file.close(); +} + +bool AdBlockCustomList::canEditRules() const +{ + return true; +} + +bool AdBlockCustomList::canBeRemoved() const +{ + return false; +} + +bool AdBlockCustomList::containsFilter(const QString &filter) const +{ + foreach (const AdBlockRule* rule, m_rules) { + if (rule->filter() == filter) { + return true; + } + } + + return false; +} + +bool AdBlockCustomList::removeFilter(const QString &filter) +{ + for (int i = 0; i < m_rules.count(); ++i) { + const AdBlockRule* rule = m_rules.at(i); + + if (rule->filter() == filter) { + return removeRule(i); + } + } + + return false; +} + +int AdBlockCustomList::addRule(AdBlockRule* rule) +{ + m_rules.append(rule); + + emit subscriptionChanged(); + + if (rule->isCssRule()) + mApp->reloadUserStyleSheet(); + + return m_rules.count() - 1; +} + +bool AdBlockCustomList::removeRule(int offset) +{ + if (!QzTools::containsIndex(m_rules, offset)) { + return false; + } + + AdBlockRule* rule = m_rules.at(offset); + const QString filter = rule->filter(); + + m_rules.remove(offset); + + emit subscriptionChanged(); + + if (rule->isCssRule()) + mApp->reloadUserStyleSheet(); + + AdBlockManager::instance()->removeDisabledRule(filter); + + delete rule; + return true; +} + +const AdBlockRule* AdBlockCustomList::replaceRule(AdBlockRule* rule, int offset) +{ + if (!QzTools::containsIndex(m_rules, offset)) { + return 0; + } + + AdBlockRule* oldRule = m_rules.at(offset); + m_rules[offset] = rule; + + emit subscriptionChanged(); + + if (rule->isCssRule() || oldRule->isCssRule()) + mApp->reloadUserStyleSheet(); + + delete oldRule; + return m_rules[offset]; +} diff --git a/src/network-web/adblock/adblocksubscription.h b/src/network-web/adblock/adblocksubscription.h new file mode 100755 index 000000000..d66140f82 --- /dev/null +++ b/src/network-web/adblock/adblocksubscription.h @@ -0,0 +1,136 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2016 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 . +* ============================================================ */ +/** + * Copyright (c) 2009, Benjamin C. Meyer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Benjamin Meyer nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ADBLOCKSUBSCRIPTION_H +#define ADBLOCKSUBSCRIPTION_H + +#include +#include + +#include "qzcommon.h" +#include "adblockrule.h" +#include "adblocksearchtree.h" + +class QUrl; +class QNetworkReply; + +class QUPZILLA_EXPORT AdBlockSubscription : public QObject +{ + Q_OBJECT +public: + explicit AdBlockSubscription(const QString &title, QObject* parent = 0); + ~AdBlockSubscription(); + + QString title() const; + + QString filePath() const; + void setFilePath(const QString &path); + + QUrl url() const; + void setUrl(const QUrl &url); + + virtual void loadSubscription(const QStringList &disabledRules); + virtual void saveSubscription(); + + const AdBlockRule* rule(int offset) const; + QVector allRules() const; + + const AdBlockRule* enableRule(int offset); + const AdBlockRule* disableRule(int offset); + + virtual bool canEditRules() const; + virtual bool canBeRemoved() const; + + virtual int addRule(AdBlockRule* rule); + virtual bool removeRule(int offset); + virtual const AdBlockRule* replaceRule(AdBlockRule* rule, int offset); + +public slots: + void updateSubscription(); + +signals: + void subscriptionChanged(); + void subscriptionUpdated(); + void subscriptionError(const QString &message); + +protected slots: + void subscriptionDownloaded(); + +protected: + virtual bool saveDownloadedData(const QByteArray &data); + + QNetworkReply *m_reply; + QVector m_rules; + +private: + QString m_title; + QString m_filePath; + + QUrl m_url; + bool m_updated; +}; + +class AdBlockCustomList : public AdBlockSubscription +{ + Q_OBJECT +public: + explicit AdBlockCustomList(QObject* parent = 0); + + void loadSubscription(const QStringList &disabledRules); + void saveSubscription(); + + bool canEditRules() const; + bool canBeRemoved() const; + + bool containsFilter(const QString &filter) const; + bool removeFilter(const QString &filter); + + int addRule(AdBlockRule* rule); + bool removeRule(int offset); + const AdBlockRule* replaceRule(AdBlockRule* rule, int offset); +}; + +#endif // ADBLOCKSUBSCRIPTION_H + diff --git a/src/network-web/adblock/adblocktreewidget.cpp b/src/network-web/adblock/adblocktreewidget.cpp new file mode 100755 index 000000000..317e4b61a --- /dev/null +++ b/src/network-web/adblock/adblocktreewidget.cpp @@ -0,0 +1,266 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-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 +* 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 "adblocktreewidget.h" +#include "adblocksubscription.h" + +#include +#include +#include +#include +#include + +AdBlockTreeWidget::AdBlockTreeWidget(AdBlockSubscription* subscription, QWidget* parent) + : TreeWidget(parent) + , m_subscription(subscription) + , m_topItem(0) + , m_itemChangingBlock(false) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + setDefaultItemShowMode(TreeWidget::ItemsExpanded); + setHeaderHidden(true); + setAlternatingRowColors(true); + setLayoutDirection(Qt::LeftToRight); + + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); + connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + connect(m_subscription, SIGNAL(subscriptionUpdated()), this, SLOT(subscriptionUpdated())); + connect(m_subscription, SIGNAL(subscriptionError(QString)), this, SLOT(subscriptionError(QString))); +} + +AdBlockSubscription* AdBlockTreeWidget::subscription() const +{ + return m_subscription; +} + +void AdBlockTreeWidget::showRule(const AdBlockRule* rule) +{ + if (!m_topItem && rule) { + m_ruleToBeSelected = rule->filter(); + } + else if (!m_ruleToBeSelected.isEmpty()) { + QList items = findItems(m_ruleToBeSelected, Qt::MatchRecursive); + if (!items.isEmpty()) { + QTreeWidgetItem* item = items.at(0); + + setCurrentItem(item); + scrollToItem(item, QAbstractItemView::PositionAtCenter); + } + + m_ruleToBeSelected.clear(); + } +} + +void AdBlockTreeWidget::contextMenuRequested(const QPoint &pos) +{ + if (!m_subscription->canEditRules()) { + return; + } + + QTreeWidgetItem* item = itemAt(pos); + if (!item) { + return; + } + + QMenu menu; + menu.addAction(tr("Add Rule"), this, SLOT(addRule())); + menu.addSeparator(); + QAction* deleteAction = menu.addAction(tr("Remove Rule"), this, SLOT(removeRule())); + + if (!item->parent()) { + deleteAction->setDisabled(true); + } + + menu.exec(viewport()->mapToGlobal(pos)); +} + +void AdBlockTreeWidget::itemChanged(QTreeWidgetItem* item) +{ + if (!item || m_itemChangingBlock) { + return; + } + + m_itemChangingBlock = true; + + int offset = item->data(0, Qt::UserRole + 10).toInt(); + const AdBlockRule* oldRule = m_subscription->rule(offset); + + if (item->checkState(0) == Qt::Unchecked && oldRule->isEnabled()) { + // Disable rule + const AdBlockRule* rule = m_subscription->disableRule(offset); + + adjustItemFeatures(item, rule); + } + else if (item->checkState(0) == Qt::Checked && !oldRule->isEnabled()) { + // Enable rule + const AdBlockRule* rule = m_subscription->enableRule(offset); + + adjustItemFeatures(item, rule); + } + else if (m_subscription->canEditRules()) { + // Custom rule has been changed + AdBlockRule* newRule = new AdBlockRule(item->text(0), m_subscription); + const AdBlockRule* rule = m_subscription->replaceRule(newRule, offset); + + adjustItemFeatures(item, rule); + } + + m_itemChangingBlock = false; +} + +void AdBlockTreeWidget::copyFilter() +{ + QTreeWidgetItem* item = currentItem(); + if (!item) { + return; + } + + QApplication::clipboard()->setText(item->text(0)); +} + +void AdBlockTreeWidget::addRule() +{ + if (!m_subscription->canEditRules()) { + return; + } + + QString newRule = QInputDialog::getText(this, tr("Add Custom Rule"), tr("Please write your rule here:")); + if (newRule.isEmpty()) { + return; + } + + AdBlockRule* rule = new AdBlockRule(newRule, m_subscription); + int offset = m_subscription->addRule(rule); + + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setText(0, newRule); + item->setData(0, Qt::UserRole + 10, offset); + item->setFlags(item->flags() | Qt::ItemIsEditable); + + m_itemChangingBlock = true; + m_topItem->addChild(item); + m_itemChangingBlock = false; + + adjustItemFeatures(item, rule); +} + +void AdBlockTreeWidget::removeRule() +{ + QTreeWidgetItem* item = currentItem(); + if (!item || !m_subscription->canEditRules() || item == m_topItem) { + return; + } + + int offset = item->data(0, Qt::UserRole + 10).toInt(); + + m_subscription->removeRule(offset); + deleteItem(item); +} + +void AdBlockTreeWidget::subscriptionUpdated() +{ + refresh(); + + m_itemChangingBlock = true; + m_topItem->setText(0, tr("%1 (recently updated)").arg(m_subscription->title())); + m_itemChangingBlock = false; +} + +void AdBlockTreeWidget::subscriptionError(const QString &message) +{ + refresh(); + + m_itemChangingBlock = true; + m_topItem->setText(0, tr("%1 (Error: %2)").arg(m_subscription->title(), message)); + m_itemChangingBlock = false; +} + +void AdBlockTreeWidget::adjustItemFeatures(QTreeWidgetItem* item, const AdBlockRule* rule) +{ + if (!rule->isEnabled()) { + QFont font; + font.setItalic(true); + item->setForeground(0, QColor(Qt::gray)); + + if (!rule->isComment()) { + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(0, Qt::Unchecked); + item->setFont(0, font); + } + + return; + } + + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(0, Qt::Checked); + + if (rule->isException()) { + item->setForeground(0, QColor(Qt::darkGreen)); + item->setFont(0, QFont()); + } + else if (rule->isCssRule()) { + item->setForeground(0, QColor(Qt::darkBlue)); + item->setFont(0, QFont()); + } +} + +void AdBlockTreeWidget::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_C && event->modifiers() & Qt::ControlModifier) { + copyFilter(); + } + + if (event->key() == Qt::Key_Delete) { + removeRule(); + } + + TreeWidget::keyPressEvent(event); +} + +void AdBlockTreeWidget::refresh() +{ + m_itemChangingBlock = true; + clear(); + + QFont boldFont; + boldFont.setBold(true); + + m_topItem = new QTreeWidgetItem(this); + m_topItem->setText(0, m_subscription->title()); + m_topItem->setFont(0, boldFont); + m_topItem->setExpanded(true); + addTopLevelItem(m_topItem); + + const QVector &allRules = m_subscription->allRules(); + + int index = 0; + foreach (const AdBlockRule* rule, allRules) { + QTreeWidgetItem* item = new QTreeWidgetItem(m_topItem); + item->setText(0, rule->filter()); + item->setData(0, Qt::UserRole + 10, index); + + if (m_subscription->canEditRules()) { + item->setFlags(item->flags() | Qt::ItemIsEditable); + } + + adjustItemFeatures(item, rule); + ++index; + } + + showRule(0); + m_itemChangingBlock = false; +} diff --git a/src/network-web/adblock/adblocktreewidget.h b/src/network-web/adblock/adblocktreewidget.h new file mode 100755 index 000000000..360035ba8 --- /dev/null +++ b/src/network-web/adblock/adblocktreewidget.h @@ -0,0 +1,61 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2014 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 ADBLOCKTREEWIDGET_H +#define ADBLOCKTREEWIDGET_H + +#include "qzcommon.h" +#include "treewidget.h" + +class AdBlockSubscription; +class AdBlockRule; + +class QUPZILLA_EXPORT AdBlockTreeWidget : public TreeWidget +{ + Q_OBJECT +public: + explicit AdBlockTreeWidget(AdBlockSubscription* subscription, QWidget* parent = 0); + + AdBlockSubscription* subscription() const; + + void showRule(const AdBlockRule* rule); + void refresh(); + +public slots: + void addRule(); + void removeRule(); + +private slots: + void contextMenuRequested(const QPoint &pos); + void itemChanged(QTreeWidgetItem* item); + void copyFilter(); + + void subscriptionUpdated(); + void subscriptionError(const QString &message); + +private: + void adjustItemFeatures(QTreeWidgetItem* item, const AdBlockRule* rule); + void keyPressEvent(QKeyEvent* event); + + AdBlockSubscription* m_subscription; + QTreeWidgetItem* m_topItem; + + QString m_ruleToBeSelected; + bool m_itemChangingBlock; +}; + +#endif // ADBLOCKTREEWIDGET_H diff --git a/src/network-web/adblock/adblockurlinterceptor.cpp b/src/network-web/adblock/adblockurlinterceptor.cpp new file mode 100755 index 000000000..9960c136b --- /dev/null +++ b/src/network-web/adblock/adblockurlinterceptor.cpp @@ -0,0 +1,32 @@ +/* ============================================================ +* QupZilla - QtWebEngine based browser +* Copyright (C) 2015 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 "adblockurlinterceptor.h" +#include "adblockmanager.h" + +AdBlockUrlInterceptor::AdBlockUrlInterceptor(AdBlockManager *manager) + : UrlInterceptor(manager) + , m_manager(manager) +{ +} + +void AdBlockUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) +{ + if (m_manager->block(info)) + info.block(true); +} diff --git a/src/network-web/adblock/adblockurlinterceptor.h b/src/network-web/adblock/adblockurlinterceptor.h new file mode 100755 index 000000000..e87096369 --- /dev/null +++ b/src/network-web/adblock/adblockurlinterceptor.h @@ -0,0 +1,38 @@ +/* ============================================================ +* QupZilla - QtWebEngine based browser +* Copyright (C) 2015 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 ADBLOCKURLINTERCEPTOR_H +#define ADBLOCKURLINTERCEPTOR_H + +#include "urlinterceptor.h" +#include "qzcommon.h" + +class AdBlockManager; + +class QUPZILLA_EXPORT AdBlockUrlInterceptor : public UrlInterceptor +{ +public: + explicit AdBlockUrlInterceptor(AdBlockManager* manager); + + void interceptRequest(QWebEngineUrlRequestInfo &info); + +private: + AdBlockManager *m_manager; +}; + +#endif // ADBLOCKURLINTERCEPTOR_H diff --git a/src/network-web/networkurlinterceptor.cpp b/src/network-web/networkurlinterceptor.cpp new file mode 100755 index 000000000..299c06ca9 --- /dev/null +++ b/src/network-web/networkurlinterceptor.cpp @@ -0,0 +1,59 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2015-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 +* 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 "networkurlinterceptor.h" +#include "urlinterceptor.h" +#include "settings.h" +#include "mainapplication.h" +#include "useragentmanager.h" + +NetworkUrlInterceptor::NetworkUrlInterceptor(QObject *parent) + : QWebEngineUrlRequestInterceptor(parent) + , m_sendDNT(false) +{ +} + +void NetworkUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) +{ + if (m_sendDNT) + info.setHttpHeader(QByteArrayLiteral("DNT"), QByteArrayLiteral("1")); + + info.setHttpHeader(QByteArrayLiteral("User-Agent"), mApp->userAgentManager()->userAgentForUrl(info.firstPartyUrl()).toUtf8()); + + foreach (UrlInterceptor *interceptor, m_interceptors) { + interceptor->interceptRequest(info); + } +} + +void NetworkUrlInterceptor::installUrlInterceptor(UrlInterceptor *interceptor) +{ + if (!m_interceptors.contains(interceptor)) + m_interceptors.append(interceptor); +} + +void NetworkUrlInterceptor::removeUrlInterceptor(UrlInterceptor *interceptor) +{ + m_interceptors.removeOne(interceptor); +} + +void NetworkUrlInterceptor::loadSettings() +{ + Settings settings; + settings.beginGroup("Web-Browser-Settings"); + m_sendDNT = settings.value("DoNotTrack", false).toBool(); + settings.endGroup(); +} diff --git a/src/network-web/networkurlinterceptor.h b/src/network-web/networkurlinterceptor.h new file mode 100755 index 000000000..8f862d7f2 --- /dev/null +++ b/src/network-web/networkurlinterceptor.h @@ -0,0 +1,45 @@ +/* ============================================================ +* QupZilla - QtWebEngine based browser +* Copyright (C) 2015 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 NETWORKURLINTERCEPTOR_H +#define NETWORKURLINTERCEPTOR_H + +#include + +#include "qzcommon.h" + +class UrlInterceptor; + +class QUPZILLA_EXPORT NetworkUrlInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + explicit NetworkUrlInterceptor(QObject* parent = Q_NULLPTR); + + void interceptRequest(QWebEngineUrlRequestInfo &info) Q_DECL_OVERRIDE; + + void installUrlInterceptor(UrlInterceptor *interceptor); + void removeUrlInterceptor(UrlInterceptor *interceptor); + + void loadSettings(); + +private: + QList m_interceptors; + bool m_sendDNT; +}; + +#endif // NETWORKURLINTERCEPTOR_H diff --git a/src/network-web/urlinterceptor.h b/src/network-web/urlinterceptor.h new file mode 100755 index 000000000..ac25bc358 --- /dev/null +++ b/src/network-web/urlinterceptor.h @@ -0,0 +1,32 @@ +/* ============================================================ +* QupZilla - QtWebEngine based browser +* Copyright (C) 2015 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 URLINTERCEPTOR_H +#define URLINTERCEPTOR_H + +#include +#include + +class UrlInterceptor : public QObject +{ +public: + explicit UrlInterceptor(QObject *parent = Q_NULLPTR) : QObject(parent) { } + virtual void interceptRequest(QWebEngineUrlRequestInfo &info) = 0; +}; + +#endif // URLINTERCEPTOR_H