1
mirror of https://invent.kde.org/network/falkon.git synced 2024-11-11 01:22:10 +01:00

AdBlock: Use one cache for all subscriptions

Added new class AdBlockMatcher that holds the cache of
all subscriptions with rules.
This moves the matching logic from AdBlockSubscription into
separate class.
It also fixes issue that CSS exception rules weren't able to
affect rules from different subscription.
This commit is contained in:
David Rosca 2014-10-31 15:39:02 +01:00
parent 613d18ed16
commit a0dd1ddcb7
10 changed files with 357 additions and 311 deletions

View File

@ -50,11 +50,11 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QTimer> #include <QTimer>
AdBlockBlockedNetworkReply::AdBlockBlockedNetworkReply(const AdBlockSubscription* subscription, const AdBlockRule* rule, QObject* parent) AdBlockBlockedNetworkReply::AdBlockBlockedNetworkReply(const AdBlockRule* rule, QObject* parent)
: QNetworkReply(parent) : QNetworkReply(parent)
{ {
setOperation(QNetworkAccessManager::GetOperation); setOperation(QNetworkAccessManager::GetOperation);
setError(QNetworkReply::ContentAccessDenied, QString("AdBlock: %1 (%2)").arg(subscription->title(), rule->filter())); setError(QNetworkReply::ContentAccessDenied, QString("AdBlock: %1 (%2)").arg(rule->subscription()->title(), rule->filter()));
open(QIODevice::ReadOnly); open(QIODevice::ReadOnly);

View File

@ -58,7 +58,7 @@ class QUPZILLA_EXPORT AdBlockBlockedNetworkReply : public QNetworkReply
Q_OBJECT Q_OBJECT
public: public:
AdBlockBlockedNetworkReply(const AdBlockSubscription* subscription, const AdBlockRule* rule, QObject* parent = 0); AdBlockBlockedNetworkReply(const AdBlockRule* rule, QObject* parent = 0);
void abort() {} void abort() {}
void setRequest(const QNetworkRequest &request); void setRequest(const QNetworkRequest &request);

View File

@ -17,6 +17,7 @@
* ============================================================ */ * ============================================================ */
#include "adblockmanager.h" #include "adblockmanager.h"
#include "adblockdialog.h" #include "adblockdialog.h"
#include "adblockmatcher.h"
#include "adblocksubscription.h" #include "adblocksubscription.h"
#include "adblockblockednetworkreply.h" #include "adblockblockednetworkreply.h"
#include "datapaths.h" #include "datapaths.h"
@ -45,6 +46,7 @@ AdBlockManager::AdBlockManager(QObject* parent)
, m_loaded(false) , m_loaded(false)
, m_enabled(true) , m_enabled(true)
, m_useLimitedEasyList(true) , m_useLimitedEasyList(true)
, m_matcher(new AdBlockMatcher(this))
{ {
load(); load();
} }
@ -92,33 +94,30 @@ QNetworkReply* AdBlockManager::block(const QNetworkRequest &request)
const QString urlDomain = request.url().host().toLower(); const QString urlDomain = request.url().host().toLower();
const QString urlScheme = request.url().scheme().toLower(); const QString urlScheme = request.url().scheme().toLower();
if (!isEnabled() || !canRunOnScheme(urlScheme)) { if (!isEnabled() || !canRunOnScheme(urlScheme))
return 0; return 0;
}
foreach (AdBlockSubscription* subscription, m_subscriptions) { const AdBlockRule* blockedRule = m_matcher->match(request, urlDomain, urlString);
const AdBlockRule* blockedRule = subscription->match(request, urlDomain, urlString);
if (blockedRule) { if (blockedRule) {
QVariant v = request.attribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 100)); QVariant v = request.attribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 100));
WebPage* webPage = static_cast<WebPage*>(v.value<void*>()); WebPage* webPage = static_cast<WebPage*>(v.value<void*>());
if (WebPage::isPointerSafeToUse(webPage)) { if (WebPage::isPointerSafeToUse(webPage)) {
if (!canBeBlocked(webPage->url())) { if (!canBeBlocked(webPage->url())) {
return 0; return 0;
}
webPage->addAdBlockRule(blockedRule, request.url());
} }
AdBlockBlockedNetworkReply* reply = new AdBlockBlockedNetworkReply(subscription, blockedRule, this); webPage->addAdBlockRule(blockedRule, request.url());
reply->setRequest(request); }
AdBlockBlockedNetworkReply* reply = new AdBlockBlockedNetworkReply(blockedRule, this);
reply->setRequest(request);
#ifdef ADBLOCK_DEBUG #ifdef ADBLOCK_DEBUG
qDebug() << "BLOCKED: " << timer.elapsed() << blockedRule->filter() << request.url(); qDebug() << "BLOCKED: " << timer.elapsed() << blockedRule->filter() << request.url();
#endif #endif
return reply; return reply;
}
} }
#ifdef ADBLOCK_DEBUG #ifdef ADBLOCK_DEBUG
@ -252,7 +251,6 @@ void AdBlockManager::load()
AdBlockSubscription* subscription = new AdBlockSubscription(title, this); AdBlockSubscription* subscription = new AdBlockSubscription(title, this);
subscription->setUrl(url); subscription->setUrl(url);
subscription->setFilePath(absolutePath); subscription->setFilePath(absolutePath);
connect(subscription, SIGNAL(subscriptionUpdated()), mApp, SLOT(reloadUserStyleSheet()));
m_subscriptions.append(subscription); m_subscriptions.append(subscription);
} }
@ -262,7 +260,6 @@ void AdBlockManager::load()
AdBlockSubscription* easyList = new AdBlockSubscription(tr("EasyList"), this); AdBlockSubscription* easyList = new AdBlockSubscription(tr("EasyList"), this);
easyList->setUrl(QUrl(ADBLOCK_EASYLIST_URL)); easyList->setUrl(QUrl(ADBLOCK_EASYLIST_URL));
easyList->setFilePath(DataPaths::currentProfilePath() + QLatin1String("/adblock/easylist.txt")); easyList->setFilePath(DataPaths::currentProfilePath() + QLatin1String("/adblock/easylist.txt"));
connect(easyList, SIGNAL(subscriptionUpdated()), mApp, SLOT(reloadUserStyleSheet()));
m_subscriptions.prepend(easyList); m_subscriptions.prepend(easyList);
} }
@ -270,11 +267,13 @@ void AdBlockManager::load()
// Append CustomList // Append CustomList
AdBlockCustomList* customList = new AdBlockCustomList(this); AdBlockCustomList* customList = new AdBlockCustomList(this);
m_subscriptions.append(customList); m_subscriptions.append(customList);
connect(customList, SIGNAL(subscriptionEdited()), mApp, SLOT(reloadUserStyleSheet()));
// Load all subscriptions // Load all subscriptions
foreach (AdBlockSubscription* subscription, m_subscriptions) { foreach (AdBlockSubscription* subscription, m_subscriptions) {
subscription->loadSubscription(m_disabledRules); subscription->loadSubscription(m_disabledRules);
connect(subscription, SIGNAL(subscriptionUpdated()), mApp, SLOT(reloadUserStyleSheet()));
connect(subscription, SIGNAL(subscriptionChanged()), m_matcher, SLOT(update()));
} }
if (lastUpdate.addDays(5) < QDateTime::currentDateTime()) { if (lastUpdate.addDays(5) < QDateTime::currentDateTime()) {
@ -285,6 +284,7 @@ void AdBlockManager::load()
qDebug() << "AdBlock loaded in" << timer.elapsed(); qDebug() << "AdBlock loaded in" << timer.elapsed();
#endif #endif
m_matcher->update();
m_loaded = true; m_loaded = true;
} }
@ -348,57 +348,24 @@ void AdBlockManager::setUseLimitedEasyList(bool useLimited)
bool AdBlockManager::canBeBlocked(const QUrl &url) const bool AdBlockManager::canBeBlocked(const QUrl &url) const
{ {
foreach (AdBlockSubscription* subscription, m_subscriptions) { return !m_matcher->adBlockDisabledForUrl(url);
if (subscription->adBlockDisabledForUrl(url)) {
return false;
}
}
return true;
} }
QString AdBlockManager::elementHidingRules() const QString AdBlockManager::elementHidingRules() const
{ {
if (!m_enabled) { return m_matcher->elementHidingRules();
return QString();
}
QString rules;
foreach (AdBlockSubscription* subscription, m_subscriptions) {
rules.append(subscription->elementHidingRules());
}
return rules;
} }
QString AdBlockManager::elementHidingRulesForDomain(const QUrl &url) const QString AdBlockManager::elementHidingRulesForDomain(const QUrl &url) const
{ {
if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) { if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url))
return QString(); return QString();
}
// Acid3 doesn't like the way element hiding rules are embedded into page // Acid3 doesn't like the way element hiding rules are embedded into page
if (url.host() == QLatin1String("acid3.acidtests.org")) { if (url.host() == QLatin1String("acid3.acidtests.org"))
return QString(); return QString();
}
QString rules; return m_matcher->elementHidingRulesForDomain(url.host());
foreach (AdBlockSubscription* subscription, m_subscriptions) {
if (subscription->elemHideDisabledForUrl(url)) {
return QString();
}
rules.append(subscription->elementHidingRulesForDomain(url.host()));
}
// Remove last ","
if (!rules.isEmpty()) {
rules = rules.left(rules.size() - 1);
}
return rules;
} }
AdBlockSubscription* AdBlockManager::subscriptionByName(const QString &name) const AdBlockSubscription* AdBlockManager::subscriptionByName(const QString &name) const

