Merge branch 'qutebrowser:main' into main

This commit is contained in:
Mathis 2026-01-07 11:17:14 +01:00 committed by GitHub
commit 500355e299
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 356 additions and 151 deletions

View File

@ -1,5 +1,5 @@
[tool.bumpversion]
current_version = "3.6.1"
current_version = "3.6.3"
commit = true
message = "Release v{new_version}"
tag = true

View File

@ -31,7 +31,7 @@ jobs:
- /home/runner/work/_temp/:/home/runner/work/_temp/
options: --privileged --tty
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up problem matchers
@ -48,7 +48,7 @@ jobs:
shell: bash
if: failure()
- name: Upload screenshots
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
path: |

View File

@ -34,10 +34,10 @@ jobs:
- testenv: actionlint
- testenv: package
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
.mypy_cache
@ -106,7 +106,7 @@ jobs:
- /home/runner/work/_temp/:/home/runner/work/_temp/
options: --privileged --tty
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up problem matchers
@ -121,7 +121,7 @@ jobs:
shell: bash
if: failure()
- name: Upload screenshots
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
path: |
@ -217,10 +217,10 @@ jobs:
python: "3.14"
runs-on: "${{ matrix.os }}"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
.mypy_cache
@ -269,7 +269,7 @@ jobs:
shell: bash
if: failure()
- name: Upload screenshots
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.testenv }}-${{ matrix.os }}"
path: |
@ -285,7 +285,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Initialize CodeQL

View File

@ -16,7 +16,7 @@ jobs:
- archlinux-webengine
- archlinux-webengine-unstable
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.x'

View File

@ -37,7 +37,7 @@ jobs:
runs-on: "${{ matrix.os }}"
timeout-minutes: 45
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
@ -72,7 +72,7 @@ jobs:
echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
shell: bash
- name: Upload artifacts
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: "qutebrowser-nightly-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.name }}"
path: |

View File

@ -18,7 +18,7 @@ jobs:
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python 3.9
@ -41,7 +41,7 @@ jobs:
- name: Run qutebrowser smoke test
run: "xvfb-run .venv/bin/python3 -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ':later 500 quit'"
- name: Create pull request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
committer: qutebrowser bot <bot@qutebrowser.org>
author: qutebrowser bot <bot@qutebrowser.org>

View File

@ -62,7 +62,7 @@ jobs:
console.log(`sorted: ${sorted}`);
return sorted.at(-1);
result-encoding: string
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
@ -78,7 +78,7 @@ jobs:
git config --global user.name "qutebrowser bot"
git config --global user.email "bot@qutebrowser.org"
- name: Switch to release branch
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: ${{ steps.find-branch.outputs.result }}
- name: Import GPG Key
@ -110,6 +110,7 @@ jobs:
- name: Cherry-pick release commit
if: ${{ inputs.release_type == 'patch' }}
run: |
git fetch origin main
git checkout main
git cherry-pick -x v${{ steps.bump.outputs.version }}
git push origin main
@ -163,7 +164,7 @@ jobs:
permissions:
contents: write # To upload release artifacts
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: v${{ inputs.release_type == 'reupload' && needs.prepare.outputs.version_x || needs.prepare.outputs.version }}
- name: Set up Python

View File

