summaryrefslogtreecommitdiff
path: root/qutebrowser
diff options
context:
space:
mode:
authorAnder Punnar <ander@kvlt.ee>2021-05-20 20:52:42 +0300
committerAnder Punnar <ander@kvlt.ee>2021-05-20 20:52:42 +0300
commit4e47af21586aad3846c2458dc0c8243e9d6eb792 (patch)
tree11b2073411b7decc68ba049bdfd4d97e8ab48343 /qutebrowser
parent2fc588358b27153721cc83e728e0d15b0e4d0d22 (diff)
parent90fbe1e181463379f38b8f0b01d488d1f7a586e0 (diff)
downloadqutebrowser-4e47af21586aad3846c2458dc0c8243e9d6eb792.tar.gz
qutebrowser-4e47af21586aad3846c2458dc0c8243e9d6eb792.zip
Merge remote-tracking branch 'origin/master' into 4nd3r/hostblock_subdomains
Diffstat (limited to 'qutebrowser')
-rw-r--r--qutebrowser/__init__.py2
-rw-r--r--qutebrowser/api/cmdutils.py7
-rw-r--r--qutebrowser/api/hook.py11
-rw-r--r--qutebrowser/browser/browsertab.py11
-rw-r--r--qutebrowser/browser/commands.py2
-rw-r--r--qutebrowser/browser/qutescheme.py5
-rw-r--r--qutebrowser/browser/webelem.py3
-rw-r--r--qutebrowser/browser/webengine/notification.py3
-rw-r--r--qutebrowser/browser/webengine/tabhistory.py8
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py13
-rw-r--r--qutebrowser/browser/webkit/tabhistory.py4
-rw-r--r--qutebrowser/config/config.py13
-rw-r--r--qutebrowser/config/configcommands.py4
-rw-r--r--qutebrowser/config/configdata.yml11
-rw-r--r--qutebrowser/config/configfiles.py38
-rw-r--r--qutebrowser/config/websettings.py39
-rw-r--r--qutebrowser/extensions/loader.py13
-rw-r--r--qutebrowser/javascript/quirks/googledocs.user.js14
-rw-r--r--qutebrowser/keyinput/basekeyparser.py2
-rw-r--r--qutebrowser/keyinput/keyutils.py2
-rw-r--r--qutebrowser/misc/backendproblem.py3
-rw-r--r--qutebrowser/misc/debugcachestats.py2
-rw-r--r--qutebrowser/misc/elf.py4
-rw-r--r--qutebrowser/misc/throttle.py2
-rw-r--r--qutebrowser/utils/debug.py34
-rw-r--r--qutebrowser/utils/docutils.py4
-rw-r--r--qutebrowser/utils/jinja.py8
-rw-r--r--qutebrowser/utils/objreg.py3
-rw-r--r--qutebrowser/utils/qtutils.py13
-rw-r--r--qutebrowser/utils/urlmatch.py9
-rw-r--r--qutebrowser/utils/utils.py9
-rw-r--r--qutebrowser/utils/version.py2
32 files changed, 206 insertions, 92 deletions
diff --git a/qutebrowser/__init__.py b/qutebrowser/__init__.py
index 91a49d5b5..96062d9bd 100644
--- a/qutebrowser/__init__.py
+++ b/qutebrowser/__init__.py
@@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2021 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
-__version__ = "2.2.1"
+__version__ = "2.2.2"
__version_info__ = tuple(int(part) for part in __version__.split('.'))
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."
diff --git a/qutebrowser/api/cmdutils.py b/qutebrowser/api/cmdutils.py
index 85a700c43..73c6a1bc5 100644
--- a/qutebrowser/api/cmdutils.py
+++ b/qutebrowser/api/cmdutils.py
@@ -105,6 +105,9 @@ def check_exclusive(flags: Iterable[bool], names: Iterable[str]) -> None:
raise CommandError("Only one of {} can be given!".format(argstr))
+_CmdHandlerType = Callable[..., Any]
+
+
class register: # noqa: N801,N806 pylint: disable=invalid-name
"""Decorator to register a new command handler."""
@@ -130,7 +133,7 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name
# The arguments to pass to Command.
self._kwargs = kwargs
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
"""Register the command before running the function.
Gets called when a function should be decorated.
@@ -222,7 +225,7 @@ class argument: # noqa: N801,N806 pylint: disable=invalid-name
self._argname = argname # The name of the argument to handle.
self._kwargs = kwargs # Valid ArgInfo members.
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: _CmdHandlerType) -> _CmdHandlerType:
funcname = func.__name__
if self._argname not in inspect.signature(func).parameters:
diff --git a/qutebrowser/api/hook.py b/qutebrowser/api/hook.py
index eadc310f3..884d6c67f 100644
--- a/qutebrowser/api/hook.py
+++ b/qutebrowser/api/hook.py
@@ -22,13 +22,13 @@
"""Hooks for extensions."""
import importlib
-from typing import Callable
+from typing import Callable, Any
from qutebrowser.extensions import loader
-def _add_module_info(func: Callable) -> loader.ModuleInfo:
+def _add_module_info(func: Callable[..., Any]) -> loader.ModuleInfo:
"""Add module info to the given function."""
module = importlib.import_module(func.__module__)
return loader.add_module_info(module)
@@ -48,7 +48,7 @@ class init:
message.info("Extension initialized.")
"""
- def __call__(self, func: Callable) -> Callable:
+ def __call__(self, func: loader.InitHookType) -> loader.InitHookType:
info = _add_module_info(func)
if info.init_hook is not None:
raise ValueError("init hook is already registered!")
@@ -86,7 +86,10 @@ class config_changed:
def __init__(self, option_filter: str = None) -> None:
self._filter = option_filter
- def __call__(self, func: Callable) -> Callable:
+ def __call__(
+ self,
+ func: loader.ConfigChangedHookType,
+ ) -> loader.ConfigChangedHookType:
info = _add_module_info(func)
info.config_changed_hooks.append((self._filter, func))
return func
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index cbe698009..b3d1e85f7 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -34,9 +34,10 @@ from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
from PyQt5.QtNetwork import QNetworkAccessManager
if TYPE_CHECKING:
- from PyQt5.QtWebKit import QWebHistory
+ from PyQt5.QtWebKit import QWebHistory, QWebHistoryItem
from PyQt5.QtWebKitWidgets import QWebPage
- from PyQt5.QtWebEngineWidgets import QWebEngineHistory, QWebEnginePage
+ from PyQt5.QtWebEngineWidgets import (
+ QWebEngineHistory, QWebEngineHistoryItem, QWebEnginePage)
from qutebrowser.keyinput import modeman
from qutebrowser.config import config
@@ -634,8 +635,8 @@ class AbstractHistoryPrivate:
"""Deserialize from a format produced by self.serialize."""
raise NotImplementedError
- def load_items(self, items: Sequence) -> None:
- """Deserialize from a list of WebHistoryItems."""
+ def load_items(self, items: Sequence[sessions.TabHistoryItem]) -> None:
+ """Deserialize from a list of TabHistoryItems."""
raise NotImplementedError
@@ -651,7 +652,7 @@ class AbstractHistory:
def __len__(self) -> int:
raise NotImplementedError
- def __iter__(self) -> Iterable:
+ def __iter__(self) -> Iterable[Union['QWebHistoryItem', 'QWebEngineHistoryItem']]:
raise NotImplementedError
def _check_count(self, count: int) -> None:
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
index e9653ae19..8cd73ae4f 100644
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -602,7 +602,7 @@ class CommandDispatcher:
widget = self._current_widget()
url = self._current_url()
- handlers: Dict[str, Callable] = {
+ handlers: Dict[str, Callable[..., QUrl]] = {
'prev': functools.partial(navigate.prevnext, prev=True),
'next': functools.partial(navigate.prevnext, prev=False),
'up': navigate.path_up,
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index cfb238188..031b6fe06 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -92,7 +92,8 @@ class Redirect(Exception):
# Return value: (mimetype, data) (encoded as utf-8 if a str is returned)
_HandlerRet = Tuple[str, Union[str, bytes]]
-_Handler = TypeVar('_Handler', bound=Callable[[QUrl], _HandlerRet])
+_HandlerCallable = Callable[[QUrl], _HandlerRet]
+_Handler = TypeVar('_Handler', bound=_HandlerCallable)
class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
@@ -105,7 +106,7 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
def __init__(self, name: str) -> None:
self._name = name
- self._function: Optional[Callable] = None
+ self._function: Optional[_HandlerCallable] = None
def __call__(self, function: _Handler) -> _Handler:
self._function = function
diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py
index f53ad1afb..9ec29ce07 100644
--- a/qutebrowser/browser/webelem.py
+++ b/qutebrowser/browser/webelem.py
@@ -58,7 +58,8 @@ def css_selector(group: str, url: QUrl) -> str:
return ','.join(selectors[group])
-class AbstractWebElement(collections.abc.MutableMapping):
+# MutableMapping is only generic in Python 3.9+
+class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-arg]
"""A wrapper around QtWebKit/QtWebEngine web element."""
diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py
index 50f73fca4..d8387e6d4 100644
--- a/qutebrowser/browser/webengine/notification.py
+++ b/qutebrowser/browser/webengine/notification.py
@@ -669,7 +669,8 @@ class _ServerCapabilities:
def _as_uint32(x: int) -> QVariant:
"""Convert the given int to an uint32 for DBus."""
variant = QVariant(x)
- assert variant.convert(QVariant.UInt)
+ successful = variant.convert(QVariant.UInt)
+ assert successful
return variant
diff --git a/qutebrowser/browser/webengine/tabhistory.py b/qutebrowser/browser/webengine/tabhistory.py
index d6c795fb2..ab4b05fe9 100644
--- a/qutebrowser/browser/webengine/tabhistory.py
+++ b/qutebrowser/browser/webengine/tabhistory.py
@@ -35,10 +35,10 @@ HISTORY_STREAM_VERSION = 3
def _serialize_item(item, stream):
- """Serialize a single WebHistoryItem into a QDataStream.
+ """Serialize a single TabHistoryItem into a QDataStream.
Args:
- item: The WebHistoryItem to write.
+ item: The TabHistoryItem to write.
stream: The QDataStream to write to.
"""
# Thanks to Otter Browser:
@@ -108,10 +108,10 @@ def _serialize_item(item, stream):
def serialize(items):
- """Serialize a list of WebHistoryItems to a data stream.
+ """Serialize a list of TabHistoryItems to a data stream.
Args:
- items: An iterable of WebHistoryItems.
+ items: An iterable of TabHistoryItems.
Return:
A (stream, data, user_data) tuple.
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 15784d6bf..c793a1929 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -983,6 +983,11 @@ class _Quirk:
QWebEngineScript.DocumentCreation)
world: QWebEngineScript.ScriptWorldId = QWebEngineScript.MainWorld
predicate: bool = True
+ name: Optional[str] = None
+
+ def __post_init__(self):
+ if self.name is None:
+ self.name = f"js-{self.filename.replace('_', '-')}"
class _WebEngineScripts(QObject):
@@ -1154,6 +1159,11 @@ class _WebEngineScripts(QObject):
),
_Quirk('discord'),
_Quirk(
+ 'googledocs',
+ # will be an UA quirk once we set the JS UA as well
+ name='ua-googledocs',
+ ),
+ _Quirk(
'string_replaceall',
predicate=versions.webengine < utils.VersionNumber(5, 15, 3),
),
@@ -1171,8 +1181,7 @@ class _WebEngineScripts(QObject):
if not quirk.predicate:
continue
src = resources.read_file(f'javascript/quirks/{quirk.filename}.user.js')
- name = f"js-{quirk.filename.replace('_', '-')}"
- if name not in config.val.content.site_specific_quirks.skip:
+ if quirk.name not in config.val.content.site_specific_quirks.skip:
self._inject_js(
f'quirk_{quirk.filename}',
src,
diff --git a/qutebrowser/browser/webkit/tabhistory.py b/qutebrowser/browser/webkit/tabhistory.py
index c38968b62..a707030d1 100644
--- a/qutebrowser/browser/webkit/tabhistory.py
+++ b/qutebrowser/browser/webkit/tabhistory.py
@@ -64,10 +64,10 @@ def _serialize_item(item):
def serialize(items):
- """Serialize a list of WebHistoryItems to a data stream.
+ """Serialize a list of TabHistoryItems to a data stream.
Args:
- items: An iterable of WebHistoryItems.
+ items: An iterable of TabHistoryItems.
Return:
A (stream, data, user_data) tuple.
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 374019677..437a54a33 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -97,7 +97,10 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
else:
return False
- def __call__(self, func: Callable) -> Callable:
+ def __call__(
+ self,
+ func: Callable[..., None],
+ ) -> Callable[..., None]:
"""Filter calls to the decorated function.
Gets called when a function should be decorated.
@@ -105,7 +108,9 @@ class change_filter: # noqa: N801,N806 pylint: disable=invalid-name
Adds a filter which returns if we're not interested in the change-event
and calls the wrapped function if we are.
- We assume the function passed doesn't take any parameters.
+ We assume the function passed doesn't take any parameters. However, it
+ could take a "self" argument, so we can't cleary express this in the
+ type above.
Args:
func: The function to be decorated.
@@ -173,6 +178,8 @@ class KeyConfig:
result = results[0]
if result.cmd.name != "set-cmd-text":
return cmdline
+ if not result.args:
+ return None # doesn't look like this sets a command
*flags, cmd = result.args
if "-a" in flags or "--append" in flags or not cmd.startswith(":"):
return None # doesn't look like this sets a command
@@ -307,7 +314,7 @@ class Config(QObject):
def _init_values(self) -> None:
"""Populate the self._values dict."""
- self._values: Mapping = {}
+ self._values: Mapping[str, configutils.Values] = {}
for name, opt in configdata.DATA.items():
self._values[name] = configutils.Values(opt)
diff --git a/qutebrowser/config/configcommands.py b/qutebrowser/config/configcommands.py
index 2084556da..143b02fca 100644
--- a/qutebrowser/config/configcommands.py
+++ b/qutebrowser/config/configcommands.py
@@ -21,7 +21,7 @@
import os.path
import contextlib
-from typing import TYPE_CHECKING, Iterator, List, Optional
+from typing import TYPE_CHECKING, Iterator, List, Optional, Any, Tuple
from PyQt5.QtCore import QUrl
@@ -475,7 +475,7 @@ class ConfigCommands:
raise cmdutils.CommandError("{} already exists - use --force to "
"overwrite!".format(filename))
- options: List = []
+ options: List[Tuple[Optional[urlmatch.UrlPattern], configdata.Option, Any]] = []
if defaults:
options = [(None, opt, opt.default)
for _name, opt in sorted(configdata.DATA.items())]
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index 6cb277e5c..80732d43d 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -545,6 +545,7 @@ content.site_specific_quirks.skip:
- ua-whatsapp
- ua-google
- ua-slack
+ - ua-googledocs
- js-whatsapp-web
- js-discord
- js-string-replaceall
@@ -3269,6 +3270,7 @@ fonts.tabs.unselected:
fonts.web.family.standard:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3276,6 +3278,7 @@ fonts.web.family.standard:
fonts.web.family.fixed:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3283,6 +3286,7 @@ fonts.web.family.fixed:
fonts.web.family.serif:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3290,6 +3294,7 @@ fonts.web.family.serif:
fonts.web.family.sans_serif:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3297,6 +3302,7 @@ fonts.web.family.sans_serif:
fonts.web.family.cursive:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3304,6 +3310,7 @@ fonts.web.family.cursive:
fonts.web.family.fantasy:
default: ''
+ supports_pattern: true
type:
name: FontFamily
none_ok: true
@@ -3316,6 +3323,7 @@ fonts.web.family.fantasy:
fonts.web.size.default:
default: 16
+ supports_pattern: true
type:
name: Int
minval: 1
@@ -3324,6 +3332,7 @@ fonts.web.size.default:
fonts.web.size.default_fixed:
default: 13
+ supports_pattern: true
type:
name: Int
minval: 1
@@ -3332,6 +3341,7 @@ fonts.web.size.default_fixed:
fonts.web.size.minimum:
default: 0
+ supports_pattern: true
type:
name: Int
minval: 0
@@ -3342,6 +3352,7 @@ fonts.web.size.minimum_logical:
# This is 0 as default on QtWebKit, and 6 on QtWebEngine - so let's
# just go for 6 here.
default: 6
+ supports_pattern: true
type:
name: Int
minval: 0
diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py
index f8566e2d0..6f0d0b13c 100644
--- a/qutebrowser/config/configfiles.py
+++ b/qutebrowser/config/configfiles.py
@@ -30,7 +30,7 @@ import configparser
import contextlib
import re
from typing import (TYPE_CHECKING, Any, Dict, Iterable, Iterator, List, Mapping,
- MutableMapping, Optional, cast)
+ MutableMapping, Optional, Tuple, cast)
import yaml
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QSettings, qVersion
@@ -39,7 +39,7 @@ import qutebrowser
from qutebrowser.config import (configexc, config, configdata, configutils,
configtypes)
from qutebrowser.keyinput import keyutils
-from qutebrowser.utils import standarddir, utils, qtutils, log, urlmatch
+from qutebrowser.utils import standarddir, utils, qtutils, log, urlmatch, version
if TYPE_CHECKING:
from qutebrowser.misc import savemanager
@@ -89,6 +89,7 @@ class StateConfig(configparser.ConfigParser):
self.read(self._filename, encoding='utf-8')
self.qt_version_changed = False
+ self.qtwe_version_changed = False
self.qutebrowser_version_changed = VersionChange.unknown
self._set_changed_attributes()
@@ -108,8 +109,20 @@ class StateConfig(configparser.ConfigParser):
self[sect].pop(key, None)
self['general']['qt_version'] = qVersion()
+ self['general']['qtwe_version'] = self._qtwe_version_str()
self['general']['version'] = qutebrowser.__version__
+ def _qtwe_version_str(self) -> str:
+ """Get the QtWebEngine version string.
+
+ Note that it's too early to use objects.backend here...
+ """
+ try:
+ import PyQt5.QtWebEngineWidgets # pylint: disable=unused-import
+ except ImportError:
+ return 'no'
+ return str(version.qtwebengine_versions(avoid_init=True).webengine)
+
def _set_changed_attributes(self) -> None:
"""Set qt_version_changed/qutebrowser_version_changed attributes.
@@ -123,6 +136,9 @@ class StateConfig(configparser.ConfigParser):
old_qt_version = self['general'].get('qt_version', None)
self.qt_version_changed = old_qt_version != qVersion()
+ old_qtwe_version = self['general'].get('qtwe_version', None)
+ self.qtwe_version_changed = old_qtwe_version != self._qtwe_version_str()
+
old_qutebrowser_version = self['general'].get('version', None)
if old_qutebrowser_version is None:
# https://github.com/python/typeshed/issues/2093
@@ -286,18 +302,18 @@ class YamlConfig(QObject):
self._validate_names(settings)
self._build_values(settings)
- def _load_settings_object(self, yaml_data: Any) -> '_SettingsType':
+ def _load_settings_object(self, yaml_data: Any) -> _SettingsType:
"""Load the settings from the settings: key."""
return self._pop_object(yaml_data, 'settings', dict)
- def _load_legacy_settings_object(self, yaml_data: Any) -> '_SettingsType':
+ def _load_legacy_settings_object(self, yaml_data: Any) -> _SettingsType:
data = self._pop_object(yaml_data, 'global', dict)
settings = {}
for name, value in data.items():
settings[name] = {'global': value}
return settings
- def _build_values(self, settings: Mapping) -> None:
+ def _build_values(self, settings: Mapping[str, Any]) -> None:
"""Build up self._values from the values in the given dict."""
errors = []
for name, yaml_values in settings.items():
@@ -724,9 +740,17 @@ class ConfigPyWriter:
def __init__(
self,
- options: List,
+ options: List[
+ Tuple[
+ Optional[urlmatch.UrlPattern],
+ configdata.Option,
+ Any
+ ]
+ ],
bindings: MutableMapping[str, Mapping[str, Optional[str]]],
- *, commented: bool) -> None:
+ *,
+ commented: bool,
+ ) -> None:
self._options = options
self._bindings = bindings
self._commented = commented
diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py
index 1b07baab7..7556d2b6d 100644
--- a/qutebrowser/config/websettings.py
+++ b/qutebrowser/config/websettings.py
@@ -23,7 +23,7 @@ import re
import argparse
import functools
import dataclasses
-from typing import Any, Callable, Dict, Optional
+from typing import Any, Callable, Dict, Optional, Union
from PyQt5.QtCore import QUrl, pyqtSlot, qVersion
from PyQt5.QtGui import QFont
@@ -85,7 +85,11 @@ class AttributeInfo:
"""Info about a settings attribute."""
- def __init__(self, *attributes: Any, converter: Callable = None) -> None:
+ def __init__(
+ self,
+ *attributes: Any,
+ converter: Callable[[Any], bool] = None,
+ ) -> None:
self.attributes = attributes
if converter is None:
self.converter = lambda val: val
@@ -105,9 +109,6 @@ class AbstractSettings:
def __init__(self, settings: Any) -> None:
self._settings = settings
- def _assert_not_unset(self, value: Any) -> None:
- assert value is not usertypes.UNSET