View File

@ -28,7 +28,9 @@ class QUrl;
class QNetworkReply; class QNetworkReply;
class QNetworkRequest; class QNetworkRequest;
class AdBlockRule;
class AdBlockDialog; class AdBlockDialog;
class AdBlockMatcher;
class AdBlockCustomList; class AdBlockCustomList;
class AdBlockSubscription; class AdBlockSubscription;
@ -87,6 +89,7 @@ private:
bool m_useLimitedEasyList; bool m_useLimitedEasyList;
QList<AdBlockSubscription*> m_subscriptions; QList<AdBlockSubscription*> m_subscriptions;
AdBlockMatcher* m_matcher;
QStringList m_disabledRules; QStringList m_disabledRules;
QPointer<AdBlockDialog> m_adBlockDialog; QPointer<AdBlockDialog> m_adBlockDialog;

View File

@ -0,0 +1,228 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2014 David Rosca <nowrep@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "adblockmatcher.h"
#include "adblockmanager.h"
#include "adblockrule.h"
#include "adblocksubscription.h"
AdBlockMatcher::AdBlockMatcher(AdBlockManager* manager)
: QObject(manager)
, m_manager(manager)
{
connect(manager, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool)));
}
AdBlockMatcher::~AdBlockMatcher()
{
clear();
}
const AdBlockRule* AdBlockMatcher::match(const QNetworkRequest &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<QString, const AdBlockRule*> cssRulesHash;
QVector<const AdBlockRule*> 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<QString, const AdBlockRule*> 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();
}
void AdBlockMatcher::enabledChanged(bool enabled)
{
if (enabled)
update();
else
clear();
}

