diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index b07b64fa6..9df2ce00c 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -47,6 +47,9 @@ Changed than `rofi` to ask for a password. - The `content.headers.custom` setting now accepts empty strings as values, resulting in an empty header being sent. +- Renamed settings: + * `qt.low_end_device_mode` -> `qt.chromium.low_end_device_mode` + * `qt.process_model` -> `qt.chromium.process_model` Added ~~~~~ @@ -55,6 +58,9 @@ Added more emacs-like bindings. - New `{relative_index}` field for `tabs.title.format` (and `.pinned_format`) which shows relative tab numbers. +- New `input.mode_override` option which allows overriding the current mode + based on the new URL when navigating or switching tabs. +- New `qt.chromium.sandboxing` setting which allows to disable Chromium's sandboxing (mainly intended for development and testing) Fixed ~~~~~ diff --git a/doc/help/configuring.asciidoc b/doc/help/configuring.asciidoc index 60c30f8d4..b469118ed 100644 --- a/doc/help/configuring.asciidoc +++ b/doc/help/configuring.asciidoc @@ -407,7 +407,7 @@ Pre-built colorschemes - A collection of https://github.com/chriskempson/base16[base16] color-schemes can be found in https://github.com/theova/base16-qutebrowser[base16-qutebrowser] and used with https://github.com/AuditeMarlow/base16-manager[base16-manager]. - Another collection: https://github.com/leosolid/qutebrowser-themes[qutebrowser-themes] -- https://gitlab.com/jjzmajic/qutewal[Pywal integration] +- Pywal integration: https://gitlab.com/jjzmajic/qutewal[qutewal], https://github.com/makman12/pywalQute[pywalQute] - https://github.com/arcticicestudio/nord[Nord]: https://github.com/Linuus/nord-qutebrowser[Linuus], https://github.com/KnownAsDon/QuteBrowser-Nord-Theme[KnownAsDon] - https://github.com/dracula/qutebrowser-dracula-theme[Dracula] - https://gitlab.com/lovetocode999/selenized-qutebrowser[Selenized] diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index b8705749a..0adc95fdd 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -271,6 +271,7 @@ |<>|Include hyperlinks in the keyboard focus chain when tabbing. |<>|Interpret number prefixes as counts for bindings. |<>|Whether the underlying Chromium should handle media keys. +|<>|Mode to change to when focusing on a tab/URL changes. |<>|Enable back and forward buttons on the mouse. |<>|Enable Opera-like mouse rocker gestures. |<>|Timeout (in milliseconds) for partially typed key bindings. @@ -286,13 +287,14 @@ |<>|Show a filebrowser in download prompts. |<>|Rounding radius (in pixels) for the edges of prompts. |<>|Additional arguments to pass to Qt, without leading `--`. +|<>|When to use Chromium's low-end device mode. +|<>|Which Chromium process model to use. +|<>|What sandboxing mechanisms in Chromium to use. |<>|Additional environment variables to set. |<>|Force a Qt platform to use. |<>|Force a Qt platformtheme to use. |<>|Force software rendering for QtWebEngine. |<>|Turn on Qt HighDPI scaling. -|<>|When to use Chromium's low-end device mode. -|<>|Which Chromium process model to use. |<>|Work around locale parsing issues in QtWebEngine 5.15.3. |<>|Delete the QtWebEngine Service Worker directory on every start. |<>|When/how to show the scrollbar. @@ -3587,6 +3589,22 @@ Type: <> Default: +pass:[true]+ +[[input.mode_override]] +=== input.mode_override +Mode to change to when focusing on a tab/URL changes. + +This setting supports link:configuring{outfilesuffix}#patterns[URL patterns]. + +Type: <> + +Valid values: + + * +normal+ + * +insert+ + * +passthrough+ + +Default: empty + [[input.mouse.back_forward_buttons]] === input.mouse.back_forward_buttons Enable back and forward buttons on the mouse. @@ -3755,6 +3773,73 @@ Type: <> Default: empty +[[qt.chromium.low_end_device_mode]] +=== qt.chromium.low_end_device_mode +When to use Chromium's low-end device mode. +This improves the RAM usage of renderer processes, at the expense of performance. + +This setting requires a restart. + +This setting is only available with the QtWebEngine backend. + +Type: <> + +Valid values: + + * +always+: Always use low-end device mode. + * +auto+: Decide automatically (uses low-end mode with < 1 GB available RAM). + * +never+: Never use low-end device mode. + +Default: +pass:[auto]+ + +[[qt.chromium.process_model]] +=== qt.chromium.process_model +Which Chromium process model to use. +Alternative process models use less resources, but decrease security and robustness. +See the following pages for more details: + + - https://www.chromium.org/developers/design-documents/process-models + - https://doc.qt.io/qt-5/qtwebengine-features.html#process-models + +This setting requires a restart. + +This setting is only available with the QtWebEngine backend. + +Type: <> + +Valid values: + + * +process-per-site-instance+: Pages from separate sites are put into separate processes and separate visits to the same site are also isolated. + * +process-per-site+: Pages from separate sites are put into separate processes. Unlike Process per Site Instance, all visits to the same site will share an OS process. The benefit of this model is reduced memory consumption, because more web pages will share processes. The drawbacks include reduced security, robustness, and responsiveness. + * +single-process+: Run all tabs in a single process. This should be used for debugging purposes only, and it disables `:open --private`. + +Default: +pass:[process-per-site-instance]+ + +[[qt.chromium.sandboxing]] +=== qt.chromium.sandboxing +What sandboxing mechanisms in Chromium to use. +Chromium has various sandboxing layers, which should be enabled for normal browser usage. Mainly for testing and development, it's possible to disable individual sandboxing layers via this setting. +Open `chrome://sandbox` to see the current sandbox status. +Changing this setting is only recommended if you know what you're doing, as it **disables one of Chromium's security layers**. To avoid sandboxing being accidentally disabled persistently, this setting can only be set via `config.py`, not via `:set`. +See the Chromium documentation for more details: +- https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)] + +This setting requires a restart. + +This setting can only be set in config.py. + +This setting is only available with the QtWebEngine backend. + +Type: <> + +Valid values: + + * +enable-all+: Enable all available sandboxing mechanisms. + * +disable-seccomp-bpf+: Disable the Seccomp BPF filter sandbox (Linux only). + * +disable-all+: Disable all sandboxing (**not recommended!**). + +Default: +pass:[enable-all]+ + [[qt.environ]] === qt.environ Additional environment variables to set. @@ -3820,48 +3905,6 @@ Type: <> Default: +pass:[false]+ -[[qt.low_end_device_mode]] -=== qt.low_end_device_mode -When to use Chromium's low-end device mode. -This improves the RAM usage of renderer processes, at the expense of performance. - -This setting requires a restart. - -This setting is only available with the QtWebEngine backend. - -Type: <> - -Valid values: - - * +always+: Always use low-end device mode. - * +auto+: Decide automatically (uses low-end mode with < 1 GB available RAM). - * +never+: Never use low-end device mode. - -Default: +pass:[auto]+ - -[[qt.process_model]] -=== qt.process_model -Which Chromium process model to use. -Alternative process models use less resources, but decrease security and robustness. -See the following pages for more details: - - - https://www.chromium.org/developers/design-documents/process-models - - https://doc.qt.io/qt-5/qtwebengine-features.html#process-models - -This setting requires a restart. - -This setting is only available with the QtWebEngine backend. - -Type: <> - -Valid values: - - * +process-per-site-instance+: Pages from separate sites are put into separate processes and separate visits to the same site are also isolated. - * +process-per-site+: Pages from separate sites are put into separate processes. Unlike Process per Site Instance, all visits to the same site will share an OS process. The benefit of this model is reduced memory consumption, because more web pages will share processes. The drawbacks include reduced security, robustness, and responsiveness. - * +single-process+: Run all tabs in a single process. This should be used for debugging purposes only, and it disables `:open --private`. - -Default: +pass:[process-per-site-instance]+ - [[qt.workarounds.locale]] === qt.workarounds.locale Work around locale parsing issues in QtWebEngine 5.15.3. diff --git a/doc/qutebrowser.1.asciidoc b/doc/qutebrowser.1.asciidoc index bc312f108..f22be1ddc 100644 --- a/doc/qutebrowser.1.asciidoc +++ b/doc/qutebrowser.1.asciidoc @@ -34,7 +34,7 @@ show it. *'URL'*:: URLs to open on startup (empty as a window separator). -=== optional arguments +=== options *-h*, *--help*:: show this help message and exit diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt index 9d42d3a5b..b785d8ab1 100644 --- a/misc/requirements/requirements-check-manifest.txt +++ b/misc/requirements/requirements-check-manifest.txt @@ -6,4 +6,4 @@ packaging==21.3 pep517==0.12.0 pyparsing==3.0.7 toml==0.10.2 -tomli==2.0.0 +tomli==2.0.1 diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt index ad2ff4ace..04c8358fb 100644 --- a/misc/requirements/requirements-dev.txt +++ b/misc/requirements/requirements-dev.txt @@ -5,17 +5,15 @@ build==0.7.0 bump2version==1.0.1 certifi==2021.10.8 cffi==1.15.0 -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 colorama==0.4.4 cryptography==36.0.1 -Deprecated==1.2.13 docutils==0.18.1 -github3.py==3.0.0 +github3.py==3.2.0 hunter==3.4.3 idna==3.3 -importlib-metadata==4.10.1 +importlib-metadata==4.11.2 jeepney==0.7.1 -jwcrypto==1.0 keyring==23.5.0 manhole==1.8.0 packaging==21.3 @@ -23,11 +21,12 @@ pep517==0.12.0 pkginfo==1.8.2 pycparser==2.21 Pygments==2.11.2 +PyJWT==2.3.0 Pympler==1.0.1 pyparsing==3.0.7 PyQt-builder==1.12.2 python-dateutil==2.8.2 -readme-renderer==32.0 +readme-renderer==33.0 requests==2.27.1 requests-toolbelt==0.9.1 rfc3986==2.0.0 @@ -35,11 +34,10 @@ SecretStorage==3.3.1 sip==6.5.1 six==1.16.0 toml==0.10.2 -tomli==2.0.0 -tqdm==4.62.3 +tomli==2.0.1 +tqdm==4.63.0 twine==3.8.0 uritemplate==4.1.1 # urllib3==1.26.8 webencodings==0.5.1 -wrapt==1.13.3 zipp==3.7.0 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 8d399d593..cad1d811c 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -2,18 +2,18 @@ chardet==4.0.0 diff-cover==6.4.4 -importlib-metadata==4.10.1 +importlib-metadata==4.11.2 importlib-resources==5.4.0 Jinja2==3.0.3 -lxml==4.7.1 -MarkupSafe==2.0.1 +lxml==4.8.0 +MarkupSafe==2.1.0 mypy==0.931 mypy-extensions==0.4.3 pluggy==1.0.0 Pygments==2.11.2 PyQt5-stubs==5.15.2.0 -tomli==2.0.0 +tomli==2.0.1 types-dataclasses==0.6.4 types-PyYAML==6.0.4 -typing_extensions==4.0.1 +typing_extensions==4.1.1 zipp==3.7.0 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index b7d5a67bb..c6a088800 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py altgraph==0.17.2 -pyinstaller==4.9 -pyinstaller-hooks-contrib==2022.0 +pyinstaller==4.10 +pyinstaller-hooks-contrib==2022.2 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 2f17bb26a..a251ffa40 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -3,19 +3,18 @@ astroid==2.9.3 certifi==2021.10.8 cffi==1.15.0 -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 cryptography==36.0.1 -Deprecated==1.2.13 future==0.18.2 -github3.py==3.0.0 +github3.py==3.2.0 idna==3.3 isort==5.10.1 -jwcrypto==1.0 lazy-object-proxy==1.7.1 mccabe==0.6.1 pefile==2021.9.3 -platformdirs==2.4.1 ; python_version>="3.7" +platformdirs==2.5.1 ; python_version>="3.7" pycparser==2.21 +PyJWT==2.3.0 pylint==2.12.2 python-dateutil==2.8.2 ./scripts/dev/pylint_checkers @@ -23,7 +22,7 @@ requests==2.27.1 six==1.16.0 toml==0.10.2 typed-ast==1.5.2 ; python_version<"3.8" -typing_extensions==4.0.1 +typing_extensions==4.1.1 uritemplate==4.1.1 # urllib3==1.26.8 wrapt==1.13.3 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index 5abe05374..4847b111b 100644 --- a/misc/requirements/requirements-pyroma.txt +++ b/misc/requirements/requirements-pyroma.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py certifi==2021.10.8 -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 docutils==0.18.1 idna==3.3 Pygments==2.11.2 diff --git a/misc/requirements/requirements-qutebrowser.txt-raw b/misc/requirements/requirements-qutebrowser.txt-raw index 139d599f8..63ac32f86 100644 --- a/misc/requirements/requirements-qutebrowser.txt-raw +++ b/misc/requirements/requirements-qutebrowser.txt-raw @@ -23,3 +23,5 @@ typing_extensions # from importlib-metadata #@ add: importlib-metadata<4.9 ; python_version=="3.6.*" #@ markers: zipp python_version>="3.7" #@ add: zipp<3.7 ; python_version=="3.6.*" +#@ markers: MarkupSafe python_version>="3.7" +#@ add: MarkupSafe<2.1.0 ; python_version=="3.6.*" diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index fad0d715f..04f624c37 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -3,13 +3,13 @@ alabaster==0.7.12 Babel==2.9.1 certifi==2021.10.8 -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 docutils==0.17.1 idna==3.3 imagesize==1.3.0 -importlib-metadata==4.10.1 +importlib-metadata==4.11.2 Jinja2==3.0.3 -MarkupSafe==2.0.1 +MarkupSafe==2.1.0 packaging==21.3 Pygments==2.11.2 pyparsing==3.0.7 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 6375c689b..dd28a4eb4 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -3,26 +3,25 @@ attrs==21.4.0 beautifulsoup4==4.10.0 certifi==2021.10.8 -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 cheroot==8.6.0 -click==8.0.3 -coverage==6.3.1 ; python_version>="3.7" -EasyProcess==1.1 +click==8.0.4 +coverage==6.3.2 ; python_version>="3.7" execnet==1.9.0 -filelock==3.4.2 ; python_version>="3.7" -Flask==2.0.2 +filelock==3.6.0 ; python_version>="3.7" +Flask==2.0.3 glob2==0.7 hunter==3.4.3 -hypothesis==6.36.1 ; python_version>="3.7" +hypothesis==6.39.1 ; python_version>="3.7" icdiff==2.0.4 idna==3.3 iniconfig==1.1.1 -itsdangerous==2.0.1 +itsdangerous==2.1.0 ; python_version>="3.7" jaraco.functools==3.5.0 ; python_version>="3.7" # Jinja2==3.0.3 Mako==1.1.6 manhole==1.8.0 -# MarkupSafe==2.0.1 +# MarkupSafe==2.1.0 more-itertools==8.12.0 packaging==21.3 parse==1.19.0 @@ -33,7 +32,7 @@ py==1.11.0 py-cpuinfo==8.0.0 Pygments==2.11.2 pyparsing==3.0.7 -pytest==7.0.0 +pytest==7.0.1 pytest-bdd==4.1.0 pytest-benchmark==3.4.1 pytest-cov==3.0.0 @@ -46,21 +45,23 @@ pytest-repeat==0.9.1 pytest-rerunfailures==10.2 pytest-xdist==2.5.0 pytest-xvfb==2.0.0 -PyVirtualDisplay==2.2 +PyVirtualDisplay==3.0 requests==2.27.1 requests-file==1.5.1 six==1.16.0 sortedcontainers==2.4.0 soupsieve==2.3.1 -tldextract==3.1.2 +tldextract==3.2.0 ; python_version>="3.7" toml==0.10.2 -tomli==2.0.0 ; python_version>="3.7" +tomli==2.0.1 ; python_version>="3.7" urllib3==1.26.8 vulture==2.3 -Werkzeug==2.0.2 +Werkzeug==2.0.3 jaraco.functools<3.5 ; python_version=="3.6.*" tomli<2 ; python_version=="3.6.*" filelock==3.4.1 ; python_version=="3.6.*" hypothesis<6.32 ; python_version=="3.6.*" coverage<6.3 ; python_version=="3.6.*" pytest-mock<3.7 ; python_version=="3.6.*" +itsdangerous<2.1.0 ; python_version=="3.6.*" +tldextract<3.2.0 ; python_version=="3.6.*" diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw index 0f45b5364..0aa66e0ad 100644 --- a/misc/requirements/requirements-tests.txt-raw +++ b/misc/requirements/requirements-tests.txt-raw @@ -49,3 +49,7 @@ tldextract #@ add: coverage<6.3 ; python_version=="3.6.*" #@ markers: pytest-mock python_version>="3.7" #@ add: pytest-mock<3.7 ; python_version=="3.6.*" +#@ markers: itsdangerous python_version>="3.7" +#@ add: itsdangerous<2.1.0 ; python_version=="3.6.*" +#@ markers: tldextract python_version>="3.7" +#@ add: tldextract<3.2.0 ; python_version=="3.6.*" diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index 69b32a1c7..afe214b32 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -1,18 +1,18 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py distlib==0.3.4 -filelock==3.4.2 ; python_version>="3.7" +filelock==3.6.0 ; python_version>="3.7" packaging==21.3 pip==22.0.3 ; python_version>="3.7" -platformdirs==2.4.1 ; python_version>="3.7" +platformdirs==2.5.1 ; python_version>="3.7" pluggy==1.0.0 py==1.11.0 pyparsing==3.0.7 -setuptools==60.8.1 ; python_version>="3.7" +setuptools==60.9.3 ; python_version>="3.7" six==1.16.0 toml==0.10.2 tox==3.24.5 -virtualenv==20.13.1 +virtualenv==20.13.2 wheel==0.37.1 setuptools<60 ; python_version=="3.6.*" filelock==3.4.1 ; python_version=="3.6.*" diff --git a/misc/userscripts/qute-bitwarden b/misc/userscripts/qute-bitwarden index 93d26a109..f8629d1dc 100755 --- a/misc/userscripts/qute-bitwarden +++ b/misc/userscripts/qute-bitwarden @@ -6,7 +6,7 @@ # 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 bjy +# 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. # diff --git a/misc/userscripts/qute-lastpass b/misc/userscripts/qute-lastpass index d2a72f077..edb48dfdc 100755 --- a/misc/userscripts/qute-lastpass +++ b/misc/userscripts/qute-lastpass @@ -6,7 +6,7 @@ # 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 bjy +# 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. # diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index cf5e4665a..1d41d147d 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -239,6 +239,9 @@ qt.force_platformtheme: based on the desktop environment. qt.process_model: + renamed: qt.chromium.process_model + +qt.chromium.process_model: type: name: String valid_values: @@ -268,6 +271,9 @@ qt.process_model: - https://doc.qt.io/qt-5/qtwebengine-features.html#process-models qt.low_end_device_mode: + renamed: qt.chromium.low_end_device_mode + +qt.chromium.low_end_device_mode: type: name: String valid_values: @@ -284,6 +290,39 @@ qt.low_end_device_mode: This improves the RAM usage of renderer processes, at the expense of performance. +qt.chromium.sandboxing: + type: + name: String + valid_values: + - enable-all: Enable all available sandboxing mechanisms. + - disable-seccomp-bpf: Disable the Seccomp BPF filter sandbox (Linux only). + - disable-all: Disable all sandboxing (**not recommended!**). + default: enable-all + backend: QtWebEngine + restart: true + no_autoconfig: true # due to it being dangerous + # yamllint disable rule:line-length + desc: >- + What sandboxing mechanisms in Chromium to use. + + Chromium has various sandboxing layers, which should be enabled for normal + browser usage. Mainly for testing and development, it's possible to disable + individual sandboxing layers via this setting. + + Open `chrome://sandbox` to see the current sandbox status. + + Changing this setting is only recommended if you know what you're doing, as + it **disables one of Chromium's security layers**. To avoid sandboxing being + accidentally disabled persistently, this setting can only be set via + `config.py`, not via `:set`. + + See the Chromium documentation for more details: + + - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux] + - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] + - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)] + # yamllint enable rule:line-length + qt.highdpi: type: Bool default: false @@ -1811,6 +1850,18 @@ input.match_counts: Disabling it allows for emacs-like bindings where number keys are passed through (according to `input.forward_unbound_keys`) instead. +input.mode_override: + default: null + type: + name: String + none_ok: true + valid_values: + - normal + - insert + - passthrough + supports_pattern: true + desc: Mode to change to when focusing on a tab/URL changes. + ## keyhint keyhint.blacklist: diff --git a/qutebrowser/config/qtargs.py b/qutebrowser/config/qtargs.py index 2f93b7de5..9e7f2620d 100644 --- a/qutebrowser/config/qtargs.py +++ b/qutebrowser/config/qtargs.py @@ -321,12 +321,12 @@ def _qtwebengine_settings_args(versions: version.WebEngineVersions) -> Iterator[ '--force-webrtc-ip-handling-policy=' 'disable_non_proxied_udp', }, - 'qt.process_model': { + 'qt.chromium.process_model': { 'process-per-site-instance': None, 'process-per-site': '--process-per-site', 'single-process': '--single-process', }, - 'qt.low_end_device_mode': { + 'qt.chromium.low_end_device_mode': { 'auto': None, 'always': '--enable-low-end-device-mode', 'never': '--disable-low-end-device-mode', @@ -338,6 +338,11 @@ def _qtwebengine_settings_args(versions: version.WebEngineVersions) -> Iterator[ True: '--force-prefers-reduced-motion', False: None, }, + 'qt.chromium.sandboxing': { + 'enable-all': None, + 'disable-seccomp-bpf': '--disable-seccomp-filter-sandbox', + 'disable-all': '--no-sandbox', + } } qt_514_ver = utils.VersionNumber(5, 14) diff --git a/qutebrowser/html/warning-webkit.html b/qutebrowser/html/warning-webkit.html index 975f98c1b..f5cf9bf01 100644 --- a/qutebrowser/html/warning-webkit.html +++ b/qutebrowser/html/warning-webkit.html @@ -41,8 +41,8 @@ hopefully help.