@ -15,19 +15,48 @@ breaking changes (such as renamed commands) can happen in minor releases.
// `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities.
[[v3.7.0]]
v3.7.0 (unreleased)
[[v3.6.4]]
v3.6.4 (unreleased)
-------------------
[[v3.6.2]]
v3.6.2 (unreleased)
Fixed
~~~~~
- datalist dropdowns not opening correctly on Wayland/Sway (#8831).
This was caused by an old workaround for a different QtWebEngine issue,
which is now disabled for QtWebEngine 6.6.3 and newer.
[[v3.6.3]]
v3.6.3 (2025-11-30)
-------------------
Fixed
~~~~~
- New `qt.workarounds.disable_accessibility` setting, which disables Chromium
accessibility support. By default, is it set to `auto`, which only disables
accessibility on Qt versions with known issues. This works around a bug in Qt
6.10.1 causing frequent segfaults (#8797).
[[v3.6.2]]
v3.6.2 (2025-11-27)
-------------------
Changed
~~~~~~~
* Windows and macOS releases now ship with Qt 6.10.1, which include
security patches up to Chromium 142.0.7444.162.
Fixed
~~~~~
- The version info now includes the Wayland compositor name if wayland-client is
available under a different name than `libwayland-client.so` (#8771).
- The list of Chromium extensions in `--version` / `:version` now uses the
correct Chromium data profile, also fixing a crash with Qt 6.10.1 (#8785).
- With Qt 6.10.1, `qt.workarounds.disable_hangouts_extension` now doesn't apply
on private profiles, avoiding a Qt bug leading to a crash (#8785).
[[v3.6.1]]
v3.6.1 (2025-11-03)

View File

@ -302,6 +302,7 @@
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|<<qt.highdpi,qt.highdpi>>|Turn on Qt HighDPI scaling.
|<<qt.workarounds.disable_accelerated_2d_canvas,qt.workarounds.disable_accelerated_2d_canvas>>|Disable accelerated 2d canvas to avoid graphical glitches.
|<<qt.workarounds.disable_accessibility,qt.workarounds.disable_accessibility>>|Disable accessibility to avoid crashes on Qt 6.10.1.
|<<qt.workarounds.disable_hangouts_extension,qt.workarounds.disable_hangouts_extension>>|Disable the Hangouts extension.
|<<qt.workarounds.locale,qt.workarounds.locale>>|Work around locale parsing issues in QtWebEngine 5.15.3.
|<<qt.workarounds.remove_service_workers,qt.workarounds.remove_service_workers>>|Delete the QtWebEngine Service Worker directory on every start.
@ -3996,6 +3997,24 @@ Valid values:
Default: +pass:[auto]+
[[qt.workarounds.disable_accessibility]]
=== qt.workarounds.disable_accessibility
Disable accessibility to avoid crashes on Qt 6.10.1.
This setting requires a restart.
This setting is only available with the QtWebEngine backend.
Type: <<types,String>>
Valid values:
* +always+: Disable renderer accessibility
* +auto+: Disable on Qt versions with known issues, enable otherwise
* +never+: Enable renderer accessibility
Default: +pass:[auto]+
[[qt.workarounds.disable_hangouts_extension]]
=== qt.workarounds.disable_hangouts_extension
Disable the Hangouts extension.

View File

@ -44,6 +44,8 @@
</content_rating>
<releases>
<!-- Add new releases here -->
<release version='3.6.3' date='2025-11-30'/>
<release version='3.6.2' date='2025-11-27'/>
<release version='3.6.1' date='2025-11-03'/>
<release version='3.6.0' date='2025-10-24'/>
<release version='3.5.1' date='2025-06-05'/>

View File

@ -1,19 +1,19 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
annotated-types==0.7.0
anyio==4.11.0
anyio==4.12.0
autocommand==2.2.2
backports.tarfile==1.2.0
bracex==2.6
build==1.3.0
bump-my-version==1.2.4
certifi==2025.10.5
bump-my-version==1.2.5
certifi==2025.11.12
cffi==2.0.0
charset-normalizer==3.4.4
click==8.1.8
cryptography==46.0.3
docutils==0.22.3
exceptiongroup==1.3.0
exceptiongroup==1.3.1
github3.py==4.0.1
h11==0.16.0
httpcore==1.0.9
@ -30,7 +30,7 @@ jaraco.context==6.0.1
jaraco.functools==4.0.1
jaraco.text==3.12.1
jeepney==0.9.0
keyring==25.6.0
keyring==25.7.0
manhole==1.8.1
markdown-it-py==3.0.0
mdurl==0.1.2
@ -40,14 +40,14 @@ packaging==25.0
platformdirs==4.4.0
prompt_toolkit==3.0.52
pycparser==2.23
pydantic==2.12.4
pydantic==2.12.5
pydantic-settings==2.11.0
pydantic_core==2.41.5
Pygments==2.19.2
PyJWT==2.10.1
Pympler==1.1
pyproject_hooks==1.2.0
PyQt-builder==1.19.0
PyQt-builder==1.19.1
python-dateutil==2.9.0.post0
python-dotenv==1.2.1
questionary==2.1.1
@ -60,7 +60,6 @@ rich-click==1.9.4
SecretStorage==3.3.3
sip==6.14.0
six==1.17.0
sniffio==1.3.1
tomli==2.3.0
tomlkit==0.13.3
twine==6.2.0
@ -68,7 +67,7 @@ typeguard==4.3.0
typing-inspection==0.4.2
typing_extensions==4.15.0
uritemplate==4.2.0
# urllib3==2.5.0
# urllib3==2.6.2
wcmatch==10.1
wcwidth==0.2.14
zipp==3.23.0

View File

@ -1,11 +1,12 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
chardet==5.2.0
diff_cover==9.7.1
diff_cover==10.0.0
Jinja2==3.1.6
librt==0.7.3
lxml==6.0.2
MarkupSafe==3.0.3
mypy==1.18.2
mypy==1.19.0
mypy_extensions==1.1.0
pathspec==0.12.1
pluggy==1.6.0
@ -13,7 +14,7 @@ Pygments==2.19.2
PyQt5-stubs==5.15.6.0
tomli==2.3.0
types-colorama==0.4.15.20250801
types-docutils==0.22.2.20251006
types-Pygments==2.19.0.20250809
types-docutils==0.22.3.20251115
types-Pygments==2.19.0.20251121
types-PyYAML==6.0.12.20250915
typing_extensions==4.15.0

View File

@ -1,8 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
altgraph==0.17.4
altgraph==0.17.5
importlib_metadata==8.7.0
packaging==25.0
pyinstaller==6.16.0
pyinstaller-hooks-contrib==2025.9
pyinstaller==6.17.0
pyinstaller-hooks-contrib==2025.10
zipp==3.23.0

View File

@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
astroid==3.3.11
certifi==2025.10.5
certifi==2025.11.12
cffi==2.0.0
charset-normalizer==3.4.4
cryptography==46.0.3
@ -24,5 +24,5 @@ tomli==2.3.0
tomlkit==0.13.3
typing_extensions==4.15.0
uritemplate==4.2.0
# urllib3==2.5.0
# urllib3==2.6.2
zipp==3.23.0

View File

@ -1,8 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt6==6.10.0
PyQt6-Qt6==6.10.0
PyQt6==6.10.1
PyQt6-Qt6==6.10.1
PyQt6-WebEngine==6.10.0
PyQt6-WebEngine-Qt6==6.10.0
PyQt6-WebEngine-Qt6==6.10.1
PyQt6_sip==13.10.2
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/

View File

@ -1,8 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt6==6.10.0
PyQt6-Qt6==6.10.0
PyQt6==6.10.1
PyQt6-Qt6==6.10.1
PyQt6-WebEngine==6.10.0
PyQt6-WebEngine-Qt6==6.10.0
PyQt6-WebEngine-Qt6==6.10.1
PyQt6_sip==13.10.2
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/

View File

@ -1,8 +1,8 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt6==6.10.0
PyQt6-Qt6==6.10.0
PyQt6==6.10.1
PyQt6-Qt6==6.10.1
PyQt6-WebEngine==6.10.0
PyQt6-WebEngine-Qt6==6.10.0
PyQt6-WebEngine-Qt6==6.10.1
PyQt6_sip==13.10.2
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/

View File

@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
build==1.3.0
certifi==2025.10.5
certifi==2025.11.12
charset-normalizer==3.4.4
check-manifest==0.51
docutils==0.22.3
@ -10,9 +10,9 @@ importlib_metadata==8.7.0
packaging==25.0
Pygments==2.19.2
pyproject_hooks==1.2.0
pyroma==5.0
pyroma==5.0.1
requests==2.32.5
tomli==2.3.0
trove-classifiers==2025.9.11.17
urllib3==2.5.0
trove-classifiers==2025.12.1.14
urllib3==2.6.2
zipp==3.23.0

View File

@ -2,7 +2,7 @@
alabaster==0.7.16
babel==2.17.0
certifi==2025.10.5
certifi==2025.11.12
charset-normalizer==3.4.4
docutils==0.21.2
idna==3.11
@ -22,5 +22,5 @@ sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
tomli==2.3.0
urllib3==2.5.0
urllib3==2.6.2
zipp==3.23.0

View File

@ -3,15 +3,15 @@
attrs==25.4.0
autocommand==2.2.2
backports.tarfile==1.2.0
beautifulsoup4==4.14.2
beautifulsoup4==4.14.3
blinker==1.9.0
certifi==2025.10.5
certifi==2025.11.12
charset-normalizer==3.4.4
cheroot==11.1.2
click==8.1.8
coverage==7.10.7
exceptiongroup==1.3.0
execnet==2.1.1
exceptiongroup==1.3.1
execnet==2.1.2
filelock==3.19.1
Flask==3.1.2
gherkin-official==29.0.0
@ -61,7 +61,7 @@ tldextract==5.3.0
tomli==2.3.0
typeguard==4.3.0
typing_extensions==4.15.0
urllib3==2.5.0
urllib3==2.6.2
vulture==2.14
Werkzeug==3.1.3
Werkzeug==3.1.4
zipp==3.23.0

View File

@ -1,6 +1,6 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
cachetools==6.2.1
cachetools==6.2.3
chardet==5.2.0
colorama==0.4.6
distlib==0.4.0

View File

@ -14,7 +14,7 @@ __copyright__ = "Copyright 2013-{} Florian Bruhin (The Compiler)".format(_year)
__license__ = "GPL-3.0-or-later"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
__version__ = "3.6.1"
__version__ = "3.6.3"
__version_info__ = tuple(int(part) for part in __version__.split('.'))
__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."

View File

@ -10,7 +10,7 @@ from qutebrowser.qt.gui import QKeyEvent
from qutebrowser.qt.widgets import QWidget
from qutebrowser.config import config
from qutebrowser.utils import log, message, usertypes, qtutils
from qutebrowser.utils import log, message, usertypes, qtutils, version, utils
from qutebrowser.keyinput import modeman, keyutils
@ -55,16 +55,16 @@ class ChildEventFilter(QObject):
# - This is a child event filter on a tab (self._widget is not None)
# - We find an old existing child which is a QQuickWidget and is
# currently focused.
# - We're using QtWebEngine >= 6.4 (older versions are not affected)
# - We're using an affected QtWebEngine version
children = [
c for c in self._widget.findChildren(
QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
if c is not child and
c.hasFocus() and
c.metaObject() is not None and
c.metaObject().className() == "QQuickWidget"
c.metaObject().className() == "QQuickWidget" # Qt 6.4+
]
if children:
if children and version.qtwebengine_versions().webengine < utils.VersionNumber(6, 6, 3):
log.misc.debug("Focusing new child")
child.setFocus()

View File

@ -431,6 +431,17 @@ def _maybe_disable_hangouts_extension(profile: QWebEngineProfile) -> None:
except AttributeError:
return # added in QtWebEngine 6.10
qtwe_versions = version.qtwebengine_versions(avoid_init=True)
if (
qtwe_versions.webengine == utils.VersionNumber(6, 10, 1)
and profile.isOffTheRecord()
):
# WORKAROUND for https://github.com/qutebrowser/qutebrowser/issues/8785
log.misc.warning(
"Not disabling Hangouts extension on private profile to avoid "
"QtWebEngine crash with Qt 6.10.1")
return
assert ext_manager is not None # mypy
for info in ext_manager.extensions():
if info.id() == pakjoy.HANGOUTS_EXT_ID:
@ -458,14 +469,18 @@ def _clear_webengine_permissions_json():
)
def default_qt_profile() -> QWebEngineProfile:
"""Get the default profile from Qt."""
if machinery.IS_QT6:
return QWebEngineProfile("Default")
else:
return QWebEngineProfile.defaultProfile()
def _init_default_profile():
"""Init the default QWebEngineProfile."""
global default_profile
if machinery.IS_QT6:
default_profile = QWebEngineProfile("Default")
else:
default_profile = QWebEngineProfile.defaultProfile()
default_profile = default_qt_profile()
assert not default_profile.isOffTheRecord()
assert parsed_user_agent is None # avoid earlier profile initialization
@ -526,7 +541,7 @@ def _init_site_specific_quirks():
# "{qt_key}/{qt_version} "
# "{upstream_browser_key}/{upstream_browser_version_short} "
# "Safari/{webkit_version}")
firefox_ua = "Mozilla/5.0 ({os_info}; rv:144.0) Gecko/20100101 Firefox/144.0"
firefox_ua = "Mozilla/5.0 ({os_info}; rv:145.0) Gecko/20100101 Firefox/145.0"
# Needed for gitlab.gnome.org which blocks old Chromium versions outright,
# except when QtWebEngine/... is in the UA.

View File

@ -422,6 +422,19 @@ qt.workarounds.disable_hangouts_extension:
disabled to avoid crashes on Qt 6.5.0 to 6.5.3 if dark mode is enabled,
as well as on Qt 6.6.0.
qt.workarounds.disable_accessibility:
type:
name: String
valid_values:
- always: Disable renderer accessibility
- auto: Disable on Qt versions with known issues, enable otherwise
- never: Enable renderer accessibility
default: auto
backend: QtWebEngine
restart: true
desc: >-
Disable accessibility to avoid crashes on Qt 6.10.1.
## auto_save
auto_save.interval:
@ -773,14 +786,14 @@ content.headers.user_agent:
# Vim-protip: Place your cursor below this comment and run
# :r!python scripts/dev/ua_fetch.py
- - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
- Chrome 141 macOS
(KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
- Chrome 142 macOS
- - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/141.0.0.0 Safari/537.36"
- Chrome 141 Win10
like Gecko) Chrome/142.0.0.0 Safari/537.36"
- Chrome 142 Win10
- - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/141.0.0.0 Safari/537.36"
- Chrome 141 Linux
Gecko) Chrome/142.0.0.0 Safari/537.36"
- Chrome 142 Linux
supports_pattern: true
desc: |
User agent to send.

View File

@ -159,10 +159,9 @@ def _qtwebengine_features( # noqa: C901
# TODO adjust if fixed in Qt 6.9.2+
disabled_features.append('DocumentPictureInPictureAPI')
if versions.webengine >= utils.VersionNumber(6, 9):
if utils.VersionNumber(6, 9) <= versions.webengine < utils.VersionNumber(6, 10, 1):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-135787
# and https://bugreports.qt.io/browse/QTBUG-141096
# TODO adjust if fixed in Qt 6.9.2+
disabled_features.append('PermissionElement')
if not config.val.input.media_keys:
@ -363,6 +362,16 @@ _WEBENGINE_SETTINGS: dict[str, dict[Any, Optional[_SettingValueType]]] = {
and versions.webengine < utils.VersionNumber(6, 8, 2)
else None,
},
'qt.workarounds.disable_accessibility': {
'always': '--disable-renderer-accessibility',
'never': None,
# WORKAROUND for https://qt-project.atlassian.net/browse/QTBUG-142320
'auto': lambda versions: '--disable-renderer-accessibility'
if machinery.IS_QT6
and versions.webengine
and versions.webengine == utils.VersionNumber(6, 10, 1)
else None,
},
}

View File

@ -23,6 +23,7 @@ import datetime
from typing import NoReturn
try:
import tkinter
import tkinter.messagebox
except ImportError:
tkinter = None # type: ignore[assignment]

View File

@ -654,9 +654,11 @@ class WebEngineVersions:
utils.VersionNumber(6, 9): (_BASES[130], '133.0.6943.141'), # 2025-02-25
utils.VersionNumber(6, 9, 1): (_BASES[130], '136.0.7103.114'), # 2025-05-13
utils.VersionNumber(6, 9, 2): (_BASES[130], '139.0.7258.67'), # 2025-07-29
utils.VersionNumber(6, 9, 3): (_BASES[130], '140.0.7339.207'), # 2025-09-22
## Qt 6.10
utils.VersionNumber(6, 10): (_BASES[134], '140.0.7339.207'), # 2025-09-22
utils.VersionNumber(6, 10, 1): (_BASES[134], '142.0.7444.162'), # 2025-11-11
}
def __post_init__(self) -> None:
@ -930,12 +932,18 @@ def _webengine_extensions() -> Sequence[str]:
lines: list[str] = []
if (
objects.backend == usertypes.Backend.QtWebEngine
and "avoid-chromium-init" not in objects.debug_flags
and machinery.IS_QT6 # mypy; TODO early return once Qt 5 is dropped
):
from qutebrowser.qt.webenginecore import QWebEngineProfile
profile = QWebEngineProfile.defaultProfile()
assert profile is not None # mypy
from qutebrowser.browser.webengine import webenginesettings
lines.append("WebExtensions:")
if webenginesettings.default_profile:
profile = webenginesettings.default_profile
elif "avoid-chromium-init" in objects.debug_flags:
lines[0] += " unknown (avoiding init)"
return lines
else:
profile = webenginesettings.default_qt_profile()
try:
ext_manager = profile.extensionManager()
@ -944,7 +952,6 @@ def _webengine_extensions() -> Sequence[str]:
return []
assert ext_manager is not None # mypy
lines.append("WebExtensions:")
if not ext_manager.extensions():
lines[0] += " none"

View File

@ -134,7 +134,7 @@ def _smoke_test_run(
return subprocess.run(argv, check=True, capture_output=True)
def smoke_test(executable: pathlib.Path, debug: bool) -> None:
def smoke_test(executable: pathlib.Path, debug_build: bool) -> None:
"""Try starting the given qutebrowser executable."""
stdout_whitelist = []
stderr_whitelist = [
@ -204,8 +204,19 @@ def smoke_test(executable: pathlib.Path, debug: bool) -> None:
r'\(0x80004002\)'),
])
proc = _smoke_test_run(executable)
if debug:
try:
proc = _smoke_test_run(executable)
except subprocess.CalledProcessError as e:
print(f"Smoke test failed: {e}, running with --debug")
smoke_test_debug(
executable,
original_stdout=e.stdout.decode("utf-8"),
original_stderr=e.stderr.decode("utf-8"),
issue_description=str(e),
)
return
if debug_build:
print("Skipping output check for debug build")
return
@ -214,48 +225,64 @@ def smoke_test(executable: pathlib.Path, debug: bool) -> None:
if stdout or stderr:
print("Unexpected output, running with --debug")
proc = _smoke_test_run(executable, '--debug')
debug_stdout = proc.stdout.decode('utf-8')
debug_stderr = proc.stderr.decode('utf-8')
smoke_test_debug(
executable,
original_stdout=stdout,
original_stderr=stderr,
issue_description="Unexpected output",
)
lines = [
"Unexpected output!",
def smoke_test_debug(
executable: pathlib.Path,
*,
original_stdout: str,
original_stderr: str,
issue_description: str,
) -> None:
"""Run smoke test in debug mode to get more output."""
proc = _smoke_test_run(executable, '--debug')
debug_stdout = proc.stdout.decode('utf-8')
debug_stderr = proc.stderr.decode('utf-8')
lines = [
issue_description,
"",
]
if original_stdout:
lines += [
"stdout",
"------",
"",
original_stdout,
"",
]
if original_stderr:
lines += [
"stderr",
"------",
"",
original_stderr,
"",
]
if debug_stdout:
lines += [
"debug rerun stdout",
"------------------",
"",
debug_stdout,
"",
]
if debug_stderr:
lines += [
"debug rerun stderr",
"------------------",
"",
debug_stderr,
"",
]
if stdout:
lines += [
"stdout",
"------",
"",
stdout,
"",
]
if stderr:
lines += [
"stderr",
"------",
"",
stderr,
"",
]
if debug_stdout:
lines += [
"debug rerun stdout",
"------------------",
"",
debug_stdout,
"",
]
if debug_stderr:
lines += [
"debug rerun stderr",
"------------------",
"",
debug_stderr,
"",
]
raise Exception("\n".join(lines)) # pylint: disable=broad-exception-raised
raise Exception("\n".join(lines)) # pylint: disable=broad-exception-raised
def verify_windows_exe(exe_path: pathlib.Path) -> None:
@ -311,7 +338,7 @@ def build_mac(
dist_path = pathlib.Path("dist")
utils.print_title("Running pre-dmg smoke test")
smoke_test(_mac_bin_path(dist_path), debug=debug)
smoke_test(_mac_bin_path(dist_path), debug_build=debug)
if skip_packaging:
return []
@ -334,7 +361,7 @@ def build_mac(
subprocess.run(['hdiutil', 'attach', dmg_path,
'-mountpoint', tmp_path], check=True)
try:
smoke_test(_mac_bin_path(tmp_path), debug=debug)
smoke_test(_mac_bin_path(tmp_path), debug_build=debug)
finally:
print("Waiting 10s for dmg to be detachable...")
time.sleep(10)
@ -419,7 +446,7 @@ def _build_windows_single(
verify_windows_exe(exe_path)
utils.print_title("Running smoke test")
smoke_test(exe_path, debug=debug)
smoke_test(exe_path, debug_build=debug)
utils.print_title("Generating versions.txt")
_generate_versions_file(out_path)

View File

@ -23,6 +23,7 @@
"Mako": "https://docs.makotemplates.org/en/latest/changelog.html",
"hypothesis": "https://hypothesis.readthedocs.io/en/latest/changes.html",
"mypy": "https://github.com/python/mypy/blob/master/CHANGELOG.md",
"librt": "https://github.com/mypyc/librt/commits/master/",
"types-PyYAML": "https://github.com/python/typeshed/commits/main/stubs/PyYAML",
"types-colorama": "https://github.com/python/typeshed/commits/main/stubs/colorama",
"types-docutils": "https://github.com/python/typeshed/commits/main/stubs/docutils",
@ -101,7 +102,6 @@
"h11": "https://h11.readthedocs.io/en/latest/changes.html",
"httpcore": "https://github.com/encode/httpcore/blob/master/CHANGELOG.md",
"httpx": "https://github.com/encode/httpx/blob/master/CHANGELOG.md",
"sniffio": "https://sniffio.readthedocs.io/en/latest/history.html",
"six": "https://github.com/benjaminp/six/blob/master/CHANGES",
"altgraph": "https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst",
"urllib3": "https://github.com/urllib3/urllib3/blob/main/CHANGES.rst",

View File

@ -15,9 +15,11 @@
console.log("[PASS] Positions equal: " + old_position);
}
}
requestAnimationFrame(() => console.log('simple loaded'))
</script>
</head>
<body onload="console.log('simple loaded')">
<body>
<a href="/data/hello.txt" id="link">Just a link</a>
<button>blub</button>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus</p>

View File

@ -75,6 +75,7 @@ Feature: Saving and loading sessions
@qtwebkit_skip
Scenario: Scrolling (qtwebengine)
When I open data/scroll/simple.html
And I wait for "* simple loaded" in the log
And I run :scroll-px 10 20
And I wait until the scroll position changed to 10/20
Then the session should look like:

View File

@ -50,13 +50,13 @@ def fresh_instance(quteproc):
# on PyQt6.8 we disable that with the new API, otherwise restart the
# browser to make it forget previous prompts.
#
# Qt 6.10 Beta 4 accidentally persists some permissions;
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-140194
# Starting with Qt 6.10, QtWebEngine unconditionally persists some permissions;
# see https://bugreports.qt.io/browse/QTBUG-140194
if (
qtutils.version_check("6.8", compiled=False)
and PYQT_WEBENGINE_VERSION
and PYQT_WEBENGINE_VERSION < 0x60800
) or qtutils.version_check("6.10", compiled=False, exact=True):
) or qtutils.version_check("6.10", compiled=False):
quteproc.terminate()
quteproc.start()

View File

@ -21,6 +21,7 @@ import pytest
from qutebrowser.qt.core import QProcess, QPoint
from helpers import testutils
from end2end.fixtures import quteprocess
from qutebrowser.utils import qtutils, utils, version
@ -251,6 +252,7 @@ def test_optimize(request, quteproc_new, capfd, level):
def test_version(request):
"""Test invocation with --version argument."""
args = ['-m', 'qutebrowser', '--version'] + _base_args(request.config)
args.remove("--json-logging")
# can't use quteproc_new here because it's confused by
# early process termination
proc = QProcess()
@ -611,6 +613,26 @@ def test_service_worker_workaround(
assert not service_worker_dir.exists()
@pytest.mark.qt6_only
def test_disable_hangouts_extension_crash(
quteproc_new: quteprocess.QuteProc,
request: pytest.FixtureRequest,
webengine_versions: version.WebEngineVersions,
):
"""Make sure disabling the Hangouts extension doesn't crash."""
args = _base_args(request.config) + [
'--temp-basedir',
'-s', 'qt.workarounds.disable_hangouts_extension', 'true',
]
quteproc_new.start(args)
if webengine_versions.webengine == utils.VersionNumber(6, 10, 1):
line = quteproc_new.wait_for(message="Not disabling Hangouts extension *")
line.expected = True
quteproc_new.send_cmd(':quit')
quteproc_new.wait_for_quit()
@pytest.mark.parametrize('store', [True, False])
def test_cookies_store(quteproc_new, request, short_tmpdir, store):
# Start test process

View File

@ -12,7 +12,7 @@ import pytest
from qutebrowser.qt import machinery
from qutebrowser import qutebrowser
from qutebrowser.config import qtargs, configdata
from qutebrowser.utils import usertypes, version
from qutebrowser.utils import usertypes, version, utils
@pytest.fixture
@ -52,6 +52,7 @@ def reduce_args(config_stub, version_patcher, monkeypatch):
config_stub.val.scrolling.bar = 'never'
config_stub.val.qt.chromium.experimental_web_platform_features = 'never'
config_stub.val.qt.workarounds.disable_accelerated_2d_canvas = 'never'
config_stub.val.qt.workarounds.disable_accessibility = 'never'
monkeypatch.setattr(qtargs.utils, 'is_mac', False)
# Avoid WebRTC pipewire feature
monkeypatch.setattr(qtargs.utils, 'is_linux', False)
@ -117,6 +118,14 @@ def test_no_webengine_available(monkeypatch, config_stub, parser, stubs):
assert args == [sys.argv[0]]
_XFAIL_FUTURE_QT = (
pytest.mark.xfail(
utils.VersionNumber(6, 11) not in version.WebEngineVersions._CHROMIUM_VERSIONS,
reason="Unknown security patch version for Qt 6.11 so far",
),
)
@pytest.mark.usefixtures('reduce_args')
class TestWebEngineArgs:
@ -190,6 +199,40 @@ class TestWebEngineArgs:
args = qtargs.qt_args(parsed)
assert ('--disable-accelerated-2d-canvas' in args) == has_arg
@pytest.mark.parametrize(
"qt_version, qt6, value, has_arg",
[
("5.15.2", False, "auto", False),
# 6.8.5 is broken too, but commercial-only
("6.10.0", True, "always", True),
("6.10.0", True, "auto", False),
("6.10.1", True, "auto", True),
("6.10.1", True, "never", False),
("6.10.2", True, "always", True),
("6.10.2", True, "auto", False),
pytest.param("6.11.0", True, "always", True, marks=_XFAIL_FUTURE_QT),
pytest.param("6.11.0", True, "auto", False, marks=_XFAIL_FUTURE_QT),
],
)
def test_disable_accessibility(
self,
parser,
version_patcher,
config_stub,
monkeypatch,
qt_version,
qt6,
value,
has_arg,
):
version_patcher(qt_version)
config_stub.val.qt.workarounds.disable_accessibility = value
monkeypatch.setattr(machinery, 'IS_QT6', qt6)
parsed = parser.parse_args([])
args = qtargs.qt_args(parsed)
assert ('--disable-renderer-accessibility' in args) == has_arg
@pytest.mark.parametrize('flags, args', [
([], []),
(['--debug-flag', 'chromium'], ['--enable-logging', '--v=1']),
@ -471,6 +514,9 @@ class TestWebEngineArgs:
# Qt 6.9
('6.9.0', "DocumentPictureInPictureAPI,PermissionElement"),
('6.9.1', "DocumentPictureInPictureAPI,PermissionElement"),
# Qt 6.10
('6.10.0', "DocumentPictureInPictureAPI,PermissionElement"),
('6.10.1', "DocumentPictureInPictureAPI"),
])
def test_disable_feature_workaround(
self, parser, version_patcher, qt_version, disabled

View File

@ -1449,11 +1449,9 @@ def test_version_info(params, stubs, monkeypatch, config_stub):
monkeypatch.delattr(version, 'qtutils.qWebKitVersion', raising=False)
if machinery.IS_QT6:
monkeypatch.setattr(
QWebEngineProfile,
"defaultProfile",
lambda: FakeExtensionProfile(
FakeExtensionManager([FakeExtensionInfo("ext1")])
),
webenginesettings,
"default_profile",
FakeExtensionProfile(FakeExtensionManager([FakeExtensionInfo("ext1")])),
)
substitutions['webextensions'] = (
"\n"
@ -1592,20 +1590,35 @@ class TestOpenGLInfo:
class TestWebEngineExtensions:
def test_qtwebkit(self, monkeypatch: pytest.MonkeyPatch) -> None:
assert webenginesettings.default_profile is None # -> default_qt_profile() used
monkeypatch.setattr(version.objects, "backend", usertypes.Backend.QtWebKit)
monkeypatch.setattr(QWebEngineProfile, "defaultProfile", lambda: 1/0)
monkeypatch.setattr(webenginesettings, "default_qt_profile", lambda: 1 / 0)
assert not version._webengine_extensions()
def test_avoid_chromium_init(self, monkeypatch: pytest.MonkeyPatch) -> None:
assert webenginesettings.default_profile is None # -> default_qt_profile() used
monkeypatch.setattr(version.objects, "backend", usertypes.Backend.QtWebEngine)
monkeypatch.setattr(objects, "debug_flags", {"avoid-chromium-init"})
monkeypatch.setattr(QWebEngineProfile, "defaultProfile", lambda: 1/0)
assert not version._webengine_extensions()
monkeypatch.setattr(webenginesettings, "default_qt_profile", lambda: 1 / 0)
assert version._webengine_extensions() == [
"WebExtensions: unknown (avoiding init)"
]
def test_no_extension_manager(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(QWebEngineProfile, "defaultProfile", object)
assert webenginesettings.default_profile is None # -> default_qt_profile() used
monkeypatch.setattr(webenginesettings, "default_qt_profile", object)
assert not version._webengine_extensions()
@pytest.mark.parametrize("avoid_init", [True, False])
def test_preexisting_profile(self, monkeypatch: pytest.MonkeyPatch, avoid_init: bool) -> None:
"""Test that we use the pre-existing profile if available."""
monkeypatch.setattr(webenginesettings, "default_profile", FakeExtensionProfile(FakeExtensionManager([])))
if avoid_init:
monkeypatch.setattr(objects, "debug_flags", {"avoid-chromium-init"})
result = version._webengine_extensions()
assert result == ["WebExtensions: none"]
@pytest.mark.parametrize(
"extensions, expected",
[
@ -1666,11 +1679,9 @@ class TestWebEngineExtensions:
expected: list[str],
) -> None:
monkeypatch.setattr(
QWebEngineProfile,
"defaultProfile",
lambda: FakeExtensionProfile(
FakeExtensionManager(extensions)
),
webenginesettings,
"default_profile",
FakeExtensionProfile(FakeExtensionManager(extensions)),
)
assert version._webengine_extensions() == expected