View File

@ -0,0 +1,66 @@
/* ============================================================
* QupZilla - WebKit based browser
* Copyright (C) 2014 David Rosca <nowrep@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
* ============================================================ */
#ifndef ADBLOCKMATCHER_H
#define ADBLOCKMATCHER_H
#include <QObject>
#include "qzcommon.h"
#include "adblocksearchtree.h"
class AdBlockManager;
class QUPZILLA_EXPORT AdBlockMatcher : public QObject
{
Q_OBJECT
public:
explicit AdBlockMatcher(AdBlockManager* manager);
~AdBlockMatcher();
const AdBlockRule* match(const QNetworkRequest &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 slots:
void enabledChanged(bool enabled);
private:
AdBlockManager* m_manager;
QVector<AdBlockRule*> m_createdRules;
QVector<const AdBlockRule*> m_networkExceptionRules;
QVector<const AdBlockRule*> m_networkBlockRules;
QVector<const AdBlockRule*> m_domainRestrictedCssRules;
QVector<const AdBlockRule*> m_documentRules;
QVector<const AdBlockRule*> m_elemhideRules;
QString m_elementHidingRules;
AdBlockSearchTree m_networkBlockTree;
AdBlockSearchTree m_networkExceptionTree;
};
#endif // ADBLOCKMATCHER_H

View File

@ -169,6 +169,7 @@ private:
// Use dynamic allocation to save memory // Use dynamic allocation to save memory
RegExp* m_regExp; RegExp* m_regExp;
friend class AdBlockMatcher;
friend class AdBlockSearchTree; friend class AdBlockSearchTree;
friend class AdBlockSubscription; friend class AdBlockSubscription;
}; };

View File

