qutebrowser/qutebrowser/extensions/interceptors.py

103 lines
2.6 KiB
Python

# SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later
"""Infrastructure for intercepting requests."""
import enum
import dataclasses
from collections.abc import Callable
from qutebrowser.qt.core import QUrl
class ResourceType(enum.Enum):
"""Possible request types that can be received.
Currently corresponds to the QWebEngineUrlRequestInfo Enum:
https://doc.qt.io/qt-6/qwebengineurlrequestinfo.html#ResourceType-enum
"""
main_frame = 0
sub_frame = 1
stylesheet = 2
script = 3
image = 4
font_resource = 5
sub_resource = 6
object = 7
media = 8
worker = 9
shared_worker = 10
prefetch = 11
favicon = 12
xhr = 13
ping = 14
service_worker = 15
csp_report = 16
plugin_resource = 17
# 18 is "preload", deprecated in Chromium
preload_main_frame = 19
preload_sub_frame = 20
json = 21
websocket = 254
unknown = 255
class RedirectException(Exception):
"""Raised when the request was invalid, or a request was already made."""
@dataclasses.dataclass
class Request:
"""A request which can be intercepted/blocked."""
#: The URL of the page being shown.
first_party_url: QUrl | None
#: The URL of the file being requested.
request_url: QUrl
is_blocked: bool = False
#: The resource type of the request. None if not supported on this backend.
resource_type: ResourceType | None = None
def block(self) -> None:
"""Block this request."""
self.is_blocked = True
def redirect(self, url: QUrl, *, ignore_unsupported: bool = False) -> None:
"""Redirect this request.
Only some types of requests can be successfully redirected.
Improper use of this method can result in redirect loops.
This method will throw a RedirectException if the request was not possible.
Args:
url: The QUrl to try to redirect to.
ignore_unsupported: If set to True, request methods which can't be
redirected (such as POST) are silently ignored instead of throwing an
exception.
"""
# Will be overridden if the backend supports redirection
raise NotImplementedError
#: Type annotation for an interceptor function.
InterceptorType = Callable[[Request], None]
_interceptors: list[InterceptorType] = []
def register(interceptor: InterceptorType) -> None:
_interceptors.append(interceptor)
def run(info: Request) -> None:
for interceptor in _interceptors:
interceptor(info)