diff --git a/.gitignore b/.gitignore index b41285fd3..9efceef63 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ __pycache__ /doc/*.html /README.html /qutebrowser/html/doc/ -/qutebrowser/html/*.html /.venv* /.coverage /htmlcov diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 2e346b00d..1d266082d 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -32,10 +32,19 @@ Added QtWebEngine. * Opening a PDF file now doesn't start a second request anymore. * Opening PDFs on https:// sites now works properly. +- New `qt.process_model` setting which can be used to change Chromium's process + model. +- New `qt.low_end_device_mode` setting which turns on Chromium's low-end device + mode. This mode uses less RAM, but the expense of performance. +- New `content.webrtc_ip_handling_policy` setting, which allows more + fine-grained/restrictive control about which IPs are exposed via WebRTC. +- Running qutebrowser with QtWebKit or Qt < 5.9 now shows a warning (only + once), as support for those is going to be removed in a future release. Changed ~~~~~~~ +- The `content.headers.referer` setting now works on QtWebEngine. - The `:repeat` command now takes a count which is multiplied with the given "times" argument. - The default keybinding to leave passthrough mode was changed from `` @@ -67,6 +76,8 @@ Removed ~~~~~~~ - Support for importing pre-v1.0.0 history files has been removed. +- The `content.webrtc_public_interfaces_only` setting has been removed and + replaced by `content.webrtc_ip_handling_policy`. v1.4.2 ------ diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 333473dc7..608429ab5 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -158,7 +158,7 @@ |<>|Validate SSL handshakes. |<>|List of user stylesheet filenames to use. |<>|Enable WebGL. -|<>|Only expose public interfaces via WebRTC. +|<>|Which interfaces to expose via WebRTC. |<>|Limit fullscreen to the browser window (does not expand to fill the screen). |<>|Monitor load requests for cross-site scripting attempts. |<>|Directory to save downloads to. @@ -229,6 +229,8 @@ |<>|Force a Qt platform 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. |<>|Show a scrollbar. |<>|Enable smooth scrolling for web pages. |<>|When to find text on a page case-insensitively. @@ -1675,6 +1677,8 @@ Default: +pass:[true]+ === content.headers.referer When to send the Referer header. The Referer header tells websites from which website you were coming from when visiting them. +No restart is needed with QtWebKit. +This setting requires a restart. Type: <> @@ -1682,12 +1686,10 @@ Valid values: * +always+: Always send the Referer. * +never+: Never send the Referer. This is not recommended, as some sites may break. - * +same-domain+: Only send the Referer for the same domain. This will still protect your privacy, but shouldn't break any sites. + * +same-domain+: Only send the Referer for the same domain. This will still protect your privacy, but shouldn't break any sites. With QtWebEngine, the referer will still be sent for other domains, but with stripped path information. Default: +pass:[same-domain]+ -This setting is only available with the QtWebKit backend. - [[content.headers.user_agent]] === content.headers.user_agent User agent to send. Unset to send the default. @@ -2071,14 +2073,22 @@ Type: <> Default: +pass:[true]+ -[[content.webrtc_public_interfaces_only]] -=== content.webrtc_public_interfaces_only -Only expose public interfaces via WebRTC. -On Qt 5.9, this option requires a restart. On Qt 5.10, this option doesn't work at all because of a Qt bug. On Qt >= 5.11, no restart is required. +[[content.webrtc_ip_handling_policy]] +=== content.webrtc_ip_handling_policy +Which interfaces to expose via WebRTC. +On Qt 5.10, this option doesn't work because of a Qt bug. +This setting requires a restart. -Type: <> +Type: <> -Default: +pass:[false]+ +Valid values: + + * +all-interfaces+: WebRTC has the right to enumerate all interfaces and bind them to discover public interfaces. + * +default-public-and-private-interfaces+: WebRTC should only use the default route used by http. This also exposes the associated default private address. Default route is the route chosen by the OS on a multi-homed endpoint. + * +default-public-interface-only+: WebRTC should only use the default route used by http. This doesn't expose any local addresses. + * +disable-non-proxied-udp+: WebRTC should only use TCP to contact peers or servers unless the proxy server supports UDP. This doesn't expose any local addresses either. + +Default: +pass:[all-interfaces]+ On QtWebEngine, this setting requires Qt 5.9.2 or newer. @@ -2757,6 +2767,46 @@ 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. + +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]+ + +This setting is only available with the QtWebEngine backend. + +[[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. + +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]+ + +This setting is only available with the QtWebEngine backend. + [[scrolling.bar]] === scrolling.bar Show a scrollbar. diff --git a/misc/requirements/requirements-pip.txt b/misc/requirements/requirements-pip.txt index a399c0b47..a0e1a663b 100644 --- a/misc/requirements/requirements-pip.txt +++ b/misc/requirements/requirements-pip.txt @@ -3,6 +3,6 @@ appdirs==1.4.3 packaging==17.1 pyparsing==2.2.0 -setuptools==40.2.0 +setuptools==40.3.0 six==1.11.0 wheel==0.31.1 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index aa9aebeb6..f3b4e8a75 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -4,4 +4,4 @@ altgraph==0.16.1 future==0.16.0 macholib==1.11 pefile==2018.8.8 -PyInstaller==3.3.1 +PyInstaller==3.4 diff --git a/misc/requirements/requirements-pylint-master.txt b/misc/requirements/requirements-pylint-master.txt deleted file mode 100644 index 6386505fc..000000000 --- a/misc/requirements/requirements-pylint-master.txt +++ /dev/null @@ -1,23 +0,0 @@ -# This file is automatically generated by scripts/dev/recompile_requirements.py - -asn1crypto==0.24.0 --e git+https://github.com/PyCQA/astroid.git#egg=astroid -certifi==2018.8.24 -cffi==1.11.5 -chardet==3.0.4 -cryptography==2.3.1 -github3.py==1.2.0 -idna==2.7 -isort==4.3.4 -jwcrypto==0.5.0 -lazy-object-proxy==1.3.1 -mccabe==0.6.1 -pycparser==2.18 --e git+https://github.com/PyCQA/pylint.git#egg=pylint -python-dateutil==2.7.3 -./scripts/dev/pylint_checkers -requests==2.19.1 -six==1.11.0 -uritemplate==3.0.0 -urllib3==1.23 -wrapt==1.10.11 diff --git a/misc/requirements/requirements-pylint-master.txt-raw b/misc/requirements/requirements-pylint-master.txt-raw deleted file mode 100644 index 405b0ab8f..000000000 --- a/misc/requirements/requirements-pylint-master.txt-raw +++ /dev/null @@ -1,11 +0,0 @@ --e git+https://github.com/PyCQA/astroid.git#egg=astroid --e git+https://github.com/PyCQA/pylint.git#egg=pylint -./scripts/dev/pylint_checkers -requests -github3.py - -# remove @commit-id for scm installs -#@ replace: @.*# # - -# fix qute-pylint location -#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index c727ad736..ef1d29707 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -13,7 +13,7 @@ fields==5.0.0 Flask==1.0.2 glob2==0.6 hunter==2.0.2 -hypothesis==3.70.3 +hypothesis==3.71.8 itsdangerous==0.24 # Jinja2==2.10 Mako==1.0.7 @@ -24,10 +24,10 @@ parse-type==0.4.2 pluggy==0.7.1 py==1.6.0 py-cpuinfo==4.0.0 -pytest==3.6.4 # rq.filter: != 3.7, != 3.7.1, != 3.7.2, != 3.7.3, != 3.7.4 +pytest==3.6.4 # rq.filter: <3.7 pytest-bdd==2.21.0 pytest-benchmark==3.1.1 -pytest-cov==2.5.1 +pytest-cov==2.6.0 pytest-faulthandler==1.5.0 pytest-instafail==0.4.0 pytest-mock==1.10.0 diff --git a/misc/requirements/requirements-tests.txt-raw b/misc/requirements/requirements-tests.txt-raw index 2d1c378a1..26d840070 100644 --- a/misc/requirements/requirements-tests.txt-raw +++ b/misc/requirements/requirements-tests.txt-raw @@ -19,4 +19,4 @@ pytest-xvfb vulture #@ ignore: Jinja2, MarkupSafe, colorama -#@ filter: pytest != 3.7, != 3.7.1, != 3.7.2, != 3.7.3, != 3.7.4 +#@ filter: pytest <3.7 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index 398de75bf..2014e1a55 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -3,5 +3,6 @@ pluggy==0.7.1 py==1.6.0 six==1.11.0 -tox==3.2.1 +toml==0.9.6 +tox==3.3.0 virtualenv==16.0.0 diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 594764c37..3429ed5f5 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -72,9 +72,9 @@ from qutebrowser.keyinput import macros from qutebrowser.mainwindow import mainwindow, prompt from qutebrowser.misc import (readline, ipc, savemanager, sessions, crashsignal, earlyinit, sql, cmdhistory, - backendproblem) + backendproblem, objects) from qutebrowser.utils import (log, version, message, utils, urlutils, objreg, - usertypes, standarddir, error) + usertypes, standarddir, error, qtutils) # pylint: disable=unused-import # We import those to run the cmdutils.register decorators. from qutebrowser.mainwindow.statusbar import command @@ -351,10 +351,6 @@ def _open_startpage(win_id=None): def _open_special_pages(args): """Open special notification pages which are only shown once. - Currently this is: - - Quickstart page if it's the first start. - - Legacy QtWebKit warning if needed. - Args: args: The argparse namespace. """ @@ -366,25 +362,30 @@ def _open_special_pages(args): tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') - # Quickstart page + pages = [ + # state, condition, URL + ('quickstart-done', + True, + 'https://www.qutebrowser.org/quickstart.html'), - quickstart_done = general_sect.get('quickstart-done') == '1' + ('config-migration-shown', + os.path.exists(os.path.join(standarddir.config(), + 'qutebrowser.conf')), + 'qute://help/configuring.html'), - if not quickstart_done: - tabbed_browser.tabopen( - QUrl('https://www.qutebrowser.org/quickstart.html')) - general_sect['quickstart-done'] = '1' + ('webkit-warning-shown', + objects.backend == usertypes.Backend.QtWebKit, + 'qute://warning/webkit'), - # Setting migration page + ('old-qt-warning-shown', + not qtutils.version_check('5.9'), + 'qute://warning/old-qt'), + ] - needs_migration = os.path.exists( - os.path.join(standarddir.config(), 'qutebrowser.conf')) - migration_shown = general_sect.get('config-migration-shown') == '1' - - if needs_migration and not migration_shown: - tabbed_browser.tabopen(QUrl('qute://help/configuring.html'), - background=False) - general_sect['config-migration-shown'] = '1' + for state, condition, url in pages: + if general_sect.get(state) != '1' and condition: + tabbed_browser.tabopen(QUrl(url), background=False) + general_sect[state] = '1' def on_focus_changed(_old, new): diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 8dbb661e5..a66a8fdea 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -66,6 +66,11 @@ class CommandDispatcher: def _new_tabbed_browser(self, private): """Get a tabbed-browser from a new window.""" + args = QApplication.instance().arguments() + if private and '--single-process' in args: + raise cmdexc.CommandError("Private windows are unavailable with " + "the single-process process model.") + new_window = mainwindow.MainWindow(private=private) new_window.show() return new_window.tabbed_browser diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index cec8215fc..0fa9366a6 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -39,8 +39,7 @@ except ImportError: # New in Python 3.6 secrets = None -import pkg_resources -from PyQt5.QtCore import QUrlQuery, QUrl +from PyQt5.QtCore import QUrlQuery, QUrl, qVersion import qutebrowser from qutebrowser.browser import pdfjs, downloads @@ -349,9 +348,9 @@ def qute_gpl(_url): return 'text/html', utils.read_file('html/license.html') -def _asciidoc_fallback_path(path): +def _asciidoc_fallback_path(html_path): """Fall back to plaintext asciidoc if the HTML is unavailable.""" - asciidoc_path = path.replace('.html', '.asciidoc') + asciidoc_path = html_path.replace('.html', '.asciidoc') asciidoc_paths = [asciidoc_path] if asciidoc_path.startswith('html/doc/'): asciidoc_paths += [asciidoc_path.replace('html/doc/', '../doc/help/'), @@ -416,17 +415,6 @@ def qute_help(url): return 'text/html', data -@add_handler('backend-warning') -def qute_backend_warning(_url): - """Handler for qute://backend-warning.""" - src = jinja.render('backend-warning.html', - distribution=version.distribution(), - Distribution=version.Distribution, - version=pkg_resources.parse_version, - title="Legacy backend warning") - return 'text/html', src - - def _qute_settings_set(url): """Handler for qute://settings/set.""" query = QUrlQuery(url) @@ -564,3 +552,19 @@ def qute_pdfjs(url): else: mimetype = utils.guess_mimetype(url.fileName(), fallback=True) return mimetype, data + + +@add_handler('warning') +def qute_warning(url): + """Handler for qute://warning.""" + path = url.path() + if path == '/old-qt': + src = jinja.render('warning-old-qt.html', + title='Old Qt warning', + qt_version=qVersion()) + elif path == '/webkit': + src = jinja.render('warning-webkit.html', + title='QtWebKit backend warning') + else: + raise NotFoundError("Invalid warning page {}".format(path)) + return 'text/html', src diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 77f82526a..da569eef6 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -166,8 +166,6 @@ class WebEngineSettings(websettings.AbstractSettings): # Qt 5.11 'content.autoplay': ('PlaybackRequiresUserGesture', lambda val: not val), - 'content.webrtc_public_interfaces_only': - ('WebRTCPublicInterfacesOnly', None), } for name, (attribute, converter) in new_attributes.items(): try: diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index a02b63cdf..17e28bf75 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -181,6 +181,51 @@ qt.force_platform: This sets the `QT_QPA_PLATFORM` environment variable and is useful to force using the XCB plugin when running QtWebEngine on Wayland. +qt.process_model: + type: + name: String + 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: process-per-site-instance + backend: QtWebEngine + restart: true + desc: >- + 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 + +qt.low_end_device_mode: + type: + name: String + 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: auto + backend: QtWebEngine + restart: true + desc: >- + When to use Chromium's low-end device mode. + + This improves the RAM usage of renderer processes, at the expense of + performance. qt.highdpi: type: Bool @@ -411,14 +456,18 @@ content.headers.referer: - never: "Never send the Referer. This is not recommended, as some sites may break." - same-domain: "Only send the Referer for the same domain. This will - still protect your privacy, but shouldn't break any sites." - backend: QtWebKit + still protect your privacy, but shouldn't break any sites. With + QtWebEngine, the referer will still be sent for other domains, but + with stripped path information." + restart: true desc: >- When to send the Referer header. The Referer header tells websites from which website you were coming from when visiting them. + No restart is needed with QtWebKit. + content.headers.user_agent: default: null type: @@ -721,18 +770,31 @@ content.webgl: supports_pattern: true desc: Enable WebGL. -content.webrtc_public_interfaces_only: - default: false - type: Bool +content.webrtc_ip_handling_policy: + default: all-interfaces + type: + name: String + valid_values: + - all-interfaces: WebRTC has the right to enumerate all interfaces and + bind them to discover public interfaces. + - default-public-and-private-interfaces: WebRTC should only use the + default route used by http. This also exposes the associated + default private address. Default route is the route chosen by the + OS on a multi-homed endpoint. + - default-public-interface-only: WebRTC should only use the default route + used by http. This doesn't expose any local addresses. + - disable-non-proxied-udp: WebRTC should only use TCP to contact peers or + servers unless the proxy server supports UDP. This doesn't expose + any local addresses either. + default: all-interfaces backend: QtWebKit: false QtWebEngine: Qt 5.9.2 + restart: true desc: >- - Only expose public interfaces via WebRTC. + Which interfaces to expose via WebRTC. - On Qt 5.9, this option requires a restart. - On Qt 5.10, this option doesn't work at all because of a Qt bug. - On Qt >= 5.11, no restart is required. + On Qt 5.10, this option doesn't work because of a Qt bug. content.xss_auditing: type: Bool diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py index a2c88d311..f74c44f32 100644 --- a/qutebrowser/config/configfiles.py +++ b/qutebrowser/config/configfiles.py @@ -276,6 +276,21 @@ class YamlConfig(QObject): del settings['bindings.default'] self._mark_changed() + # content.webrtc_public_interfaces_only got merged into + # content.webrtc_ip_handling_policy. + old = 'content.webrtc_public_interfaces_only' + new = 'content.webrtc_ip_handling_policy' + if old in settings: + settings[new] = {} + for scope, val in settings[old].items(): + if val: + settings[new][scope] = 'default-public-interface-only' + else: + settings[new][scope] = 'all-interfaces' + + del settings[old] + self._mark_changed() + self._migrate_bool(settings, 'tabs.favicons.show', 'always', 'never') self._migrate_bool(settings, 'qt.force_software_rendering', 'software-opengl', 'none') diff --git a/qutebrowser/config/configinit.py b/qutebrowser/config/configinit.py index b8c50500f..8480889af 100644 --- a/qutebrowser/config/configinit.py +++ b/qutebrowser/config/configinit.py @@ -171,24 +171,67 @@ def qt_args(namespace): argv += ['--' + arg for arg in config.val.qt.args] if objects.backend == usertypes.Backend.QtWebEngine: - if not qtutils.version_check('5.11', compiled=False): - # WORKAROUND equivalent to - # https://codereview.qt-project.org/#/c/217932/ - # Needed for Qt < 5.9.5 and < 5.10.1 - argv.append('--disable-shared-workers') - - if config.val.qt.force_software_rendering == 'chromium': - argv.append('--disable-gpu') - - if not config.val.content.canvas_reading: - argv.append('--disable-reading-from-canvas') - - if not qtutils.version_check('5.11'): - # On Qt 5.11, we can control this via QWebEngineSettings - if not config.val.content.autoplay: - argv.append('--autoplay-policy=user-gesture-required') - if config.val.content.webrtc_public_interfaces_only: - argv.append('--force-webrtc-ip-handling-policy=' - 'default_public_interface_only') + argv += list(_qtwebengine_args()) return argv + + +def _qtwebengine_args(): + """Get the QtWebEngine arguments to use based on the config.""" + if not qtutils.version_check('5.11', compiled=False): + # WORKAROUND equivalent to + # https://codereview.qt-project.org/#/c/217932/ + # Needed for Qt < 5.9.5 and < 5.10.1 + yield '--disable-shared-workers' + + settings = { + 'qt.force_software_rendering': { + 'software-opengl': None, + 'qt-quick': None, + 'chromium': '--disable-gpu', + 'none': None, + }, + 'content.canvas_reading': { + True: None, + False: '--disable-reading-from-canvas', + }, + 'content.webrtc_ip_handling_policy': { + 'all-interfaces': None, + 'default-public-and-private-interfaces': + '--force-webrtc-ip-handling-policy=' + 'default_public_and_private_interfaces', + 'default-public-interface-only': + '--force-webrtc-ip-handling-policy=' + 'default_public_interface_only', + 'disable-non-proxied-udp': + '--force-webrtc-ip-handling-policy=' + 'disable_non_proxied_udp', + }, + 'qt.process_model': { + 'process-per-site-instance': None, + 'process-per-site': '--process-per-site', + 'single-process': '--single-process', + }, + 'qt.low_end_device_mode': { + 'auto': None, + 'always': '--enable-low-end-device-mode', + 'never': '--disable-low-end-device-mode', + }, + 'content.headers.referer': { + 'always': None, + 'never': '--no-referrers', + 'same-domain': '--reduced-referrer-granularity', + } + } + + if not qtutils.version_check('5.11'): + # On Qt 5.11, we can control this via QWebEngineSettings + settings['content.autoplay'] = { + True: None, + False: '--autoplay-policy=user-gesture-required', + } + + for setting, args in sorted(settings.items()): + arg = args[config.instance.get(setting)] + if arg is not None: + yield arg diff --git a/qutebrowser/html/styled.html b/qutebrowser/html/styled.html index f4d256422..5d0adeff8 100644 --- a/qutebrowser/html/styled.html +++ b/qutebrowser/html/styled.html @@ -24,6 +24,10 @@ h1 { font-weight: normal; } +h2 { + font-weight: normal; +} + table { border-collapse: collapse; width: 100%; @@ -45,4 +49,13 @@ td { margin-left: 10px; text-decoration: none; } + +.note { + font-size: smaller; + color: grey; +} + +.mono { + font-family: monospace; +} {% endblock %} diff --git a/qutebrowser/html/warning-old-qt.html b/qutebrowser/html/warning-old-qt.html new file mode 100644 index 000000000..157d50714 --- /dev/null +++ b/qutebrowser/html/warning-old-qt.html @@ -0,0 +1,24 @@ +{% extends "styled.html" %} + +{% block content %} +

{{ title }}

+Note this warning will only appear once. Use :open +qute://warning/old-qt to show it again at a later time. + +

You're using qutebrowser with Qt {{qt_version}}.

+ +

Qt 5.7 was released in June 2016, with the 5.7.1 patch release in December +2016. It is based on Chromium 49 (March 2016) with (some) security fixes up to +Chromium 54 (October 2016). It is also +not covered +by Debian security updates.

+ +

Qt 5.8 has had various bugs, and has been unsupported (but working to some +degree) in qutebrowser for a while.

+ +

Because of those security issues and the maintaince burden coming with +supporting old versions, support for Qt < 5.9 will be dropped in a future +qutebrowser release. You might want to check +alternate installation methods +which allow you to get a newer Qt.

+{% endblock %} diff --git a/qutebrowser/html/warning-webkit.html b/qutebrowser/html/warning-webkit.html new file mode 100644 index 000000000..2797ea228 --- /dev/null +++ b/qutebrowser/html/warning-webkit.html @@ -0,0 +1,82 @@ +{% extends "styled.html" %} + +{% block content %} +

{{ title }}

+Note this warning will only appear once. Use :open +qute://warning/webkit to show it again at a later time. + +

You're using qutebrowser with the QtWebKit backend.

+ +

Unfortunately, QtWebKit hasn't seen a release (including security updates) +since June 2017, and it also lacks various security features (process +isolation/sandboxing) present in QtWebEngine.

+ +

Because of those security issues and the maintaince burden coming with +supporting QtWebKit, support for it will be dropped in a future qutebrowser +release. It's recommended that you use QtWebEngine instead.

+ +

(Outdated) reasons to use QtWebKit

+

Most reasons why people preferred the QtWebKit backend aren't relevant anymore:

+ +

PDF.js support: This qutebrowser release comes with PDF.js support +for QtWebEngine.

+ +

Missing control over Referer header: This qutebrowser release +supports content.headers.referer for QtWebEngine.

+ +

Missing control over cookies: With Qt 5.11 or newer, the content.cookies.accept setting works on QtWebEngine.

+ +

Graphical glitches: The new values for the qt.force_software_rendering setting added in v1.4.0 should +hopefully help.

+ +

Missing support for notifications: Those aren't supported yet in +Qt, but support is planned to be added in Qt 5.13, released around May 2019.

+ +

Resource usage: This release adds the qt.process_model and qt.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 +by QtWebEngine/qutebrowser, and it doesn't make any connections to Google (or +any other unsolicited connections at all). Arguably, having to trust Google +also is a smaller issue than having to trust every website you visit because of +heaps of security issues...

+ +

Nouveau graphic driver: You can use QtWebEngine with software +rendering. With Qt 5.13 (~May 2019) it might be possible to run with Nouveau +without software rendering.

+ +

Wayland: It's possible to use QtWebEngine with XWayland. Some users +also seem to be able to run it natively with Qt 5.11, but currently, QUTE_SKIP_WAYLAND_CHECK=1 needs to be set in the +environment to do so.

+ +

Instability on FreeBSD: Those seem to be FreeBSD-specific crashes, +and unfortunately nobody has looked into them yet so far...

+ +

QtWebEngine being unavailable in ArchlinuxARM's PyQt package: +QtWebEngine itself is available on the armv7h/aarch64 architectures, but their +PyQt package is broken and doesn't come with QtWebEngine support. This +has +been reported in their forums, but without any change so far. It should +however be possible to rebuild the PyQt package from source with QtWebEngine +installed.

+ +

QtWebEngine being unavailable on Parabola: Claims of Parabola +developers about QtWebEngine being "non-free" have repeatedly been disputed, +and so far nobody came up with solid evidence about that being the case. Also, +note that their qutebrowser package is orphaned and was often outdated in the +past (even qutebrowser security fixes took months to arrive there). You +might be better off chosing an alternative install +method.

+ +

White flashing between loads with a custom stylesheet: This doesn't +seem to happen with qt.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/misc/backendproblem.py b/qutebrowser/misc/backendproblem.py index 363f7f23c..3bd52eb44 100644 --- a/qutebrowser/misc/backendproblem.py +++ b/qutebrowser/misc/backendproblem.py @@ -68,6 +68,11 @@ def _other_backend(backend): def _error_text(because, text, backend): """Get an error text for the given information.""" other_backend, other_setting = _other_backend(backend) + if other_backend == usertypes.Backend.QtWebKit: + warning = ("Note that QtWebKit hasn't been updated since " + "July 2017 (including security updates).") + else: + warning = "" return ("Failed to start with the {backend} backend!" "

qutebrowser tried to start with the {backend} backend but " "failed because {because}.

{text}" @@ -75,9 +80,10 @@ def _error_text(because, text, backend): "

This forces usage of the {other_backend.name} backend by " "setting the backend = '{other_setting}' option " "(if you have a config.py file, you'll need to set " - "this manually).

".format( + "this manually). {warning}

".format( backend=backend.name, because=because, text=text, - other_backend=other_backend, other_setting=other_setting)) + other_backend=other_backend, other_setting=other_setting, + warning=warning)) class _Dialog(QDialog): @@ -102,8 +108,10 @@ class _Dialog(QDialog): quit_button.clicked.connect(lambda: self.done(_Result.quit)) hbox.addWidget(quit_button) - backend_button = QPushButton("Force {} backend".format( - other_backend.name)) + backend_text = "Force {} backend".format(other_backend.name) + if other_backend == usertypes.Backend.QtWebKit: + backend_text += ' (not recommended)' + backend_button = QPushButton(backend_text) backend_button.clicked.connect(functools.partial( self._change_setting, 'backend', other_setting)) hbox.addWidget(backend_button) diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py index 341088db8..0375554b8 100644 --- a/tests/end2end/test_invocations.py +++ b/tests/end2end/test_invocations.py @@ -65,9 +65,16 @@ def temp_basedir_env(tmpdir, short_tmpdir): runtime_dir.ensure(dir=True) runtime_dir.chmod(0o700) - (data_dir / 'qutebrowser' / 'state').write_text( - '[general]\nquickstart-done = 1\nbackend-warning-shown=1', - encoding='utf-8', ensure=True) + lines = [ + '[general]', + 'quickstart-done = 1', + 'backend-warning-shown = 1', + 'old-qt-warning-shown = 1', + 'webkit-warning-shown = 1', + ] + + state_file = data_dir / 'qutebrowser' / 'state' + state_file.write_text('\n'.join(lines), encoding='utf-8', ensure=True) env = { 'XDG_DATA_HOME': str(data_dir), diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py index 8f2c83a85..2b793b58b 100644 --- a/tests/unit/config/test_configfiles.py +++ b/tests/unit/config/test_configfiles.py @@ -235,6 +235,21 @@ class TestYaml: data = autoconfig.read() assert 'bindings.default' not in data + @pytest.mark.parametrize('public_only, expected', [ + (True, 'default-public-interface-only'), + (False, 'all-interfaces'), + ]) + def test_webrtc(self, yaml, autoconfig, public_only, expected): + """Tests for migration of content.webrtc_public_interfaces_only.""" + autoconfig.write({'content.webrtc_public_interfaces_only': + {'global': public_only}}) + + yaml.load() + yaml._save() + + data = autoconfig.read() + assert data['content.webrtc_ip_handling_policy']['global'] == expected + @pytest.mark.parametrize('show, expected', [ (True, 'always'), (False, 'never'), diff --git a/tests/unit/config/test_configinit.py b/tests/unit/config/test_configinit.py index 56f2a3c90..40f143086 100644 --- a/tests/unit/config/test_configinit.py +++ b/tests/unit/config/test_configinit.py @@ -359,10 +359,11 @@ class TestQtArgs: return parser @pytest.fixture(autouse=True) - def patch_version_check(self, monkeypatch): - """Make sure no --disable-shared-workers argument gets added.""" + def reduce_args(self, monkeypatch, config_stub): + """Make sure no --disable-shared-workers/referer argument get added.""" monkeypatch.setattr(configinit.qtutils, 'version_check', lambda version, compiled=False: True) + config_stub.val.content.headers.referer = 'always' @pytest.mark.parametrize('args, expected', [ # No Qt arguments @@ -438,23 +439,35 @@ class TestQtArgs: assert ('--autoplay-policy=user-gesture-required' in args) == added @utils.qt59 - @pytest.mark.parametrize('new_version, public_only, added', [ - (True, True, False), # new enough to not need it - (False, False, False), # option disabled - (False, True, True), + @pytest.mark.parametrize('policy, arg', [ + ('all-interfaces', None), + + ('default-public-and-private-interfaces', + '--force-webrtc-ip-handling-policy=' + 'default_public_and_private_interfaces'), + + ('default-public-interface-only', + '--force-webrtc-ip-handling-policy=' + 'default_public_interface_only'), + + ('disable-non-proxied-udp', + '--force-webrtc-ip-handling-policy=' + 'disable_non_proxied_udp'), ]) def test_webrtc(self, config_stub, monkeypatch, parser, - new_version, public_only, added): + policy, arg): monkeypatch.setattr(configinit.objects, 'backend', usertypes.Backend.QtWebEngine) - config_stub.val.content.webrtc_public_interfaces_only = public_only - monkeypatch.setattr(configinit.qtutils, 'version_check', - lambda version, compiled=False: new_version) + config_stub.val.content.webrtc_ip_handling_policy = policy parsed = parser.parse_args([]) args = configinit.qt_args(parsed) - arg = '--force-webrtc-ip-handling-policy=default_public_interface_only' - assert (arg in args) == added + + if arg is None: + assert not any(a.startswith('--force-webrtc-ip-handling-policy=') + for a in args) + else: + assert arg in args @pytest.mark.parametrize('canvas_reading, added', [ (True, False), # canvas reading enabled @@ -470,6 +483,67 @@ class TestQtArgs: args = configinit.qt_args(parsed) assert ('--disable-reading-from-canvas' in args) == added + @pytest.mark.parametrize('process_model, added', [ + ('process-per-site-instance', False), + ('process-per-site', True), + ('single-process', True), + ]) + def test_process_model(self, config_stub, monkeypatch, parser, + process_model, added): + monkeypatch.setattr(configinit.objects, 'backend', + usertypes.Backend.QtWebEngine) + + config_stub.val.qt.process_model = process_model + parsed = parser.parse_args([]) + args = configinit.qt_args(parsed) + + if added: + assert '--' + process_model in args + else: + assert '--process-per-site' not in args + assert '--single-process' not in args + assert '--process-per-site-instance' not in args + assert '--process-per-tab' not in args + + @pytest.mark.parametrize('low_end_device_mode, arg', [ + ('auto', None), + ('always', '--enable-low-end-device-mode'), + ('never', '--disable-low-end-device-mode'), + ]) + def test_low_end_device_mode(self, config_stub, monkeypatch, parser, + low_end_device_mode, arg): + monkeypatch.setattr(configinit.objects, 'backend', + usertypes.Backend.QtWebEngine) + + config_stub.val.qt.low_end_device_mode = low_end_device_mode + parsed = parser.parse_args([]) + args = configinit.qt_args(parsed) + + if arg is None: + assert '--enable-low-end-device-mode' not in args + assert '--disable-low-end-device-mode' not in args + else: + assert arg in args + + @pytest.mark.parametrize('referer, arg', [ + ('always', None), + ('never', '--no-referrers'), + ('same-domain', '--reduced-referrer-granularity'), + ]) + def test_referer(self, config_stub, monkeypatch, parser, referer, arg): + monkeypatch.setattr(configinit.objects, 'backend', + usertypes.Backend.QtWebEngine) + + config_stub.val.content.headers.referer = referer + parsed = parser.parse_args([]) + args = configinit.qt_args(parsed) + + if arg is None: + assert '--no-referrers' not in args + assert '--reduced-referrer-granularity' not in args + else: + assert arg in args + @pytest.mark.parametrize('arg, confval, used', [ # overridden by commandline arg