diff --git a/qutebrowser/api/requests.py b/qutebrowser/api/requests.py new file mode 100644 index 000000000..990faec88 --- /dev/null +++ b/qutebrowser/api/requests.py @@ -0,0 +1,37 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2018 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser 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. +# +# qutebrowser 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 qutebrowser. If not, see . + +"""APIs related to intercepting/blocking requests.""" + +import typing + +import attr +from PyQt5.QtCore import QUrl + +from qutebrowser.extensions import requests +# pylint: disable=unused-import +from qutebrowser.extensions.requests import Request + + +def register_filter(reqfilter: requests.RequestFilterType) -> None: + """Register a request filter. + + Whenever a request happens, the filter gets called with a Request object. + """ + requests.register_filter(reqfilter) diff --git a/qutebrowser/browser/adblock.py b/qutebrowser/browser/adblock.py index 216e9cc98..e9d171f26 100644 --- a/qutebrowser/browser/adblock.py +++ b/qutebrowser/browser/adblock.py @@ -119,9 +119,15 @@ class HostBlocker: return False host = url.host() - return ((host in self._blocked_hosts or - host in self._config_blocked_hosts) and - not _is_whitelisted_url(url)) + blocked = ((host in self._blocked_hosts or + host in self._config_blocked_hosts) and + not _is_whitelisted_url(url)) + + if blocked: + logger.info("Request to {} blocked by host blocker." + .format(url.host())) + + return blocked def _read_hosts_file(self, filename, target): """Read hosts from the given filename. diff --git a/qutebrowser/browser/webengine/interceptor.py b/qutebrowser/browser/webengine/interceptor.py index 516dd0899..863234dc6 100644 --- a/qutebrowser/browser/webengine/interceptor.py +++ b/qutebrowser/browser/webengine/interceptor.py @@ -26,15 +26,15 @@ from PyQt5.QtWebEngineCore import (QWebEngineUrlRequestInterceptor, from qutebrowser.config import config from qutebrowser.browser import shared from qutebrowser.utils import utils, log, debug +from qutebrowser.extensions import requests class RequestInterceptor(QWebEngineUrlRequestInterceptor): """Handle ad blocking and custom headers.""" - def __init__(self, host_blocker, args, parent=None): + def __init__(self, args, parent=None): super().__init__(parent) - self._host_blocker = host_blocker self._args = args def install(self, profile): @@ -84,9 +84,10 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor): return # FIXME:qtwebengine only block ads for NavigationTypeOther? - if self._host_blocker.is_blocked(url, first_party): - log.webview.info("Request to {} blocked by host blocker.".format( - url.host())) + request = requests.Request(first_party_url=first_party, + request_url=url) + requests.run_filters(request) + if request.is_blocked: info.block(True) for header, value in shared.custom_headers(url=url): diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index a74d866ea..22380cb1f 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -60,10 +60,8 @@ def init(): _qute_scheme_handler.install(webenginesettings.private_profile) log.init.debug("Initializing request interceptor...") - host_blocker = objreg.get('host-blocker') args = objreg.get('args') - req_interceptor = interceptor.RequestInterceptor( - host_blocker, args=args, parent=app) + req_interceptor = interceptor.RequestInterceptor(args=args, parent=app) req_interceptor.install(webenginesettings.default_profile) req_interceptor.install(webenginesettings.private_profile) diff --git a/qutebrowser/browser/webkit/mhtml.py b/qutebrowser/browser/webkit/mhtml.py index 1ecebed2d..c390ab6b3 100644 --- a/qutebrowser/browser/webkit/mhtml.py +++ b/qutebrowser/browser/webkit/mhtml.py @@ -39,6 +39,7 @@ from PyQt5.QtCore import QUrl from qutebrowser.browser import downloads from qutebrowser.browser.webkit import webkitelem from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils +from qutebrowser.extensions import requests @attr.s @@ -354,8 +355,9 @@ class _Downloader: # qute, see the comments/discussion on # https://github.com/qutebrowser/qutebrowser/pull/962#discussion_r40256987 # and https://github.com/qutebrowser/qutebrowser/issues/1053 - host_blocker = objreg.get('host-blocker') - if host_blocker.is_blocked(url): + request = requests.Request(first_party_url=None, request_url=url) + requests.run_filters(request) + if request.is_blocked: log.downloads.debug("Skipping {}, host-blocked".format(url)) # We still need an empty file in the output, QWebView can be pretty # picky about displaying a file correctly when not all assets are diff --git a/qutebrowser/browser/webkit/network/networkmanager.py b/qutebrowser/browser/webkit/network/networkmanager.py index 2ca1ae0d9..9562ba918 100644 --- a/qutebrowser/browser/webkit/network/networkmanager.py +++ b/qutebrowser/browser/webkit/network/networkmanager.py @@ -38,6 +38,7 @@ if MYPY: from qutebrowser.utils import (message, log, usertypes, utils, objreg, urlutils, debug) from qutebrowser.browser import shared +from qutebrowser.extensions import requests from qutebrowser.browser.webkit import certificateerror from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply, filescheme) @@ -405,10 +406,10 @@ class NetworkManager(QNetworkAccessManager): # the webpage shutdown here. current_url = QUrl() - host_blocker = objreg.get('host-blocker') - if host_blocker.is_blocked(req.url(), current_url): - log.webview.info("Request to {} blocked by host blocker.".format( - req.url().host())) + request = requests.Request(first_party_url=current_url, + request_url=req.url()) + requests.run_filters(request) + if request.is_blocked: return networkreply.ErrorNetworkReply( req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied, self) diff --git a/qutebrowser/extensions/requests.py b/qutebrowser/extensions/requests.py new file mode 100644 index 000000000..8364dc76d --- /dev/null +++ b/qutebrowser/extensions/requests.py @@ -0,0 +1,53 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2018 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser 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. +# +# qutebrowser 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 qutebrowser. If not, see . + +"""Infrastructure for filtering requests.""" + +import typing + +import attr + + +@attr.s +class Request: + + """A request which can be blocked.""" + + first_party_url = attr.ib() # type: QUrl + request_url = attr.ib() # type: QUrl + is_blocked = attr.ib(False) # type: bool + + def block(self): + """Block this request.""" + self.is_blocked = True + + +RequestFilterType = typing.Callable[[Request], None] + + +_request_filters = [] # type: typing.List[RequestFilterType] + + +def register_filter(reqfilter: RequestFilterType) -> None: + _request_filters.append(reqfilter) + + +def run_filters(info): + for reqfilter in _request_filters: + reqfilter(info)