notification support was added for Qt 5.13.0.

Resource usage: qutebrowser v1.5.0 added the qt.process_model and qt.low_end_device_mode settings which can be used to +class="mono">qt.chromium.process_model and qt.chromium.low_end_device_mode settings which can be used to decrease the resource usage of QtWebEngine (but come with other drawbacks).

Not trusting Google: Various people have checked the connections made @@ -78,7 +78,7 @@ security fixes took months to arrive there). You might be better off choosing an method.

White flashing between loads with a custom stylesheet: This doesn't -seem to happen with qt.process_model = single-process +seem to happen with qt.chromium.process_model = single-process set. However, note that that setting comes with decreased security and stability, but QtWebKit doesn't have any process isolation at all.

{% endblock %} diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index c3f06e185..da688367e 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -223,6 +223,10 @@ class TabbedBrowser(QWidget): # https://bugreports.qt.io/browse/QTBUG-65223 self.cur_load_finished.connect(self._leave_modes_on_load) + # handle mode_override + self.current_tab_changed.connect(lambda tab: self._mode_override(tab.url())) + self.cur_url_changed.connect(self._mode_override) + # This init is never used, it is immediately thrown away in the next # line. self.undo_stack: UndoStackType = collections.deque() @@ -777,6 +781,23 @@ class TabbedBrowser(QWidget): if not self.widget.page_title(idx): self.widget.set_page_title(idx, url.toDisplayString()) + def _mode_override(self, url: QUrl) -> None: + """Override mode if url matches pattern. + + Args: + url: The QUrl to match for + """ + if not url.isValid(): + return + mode = config.instance.get('input.mode_override', url=url) + if mode: + log.modes.debug(f"Mode change to {mode} triggered for url {url}") + modeman.enter( + self._win_id, + usertypes.KeyMode[mode], + reason='mode_override', + ) + @pyqtSlot(browsertab.AbstractTab) def _on_icon_changed(self, tab): """Set the icon of a tab. diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 8da86dd00..33acdce57 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -724,7 +724,7 @@ class WebEngineVersions: ) -def qtwebengine_versions(avoid_init: bool = False) -> WebEngineVersions: +def qtwebengine_versions(*, avoid_init: bool = False) -> WebEngineVersions: """Get the QtWebEngine and Chromium version numbers. If we have a parsed user agent, we use it here. If not, we avoid initializing diff --git a/requirements.txt b/requirements.txt index 77d061810..6c21e38fe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,16 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -adblock==0.5.1 +adblock==0.5.2 colorama==0.4.4 dataclasses==0.6 ; python_version<"3.7" -importlib-metadata==4.10.1 ; python_version=="3.7.*" +importlib-metadata==4.11.2 ; python_version=="3.7.*" importlib-resources==5.4.0 ; python_version<"3.9" Jinja2==3.0.3 -MarkupSafe==2.0.1 +MarkupSafe==2.1.0 ; python_version>="3.7" Pygments==2.11.2 PyYAML==6.0 -typing_extensions==4.0.1 ; python_version<"3.8" +typing_extensions==4.1.1 ; python_version<"3.8" zipp==3.7.0 ; python_version>="3.7" importlib-metadata<4.9 ; python_version=="3.6.*" zipp<3.7 ; python_version=="3.6.*" +MarkupSafe<2.1.0 ; python_version=="3.6.*" diff --git a/scripts/dev/changelog_urls.json b/scripts/dev/changelog_urls.json index cf661a22c..53b62b0df 100644 --- a/scripts/dev/changelog_urls.json +++ b/scripts/dev/changelog_urls.json @@ -8,7 +8,6 @@ "pytest-xdist": "https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst", "pytest-forked": "https://github.com/pytest-dev/pytest-forked/blob/master/CHANGELOG.rst", "pytest-xvfb": "https://github.com/The-Compiler/pytest-xvfb/blob/master/CHANGELOG.rst", - "EasyProcess": "https://github.com/ponty/EasyProcess/commits/master", "PyVirtualDisplay": "https://github.com/ponty/PyVirtualDisplay/commits/master", "execnet": "https://execnet.readthedocs.io/en/latest/changelog.html", "pytest-rerunfailures": "https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst", @@ -95,7 +94,6 @@ "altgraph": "https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst", "urllib3": "https://github.com/urllib3/urllib3/blob/master/CHANGES.rst", "lxml": "https://github.com/lxml/lxml/blob/master/CHANGES.txt", - "jwcrypto": "https://github.com/latchset/jwcrypto/commits/master", "wrapt": "https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst", "pep517": "https://github.com/pypa/pep517/blob/master/doc/changelog.rst", "cryptography": "https://cryptography.io/en/latest/changelog.html", @@ -129,7 +127,7 @@ "yamllint": "https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst", "pathspec": "https://github.com/cpburnz/python-path-specification/blob/master/CHANGES.rst", "filelock": "https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst", - "github3.py": "https://github3py.readthedocs.io/en/master/release-notes/index.html", + "github3.py": "https://github3.readthedocs.io/en/latest/release-notes/index.html", "manhole": "https://github.com/ionelmc/python-manhole/blob/master/CHANGELOG.rst", "pycparser": "https://github.com/eliben/pycparser/blob/master/CHANGES", "python-dateutil": "https://dateutil.readthedocs.io/en/stable/changelog.html", @@ -147,7 +145,6 @@ "setuptools": "https://setuptools.readthedocs.io/en/latest/history.html", "future": "https://python-future.org/whatsnew.html", "pefile": "https://github.com/erocarrera/pefile/commits/master", - "Deprecated": "https://github.com/tantale/deprecated/blob/master/CHANGELOG.rst", "SecretStorage": "https://github.com/mitya57/secretstorage/blob/master/changelog", "bleach": "https://github.com/mozilla/bleach/blob/main/CHANGES", "jeepney": "https://gitlab.com/takluyver/jeepney/-/blob/master/docs/release-notes.rst", @@ -158,5 +155,6 @@ "rfc3986": "https://rfc3986.readthedocs.io/en/latest/release-notes/index.html", "tqdm": "https://tqdm.github.io/releases/", "twine": "https://twine.readthedocs.io/en/stable/changelog.html", - "webencodings": "https://github.com/gsnedders/python-webencodings/commits/master" + "webencodings": "https://github.com/gsnedders/python-webencodings/commits/master", + "PyJWT": "https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst" } diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py index 375bb1eb7..febd2bf8a 100755 --- a/scripts/dev/src2asciidoc.py +++ b/scripts/dev/src2asciidoc.py @@ -536,7 +536,9 @@ def regenerate_manpage(filename): # pylint: disable=protected-access for group in parser._action_groups: groupdata = [] - groupdata.append('=== {}'.format(group.title)) + # https://bugs.python.org/issue9694 backport + title = "options" if group.title == "optional arguments" else group.title + groupdata.append('=== {}'.format(title)) if group.description is not None: groupdata.append(group.description) for action in group._group_actions: diff --git a/tests/conftest.py b/tests/conftest.py index ad82b4f24..84cae784b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -194,9 +194,8 @@ def pytest_ignore_collect(path): @pytest.fixture(scope='session') def qapp_args(): """Make QtWebEngine unit tests run on older Qt versions + newer kernels.""" - seccomp_args = testutils.seccomp_args(qt_flag=False) - if seccomp_args: - return [sys.argv[0]] + seccomp_args + if testutils.disable_seccomp_bpf_sandbox(): + return [sys.argv[0], testutils.DISABLE_SECCOMP_BPF_FLAG] return [] diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 3715d5765..54e84a10e 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1741,3 +1741,22 @@ Feature: Tab management And I run :undo And I run :message-info "Still alive!" Then the message "Still alive!" should be shown + + Scenario: Passthrough mode override + When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'passthrough' + And I open data/numbers/1.txt + Then "Entering mode KeyMode.passthrough (reason: mode_override)" should be logged + + Scenario: Insert mode override + When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'insert' + And I open data/numbers/1.txt + Then "Entering mode KeyMode.insert (reason: mode_override)" should be logged + + Scenario: Mode override on tab switch + When I run :set -u localhost:*/data/numbers/1.txt input.mode_override 'insert' + And I open data/numbers/1.txt + And I wait for "Entering mode KeyMode.insert (reason: mode_override)" in the log + And I run :fake-key -g + And I open data/numbers/2.txt in a new tab + And I run :tab-prev + Then "Entering mode KeyMode.insert (reason: mode_override)" should be logged diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 14f34b52c..ab8f28d26 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -549,8 +549,8 @@ class QuteProc(testprocess.Process): '--debug-flag', 'werror', '--debug-flag', 'test-notification-service'] - if self.request.config.webengine: - args += testutils.seccomp_args(qt_flag=True) + if self.request.config.webengine and testutils.disable_seccomp_bpf_sandbox(): + args += testutils.DISABLE_SECCOMP_BPF_ARGS args.append('about:blank') return args diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py index b860feed0..43809cfd4 100644 --- a/tests/end2end/test_invocations.py +++ b/tests/end2end/test_invocations.py @@ -56,8 +56,8 @@ def _base_args(config): else: args += ['--backend', 'webkit'] - if config.webengine: - args += testutils.seccomp_args(qt_flag=True) + if config.webengine and testutils.disable_seccomp_bpf_sandbox(): + args += testutils.DISABLE_SECCOMP_BPF_ARGS args.append('about:blank') return args @@ -826,3 +826,81 @@ def test_json_logging_without_debug(request, quteproc_new, runtime_tmpdir): quteproc_new.exit_expected = True quteproc_new.start(args, env={'XDG_RUNTIME_DIR': str(runtime_tmpdir)}) assert not quteproc_new.is_running() + + +@pytest.mark.qtwebkit_skip +@pytest.mark.parametrize( + 'sandboxing, has_namespaces, has_seccomp, has_yama, expected_result', [ + ('enable-all', True, True, True, "You are adequately sandboxed."), + ('disable-seccomp-bpf', True, False, True, "You are NOT adequately sandboxed."), + ('disable-all', False, False, False, "You are NOT adequately sandboxed."), + ] +) +def test_sandboxing( + request, quteproc_new, sandboxing, + has_namespaces, has_seccomp, has_yama, expected_result, +): + if not request.config.webengine: + pytest.skip("Skipped with QtWebKit") + elif sandboxing == "enable-all" and testutils.disable_seccomp_bpf_sandbox(): + pytest.skip("Full sandboxing not supported") + + args = _base_args(request.config) + [ + '--temp-basedir', + '-s', 'qt.chromium.sandboxing', sandboxing, + ] + quteproc_new.start(args) + + quteproc_new.open_url('chrome://sandbox') + text = quteproc_new.get_content() + print(text) + + not_found_msg = ("The webpage at chrome://sandbox/ might be temporarily down or " + "it may have moved permanently to a new web address.") + if not_found_msg in text.split("\n"): + line = quteproc_new.wait_for(message='Load error: ERR_INVALID_URL') + line.expected = True + pytest.skip("chrome://sandbox/ not supported") + + bpf_text = "Seccomp-BPF sandbox" + yama_text = "Ptrace Protection with Yama LSM" + + if "\n\n\n" in text: + # Qt 5.12 + header, rest = text.split("\n", maxsplit=1) + rest, result = rest.rsplit("\n\n", maxsplit=1) + lines = rest.replace("\t\n", "\t").split("\n\n\n") + + expected_status = { + "Namespace Sandbox": "Yes" if has_namespaces else "No", + "Network namespaces": "Yes" if has_namespaces else "No", + "PID namespaces": "Yes" if has_namespaces else "No", + "SUID Sandbox": "No", + + bpf_text: "Yes" if has_seccomp else "No", + f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No", + + "Yama LSM Enforcing": "Yes" if has_yama else "No", + } + else: + header, *lines, empty, result = text.split("\n") + assert not empty + + expected_status = { + "Layer 1 Sandbox": "Namespace" if has_namespaces else "None", + + "PID namespaces": "Yes" if has_namespaces else "No", + "Network namespaces": "Yes" if has_namespaces else "No", + + bpf_text: "Yes" if has_seccomp else "No", + f"{bpf_text} supports TSYNC": "Yes" if has_seccomp else "No", + + f"{yama_text} (Broker)": "Yes" if has_yama else "No", + f"{yama_text} (Non-broker)": "No", + } + + assert header == "Sandbox Status" + assert result == expected_result + + status = dict(line.split("\t") for line in lines) + assert status == expected_status diff --git a/tests/helpers/testutils.py b/tests/helpers/testutils.py index 8bb622133..c607718ab 100644 --- a/tests/helpers/testutils.py +++ b/tests/helpers/testutils.py @@ -31,14 +31,9 @@ import importlib.machinery import pytest -from PyQt5.QtCore import qVersion from PyQt5.QtGui import QColor -try: - from PyQt5.QtWebEngine import PYQT_WEBENGINE_VERSION_STR -except ImportError: - PYQT_WEBENGINE_VERSION_STR = None -from qutebrowser.utils import qtutils, log, utils +from qutebrowser.utils import qtutils, log, utils, version ON_CI = 'CI' in os.environ @@ -267,35 +262,38 @@ def easyprivacy_txt(): return _decompress_gzip_datafile("easyprivacy.txt.gz") -def seccomp_args(qt_flag): - """Get necessary flags to disable the seccomp BPF sandbox. +DISABLE_SECCOMP_BPF_FLAG = "--disable-seccomp-filter-sandbox" +DISABLE_SECCOMP_BPF_ARGS = ["-s", "qt.chromium.sandboxing", "disable-seccomp-bpf"] + + +def disable_seccomp_bpf_sandbox(): + """Check whether we need to disable the seccomp BPF sandbox. This is needed for some QtWebEngine setups, with older Qt versions but newer kernels. - - Args: - qt_flag: Add a '--qt-flag' argument. """ + try: + from PyQt5 import QtWebEngine # pylint: disable=unused-import + except ImportError: + # no QtWebEngine available + return False + affected_versions = set() for base, patch_range in [ - # 5.12.0 to 5.12.7 (inclusive) - ('5.12', range(0, 8)), + # 5.12.0 to 5.12.10 (inclusive) + ('5.12', range(0, 11)), # 5.13.0 to 5.13.2 (inclusive) ('5.13', range(0, 3)), # 5.14.0 ('5.14', [0]), + # 5.15.0 to 5.15.2 (inclusive) + ('5.15', range(0, 3)), ]: for patch in patch_range: - affected_versions.add('{}.{}'.format(base, patch)) + affected_versions.add(utils.VersionNumber.parse(f'{base}.{patch}')) - version = (PYQT_WEBENGINE_VERSION_STR - if PYQT_WEBENGINE_VERSION_STR is not None - else qVersion()) - if version in affected_versions: - disable_arg = 'disable-seccomp-filter-sandbox' - return ['--qt-flag', disable_arg] if qt_flag else ['--' + disable_arg] - - return [] + versions = version.qtwebengine_versions(avoid_init=True) + return versions.webengine in affected_versions def import_userscript(name): diff --git a/tests/unit/config/test_qtargs.py b/tests/unit/config/test_qtargs.py index d95382624..076ff6e3c 100644 --- a/tests/unit/config/test_qtargs.py +++ b/tests/unit/config/test_qtargs.py @@ -249,7 +249,7 @@ class TestWebEngineArgs: ('single-process', True), ]) def test_process_model(self, config_stub, parser, process_model, added): - config_stub.val.qt.process_model = process_model + config_stub.val.qt.chromium.process_model = process_model parsed = parser.parse_args([]) args = qtargs.qt_args(parsed) @@ -267,7 +267,7 @@ class TestWebEngineArgs: ('never', '--disable-low-end-device-mode'), ]) def test_low_end_device_mode(self, config_stub, parser, low_end_device_mode, arg): - config_stub.val.qt.low_end_device_mode = low_end_device_mode + config_stub.val.qt.chromium.low_end_device_mode = low_end_device_mode parsed = parser.parse_args([]) args = qtargs.qt_args(parsed) @@ -277,6 +277,28 @@ class TestWebEngineArgs: else: assert arg in args + @pytest.mark.parametrize('sandboxing, arg', [ + ('enable-all', None), + ('disable-seccomp-bpf', '--disable-seccomp-filter-sandbox'), + ('disable-all', '--no-sandbox'), + ]) + def test_sandboxing(self, config_stub, parser, sandboxing, arg): + config_stub.val.qt.chromium.sandboxing = sandboxing + parsed = parser.parse_args([]) + args = qtargs.qt_args(parsed) + + remaining_flags = { + '--no-sandbox', + '--disable-seccomp-filter-sandbox', + } + if arg is not None: + remaining_flags.remove(arg) + + if arg is not None: + assert arg in args + + assert not set(args) & remaining_flags + @pytest.mark.parametrize('qt_version, referer, arg', [ # 'always' -> no arguments ('5.15.0', 'always', None),