@ -128,8 +128,6 @@ void AdBlockSubscription::loadSubscription(const QStringList &disabledRules)
m_rules.append(rule); m_rules.append(rule);
} }
populateCache();
// Initial update // Initial update
if (m_rules.isEmpty() && !m_updated) { if (m_rules.isEmpty() && !m_updated) {
QTimer::singleShot(0, this, SLOT(updateSubscription())); QTimer::singleShot(0, this, SLOT(updateSubscription()));
@ -176,7 +174,9 @@ void AdBlockSubscription::subscriptionDownloaded()
} }
loadSubscription(AdBlockManager::instance()->disabledRules()); loadSubscription(AdBlockManager::instance()->disabledRules());
emit subscriptionUpdated(); emit subscriptionUpdated();
emit subscriptionChanged();
} }
bool AdBlockSubscription::saveDownloadedData(const QByteArray &data) bool AdBlockSubscription::saveDownloadedData(const QByteArray &data)
@ -210,103 +210,6 @@ bool AdBlockSubscription::saveDownloadedData(const QByteArray &data)
return true; return true;
} }
const AdBlockRule* AdBlockSubscription::match(const QNetworkRequest &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 AdBlockSubscription::adBlockDisabledForUrl(const QUrl &url) const
{
int count = m_documentRules.count();
for (int i = 0; i < count; ++i) {
const AdBlockRule* rule = m_documentRules.at(i);
if (rule->urlMatch(url)) {
return true;
}
}
return false;
}
bool AdBlockSubscription::elemHideDisabledForUrl(const QUrl &url) const
{
if (adBlockDisabledForUrl(url)) {
return true;
}
int count = m_elemhideRules.count();
for (int i = 0; i < count; ++i) {
const AdBlockRule* rule = m_elemhideRules.at(i);
if (rule->urlMatch(url)) {
return true;
}
}
return false;
}
QString AdBlockSubscription::elementHidingRules() const
{
return m_elementHidingRules;
}
QString AdBlockSubscription::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("{display:none !important;}\n");
addedRulesCount = 0;
}
else {
rules.append(rule->cssSelector() + QLatin1Char(','));
addedRulesCount++;
}
}
if (addedRulesCount != 0) {
rules = rules.left(rules.size() - 1);
rules.append("{display:none !important;}\n");
}
return rules;
}
const AdBlockRule* AdBlockSubscription::rule(int offset) const const AdBlockRule* AdBlockSubscription::rule(int offset) const
{ {
if (!QzTools::containsIndex(m_rules, offset)) { if (!QzTools::containsIndex(m_rules, offset)) {
@ -331,10 +234,10 @@ const AdBlockRule* AdBlockSubscription::enableRule(int offset)
rule->setEnabled(true); rule->setEnabled(true);
AdBlockManager::instance()->removeDisabledRule(rule->filter()); AdBlockManager::instance()->removeDisabledRule(rule->filter());
if (rule->isCssRule()) { emit subscriptionChanged();
populateCache();
if (rule->isCssRule())
mApp->reloadUserStyleSheet(); mApp->reloadUserStyleSheet();
}
return rule; return rule;
} }
@ -349,10 +252,10 @@ const AdBlockRule* AdBlockSubscription::disableRule(int offset)
rule->setEnabled(false); rule->setEnabled(false);
AdBlockManager::instance()->addDisabledRule(rule->filter()); AdBlockManager::instance()->addDisabledRule(rule->filter());
if (rule->isCssRule()) { emit subscriptionChanged();
populateCache();
if (rule->isCssRule())
mApp->reloadUserStyleSheet(); mApp->reloadUserStyleSheet();
}
return rule; return rule;
} }
@ -386,112 +289,6 @@ const AdBlockRule* AdBlockSubscription::replaceRule(AdBlockRule* rule, int offse
return 0; return 0;
} }
void AdBlockSubscription::populateCache()
{
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();
QHash<QString, const AdBlockRule*> cssRulesHash;
QVector<const AdBlockRule*> exceptionCssRules;
int count = m_rules.count();
for (int i = 0; i < count; ++i) {
const AdBlockRule* rule = m_rules.at(i);
// 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);
}
}
}
count = exceptionCssRules.count();
for (int i = 0; i < count; ++i) {
const AdBlockRule* rule = exceptionCssRules.at(i);
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<QString, const AdBlockRule*> 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;} "));
}
}
AdBlockSubscription::~AdBlockSubscription() AdBlockSubscription::~AdBlockSubscription()
{ {
qDeleteAll(m_rules); qDeleteAll(m_rules);
@ -519,12 +316,13 @@ void AdBlockCustomList::loadSubscription(const QStringList &disabledRules)
QFile file(filePath()); QFile file(filePath());
if (file.open(QFile::WriteOnly | QFile::Append)) { if (file.open(QFile::WriteOnly | QFile::Append)) {
QTextStream stream(&file); QTextStream stream(&file);
stream.setCodec("UTF-8");
if (!rules.contains(ddg1)) if (!rules.contains(ddg1 + QL1S("\n")))
stream << ddg1; stream << ddg1 << endl;
if (!rules.contains(ddg2)) if (!rules.contains(QL1S("\n") + ddg2))
stream << ddg2; stream << ddg2 << endl;
} }
file.close(); file.close();
@ -590,9 +388,11 @@ bool AdBlockCustomList::removeFilter(const QString &filter)
int AdBlockCustomList::addRule(AdBlockRule* rule) int AdBlockCustomList::addRule(AdBlockRule* rule)
{ {
m_rules.append(rule); m_rules.append(rule);
populateCache();
emit subscriptionEdited(); emit subscriptionChanged();
if (rule->isCssRule())
mApp->reloadUserStyleSheet();
return m_rules.count() - 1; return m_rules.count() - 1;
} }
@ -607,9 +407,11 @@ bool AdBlockCustomList::removeRule(int offset)
const QString filter = rule->filter(); const QString filter = rule->filter();
m_rules.remove(offset); m_rules.remove(offset);
populateCache();
emit subscriptionEdited(); emit subscriptionChanged();
if (rule->isCssRule())
mApp->reloadUserStyleSheet();
AdBlockManager::instance()->removeDisabledRule(filter); AdBlockManager::instance()->removeDisabledRule(filter);
@ -623,12 +425,14 @@ const AdBlockRule* AdBlockCustomList::replaceRule(AdBlockRule* rule, int offset)
return 0; return 0;
} }
delete m_rules.at(offset); AdBlockRule* oldRule = m_rules.at(offset);
m_rules[offset] = rule; m_rules[offset] = rule;
populateCache();
emit subscriptionEdited(); emit subscriptionChanged();
if (rule->isCssRule() || oldRule->isCssRule())
mApp->reloadUserStyleSheet();
delete oldRule;
return m_rules[offset]; return m_rules[offset];
} }

