diff --git a/src/lib/adblock/adblockicon.cpp b/src/lib/adblock/adblockicon.cpp index ccd7c1ee..38e59d0f 100644 --- a/src/lib/adblock/adblockicon.cpp +++ b/src/lib/adblock/adblockicon.cpp @@ -1,168 +1,178 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "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 AdBlockIcon::AdBlockIcon(QObject *parent) : AbstractButtonInterface(parent) { setTitle(tr("AdBlock")); setIcon(QIcon(QSL(":adblock/data/adblock.png"))); updateState(); connect(this, &AbstractButtonInterface::clicked, this, &AdBlockIcon::clicked); connect(this, &AbstractButtonInterface::webPageChanged, this, &AdBlockIcon::webPageChanged); connect(AdBlockManager::instance(), &AdBlockManager::enabledChanged, this, &AdBlockIcon::updateState); -} - -AdBlockIcon::~AdBlockIcon() -{ - for (int i = 0; i < m_blockedPopups.count(); ++i) - delete m_blockedPopups.at(i).first; + connect(AdBlockManager::instance(), &AdBlockManager::blockedRequestsChanged, this, &AdBlockIcon::blockedRequestsChanged); } QString AdBlockIcon::id() const { return QSL("adblock-icon"); } QString AdBlockIcon::name() const { return tr("AdBlock Icon"); } 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::updateState() { WebPage *page = webPage(); if (!page) { setActive(false); setToolTip(name()); + setBadgeText(QString()); return; } if (!AdBlockManager::instance()->isEnabled()) { setActive(false); setToolTip(tr("AdBlock is disabled")); + setBadgeText(QString()); return; } if (!AdBlockManager::instance()->canRunOnScheme(page->url().scheme())) { setActive(false); setToolTip(tr("AdBlock is disabled on this site ")); + setBadgeText(QString()); return; } + setActive(true); setToolTip(tr("AdBlock is active")); + updateBadgeText(); +} + +void AdBlockIcon::updateBadgeText() +{ + WebPage *page = webPage(); + if (!page) { + return; + } + const int count = AdBlockManager::instance()->blockedRequestsForUrl(page->url()).count(); + if (count > 0) { + setBadgeText(QString::number(count)); + } else { + setBadgeText(QString()); + } } void AdBlockIcon::webPageChanged(WebPage *page) { updateState(); if (m_page) { disconnect(m_page.data(), &QWebEnginePage::urlChanged, this, &AdBlockIcon::updateState); } m_page = page; if (m_page) { connect(m_page.data(), &QWebEnginePage::urlChanged, this, &AdBlockIcon::updateState); } } void AdBlockIcon::clicked(ClickController *controller) { WebPage *page = webPage(); if (!page) { return; } AdBlockManager* manager = AdBlockManager::instance(); AdBlockCustomList* customList = manager->customList(); const QUrl pageUrl = page->url(); QMenu menu; menu.addAction(tr("Show AdBlock &Settings"), manager, SLOT(showDialog())); menu.addSeparator(); if (!pageUrl.host().isEmpty() && manager->isEnabled() && 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("&&")); + menu.exec(controller->popupPosition(menu.sizeHint())); +} - QAction* action = menu.addAction(actionText, manager, SLOT(showRule())); - action->setData(QVariant::fromValue((void*)pair.first)); - } +void AdBlockIcon::blockedRequestsChanged(const QUrl &url) +{ + WebPage *page = webPage(); + if (!page || url != page->url()) { + return; } - - menu.exec(controller->popupPosition(menu.sizeHint())); + updateState(); } diff --git a/src/lib/adblock/adblockicon.h b/src/lib/adblock/adblockicon.h index 5b75294f..3e5a6dc9 100644 --- a/src/lib/adblock/adblockicon.h +++ b/src/lib/adblock/adblockicon.h @@ -1,59 +1,49 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #ifndef ADBLOCKICON_H #define ADBLOCKICON_H #include #include "qzcommon.h" #include "abstractbuttoninterface.h" -class QUrl; - -class AdBlockRule; - -<<<<<<< HEAD -class FALKON_EXPORT AdBlockIcon : public ClickableLabel -||||||| parent of d11997ee... AdBlockIcon: Move from statusbar to navigationbar as tool button -class QUPZILLA_EXPORT AdBlockIcon : public ClickableLabel -======= -class QUPZILLA_EXPORT AdBlockIcon : public AbstractButtonInterface ->>>>>>> d11997ee... AdBlockIcon: Move from statusbar to navigationbar as tool button +class FALKON_EXPORT AdBlockIcon : public AbstractButtonInterface { Q_OBJECT public: explicit AdBlockIcon(QObject *parent = nullptr); - ~AdBlockIcon(); QString id() const override; QString name() const override; private slots: void toggleCustomFilter(); private: void updateState(); + void updateBadgeText(); void webPageChanged(WebPage *page); void clicked(ClickController *controller); + void blockedRequestsChanged(const QUrl &url); QPointer m_page; - QVector > m_blockedPopups; }; #endif // ADBLOCKICON_H diff --git a/src/lib/adblock/adblockmanager.cpp b/src/lib/adblock/adblockmanager.cpp index e5b5ea9a..e8443870 100644 --- a/src/lib/adblock/adblockmanager.cpp +++ b/src/lib/adblock/adblockmanager.cpp @@ -1,448 +1,464 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "adblockmanager.h" #include "adblockdialog.h" #include "adblockmatcher.h" #include "adblocksubscription.h" #include "adblockurlinterceptor.h" #include "datapaths.h" #include "mainapplication.h" #include "webpage.h" #include "qztools.h" #include "browserwindow.h" #include "settings.h" #include "networkmanager.h" #include #include #include #include #include #include #include #include //#define ADBLOCK_DEBUG #ifdef ADBLOCK_DEBUG #include #endif Q_GLOBAL_STATIC(AdBlockManager, qz_adblock_manager) AdBlockManager::AdBlockManager(QObject* parent) : QObject(parent) , m_loaded(false) , m_enabled(true) , m_matcher(new AdBlockMatcher(this)) , m_interceptor(new AdBlockUrlInterceptor(this)) { + qRegisterMetaType(); + load(); } AdBlockManager::~AdBlockManager() { qDeleteAll(m_subscriptions); } AdBlockManager* AdBlockManager::instance() { return qz_adblock_manager(); } void AdBlockManager::setEnabled(bool enabled) { if (m_enabled == enabled) { return; } m_enabled = enabled; emit enabledChanged(enabled); Settings settings; settings.beginGroup("AdBlock"); settings.setValue("enabled", m_enabled); settings.endGroup(); load(); mApp->reloadUserStyleSheet(); QMutexLocker locker(&m_mutex); if (m_enabled) { m_matcher->update(); } else { m_matcher->clear(); } } QList AdBlockManager::subscriptions() const { return m_subscriptions; } -bool AdBlockManager::block(QWebEngineUrlRequestInfo &request) +bool AdBlockManager::block(QWebEngineUrlRequestInfo &request, QString &ruleFilter) { 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; - + ruleFilter = blockedRule->filter(); if (request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) { QUrl url(QSL("falkon:adblock")); QUrlQuery query; query.addQueryItem(QSL("rule"), blockedRule->filter()); query.addQueryItem(QSL("subscription"), blockedRule->subscription()->title()); url.setQuery(query); request.redirect(url); - } - else { + } 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; + return blockedRule; +} + +QVector AdBlockManager::blockedRequestsForUrl(const QUrl &url) const +{ + return m_blockedRequests.value(url); +} + +void AdBlockManager::clearBlockedRequestsForUrl(const QUrl &url) +{ + if (m_blockedRequests.remove(url)) { + emit blockedRequestsChanged(url); + } } QStringList AdBlockManager::disabledRules() const { return m_disabledRules; } void AdBlockManager::addDisabledRule(const QString &filter) { m_disabledRules.append(filter); } void AdBlockManager::removeDisabledRule(const QString &filter) { m_disabledRules.removeOne(filter); } bool AdBlockManager::addSubscriptionFromUrl(const QUrl &url) { const QList > queryItems = QUrlQuery(url).queryItems(QUrl::FullyDecoded); QString subscriptionTitle; QString subscriptionUrl; for (int i = 0; i < queryItems.count(); ++i) { QPair pair = queryItems.at(i); if (pair.first == QL1S("location")) subscriptionUrl = pair.second; else if (pair.first == QL1S("title")) subscriptionTitle = pair.second; } if (subscriptionTitle.isEmpty() || subscriptionUrl.isEmpty()) return false; const QString message = AdBlockManager::tr("Do you want to add %1 subscription?").arg(subscriptionTitle); QMessageBox::StandardButton result = QMessageBox::question(0, AdBlockManager::tr("AdBlock Subscription"), message, QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::Yes) { AdBlockManager::instance()->addSubscription(subscriptionTitle, subscriptionUrl); AdBlockManager::instance()->showDialog(); } return true; } AdBlockSubscription* AdBlockManager::addSubscription(const QString &title, const QString &url) { if (title.isEmpty() || url.isEmpty()) { return 0; } QString fileName = QzTools::filterCharsFromFilename(title.toLower()) + ".txt"; QString filePath = QzTools::ensureUniqueFilename(DataPaths::currentProfilePath() + "/adblock/" + fileName); QByteArray data = QString("Title: %1\nUrl: %2\n[Adblock Plus 1.1.1]").arg(title, url).toLatin1(); QSaveFile file(filePath); if (!file.open(QFile::WriteOnly)) { qWarning() << "AdBlockManager: Cannot write to file" << filePath; return 0; } file.write(data); file.commit(); AdBlockSubscription* subscription = new AdBlockSubscription(title, this); subscription->setUrl(QUrl(url)); subscription->setFilePath(filePath); subscription->loadSubscription(m_disabledRules); m_subscriptions.insert(m_subscriptions.count() - 1, subscription); connect(subscription, SIGNAL(subscriptionUpdated()), mApp, SLOT(reloadUserStyleSheet())); connect(subscription, SIGNAL(subscriptionChanged()), this, SLOT(updateMatcher())); return subscription; } bool AdBlockManager::removeSubscription(AdBlockSubscription* subscription) { QMutexLocker locker(&m_mutex); if (!m_subscriptions.contains(subscription) || !subscription->canBeRemoved()) { return false; } QFile(subscription->filePath()).remove(); m_subscriptions.removeOne(subscription); m_matcher->update(); delete subscription; return true; } AdBlockCustomList* AdBlockManager::customList() const { foreach (AdBlockSubscription* subscription, m_subscriptions) { AdBlockCustomList* list = qobject_cast(subscription); if (list) { return list; } } return 0; } void AdBlockManager::load() { QMutexLocker locker(&m_mutex); if (m_loaded) { return; } #ifdef ADBLOCK_DEBUG QElapsedTimer timer; timer.start(); #endif Settings settings; settings.beginGroup("AdBlock"); m_enabled = settings.value("enabled", m_enabled).toBool(); m_disabledRules = settings.value("disabledRules", QStringList()).toStringList(); QDateTime lastUpdate = settings.value("lastUpdate", QDateTime()).toDateTime(); settings.endGroup(); if (!m_enabled) { return; } QDir adblockDir(DataPaths::currentProfilePath() + "/adblock"); // Create if neccessary if (!adblockDir.exists()) { QDir(DataPaths::currentProfilePath()).mkdir("adblock"); } foreach (const QString &fileName, adblockDir.entryList(QStringList("*.txt"), QDir::Files)) { if (fileName == QLatin1String("customlist.txt")) { continue; } const QString absolutePath = adblockDir.absoluteFilePath(fileName); QFile file(absolutePath); if (!file.open(QFile::ReadOnly)) { continue; } QTextStream textStream(&file); textStream.setCodec("UTF-8"); QString title = textStream.readLine(1024).remove(QLatin1String("Title: ")); QUrl url = QUrl(textStream.readLine(1024).remove(QLatin1String("Url: "))); if (title.isEmpty() || !url.isValid()) { qWarning() << "AdBlockManager: Invalid subscription file" << absolutePath; continue; } AdBlockSubscription* subscription = new AdBlockSubscription(title, this); subscription->setUrl(url); subscription->setFilePath(absolutePath); m_subscriptions.append(subscription); } // 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; + connect(m_interceptor, &AdBlockUrlInterceptor::requestBlocked, this, [this](const AdBlockedRequest &request) { + m_blockedRequests[request.firstPartyUrl].append(request); + emit blockedRequestsChanged(request.firstPartyUrl); + }); + mApp->networkManager()->installUrlInterceptor(m_interceptor); } void AdBlockManager::updateMatcher() { QMutexLocker locker(&m_mutex); m_matcher->update(); } void AdBlockManager::updateAllSubscriptions() { foreach (AdBlockSubscription* subscription, m_subscriptions) { subscription->updateSubscription(); } Settings settings; settings.beginGroup("AdBlock"); settings.setValue("lastUpdate", QDateTime::currentDateTime()); settings.endGroup(); } void AdBlockManager::save() { if (!m_loaded) { return; } foreach (AdBlockSubscription* subscription, m_subscriptions) { subscription->saveSubscription(); } Settings settings; settings.beginGroup("AdBlock"); settings.setValue("enabled", m_enabled); settings.setValue("disabledRules", m_disabledRules); settings.endGroup(); } bool AdBlockManager::isEnabled() const { return m_enabled; } bool AdBlockManager::canRunOnScheme(const QString &scheme) const { return !(scheme == QL1S("file") || scheme == QL1S("qrc") || scheme == QL1S("view-source") || scheme == QL1S("falkon") || scheme == QL1S("data") || scheme == QL1S("abp")); } bool AdBlockManager::canBeBlocked(const QUrl &url) const { return !m_matcher->adBlockDisabledForUrl(url); } QString AdBlockManager::elementHidingRules(const QUrl &url) const { if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) return QString(); return m_matcher->elementHidingRules(); } QString AdBlockManager::elementHidingRulesForDomain(const QUrl &url) const { if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) return QString(); return m_matcher->elementHidingRulesForDomain(url.host()); } AdBlockSubscription* AdBlockManager::subscriptionByName(const QString &name) const { foreach (AdBlockSubscription* subscription, m_subscriptions) { if (subscription->title() == name) { return subscription; } } return 0; } AdBlockDialog* AdBlockManager::showDialog() { 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/lib/adblock/adblockmanager.h b/src/lib/adblock/adblockmanager.h index 81fc1eab..d32e6ec3 100644 --- a/src/lib/adblock/adblockmanager.h +++ b/src/lib/adblock/adblockmanager.h @@ -1,101 +1,115 @@ /* ============================================================ * Falkon - Qt web browser -* Copyright (C) 2010-2017 David Rosca +* Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #ifndef ADBLOCKMANAGER_H #define ADBLOCKMANAGER_H #include #include #include #include +#include +#include #include "qzcommon.h" -class QUrl; -class QWebEngineUrlRequestInfo; - class AdBlockRule; class AdBlockDialog; class AdBlockMatcher; class AdBlockCustomList; class AdBlockSubscription; class AdBlockUrlInterceptor; +struct AdBlockedRequest +{ + QUrl requestUrl; + QUrl firstPartyUrl; + QByteArray requestMethod; + QWebEngineUrlRequestInfo::ResourceType resourceType; + QWebEngineUrlRequestInfo::NavigationType navigationType; + QString rule; +}; +Q_DECLARE_METATYPE(AdBlockedRequest) + class FALKON_EXPORT AdBlockManager : public QObject { Q_OBJECT public: AdBlockManager(QObject* parent = 0); ~AdBlockManager(); void load(); void save(); bool isEnabled() const; bool canRunOnScheme(const QString &scheme) const; + bool canBeBlocked(const QUrl &url) const; QString elementHidingRules(const QUrl &url) const; QString elementHidingRulesForDomain(const QUrl &url) const; AdBlockSubscription* subscriptionByName(const QString &name) const; QList subscriptions() const; - bool block(QWebEngineUrlRequestInfo &request); + bool block(QWebEngineUrlRequestInfo &request, QString &ruleFilter); + + QVector blockedRequestsForUrl(const QUrl &url) const; + void clearBlockedRequestsForUrl(const QUrl &url); QStringList disabledRules() const; void addDisabledRule(const QString &filter); void removeDisabledRule(const QString &filter); bool addSubscriptionFromUrl(const QUrl &url); AdBlockSubscription* addSubscription(const QString &title, const QString &url); bool removeSubscription(AdBlockSubscription* subscription); AdBlockCustomList* customList() const; static AdBlockManager* instance(); signals: void enabledChanged(bool enabled); + void blockedRequestsChanged(const QUrl &url); 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; + QHash> m_blockedRequests; }; #endif // ADBLOCKMANAGER_H diff --git a/src/lib/adblock/adblockurlinterceptor.cpp b/src/lib/adblock/adblockurlinterceptor.cpp index 642ed6e9..e9cf29af 100644 --- a/src/lib/adblock/adblockurlinterceptor.cpp +++ b/src/lib/adblock/adblockurlinterceptor.cpp @@ -1,32 +1,43 @@ /* ============================================================ * Falkon - Qt web browser -* Copyright (C) 2015 David Rosca +* Copyright (C) 2015-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "adblockurlinterceptor.h" -#include "adblockmanager.h" +#include "adblockrule.h" AdBlockUrlInterceptor::AdBlockUrlInterceptor(AdBlockManager *manager) : UrlInterceptor(manager) , m_manager(manager) { } -void AdBlockUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) +void AdBlockUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &request) { - if (m_manager->block(info)) - info.block(true); + QString ruleFilter; + if (!m_manager->block(request, ruleFilter)) { + return; + } + + AdBlockedRequest r; + r.requestUrl = request.requestUrl(); + r.firstPartyUrl = request.firstPartyUrl(); + r.requestMethod = request.requestMethod(); + r.resourceType = request.resourceType(); + r.navigationType = request.navigationType(); + r.rule = ruleFilter; + emit requestBlocked(r); } diff --git a/src/lib/adblock/adblockurlinterceptor.h b/src/lib/adblock/adblockurlinterceptor.h index 3e31b1a6..eca82ef5 100644 --- a/src/lib/adblock/adblockurlinterceptor.h +++ b/src/lib/adblock/adblockurlinterceptor.h @@ -1,38 +1,44 @@ /* ============================================================ * Falkon - Qt web browser -* Copyright (C) 2015 David Rosca +* Copyright (C) 2015-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #ifndef ADBLOCKURLINTERCEPTOR_H #define ADBLOCKURLINTERCEPTOR_H -#include "urlinterceptor.h" #include "qzcommon.h" +#include "urlinterceptor.h" +#include "adblockmanager.h" class AdBlockManager; class FALKON_EXPORT AdBlockUrlInterceptor : public UrlInterceptor { + Q_OBJECT + public: - explicit AdBlockUrlInterceptor(AdBlockManager* manager); + explicit AdBlockUrlInterceptor(AdBlockManager *manager); + + void interceptRequest(QWebEngineUrlRequestInfo &request); - void interceptRequest(QWebEngineUrlRequestInfo &info); +signals: + void requestBlocked(const AdBlockedRequest &request); private: AdBlockManager *m_manager; }; #endif // ADBLOCKURLINTERCEPTOR_H diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp index 6c8a560f..ee2fd799 100644 --- a/src/lib/webengine/webpage.cpp +++ b/src/lib/webengine/webpage.cpp @@ -1,616 +1,621 @@ /* ============================================================ * Falkon - Qt web browser * Copyright (C) 2010-2018 David Rosca * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ============================================================ */ #include "webpage.h" #include "tabbedwebview.h" #include "browserwindow.h" #include "pluginproxy.h" #include "downloadmanager.h" #include "mainapplication.h" #include "checkboxdialog.h" #include "widget.h" #include "qztools.h" #include "speeddial.h" #include "autofill.h" #include "popupwebview.h" #include "popupwindow.h" #include "iconprovider.h" #include "qzsettings.h" #include "useragentmanager.h" #include "delayedfilewatcher.h" #include "searchenginesmanager.h" #include "html5permissions/html5permissionsmanager.h" #include "javascript/externaljsobject.h" #include "tabwidget.h" #include "networkmanager.h" #include "webhittestresult.h" #include "adblock/adblockmanager.h" #ifdef NONBLOCK_JS_DIALOGS #include "ui_jsconfirm.h" #include "ui_jsalert.h" #include "ui_jsprompt.h" #include #endif #include #include #include #include #include #include #include #include #include #include QString WebPage::s_lastUploadLocation = QDir::homePath(); QUrl WebPage::s_lastUnsupportedUrl; QTime WebPage::s_lastUnsupportedUrlTime; WebPage::WebPage(QObject* parent) : QWebEnginePage(mApp->webProfile(), parent) , m_fileWatcher(0) , m_runningLoop(0) , m_loadProgress(-1) , m_blockAlerts(false) , m_secureStatus(false) { QWebChannel *channel = new QWebChannel(this); ExternalJsObject::setupWebChannel(channel, this); setWebChannel(channel); connect(this, &QWebEnginePage::loadProgress, this, &WebPage::progress); connect(this, &QWebEnginePage::loadFinished, this, &WebPage::finished); connect(this, &QWebEnginePage::urlChanged, this, &WebPage::urlChanged); connect(this, &QWebEnginePage::featurePermissionRequested, this, &WebPage::featurePermissionRequested); connect(this, &QWebEnginePage::windowCloseRequested, this, &WebPage::windowCloseRequested); connect(this, &QWebEnginePage::fullScreenRequested, this, &WebPage::fullScreenRequested); connect(this, &QWebEnginePage::renderProcessTerminated, this, &WebPage::renderProcessTerminated); connect(this, &QWebEnginePage::authenticationRequired, this, [this](const QUrl &url, QAuthenticator *auth) { mApp->networkManager()->authentication(url, auth, view()); }); connect(this, &QWebEnginePage::proxyAuthenticationRequired, this, [this](const QUrl &, QAuthenticator *auth, const QString &proxyHost) { mApp->networkManager()->proxyAuthentication(proxyHost, auth, view()); }); // Workaround QWebEnginePage not scrolling to anchors when opened in background tab m_contentsResizedConnection = connect(this, &QWebEnginePage::contentsSizeChanged, this, [this]() { const QString fragment = url().fragment(); if (!fragment.isEmpty()) { const QString src = QSL("var els = document.querySelectorAll(\"[name='%1']\"); if (els.length) els[0].scrollIntoView();"); runJavaScript(src.arg(fragment)); } disconnect(m_contentsResizedConnection); }); } WebPage::~WebPage() { mApp->plugins()->emitWebPageDeleted(this); if (m_runningLoop) { m_runningLoop->exit(1); m_runningLoop = 0; } } WebView *WebPage::view() const { return static_cast(QWebEnginePage::view()); } bool WebPage::execPrintPage(QPrinter *printer, int timeout) { QPointer loop = new QEventLoop; bool result = false; QTimer::singleShot(timeout, loop.data(), &QEventLoop::quit); print(printer, [loop, &result](bool res) { if (loop && loop->isRunning()) { result = res; loop->quit(); } }); loop->exec(); delete loop; return result; } QVariant WebPage::execJavaScript(const QString &scriptSource, quint32 worldId, int timeout) { QPointer loop = new QEventLoop; QVariant result; QTimer::singleShot(timeout, loop.data(), &QEventLoop::quit); runJavaScript(scriptSource, worldId, [loop, &result](const QVariant &res) { if (loop && loop->isRunning()) { result = res; loop->quit(); } }); loop->exec(QEventLoop::ExcludeUserInputEvents); delete loop; return result; } QPointF WebPage::mapToViewport(const QPointF &pos) const { return QPointF(pos.x() / zoomFactor(), pos.y() / zoomFactor()); } WebHitTestResult WebPage::hitTestContent(const QPoint &pos) const { return WebHitTestResult(this, pos); } void WebPage::scroll(int x, int y) { runJavaScript(QSL("window.scrollTo(window.scrollX + %1, window.scrollY + %2)").arg(x).arg(y), WebPage::SafeJsWorld); } void WebPage::setScrollPosition(const QPointF &pos) { const QPointF v = mapToViewport(pos.toPoint()); runJavaScript(QSL("window.scrollTo(%1, %2)").arg(v.x()).arg(v.y()), WebPage::SafeJsWorld); } bool WebPage::isRunningLoop() { return m_runningLoop; } bool WebPage::isLoading() const { return m_loadProgress < 100; } void WebPage::urlChanged(const QUrl &url) { Q_UNUSED(url) if (isLoading()) { m_blockAlerts = false; } } void WebPage::progress(int prog) { m_loadProgress = prog; bool secStatus = url().scheme() == QL1S("https"); if (secStatus != m_secureStatus) { m_secureStatus = secStatus; emit privacyChanged(secStatus); } } void WebPage::finished() { progress(100); // File scheme watcher if (url().scheme() == QLatin1String("file")) { QFileInfo info(url().toLocalFile()); if (info.isFile()) { if (!m_fileWatcher) { m_fileWatcher = new DelayedFileWatcher(this); connect(m_fileWatcher, SIGNAL(delayedFileChanged(QString)), this, SLOT(watchedFileChanged(QString))); } const QString filePath = url().toLocalFile(); if (QFile::exists(filePath) && !m_fileWatcher->files().contains(filePath)) { m_fileWatcher->addPath(filePath); } } } else if (m_fileWatcher && !m_fileWatcher->files().isEmpty()) { m_fileWatcher->removePaths(m_fileWatcher->files()); } // AutoFill m_passwordEntries = mApp->autoFill()->completePage(this, url()); } void WebPage::watchedFileChanged(const QString &file) { if (url().toLocalFile() == file) { triggerAction(QWebEnginePage::Reload); } } void WebPage::handleUnknownProtocol(const QUrl &url) { const QString protocol = url.scheme(); if (protocol == QLatin1String("mailto")) { desktopServicesOpen(url); return; } if (qzSettings->blockedProtocols.contains(protocol)) { qDebug() << "WebPage::handleUnknownProtocol Protocol" << protocol << "is blocked!"; return; } if (qzSettings->autoOpenProtocols.contains(protocol)) { desktopServicesOpen(url); return; } CheckBoxDialog dialog(QMessageBox::Yes | QMessageBox::No, view()); dialog.setDefaultButton(QMessageBox::Yes); const QString wrappedUrl = QzTools::alignTextToWidth(url.toString(), "
", dialog.fontMetrics(), 450); const QString text = tr("Falkon cannot handle %1: links. The requested link " "is
  • %2
Do you want Falkon to try " "open this link in system application?").arg(protocol, wrappedUrl); dialog.setText(text); dialog.setCheckBoxText(tr("Remember my choice for this protocol")); dialog.setWindowTitle(tr("External Protocol Request")); dialog.setIcon(QMessageBox::Question); switch (dialog.exec()) { case QMessageBox::Yes: if (dialog.isChecked()) { qzSettings->autoOpenProtocols.append(protocol); qzSettings->saveSettings(); } QDesktopServices::openUrl(url); break; case QMessageBox::No: if (dialog.isChecked()) { qzSettings->blockedProtocols.append(protocol); qzSettings->saveSettings(); } break; default: break; } } void WebPage::desktopServicesOpen(const QUrl &url) { // Open same url only once in 2 secs const int sameUrlTimeout = 2 * 1000; if (s_lastUnsupportedUrl != url || s_lastUnsupportedUrlTime.isNull() || s_lastUnsupportedUrlTime.elapsed() > sameUrlTimeout) { s_lastUnsupportedUrl = url; s_lastUnsupportedUrlTime.restart(); QDesktopServices::openUrl(url); } else { qWarning() << "WebPage::desktopServicesOpen Url" << url << "has already been opened!\n" "Ignoring it to prevent infinite loop!"; } } void WebPage::windowCloseRequested() { if (!view()) return; view()->closeView(); } void WebPage::fullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest) { view()->requestFullScreen(fullScreenRequest.toggleOn()); const bool accepted = fullScreenRequest.toggleOn() == view()->isFullScreen(); if (accepted) fullScreenRequest.accept(); else fullScreenRequest.reject(); } void WebPage::featurePermissionRequested(const QUrl &origin, const QWebEnginePage::Feature &feature) { if (feature == MouseLock && view()->isFullScreen()) setFeaturePermission(origin, feature, PermissionGrantedByUser); else mApp->html5PermissionsManager()->requestPermissions(this, origin, feature); } void WebPage::renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode) { Q_UNUSED(exitCode) if (terminationStatus == NormalTerminationStatus) return; QTimer::singleShot(0, this, [this]() { QString page = QzTools::readAllFileContents(":html/tabcrash.html"); page.replace(QL1S("%IMAGE%"), QzTools::pixmapToDataUrl(IconProvider::standardIcon(QStyle::SP_MessageBoxWarning).pixmap(45)).toString()); page.replace(QL1S("%TITLE%"), tr("Failed loading page")); page.replace(QL1S("%HEADING%"), tr("Failed loading page")); page.replace(QL1S("%LI-1%"), tr("Something went wrong while loading this page.")); page.replace(QL1S("%LI-2%"), tr("Try reloading the page or closing some tabs to make more memory available.")); page.replace(QL1S("%RELOAD-PAGE%"), tr("Reload page")); page = QzTools::applyDirectionToPage(page); load(url()); // Workaround for QtWebEngine crash setHtml(page.toUtf8(), url()); }); } bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) { if (!mApp->plugins()->acceptNavigationRequest(this, url, type, isMainFrame)) return false; // AdBlock - if (url.scheme() == QL1S("abp") && AdBlockManager::instance()->addSubscriptionFromUrl(url)) + AdBlockManager *manager = AdBlockManager::instance(); + if (isMainFrame) { + manager->clearBlockedRequestsForUrl(WebPage::url()); + } + if (url.scheme() == QL1S("abp") && AdBlockManager::instance()->addSubscriptionFromUrl(url)) { return false; + } return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); } bool WebPage::certificateError(const QWebEngineCertificateError &error) { return mApp->networkManager()->certificateError(error, view()); } QStringList WebPage::chooseFiles(QWebEnginePage::FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes) { Q_UNUSED(acceptedMimeTypes); QStringList files; QString suggestedFileName = s_lastUploadLocation; if (!oldFiles.isEmpty()) suggestedFileName = oldFiles.at(0); switch (mode) { case FileSelectOpen: files = QStringList(QzTools::getOpenFileName("WebPage-ChooseFile", view(), tr("Choose file..."), suggestedFileName)); break; case FileSelectOpenMultiple: files = QzTools::getOpenFileNames("WebPage-ChooseFile", view(), tr("Choose files..."), suggestedFileName); break; default: files = QWebEnginePage::chooseFiles(mode, oldFiles, acceptedMimeTypes); break; } if (!files.isEmpty()) s_lastUploadLocation = files.at(0); return files; } bool WebPage::hasMultipleUsernames() const { return m_passwordEntries.count() > 1; } QVector WebPage::autoFillData() const { return m_passwordEntries; } bool WebPage::javaScriptPrompt(const QUrl &securityOrigin, const QString &msg, const QString &defaultValue, QString* result) { Q_UNUSED(securityOrigin) #ifndef NONBLOCK_JS_DIALOGS return QWebEnginePage::javaScriptPrompt(securityOrigin, msg, defaultValue, result); #else if (m_runningLoop) { return false; } ResizableFrame* widget = new ResizableFrame(view()->overlayWidget()); widget->setObjectName("jsFrame"); Ui_jsPrompt* ui = new Ui_jsPrompt(); ui->setupUi(widget); ui->message->setText(msg); ui->lineEdit->setText(defaultValue); ui->lineEdit->setFocus(); widget->resize(view()->size()); widget->show(); connect(view(), SIGNAL(viewportResized(QSize)), widget, SLOT(slotResize(QSize))); connect(ui->lineEdit, SIGNAL(returnPressed()), ui->buttonBox->button(QDialogButtonBox::Ok), SLOT(animateClick())); QEventLoop eLoop; m_runningLoop = &eLoop; connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), &eLoop, SLOT(quit())); if (eLoop.exec() == 1) { return result; } m_runningLoop = 0; QString x = ui->lineEdit->text(); bool _result = ui->buttonBox->clickedButtonRole() == QDialogButtonBox::AcceptRole; *result = x; delete widget; view()->setFocus(); return _result; #endif } bool WebPage::javaScriptConfirm(const QUrl &securityOrigin, const QString &msg) { Q_UNUSED(securityOrigin) #ifndef NONBLOCK_JS_DIALOGS return QWebEnginePage::javaScriptConfirm(securityOrigin, msg); #else if (m_runningLoop) { return false; } ResizableFrame* widget = new ResizableFrame(view()->overlayWidget()); widget->setObjectName("jsFrame"); Ui_jsConfirm* ui = new Ui_jsConfirm(); ui->setupUi(widget); ui->message->setText(msg); ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); widget->resize(view()->size()); widget->show(); connect(view(), SIGNAL(viewportResized(QSize)), widget, SLOT(slotResize(QSize))); QEventLoop eLoop; m_runningLoop = &eLoop; connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), &eLoop, SLOT(quit())); if (eLoop.exec() == 1) { return false; } m_runningLoop = 0; bool result = ui->buttonBox->clickedButtonRole() == QDialogButtonBox::AcceptRole; delete widget; view()->setFocus(); return result; #endif } void WebPage::javaScriptAlert(const QUrl &securityOrigin, const QString &msg) { Q_UNUSED(securityOrigin) if (m_blockAlerts || m_runningLoop) { return; } #ifndef NONBLOCK_JS_DIALOGS QString title = tr("JavaScript alert"); if (!url().host().isEmpty()) { title.append(QString(" - %1").arg(url().host())); } CheckBoxDialog dialog(QMessageBox::Ok, view()); dialog.setDefaultButton(QMessageBox::Ok); dialog.setWindowTitle(title); dialog.setText(msg); dialog.setCheckBoxText(tr("Prevent this page from creating additional dialogs")); dialog.setIcon(QMessageBox::Information); dialog.exec(); m_blockAlerts = dialog.isChecked(); #else ResizableFrame* widget = new ResizableFrame(view()->overlayWidget()); widget->setObjectName("jsFrame"); Ui_jsAlert* ui = new Ui_jsAlert(); ui->setupUi(widget); ui->message->setText(msg); ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); widget->resize(view()->size()); widget->show(); connect(view(), SIGNAL(viewportResized(QSize)), widget, SLOT(slotResize(QSize))); QEventLoop eLoop; m_runningLoop = &eLoop; connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), &eLoop, SLOT(quit())); if (eLoop.exec() == 1) { return; } m_runningLoop = 0; m_blockAlerts = ui->preventAlerts->isChecked(); delete widget; view()->setFocus(); #endif } void WebPage::setJavaScriptEnabled(bool enabled) { settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, enabled); } QWebEnginePage* WebPage::createWindow(QWebEnginePage::WebWindowType type) { TabbedWebView *tView = qobject_cast(view()); BrowserWindow *window = tView ? tView->browserWindow() : mApp->getWindow(); auto createTab = [=](Qz::NewTabPositionFlags pos) { int index = window->tabWidget()->addView(QUrl(), pos); TabbedWebView* view = window->weView(index); view->setPage(new WebPage); // Workaround focus issue when creating tab if (pos.testFlag(Qz::NT_SelectedTab)) { QPointer pview = view; QTimer::singleShot(0, this, [pview]() { if (pview && pview->webTab()->isCurrentTab()) { pview->setFocus(); } }); } return view->page(); }; switch (type) { case QWebEnginePage::WebBrowserWindow: { BrowserWindow *window = mApp->createWindow(Qz::BW_NewWindow); WebPage *page = new WebPage; window->setStartPage(page); return page; } case QWebEnginePage::WebDialog: if (!qzSettings->openPopupsInTabs) { PopupWebView* view = new PopupWebView; view->setPage(new WebPage); PopupWindow* popup = new PopupWindow(view); popup->show(); window->addDeleteOnCloseWidget(popup); return view->page(); } // else fallthrough case QWebEnginePage::WebBrowserTab: return createTab(Qz::NT_CleanSelectedTab); case QWebEnginePage::WebBrowserBackgroundTab: return createTab(Qz::NT_CleanNotSelectedTab); default: break; } return Q_NULLPTR; }