View File

@ -77,14 +77,6 @@ public:
virtual void loadSubscription(const QStringList &disabledRules); virtual void loadSubscription(const QStringList &disabledRules);
virtual void saveSubscription(); virtual void saveSubscription();
const AdBlockRule* match(const QNetworkRequest &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;
const AdBlockRule* rule(int offset) const; const AdBlockRule* rule(int offset) const;
QVector<AdBlockRule*> allRules() const; QVector<AdBlockRule*> allRules() const;
@ -102,6 +94,7 @@ public slots:
void updateSubscription(); void updateSubscription();
signals: signals:
void subscriptionChanged();
void subscriptionUpdated(); void subscriptionUpdated();
void subscriptionError(const QString &message); void subscriptionError(const QString &message);
@ -111,23 +104,8 @@ protected slots:
protected: protected:
virtual bool saveDownloadedData(const QByteArray &data); virtual bool saveDownloadedData(const QByteArray &data);
void populateCache();
FollowRedirectReply* m_reply; FollowRedirectReply* m_reply;
QVector<AdBlockRule*> m_rules; QVector<AdBlockRule*> m_rules;
QVector<AdBlockRule*> m_createdRules;
QString m_elementHidingRules;
QVector<const AdBlockRule*> m_networkExceptionRules;
QVector<const AdBlockRule*> m_networkBlockRules;
QVector<const AdBlockRule*> m_domainRestrictedCssRules;
QVector<const AdBlockRule*> m_documentRules;
QVector<const AdBlockRule*> m_elemhideRules;
AdBlockSearchTree m_networkBlockTree;
AdBlockSearchTree m_networkExceptionTree;
private: private:
QString m_title; QString m_title;
@ -155,9 +133,6 @@ public:
int addRule(AdBlockRule* rule); int addRule(AdBlockRule* rule);
bool removeRule(int offset); bool removeRule(int offset);
const AdBlockRule* replaceRule(AdBlockRule* rule, int offset); const AdBlockRule* replaceRule(AdBlockRule* rule, int offset);
signals:
void subscriptionEdited();
}; };
#endif // ADBLOCKSUBSCRIPTION_H #endif // ADBLOCKSUBSCRIPTION_H

View File

@ -242,6 +242,7 @@ SOURCES += \
webtab/searchtoolbar.cpp \ webtab/searchtoolbar.cpp \
webtab/tabbedwebview.cpp \ webtab/tabbedwebview.cpp \
webtab/webtab.cpp \ webtab/webtab.cpp \
adblock/adblockmatcher.cpp
HEADERS += \ HEADERS += \
3rdparty/ecwin7.h \ 3rdparty/ecwin7.h \
@ -445,6 +446,7 @@ HEADERS += \
webtab/searchtoolbar.h \ webtab/searchtoolbar.h \
webtab/tabbedwebview.h \ webtab/tabbedwebview.h \
webtab/webtab.h \ webtab/webtab.h \
adblock/adblockmatcher.h
FORMS += \ FORMS += \
adblock/adblockaddsubscriptiondialog.ui \ adblock/adblockaddsubscriptiondialog.ui \