Compare commits
60 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
7e3df43463 | |
|
|
9ae082b29b | |
|
|
b417f2a23b | |
|
|
615cee7309 | |
|
|
65c1ca9691 | |
|
|
12bed611c5 | |
|
|
bc72687d7d | |
|
|
c32b7d4b60 | |
|
|
3f9ef123e7 | |
|
|
f2547f8a09 | |
|
|
edd5114492 | |
|
|
184a242937 | |
|
|
17c19a09b7 | |
|
|
88aa47c377 | |
|
|
0570545342 | |
|
|
f408f20ad9 | |
|
|
500a8df209 | |
|
|
13d9904b90 | |
|
|
b3e4dba731 | |
|
|
4164205663 | |
|
|
f5e2660890 | |
|
|
69f3882ce3 | |
|
|
8e42727d31 | |
|
|
31a5737c61 | |
|
|
8ae5e3d83b | |
|
|
4f40a8b46b | |
|
|
59a64af67f | |
|
|
66cbe0d9c9 | |
|
|
0ef5053a65 | |
|
|
6ddff3ae0d | |
|
|
9316d428ef | |
|
|
71ed8cdbf5 | |
|
|
62fdb15532 | |
|
|
bc191b798d | |
|
|
f8fbb0609f | |
|
|
55fb26fce1 | |
|
|
25dc019886 | |
|
|
81d7b6a74c | |
|
|
6ec5504ab3 | |
|
|
9b69c889ef | |
|
|
4e87ef303f | |
|
|
2f8234ee2e | |
|
|
a8f0b47451 | |
|
|
4a5b7bd6e4 | |
|
|
b89bf07d1e | |
|
|
70bf4689fc | |
|
|
1cbb6fccf0 | |
|
|
d3e4245d0f | |
|
|
3160048619 | |
|
|
aa93eb1614 | |
|
|
df9cef3a58 | |
|
|
214e2e9ac2 | |
|
|
68574b88cc | |
|
|
6e8e24050d | |
|
|
5fe9bf97e3 | |
|
|
b646d606d7 | |
|
|
1e4ddc2c6b | |
|
|
3fce0518bd | |
|
|
3808ebfdb3 | |
|
|
0421aacd64 |
|
|
@ -1,5 +1,5 @@
|
||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "3.6.0"
|
current_version = "3.6.3"
|
||||||
commit = true
|
commit = true
|
||||||
message = "Release v{new_version}"
|
message = "Release v{new_version}"
|
||||||
tag = true
|
tag = true
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- testenv: bleeding
|
- testenv: bleeding
|
||||||
image: "archlinux-webengine-unstable-qt6"
|
|
||||||
- testenv: bleeding-qt5
|
|
||||||
image: "archlinux-webengine-unstable"
|
image: "archlinux-webengine-unstable"
|
||||||
container:
|
container:
|
||||||
image: "qutebrowser/ci:${{ matrix.image }}"
|
image: "qutebrowser/ci:${{ matrix.image }}"
|
||||||
|
|
@ -33,14 +31,13 @@ jobs:
|
||||||
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
||||||
options: --privileged --tty
|
options: --privileged --tty
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up problem matchers
|
- name: Set up problem matchers
|
||||||
run: "python scripts/dev/ci/problemmatchers.py py3 ${{ runner.temp }}"
|
run: "python scripts/dev/ci/problemmatchers.py py3 ${{ runner.temp }}"
|
||||||
- name: Upgrade 3rd party assets
|
- name: Upgrade 3rd party assets
|
||||||
run: "tox exec -e ${{ matrix.testenv }} -- python scripts/dev/update_3rdparty.py --gh-token ${{ secrets.GITHUB_TOKEN }} --modern-pdfjs"
|
run: "tox exec -e ${{ matrix.testenv }} -- python scripts/dev/update_3rdparty.py --gh-token ${{ secrets.GITHUB_TOKEN }} --modern-pdfjs"
|
||||||
if: "endsWith(matrix.image, '-qt6')"
|
|
||||||
- name: Run tox
|
- name: Run tox
|
||||||
run: dbus-run-session tox -e ${{ matrix.testenv }}
|
run: dbus-run-session tox -e ${{ matrix.testenv }}
|
||||||
- name: Gather info
|
- name: Gather info
|
||||||
|
|
@ -51,7 +48,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
if: failure()
|
if: failure()
|
||||||
- name: Upload screenshots
|
- name: Upload screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ jobs:
|
||||||
- testenv: actionlint
|
- testenv: actionlint
|
||||||
- testenv: package
|
- testenv: package
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
|
|
@ -47,7 +47,7 @@ jobs:
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '22.x'
|
node-version: '22.x'
|
||||||
if: "matrix.testenv == 'eslint'"
|
if: "matrix.testenv == 'eslint'"
|
||||||
|
|
@ -90,14 +90,10 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- testenv: py-qt5
|
- testenv: py
|
||||||
image: archlinux-webengine
|
image: archlinux-webengine
|
||||||
- testenv: py-qt5
|
- testenv: py
|
||||||
image: archlinux-webengine-unstable
|
image: archlinux-webengine-unstable
|
||||||
- testenv: py
|
|
||||||
image: archlinux-webengine-qt6
|
|
||||||
- testenv: py
|
|
||||||
image: archlinux-webengine-unstable-qt6
|
|
||||||
container:
|
container:
|
||||||
image: "qutebrowser/ci:${{ matrix.image }}"
|
image: "qutebrowser/ci:${{ matrix.image }}"
|
||||||
env:
|
env:
|
||||||
|
|
@ -110,7 +106,7 @@ jobs:
|
||||||
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
||||||
options: --privileged --tty
|
options: --privileged --tty
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up problem matchers
|
- name: Set up problem matchers
|
||||||
|
|
@ -125,7 +121,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
if: failure()
|
if: failure()
|
||||||
- name: Upload screenshots
|
- name: Upload screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -202,16 +198,16 @@ jobs:
|
||||||
- testenv: py314-pyqt610
|
- testenv: py314-pyqt610
|
||||||
os: ubuntu-24.04
|
os: ubuntu-24.04
|
||||||
python: "3.14"
|
python: "3.14"
|
||||||
### macOS Ventura
|
|
||||||
- testenv: py314-pyqt610
|
|
||||||
os: macos-13
|
|
||||||
python: "3.14"
|
|
||||||
args: "tests/unit" # Only run unit tests on macOS
|
|
||||||
### macOS Sonoma (M1 runner)
|
### macOS Sonoma (M1 runner)
|
||||||
- testenv: py314-pyqt610
|
- testenv: py314-pyqt610
|
||||||
os: macos-14
|
os: macos-14
|
||||||
python: "3.14"
|
python: "3.14"
|
||||||
args: "tests/unit" # Only run unit tests on macOS
|
args: "tests/unit" # Only run unit tests on macOS
|
||||||
|
### macOS Sequoia (Intel runner)
|
||||||
|
- testenv: py314-pyqt610
|
||||||
|
os: macos-15-intel
|
||||||
|
python: "3.14"
|
||||||
|
args: "tests/unit" # Only run unit tests on macOS
|
||||||
### Windows
|
### Windows
|
||||||
- testenv: py314-pyqt610
|
- testenv: py314-pyqt610
|
||||||
os: windows-2022
|
os: windows-2022
|
||||||
|
|
@ -221,10 +217,10 @@ jobs:
|
||||||
python: "3.14"
|
python: "3.14"
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
|
|
@ -273,7 +269,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
if: failure()
|
if: failure()
|
||||||
- name: Upload screenshots
|
- name: Upload screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.testenv }}-${{ matrix.os }}"
|
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.testenv }}-${{ matrix.os }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -289,7 +285,7 @@ jobs:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,8 @@ jobs:
|
||||||
image:
|
image:
|
||||||
- archlinux-webengine
|
- archlinux-webengine
|
||||||
- archlinux-webengine-unstable
|
- archlinux-webengine-unstable
|
||||||
- archlinux-webengine-unstable-qt6
|
|
||||||
- archlinux-webengine-qt6
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: macos-13
|
- os: macos-15-intel
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos-intel
|
name: macos-intel
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
|
|
@ -23,7 +23,7 @@ jobs:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: windows
|
name: windows
|
||||||
- os: macos-13
|
- os: macos-15-intel
|
||||||
args: --debug
|
args: --debug
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos-debug-intel
|
name: macos-debug-intel
|
||||||
|
|
@ -37,7 +37,7 @@ jobs:
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
|
|
@ -72,7 +72,7 @@ jobs:
|
||||||
echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "qutebrowser-nightly-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.name }}"
|
name: "qutebrowser-nightly-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.name }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ jobs:
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up Python 3.9
|
- name: Set up Python 3.9
|
||||||
|
|
@ -41,7 +41,7 @@ jobs:
|
||||||
- name: Run qutebrowser smoke test
|
- name: Run qutebrowser smoke test
|
||||||
run: "xvfb-run .venv/bin/python3 -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ':later 500 quit'"
|
run: "xvfb-run .venv/bin/python3 -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ':later 500 quit'"
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
committer: qutebrowser bot <bot@qutebrowser.org>
|
committer: qutebrowser bot <bot@qutebrowser.org>
|
||||||
author: qutebrowser bot <bot@qutebrowser.org>
|
author: qutebrowser bot <bot@qutebrowser.org>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ jobs:
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.bump.outputs.version }}
|
version: ${{ steps.bump.outputs.version }}
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
version_x: ${{ steps.bump.outputs.version_x }}
|
||||||
|
release_id: ${{ inputs.release_type == 'reupload' && steps.find-release.outputs.result || steps.create-release.outputs.id }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # To push release commit/tag
|
contents: write # To push release commit/tag
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -61,7 +62,7 @@ jobs:
|
||||||
console.log(`sorted: ${sorted}`);
|
console.log(`sorted: ${sorted}`);
|
||||||
return sorted.at(-1);
|
return sorted.at(-1);
|
||||||
result-encoding: string
|
result-encoding: string
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
|
|
@ -77,7 +78,7 @@ jobs:
|
||||||
git config --global user.name "qutebrowser bot"
|
git config --global user.name "qutebrowser bot"
|
||||||
git config --global user.email "bot@qutebrowser.org"
|
git config --global user.email "bot@qutebrowser.org"
|
||||||
- name: Switch to release branch
|
- name: Switch to release branch
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.find-branch.outputs.result }}
|
ref: ${{ steps.find-branch.outputs.result }}
|
||||||
- name: Import GPG Key
|
- name: Import GPG Key
|
||||||
|
|
@ -109,6 +110,7 @@ jobs:
|
||||||
- name: Cherry-pick release commit
|
- name: Cherry-pick release commit
|
||||||
if: ${{ inputs.release_type == 'patch' }}
|
if: ${{ inputs.release_type == 'patch' }}
|
||||||
run: |
|
run: |
|
||||||
|
git fetch origin main
|
||||||
git checkout main
|
git checkout main
|
||||||
git cherry-pick -x v${{ steps.bump.outputs.version }}
|
git cherry-pick -x v${{ steps.bump.outputs.version }}
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|
@ -126,12 +128,34 @@ jobs:
|
||||||
tag_name: v${{ steps.bump.outputs.version }}
|
tag_name: v${{ steps.bump.outputs.version }}
|
||||||
draft: true
|
draft: true
|
||||||
body: "*Release artifacts for this release are currently being uploaded...*"
|
body: "*Release artifacts for this release are currently being uploaded...*"
|
||||||
|
- name: Find GitHub draft release
|
||||||
|
if: ${{ inputs.release_type == 'reupload' }}
|
||||||
|
id: find-release
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const releases = await github.paginate(github.rest.repos.listReleases, {
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
});
|
||||||
|
const names = releases.map(release => release.name);
|
||||||
|
console.log(`releases: ${names}`);
|
||||||
|
|
||||||
|
const release = releases.find(release => release.tag_name === "v${{ steps.bump.outputs.version }}");
|
||||||
|
if (release === undefined) {
|
||||||
|
core.setFailed(`No release found with tag v${{ steps.bump.outputs.version }}!`);
|
||||||
|
}
|
||||||
|
if (!release.draft) {
|
||||||
|
core.setFailed(`Release ${release.tag_name} is not a draft release!`);
|
||||||
|
}
|
||||||
|
return release.id;
|
||||||
|
result-encoding: string
|
||||||
release:
|
release:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: macos-13
|
- os: macos-14-large # Intel
|
||||||
- os: macos-14
|
- os: macos-14 # Apple Silicon
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
|
|
@ -140,9 +164,9 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # To upload release artifacts
|
contents: write # To upload release artifacts
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: v${{ needs.prepare.outputs.version }}
|
ref: v${{ inputs.release_type == 'reupload' && needs.prepare.outputs.version_x || needs.prepare.outputs.version }}
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,60 @@ breaking changes (such as renamed commands) can happen in minor releases.
|
||||||
// `Fixed` for any bug fixes.
|
// `Fixed` for any bug fixes.
|
||||||
// `Security` to invite users to upgrade in case of vulnerabilities.
|
// `Security` to invite users to upgrade in case of vulnerabilities.
|
||||||
|
|
||||||
|
[[v3.6.4]]
|
||||||
|
v3.6.4 (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)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- A regression in v3.6.0 where the page didn't have keyboard focus after closing
|
||||||
|
the completion, so e.g. typing in an input field after hinting didn't work.
|
||||||
|
(#8750)
|
||||||
|
|
||||||
[[v3.6.0]]
|
[[v3.6.0]]
|
||||||
v3.6.0 (2025-10-24)
|
v3.6.0 (2025-10-24)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
|
||||||
|
|
@ -602,6 +602,7 @@ Info pages:
|
||||||
- chrome://device-log/ (QtWebEngine >= 6.3)
|
- chrome://device-log/ (QtWebEngine >= 6.3)
|
||||||
- chrome://gpu/
|
- chrome://gpu/
|
||||||
- chrome://sandbox/ (Linux only)
|
- chrome://sandbox/ (Linux only)
|
||||||
|
- chrome://qt/ (QtWebEngine >= 6.7)
|
||||||
|
|
||||||
Misc. / Debugging pages:
|
Misc. / Debugging pages:
|
||||||
|
|
||||||
|
|
@ -612,6 +613,7 @@ Misc. / Debugging pages:
|
||||||
- chrome://ukm/ (QtWebEngine >= 5.15.3)
|
- chrome://ukm/ (QtWebEngine >= 5.15.3)
|
||||||
- chrome://user-actions/ (QtWebEngine >= 5.15.3)
|
- chrome://user-actions/ (QtWebEngine >= 5.15.3)
|
||||||
- chrome://webrtc-logs/ (QtWebEngine >= 5.15.3)
|
- chrome://webrtc-logs/ (QtWebEngine >= 5.15.3)
|
||||||
|
- chrome://extensions/ (QtWebEngine >= 6.10)
|
||||||
|
|
||||||
Internals pages:
|
Internals pages:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ The comma prefix is used to make sure user-defined bindings don't conflict with
|
||||||
the built-in ones.
|
the built-in ones.
|
||||||
+
|
+
|
||||||
Note that you might need an additional package (e.g.
|
Note that you might need an additional package (e.g.
|
||||||
https://www.archlinux.org/packages/community/any/youtube-dl/[youtube-dl] on
|
https://archlinux.org/packages/extra/any/yt-dlp/[yt-dlp] on
|
||||||
Archlinux) to play web videos with mpv.
|
Archlinux) to play web videos with mpv.
|
||||||
+
|
+
|
||||||
There is a very useful script for mpv, which emulates "unique application"
|
There is a very useful script for mpv, which emulates "unique application"
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ customizable for a given <<patterns,URL patterns>>.
|
||||||
|
|
||||||
[source,python]
|
[source,python]
|
||||||
----
|
----
|
||||||
config.set('content.images', False, '*://example.com/')
|
config.set('content.images', False, '*://example.com/*')
|
||||||
----
|
----
|
||||||
|
|
||||||
Alternatively, you can use `with config.pattern(...) as p:` to get a shortcut
|
Alternatively, you can use `with config.pattern(...) as p:` to get a shortcut
|
||||||
|
|
@ -187,7 +187,7 @@ similar to `c.` which is scoped to the given domain:
|
||||||
|
|
||||||
[source,python]
|
[source,python]
|
||||||
----
|
----
|
||||||
with config.pattern('*://example.com/') as p:
|
with config.pattern('*://example.com/*') as p:
|
||||||
p.content.images = False
|
p.content.images = False
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,7 @@
|
||||||
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|
||||||
|<<qt.highdpi,qt.highdpi>>|Turn on Qt HighDPI scaling.
|
|<<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_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.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.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.
|
|<<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]+
|
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]]
|
||||||
=== qt.workarounds.disable_hangouts_extension
|
=== qt.workarounds.disable_hangouts_extension
|
||||||
Disable the Hangouts extension.
|
Disable the Hangouts extension.
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
<!-- Add new releases here -->
|
<!-- 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.6.0' date='2025-10-24'/>
|
||||||
<release version='3.5.1' date='2025-06-05'/>
|
<release version='3.5.1' date='2025-06-05'/>
|
||||||
<release version='3.5.0' date='2025-04-12'/>
|
<release version='3.5.0' date='2025-04-12'/>
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
annotated-types==0.7.0
|
annotated-types==0.7.0
|
||||||
anyio==4.11.0
|
anyio==4.12.0
|
||||||
autocommand==2.2.2
|
autocommand==2.2.2
|
||||||
backports.tarfile==1.2.0
|
backports.tarfile==1.2.0
|
||||||
bracex==2.6
|
bracex==2.6
|
||||||
build==1.3.0
|
build==1.3.0
|
||||||
bump-my-version==1.2.4
|
bump-my-version==1.2.5
|
||||||
certifi==2025.10.5
|
certifi==2025.11.12
|
||||||
cffi==2.0.0
|
cffi==2.0.0
|
||||||
charset-normalizer==3.4.4
|
charset-normalizer==3.4.4
|
||||||
click==8.1.8
|
click==8.1.8
|
||||||
cryptography==46.0.3
|
cryptography==46.0.3
|
||||||
docutils==0.22.2
|
docutils==0.22.3
|
||||||
exceptiongroup==1.3.0
|
exceptiongroup==1.3.1
|
||||||
github3.py==4.0.1
|
github3.py==4.0.1
|
||||||
h11==0.16.0
|
h11==0.16.0
|
||||||
httpcore==1.0.9
|
httpcore==1.0.9
|
||||||
|
|
@ -30,37 +30,36 @@ jaraco.context==6.0.1
|
||||||
jaraco.functools==4.0.1
|
jaraco.functools==4.0.1
|
||||||
jaraco.text==3.12.1
|
jaraco.text==3.12.1
|
||||||
jeepney==0.9.0
|
jeepney==0.9.0
|
||||||
keyring==25.6.0
|
keyring==25.7.0
|
||||||
manhole==1.8.1
|
manhole==1.8.1
|
||||||
markdown-it-py==3.0.0
|
markdown-it-py==3.0.0
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
more-itertools==10.8.0
|
more-itertools==10.8.0
|
||||||
nh3==0.3.1
|
nh3==0.3.2
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
platformdirs==4.4.0
|
platformdirs==4.4.0
|
||||||
prompt_toolkit==3.0.52
|
prompt_toolkit==3.0.52
|
||||||
pycparser==2.23
|
pycparser==2.23
|
||||||
pydantic==2.12.3
|
pydantic==2.12.5
|
||||||
pydantic-settings==2.11.0
|
pydantic-settings==2.11.0
|
||||||
pydantic_core==2.41.4
|
pydantic_core==2.41.5
|
||||||
Pygments==2.19.2
|
Pygments==2.19.2
|
||||||
PyJWT==2.10.1
|
PyJWT==2.10.1
|
||||||
Pympler==1.1
|
Pympler==1.1
|
||||||
pyproject_hooks==1.2.0
|
pyproject_hooks==1.2.0
|
||||||
PyQt-builder==1.19.0
|
PyQt-builder==1.19.1
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
python-dotenv==1.1.1
|
python-dotenv==1.2.1
|
||||||
questionary==2.1.1
|
questionary==2.1.1
|
||||||
readme_renderer==44.0
|
readme_renderer==44.0
|
||||||
requests==2.32.5
|
requests==2.32.5
|
||||||
requests-toolbelt==1.0.0
|
requests-toolbelt==1.0.0
|
||||||
rfc3986==2.0.0
|
rfc3986==2.0.0
|
||||||
rich==14.2.0
|
rich==14.2.0
|
||||||
rich-click==1.9.3
|
rich-click==1.9.4
|
||||||
SecretStorage==3.3.3
|
SecretStorage==3.3.3
|
||||||
sip==6.14.0
|
sip==6.14.0
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
sniffio==1.3.1
|
|
||||||
tomli==2.3.0
|
tomli==2.3.0
|
||||||
tomlkit==0.13.3
|
tomlkit==0.13.3
|
||||||
twine==6.2.0
|
twine==6.2.0
|
||||||
|
|
@ -68,7 +67,7 @@ typeguard==4.3.0
|
||||||
typing-inspection==0.4.2
|
typing-inspection==0.4.2
|
||||||
typing_extensions==4.15.0
|
typing_extensions==4.15.0
|
||||||
uritemplate==4.2.0
|
uritemplate==4.2.0
|
||||||
# urllib3==2.5.0
|
# urllib3==2.6.2
|
||||||
wcmatch==10.1
|
wcmatch==10.1
|
||||||
wcwidth==0.2.14
|
wcwidth==0.2.14
|
||||||
zipp==3.23.0
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
chardet==5.2.0
|
chardet==5.2.0
|
||||||
diff_cover==9.7.1
|
diff_cover==10.0.0
|
||||||
Jinja2==3.1.6
|
Jinja2==3.1.6
|
||||||
|
librt==0.7.3
|
||||||
lxml==6.0.2
|
lxml==6.0.2
|
||||||
MarkupSafe==3.0.3
|
MarkupSafe==3.0.3
|
||||||
mypy==1.18.2
|
mypy==1.19.0
|
||||||
mypy_extensions==1.1.0
|
mypy_extensions==1.1.0
|
||||||
pathspec==0.12.1
|
pathspec==0.12.1
|
||||||
pluggy==1.6.0
|
pluggy==1.6.0
|
||||||
|
|
@ -13,7 +14,7 @@ Pygments==2.19.2
|
||||||
PyQt5-stubs==5.15.6.0
|
PyQt5-stubs==5.15.6.0
|
||||||
tomli==2.3.0
|
tomli==2.3.0
|
||||||
types-colorama==0.4.15.20250801
|
types-colorama==0.4.15.20250801
|
||||||
types-docutils==0.22.2.20251006
|
types-docutils==0.22.3.20251115
|
||||||
types-Pygments==2.19.0.20250809
|
types-Pygments==2.19.0.20251121
|
||||||
types-PyYAML==6.0.12.20250915
|
types-PyYAML==6.0.12.20250915
|
||||||
typing_extensions==4.15.0
|
typing_extensions==4.15.0
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
altgraph==0.17.4
|
altgraph==0.17.5
|
||||||
importlib_metadata==8.7.0
|
importlib_metadata==8.7.0
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
pyinstaller==6.16.0
|
pyinstaller==6.17.0
|
||||||
pyinstaller-hooks-contrib==2025.9
|
pyinstaller-hooks-contrib==2025.10
|
||||||
zipp==3.23.0
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
astroid==3.3.11
|
astroid==3.3.11
|
||||||
certifi==2025.10.5
|
certifi==2025.11.12
|
||||||
cffi==2.0.0
|
cffi==2.0.0
|
||||||
charset-normalizer==3.4.4
|
charset-normalizer==3.4.4
|
||||||
cryptography==46.0.3
|
cryptography==46.0.3
|
||||||
|
|
@ -24,5 +24,5 @@ tomli==2.3.0
|
||||||
tomlkit==0.13.3
|
tomlkit==0.13.3
|
||||||
typing_extensions==4.15.0
|
typing_extensions==4.15.0
|
||||||
uritemplate==4.2.0
|
uritemplate==4.2.0
|
||||||
# urllib3==2.5.0
|
# urllib3==2.6.2
|
||||||
zipp==3.23.0
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt5==5.15.11 # rq.filter: < 5.16
|
PyQt5==5.15.11 # rq.filter: < 5.16
|
||||||
PyQt5-Qt5==5.15.17
|
PyQt5-Qt5==5.15.18
|
||||||
PyQt5_sip==12.17.1
|
PyQt5_sip==12.17.1
|
||||||
PyQtWebEngine==5.15.7 # rq.filter: < 5.16
|
PyQtWebEngine==5.15.7 # rq.filter: < 5.16
|
||||||
PyQtWebEngine-Qt5==5.15.17
|
PyQtWebEngine-Qt5==5.15.18
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt5==5.15.11
|
PyQt5==5.15.11
|
||||||
PyQt5-Qt5==5.15.17
|
PyQt5-Qt5==5.15.18
|
||||||
PyQt5_sip==12.17.1
|
PyQt5_sip==12.17.1
|
||||||
PyQtWebEngine==5.15.7
|
PyQtWebEngine==5.15.7
|
||||||
PyQtWebEngine-Qt5==5.15.17
|
PyQtWebEngine-Qt5==5.15.18
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt6==6.10.0
|
PyQt6==6.10.1
|
||||||
PyQt6-Qt6==6.10.0
|
PyQt6-Qt6==6.10.1
|
||||||
PyQt6-WebEngine==6.10.0
|
PyQt6-WebEngine==6.10.0
|
||||||
PyQt6-WebEngine-Qt6==6.10.0
|
PyQt6-WebEngine-Qt6==6.10.1
|
||||||
PyQt6_sip==13.10.2
|
PyQt6_sip==13.10.2
|
||||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt6==6.10.0
|
PyQt6==6.10.1
|
||||||
PyQt6-Qt6==6.10.0
|
PyQt6-Qt6==6.10.1
|
||||||
PyQt6-WebEngine==6.10.0
|
PyQt6-WebEngine==6.10.0
|
||||||
PyQt6-WebEngine-Qt6==6.10.0
|
PyQt6-WebEngine-Qt6==6.10.1
|
||||||
PyQt6_sip==13.10.2
|
PyQt6_sip==13.10.2
|
||||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt6==6.10.0
|
PyQt6==6.10.1
|
||||||
PyQt6-Qt6==6.10.0
|
PyQt6-Qt6==6.10.1
|
||||||
PyQt6-WebEngine==6.10.0
|
PyQt6-WebEngine==6.10.0
|
||||||
PyQt6-WebEngine-Qt6==6.10.0
|
PyQt6-WebEngine-Qt6==6.10.1
|
||||||
PyQt6_sip==13.10.2
|
PyQt6_sip==13.10.2
|
||||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
build==1.3.0
|
build==1.3.0
|
||||||
certifi==2025.10.5
|
certifi==2025.11.12
|
||||||
charset-normalizer==3.4.4
|
charset-normalizer==3.4.4
|
||||||
check-manifest==0.51
|
check-manifest==0.51
|
||||||
docutils==0.22.2
|
docutils==0.22.3
|
||||||
idna==3.11
|
idna==3.11
|
||||||
importlib_metadata==8.7.0
|
importlib_metadata==8.7.0
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
Pygments==2.19.2
|
Pygments==2.19.2
|
||||||
pyproject_hooks==1.2.0
|
pyproject_hooks==1.2.0
|
||||||
pyroma==5.0
|
pyroma==5.0.1
|
||||||
requests==2.32.5
|
requests==2.32.5
|
||||||
tomli==2.3.0
|
tomli==2.3.0
|
||||||
trove-classifiers==2025.9.11.17
|
trove-classifiers==2025.12.1.14
|
||||||
urllib3==2.5.0
|
urllib3==2.6.2
|
||||||
zipp==3.23.0
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
alabaster==0.7.16
|
alabaster==0.7.16
|
||||||
babel==2.17.0
|
babel==2.17.0
|
||||||
certifi==2025.10.5
|
certifi==2025.11.12
|
||||||
charset-normalizer==3.4.4
|
charset-normalizer==3.4.4
|
||||||
docutils==0.21.2
|
docutils==0.21.2
|
||||||
idna==3.11
|
idna==3.11
|
||||||
|
|
@ -22,5 +22,5 @@ sphinxcontrib-jsmath==1.0.1
|
||||||
sphinxcontrib-qthelp==2.0.0
|
sphinxcontrib-qthelp==2.0.0
|
||||||
sphinxcontrib-serializinghtml==2.0.0
|
sphinxcontrib-serializinghtml==2.0.0
|
||||||
tomli==2.3.0
|
tomli==2.3.0
|
||||||
urllib3==2.5.0
|
urllib3==2.6.2
|
||||||
zipp==3.23.0
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@
|
||||||
attrs==25.4.0
|
attrs==25.4.0
|
||||||
autocommand==2.2.2
|
autocommand==2.2.2
|
||||||
backports.tarfile==1.2.0
|
backports.tarfile==1.2.0
|
||||||
beautifulsoup4==4.14.2
|
beautifulsoup4==4.14.3
|
||||||
blinker==1.9.0
|
blinker==1.9.0
|
||||||
certifi==2025.10.5
|
certifi==2025.11.12
|
||||||
charset-normalizer==3.4.4
|
charset-normalizer==3.4.4
|
||||||
cheroot==11.0.0
|
cheroot==11.1.2
|
||||||
click==8.1.8
|
click==8.1.8
|
||||||
coverage==7.10.7
|
coverage==7.10.7
|
||||||
exceptiongroup==1.3.0
|
exceptiongroup==1.3.1
|
||||||
execnet==2.1.1
|
execnet==2.1.2
|
||||||
filelock==3.19.1
|
filelock==3.19.1
|
||||||
Flask==3.1.2
|
Flask==3.1.2
|
||||||
gherkin-official==29.0.0
|
gherkin-official==29.0.0
|
||||||
|
|
@ -42,7 +42,7 @@ py-cpuinfo==9.0.0
|
||||||
Pygments==2.19.2
|
Pygments==2.19.2
|
||||||
pytest==8.4.2
|
pytest==8.4.2
|
||||||
pytest-bdd==8.1.0
|
pytest-bdd==8.1.0
|
||||||
pytest-benchmark==5.1.0
|
pytest-benchmark==5.2.3
|
||||||
pytest-cov==7.0.0
|
pytest-cov==7.0.0
|
||||||
pytest-instafail==0.5.0
|
pytest-instafail==0.5.0
|
||||||
pytest-mock==3.15.1
|
pytest-mock==3.15.1
|
||||||
|
|
@ -61,7 +61,7 @@ tldextract==5.3.0
|
||||||
tomli==2.3.0
|
tomli==2.3.0
|
||||||
typeguard==4.3.0
|
typeguard==4.3.0
|
||||||
typing_extensions==4.15.0
|
typing_extensions==4.15.0
|
||||||
urllib3==2.5.0
|
urllib3==2.6.2
|
||||||
vulture==2.14
|
vulture==2.14
|
||||||
Werkzeug==3.1.3
|
Werkzeug==3.1.4
|
||||||
zipp==3.23.0
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
cachetools==6.2.1
|
cachetools==6.2.3
|
||||||
chardet==5.2.0
|
chardet==5.2.0
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
distlib==0.4.0
|
distlib==0.4.0
|
||||||
filelock==3.19.1
|
filelock==3.19.1
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
pip==25.2
|
pip==25.3
|
||||||
platformdirs==4.4.0
|
platformdirs==4.4.0
|
||||||
pluggy==1.6.0
|
pluggy==1.6.0
|
||||||
pyproject-api==1.9.1
|
pyproject-api==1.9.1
|
||||||
|
|
@ -14,6 +14,6 @@ setuptools==80.9.0
|
||||||
tomli==2.3.0
|
tomli==2.3.0
|
||||||
tox==4.30.3 ; python_full_version!="3.14.0b1"
|
tox==4.30.3 ; python_full_version!="3.14.0b1"
|
||||||
typing_extensions==4.15.0
|
typing_extensions==4.15.0
|
||||||
virtualenv==20.35.3
|
virtualenv==20.35.4
|
||||||
wheel==0.45.1
|
wheel==0.45.1
|
||||||
tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1"
|
tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ __copyright__ = "Copyright 2013-{} Florian Bruhin (The Compiler)".format(_year)
|
||||||
__license__ = "GPL-3.0-or-later"
|
__license__ = "GPL-3.0-or-later"
|
||||||
__maintainer__ = __author__
|
__maintainer__ = __author__
|
||||||
__email__ = "mail@qutebrowser.org"
|
__email__ = "mail@qutebrowser.org"
|
||||||
__version__ = "3.6.0"
|
__version__ = "3.6.3"
|
||||||
__version_info__ = tuple(int(part) for part in __version__.split('.'))
|
__version_info__ = tuple(int(part) for part in __version__.split('.'))
|
||||||
__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."
|
__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from qutebrowser.qt.gui import QKeyEvent
|
||||||
from qutebrowser.qt.widgets import QWidget
|
from qutebrowser.qt.widgets import QWidget
|
||||||
|
|
||||||
from qutebrowser.config import config
|
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
|
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)
|
# - 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
|
# - We find an old existing child which is a QQuickWidget and is
|
||||||
# currently focused.
|
# currently focused.
|
||||||
# - We're using QtWebEngine >= 6.4 (older versions are not affected)
|
# - We're using an affected QtWebEngine version
|
||||||
children = [
|
children = [
|
||||||
c for c in self._widget.findChildren(
|
c for c in self._widget.findChildren(
|
||||||
QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
|
QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
|
||||||
if c is not child and
|
if c is not child and
|
||||||
c.hasFocus() and
|
c.hasFocus() and
|
||||||
c.metaObject() is not None 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")
|
log.misc.debug("Focusing new child")
|
||||||
child.setFocus()
|
child.setFocus()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -431,6 +431,17 @@ def _maybe_disable_hangouts_extension(profile: QWebEngineProfile) -> None:
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return # added in QtWebEngine 6.10
|
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
|
assert ext_manager is not None # mypy
|
||||||
for info in ext_manager.extensions():
|
for info in ext_manager.extensions():
|
||||||
if info.id() == pakjoy.HANGOUTS_EXT_ID:
|
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():
|
def _init_default_profile():
|
||||||
"""Init the default QWebEngineProfile."""
|
"""Init the default QWebEngineProfile."""
|
||||||
global default_profile
|
global default_profile
|
||||||
|
default_profile = default_qt_profile()
|
||||||
if machinery.IS_QT6:
|
|
||||||
default_profile = QWebEngineProfile("Default")
|
|
||||||
else:
|
|
||||||
default_profile = QWebEngineProfile.defaultProfile()
|
|
||||||
assert not default_profile.isOffTheRecord()
|
assert not default_profile.isOffTheRecord()
|
||||||
|
|
||||||
assert parsed_user_agent is None # avoid earlier profile initialization
|
assert parsed_user_agent is None # avoid earlier profile initialization
|
||||||
|
|
@ -526,7 +541,7 @@ def _init_site_specific_quirks():
|
||||||
# "{qt_key}/{qt_version} "
|
# "{qt_key}/{qt_version} "
|
||||||
# "{upstream_browser_key}/{upstream_browser_version_short} "
|
# "{upstream_browser_key}/{upstream_browser_version_short} "
|
||||||
# "Safari/{webkit_version}")
|
# "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,
|
# Needed for gitlab.gnome.org which blocks old Chromium versions outright,
|
||||||
# except when QtWebEngine/... is in the UA.
|
# except when QtWebEngine/... is in the UA.
|
||||||
|
|
|
||||||
|
|
@ -333,8 +333,13 @@ class Config(QObject):
|
||||||
pattern, hide_userconfig=hide_userconfig)
|
pattern, hide_userconfig=hide_userconfig)
|
||||||
|
|
||||||
self.changed.emit(opt.name)
|
self.changed.emit(opt.name)
|
||||||
log.config.debug("Config option changed: {} = {}".format(
|
|
||||||
opt.name, value))
|
if pattern is not None:
|
||||||
|
log.config.debug("Config option changed: {} = {} for {}".format(
|
||||||
|
opt.name, value, pattern))
|
||||||
|
else:
|
||||||
|
log.config.debug("Config option changed: {} = {}".format(
|
||||||
|
opt.name, value))
|
||||||
|
|
||||||
def _check_yaml(self, opt: 'configdata.Option', save_yaml: bool) -> None:
|
def _check_yaml(self, opt: 'configdata.Option', save_yaml: bool) -> None:
|
||||||
"""Make sure the given option may be set in autoconfig.yml."""
|
"""Make sure the given option may be set in autoconfig.yml."""
|
||||||
|
|
|
||||||
|
|
@ -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,
|
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.
|
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
|
||||||
|
|
||||||
auto_save.interval:
|
auto_save.interval:
|
||||||
|
|
@ -773,14 +786,14 @@ content.headers.user_agent:
|
||||||
# Vim-protip: Place your cursor below this comment and run
|
# Vim-protip: Place your cursor below this comment and run
|
||||||
# :r!python scripts/dev/ua_fetch.py
|
# :r!python scripts/dev/ua_fetch.py
|
||||||
- - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
|
- - "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"
|
(KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
|
||||||
- Chrome 141 macOS
|
- Chrome 142 macOS
|
||||||
- - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
|
- - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
|
||||||
like Gecko) Chrome/141.0.0.0 Safari/537.36"
|
like Gecko) Chrome/142.0.0.0 Safari/537.36"
|
||||||
- Chrome 141 Win10
|
- Chrome 142 Win10
|
||||||
- - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like
|
- - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like
|
||||||
Gecko) Chrome/141.0.0.0 Safari/537.36"
|
Gecko) Chrome/142.0.0.0 Safari/537.36"
|
||||||
- Chrome 141 Linux
|
- Chrome 142 Linux
|
||||||
supports_pattern: true
|
supports_pattern: true
|
||||||
desc: |
|
desc: |
|
||||||
User agent to send.
|
User agent to send.
|
||||||
|
|
|
||||||
|
|
@ -159,10 +159,9 @@ def _qtwebengine_features( # noqa: C901
|
||||||
# TODO adjust if fixed in Qt 6.9.2+
|
# TODO adjust if fixed in Qt 6.9.2+
|
||||||
disabled_features.append('DocumentPictureInPictureAPI')
|
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
|
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-135787
|
||||||
# and https://bugreports.qt.io/browse/QTBUG-141096
|
# and https://bugreports.qt.io/browse/QTBUG-141096
|
||||||
# TODO adjust if fixed in Qt 6.9.2+
|
|
||||||
disabled_features.append('PermissionElement')
|
disabled_features.append('PermissionElement')
|
||||||
|
|
||||||
if not config.val.input.media_keys:
|
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)
|
and versions.webengine < utils.VersionNumber(6, 8, 2)
|
||||||
else None,
|
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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ li {
|
||||||
the required packages for pdf.js are also installed.
|
the required packages for pdf.js are also installed.
|
||||||
<br/>
|
<br/>
|
||||||
The package is named
|
The package is named
|
||||||
<a href="https://archlinux.org/packages/community/any/pdfjs/"><b>pdfjs</b></a> on Archlinux
|
<a href="https://archlinux.org/packages/extra/any/pdfjs-legacy/"><b>pdfjs-legacy</b></a> on Archlinux
|
||||||
and <a href="https://packages.debian.org/bullseye/libjs-pdf"><b>libjs-pdf</b></a> on Debian.
|
and <a href="https://packages.debian.org/bullseye/libjs-pdf"><b>libjs-pdf</b></a> on Debian.
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -286,20 +286,16 @@ class StatusBar(QWidget):
|
||||||
strategy = config.val.statusbar.show
|
strategy = config.val.statusbar.show
|
||||||
tab = self._current_tab()
|
tab = self._current_tab()
|
||||||
if tab is not None and tab.data.fullscreen:
|
if tab is not None and tab.data.fullscreen:
|
||||||
self.release_focus.emit()
|
|
||||||
self.hide()
|
self.hide()
|
||||||
elif strategy == 'never':
|
elif strategy == 'never':
|
||||||
self.release_focus.emit()
|
|
||||||
self.hide()
|
self.hide()
|
||||||
elif strategy == 'in-mode':
|
elif strategy == 'in-mode':
|
||||||
try:
|
try:
|
||||||
mode_manager = modeman.instance(self._win_id)
|
mode_manager = modeman.instance(self._win_id)
|
||||||
except modeman.UnavailableError:
|
except modeman.UnavailableError:
|
||||||
self.release_focus.emit()
|
|
||||||
self.hide()
|
self.hide()
|
||||||
else:
|
else:
|
||||||
if mode_manager.mode == usertypes.KeyMode.normal:
|
if mode_manager.mode == usertypes.KeyMode.normal:
|
||||||
self.release_focus.emit()
|
|
||||||
self.hide()
|
self.hide()
|
||||||
else:
|
else:
|
||||||
self.show()
|
self.show()
|
||||||
|
|
@ -371,6 +367,7 @@ class StatusBar(QWidget):
|
||||||
def _hide_cmd_widget(self):
|
def _hide_cmd_widget(self):
|
||||||
"""Show temporary text instead of command widget."""
|
"""Show temporary text instead of command widget."""
|
||||||
log.statusbar.debug("Hiding cmd widget")
|
log.statusbar.debug("Hiding cmd widget")
|
||||||
|
self.release_focus.emit()
|
||||||
self._stack.setCurrentWidget(self.txt)
|
self._stack.setCurrentWidget(self.txt)
|
||||||
self.maybe_hide()
|
self.maybe_hide()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ def parse_fatal_stacktrace(text):
|
||||||
lines = [
|
lines = [
|
||||||
r'(?P<type>Fatal Python error|Windows fatal exception): (?P<msg>.*)',
|
r'(?P<type>Fatal Python error|Windows fatal exception): (?P<msg>.*)',
|
||||||
r' *',
|
r' *',
|
||||||
r'(Current )?[Tt]hread [^ ]* \(most recent call first\): *',
|
r'(Current )?[Tt]hread .* \(most recent call first\): *',
|
||||||
r' File ".*", line \d+ in (?P<func>.*)',
|
r' (File ".*", line \d+ in (?P<func>.*)|<no Python frame>)',
|
||||||
]
|
]
|
||||||
m = re.search('\n'.join(lines), text)
|
m = re.search('\n'.join(lines), text)
|
||||||
if m is None:
|
if m is None:
|
||||||
|
|
@ -58,7 +58,7 @@ def parse_fatal_stacktrace(text):
|
||||||
else:
|
else:
|
||||||
msg = m.group('msg')
|
msg = m.group('msg')
|
||||||
typ = m.group('type')
|
typ = m.group('type')
|
||||||
func = m.group('func')
|
func = m.group('func') or ''
|
||||||
if typ == 'Windows fatal exception':
|
if typ == 'Windows fatal exception':
|
||||||
msg = 'Windows ' + msg
|
msg = 'Windows ' + msg
|
||||||
return msg, func
|
return msg, func
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import datetime
|
||||||
from typing import NoReturn
|
from typing import NoReturn
|
||||||
try:
|
try:
|
||||||
import tkinter
|
import tkinter
|
||||||
|
import tkinter.messagebox
|
||||||
except ImportError:
|
except ImportError:
|
||||||
tkinter = None # type: ignore[assignment]
|
tkinter = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,15 @@ class _WaylandDisplayStruct(ctypes.Structure):
|
||||||
_WaylandDisplay = NewType("_WaylandDisplay", "ctypes._Pointer[_WaylandDisplayStruct]")
|
_WaylandDisplay = NewType("_WaylandDisplay", "ctypes._Pointer[_WaylandDisplayStruct]")
|
||||||
|
|
||||||
|
|
||||||
def _load_libwayland_client() -> ctypes.CDLL:
|
def _load_library(name: str) -> ctypes.CDLL:
|
||||||
"""Load the Wayland client library."""
|
lib = ctypes.util.find_library(name)
|
||||||
|
if lib is None:
|
||||||
|
raise Error(f"{name} library not found")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return ctypes.CDLL("libwayland-client.so")
|
return ctypes.CDLL(lib)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise Error(f"Failed to load libwayland-client: {e}")
|
raise Error(f"Failed to load {name} library: {e}")
|
||||||
|
|
||||||
|
|
||||||
def _pid_from_fd(fd: int) -> int:
|
def _pid_from_fd(fd: int) -> int:
|
||||||
|
|
@ -113,7 +116,7 @@ def wayland_compositor_name() -> str:
|
||||||
Approach based on:
|
Approach based on:
|
||||||
https://stackoverflow.com/questions/69302630/wayland-client-get-compositor-name
|
https://stackoverflow.com/questions/69302630/wayland-client-get-compositor-name
|
||||||
"""
|
"""
|
||||||
wayland_client = _load_libwayland_client()
|
wayland_client = _load_library("wayland-client")
|
||||||
with _wayland_display(wayland_client) as display:
|
with _wayland_display(wayland_client) as display:
|
||||||
fd = _wayland_get_fd(wayland_client, display)
|
fd = _wayland_get_fd(wayland_client, display)
|
||||||
pid = _pid_from_fd(fd)
|
pid = _pid_from_fd(fd)
|
||||||
|
|
@ -136,18 +139,6 @@ _X11Display = NewType("_X11Display", "ctypes._Pointer[_X11DisplayStruct]")
|
||||||
_X11Window = NewType("_X11Window", int)
|
_X11Window = NewType("_X11Window", int)
|
||||||
|
|
||||||
|
|
||||||
def _x11_load_lib() -> ctypes.CDLL:
|
|
||||||
"""Load the X11 library."""
|
|
||||||
lib = ctypes.util.find_library("X11")
|
|
||||||
if lib is None:
|
|
||||||
raise Error("X11 library not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
return ctypes.CDLL(lib)
|
|
||||||
except OSError as e:
|
|
||||||
raise Error(f"Failed to load X11 library: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _x11_open_display(xlib: ctypes.CDLL) -> Iterator[_X11Display]:
|
def _x11_open_display(xlib: ctypes.CDLL) -> Iterator[_X11Display]:
|
||||||
"""Open a connection to the X11 display."""
|
"""Open a connection to the X11 display."""
|
||||||
|
|
@ -307,7 +298,7 @@ def _x11_get_wm_name(
|
||||||
|
|
||||||
def x11_wm_name() -> str:
|
def x11_wm_name() -> str:
|
||||||
"""Get the name of the running X11 window manager."""
|
"""Get the name of the running X11 window manager."""
|
||||||
xlib = _x11_load_lib()
|
xlib = _load_library("X11")
|
||||||
with _x11_open_display(xlib) as display:
|
with _x11_open_display(xlib) as display:
|
||||||
atoms = _X11Atoms(
|
atoms = _X11Atoms(
|
||||||
NET_SUPPORTING_WM_CHECK=_x11_intern_atom(
|
NET_SUPPORTING_WM_CHECK=_x11_intern_atom(
|
||||||
|
|
|
||||||
|
|
@ -654,9 +654,11 @@ class WebEngineVersions:
|
||||||
utils.VersionNumber(6, 9): (_BASES[130], '133.0.6943.141'), # 2025-02-25
|
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, 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, 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
|
## Qt 6.10
|
||||||
utils.VersionNumber(6, 10): (_BASES[134], '140.0.7339.207'), # 2025-09-22
|
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:
|
def __post_init__(self) -> None:
|
||||||
|
|
@ -930,12 +932,18 @@ def _webengine_extensions() -> Sequence[str]:
|
||||||
lines: list[str] = []
|
lines: list[str] = []
|
||||||
if (
|
if (
|
||||||
objects.backend == usertypes.Backend.QtWebEngine
|
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
|
and machinery.IS_QT6 # mypy; TODO early return once Qt 5 is dropped
|
||||||
):
|
):
|
||||||
from qutebrowser.qt.webenginecore import QWebEngineProfile
|
from qutebrowser.browser.webengine import webenginesettings
|
||||||
profile = QWebEngineProfile.defaultProfile()
|
lines.append("WebExtensions:")
|
||||||
assert profile is not None # mypy
|
|
||||||
|
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:
|
try:
|
||||||
ext_manager = profile.extensionManager()
|
ext_manager = profile.extensionManager()
|
||||||
|
|
@ -944,7 +952,6 @@ def _webengine_extensions() -> Sequence[str]:
|
||||||
return []
|
return []
|
||||||
assert ext_manager is not None # mypy
|
assert ext_manager is not None # mypy
|
||||||
|
|
||||||
lines.append("WebExtensions:")
|
|
||||||
if not ext_manager.extensions():
|
if not ext_manager.extensions():
|
||||||
lines[0] += " none"
|
lines[0] += " none"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ def _smoke_test_run(
|
||||||
return subprocess.run(argv, check=True, capture_output=True)
|
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."""
|
"""Try starting the given qutebrowser executable."""
|
||||||
stdout_whitelist = []
|
stdout_whitelist = []
|
||||||
stderr_whitelist = [
|
stderr_whitelist = [
|
||||||
|
|
@ -204,8 +204,19 @@ def smoke_test(executable: pathlib.Path, debug: bool) -> None:
|
||||||
r'\(0x80004002\)'),
|
r'\(0x80004002\)'),
|
||||||
])
|
])
|
||||||
|
|
||||||
proc = _smoke_test_run(executable)
|
try:
|
||||||
if debug:
|
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")
|
print("Skipping output check for debug build")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -214,48 +225,64 @@ def smoke_test(executable: pathlib.Path, debug: bool) -> None:
|
||||||
|
|
||||||
if stdout or stderr:
|
if stdout or stderr:
|
||||||
print("Unexpected output, running with --debug")
|
print("Unexpected output, running with --debug")
|
||||||
proc = _smoke_test_run(executable, '--debug')
|
smoke_test_debug(
|
||||||
debug_stdout = proc.stdout.decode('utf-8')
|
executable,
|
||||||
debug_stderr = proc.stderr.decode('utf-8')
|
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:
|
def verify_windows_exe(exe_path: pathlib.Path) -> None:
|
||||||
|
|
@ -311,7 +338,7 @@ def build_mac(
|
||||||
dist_path = pathlib.Path("dist")
|
dist_path = pathlib.Path("dist")
|
||||||
|
|
||||||
utils.print_title("Running pre-dmg smoke test")
|
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:
|
if skip_packaging:
|
||||||
return []
|
return []
|
||||||
|
|
@ -334,7 +361,7 @@ def build_mac(
|
||||||
subprocess.run(['hdiutil', 'attach', dmg_path,
|
subprocess.run(['hdiutil', 'attach', dmg_path,
|
||||||
'-mountpoint', tmp_path], check=True)
|
'-mountpoint', tmp_path], check=True)
|
||||||
try:
|
try:
|
||||||
smoke_test(_mac_bin_path(tmp_path), debug=debug)
|
smoke_test(_mac_bin_path(tmp_path), debug_build=debug)
|
||||||
finally:
|
finally:
|
||||||
print("Waiting 10s for dmg to be detachable...")
|
print("Waiting 10s for dmg to be detachable...")
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
@ -393,7 +420,7 @@ def _build_windows_single(
|
||||||
verify_windows_exe(exe_path)
|
verify_windows_exe(exe_path)
|
||||||
|
|
||||||
utils.print_title("Running smoke test")
|
utils.print_title("Running smoke test")
|
||||||
smoke_test(exe_path, debug=debug)
|
smoke_test(exe_path, debug_build=debug)
|
||||||
|
|
||||||
if skip_packaging:
|
if skip_packaging:
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
"Mako": "https://docs.makotemplates.org/en/latest/changelog.html",
|
"Mako": "https://docs.makotemplates.org/en/latest/changelog.html",
|
||||||
"hypothesis": "https://hypothesis.readthedocs.io/en/latest/changes.html",
|
"hypothesis": "https://hypothesis.readthedocs.io/en/latest/changes.html",
|
||||||
"mypy": "https://github.com/python/mypy/blob/master/CHANGELOG.md",
|
"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-PyYAML": "https://github.com/python/typeshed/commits/main/stubs/PyYAML",
|
||||||
"types-colorama": "https://github.com/python/typeshed/commits/main/stubs/colorama",
|
"types-colorama": "https://github.com/python/typeshed/commits/main/stubs/colorama",
|
||||||
"types-docutils": "https://github.com/python/typeshed/commits/main/stubs/docutils",
|
"types-docutils": "https://github.com/python/typeshed/commits/main/stubs/docutils",
|
||||||
|
|
@ -101,7 +102,6 @@
|
||||||
"h11": "https://h11.readthedocs.io/en/latest/changes.html",
|
"h11": "https://h11.readthedocs.io/en/latest/changes.html",
|
||||||
"httpcore": "https://github.com/encode/httpcore/blob/master/CHANGELOG.md",
|
"httpcore": "https://github.com/encode/httpcore/blob/master/CHANGELOG.md",
|
||||||
"httpx": "https://github.com/encode/httpx/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",
|
"six": "https://github.com/benjaminp/six/blob/master/CHANGES",
|
||||||
"altgraph": "https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst",
|
"altgraph": "https://github.com/ronaldoussoren/altgraph/blob/master/doc/changelog.rst",
|
||||||
"urllib3": "https://github.com/urllib3/urllib3/blob/main/CHANGES.rst",
|
"urllib3": "https://github.com/urllib3/urllib3/blob/main/CHANGES.rst",
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,12 @@ RUN pacman -Su --noconfirm \
|
||||||
python-tox \
|
python-tox \
|
||||||
python-distlib \
|
python-distlib \
|
||||||
libxml2-legacy \
|
libxml2-legacy \
|
||||||
{% if qt6 %}
|
qt6-base \
|
||||||
qt6-base \
|
qt6-declarative \
|
||||||
qt6-declarative \
|
qt6-webengine \
|
||||||
qt6-webengine \
|
python-pyqt6-webengine \
|
||||||
python-pyqt6-webengine \
|
pdfjs \
|
||||||
pdfjs \
|
python-pyqt6 \
|
||||||
python-pyqt6 \
|
|
||||||
{% else %}
|
|
||||||
qt5-base \
|
|
||||||
qt5-declarative \
|
|
||||||
openssl-1.1 \
|
|
||||||
qt5-webengine \
|
|
||||||
python-pyqtwebengine \
|
|
||||||
python-pyqt5 \
|
|
||||||
{% endif %}
|
|
||||||
xorg-xinit \
|
xorg-xinit \
|
||||||
xorg-server-xvfb \
|
xorg-server-xvfb \
|
||||||
ttf-bitstream-vera \
|
ttf-bitstream-vera \
|
||||||
|
|
@ -36,12 +27,7 @@ RUN useradd user -u 1001 && \
|
||||||
mkdir /home/user && \
|
mkdir /home/user && \
|
||||||
chown user:users /home/user
|
chown user:users /home/user
|
||||||
|
|
||||||
{% if qt6 %}
|
RUN python3 -c "from PyQt6 import QtWebEngineCore, QtWebEngineWidgets"
|
||||||
{% set pyqt_module = 'PyQt6' %}
|
|
||||||
{% else %}
|
|
||||||
{% set pyqt_module = 'PyQt5' %}
|
|
||||||
{% endif %}
|
|
||||||
RUN python3 -c "from {{ pyqt_module }} import QtWebEngineCore, QtWebEngineWidgets"
|
|
||||||
|
|
||||||
USER user
|
USER user
|
||||||
WORKDIR /home/user
|
WORKDIR /home/user
|
||||||
|
|
@ -49,4 +35,4 @@ RUN git config --global --add safe.directory /outside/.git
|
||||||
|
|
||||||
CMD git clone /outside qutebrowser.git && \
|
CMD git clone /outside qutebrowser.git && \
|
||||||
cd qutebrowser.git && \
|
cd qutebrowser.git && \
|
||||||
{{ python }} -m tox -e {% if qt6 %}py-qt6{% else %}py-qt5{% endif %}
|
{{ python }} -m tox -e py-qt6
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,14 @@
|
||||||
|
|
||||||
"""Generate Dockerfiles for qutebrowser's CI."""
|
"""Generate Dockerfiles for qutebrowser's CI."""
|
||||||
|
|
||||||
import sys
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
|
|
||||||
CONFIGS = {
|
CONFIGS = {
|
||||||
'archlinux-webengine': {'unstable': False, 'qt6': False},
|
'archlinux-webengine': {'unstable': False},
|
||||||
'archlinux-webengine-qt6': {'unstable': False, 'qt6': True},
|
'archlinux-webengine-unstable': {'unstable': True},
|
||||||
'archlinux-webengine-unstable': {'unstable': True, 'qt6': False},
|
|
||||||
'archlinux-webengine-unstable-qt6': {'unstable': True, 'qt6': True},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ def show_commit():
|
||||||
git_args = ['git', 'show']
|
git_args = ['git', 'show']
|
||||||
if utils.ON_CI:
|
if utils.ON_CI:
|
||||||
git_args.append("--color")
|
git_args.append("--color")
|
||||||
|
git_args.append("--no-patch") # shows entire git tree on CI (shallow clone)
|
||||||
subprocess.run(git_args, check=True)
|
subprocess.run(git_args, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,11 @@
|
||||||
console.log("[PASS] Positions equal: " + old_position);
|
console.log("[PASS] Positions equal: " + old_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(() => console.log('simple loaded'))
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="console.log('simple loaded')">
|
<body>
|
||||||
<a href="/data/hello.txt" id="link">Just a link</a>
|
<a href="/data/hello.txt" id="link">Just a link</a>
|
||||||
<button>blub</button>
|
<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>
|
<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>
|
||||||
|
|
|
||||||
|
|
@ -103,3 +103,11 @@ Feature: Using completion
|
||||||
And I run :completion-item-focus next
|
And I run :completion-item-focus next
|
||||||
And I run :cmd-set-text -s :set
|
And I run :cmd-set-text -s :set
|
||||||
Then the completion model should be option
|
Then the completion model should be option
|
||||||
|
|
||||||
|
Scenario: Page focus after using completion (#8750)
|
||||||
|
When I open data/insert_mode_settings/html/input.html
|
||||||
|
And I run :cmd-set-text :
|
||||||
|
And I run :mode-leave
|
||||||
|
And I run :click-element id qute-input
|
||||||
|
And I run :fake-key -g someinput
|
||||||
|
Then the javascript message "contents: someinput" should be logged
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ Feature: Saving and loading sessions
|
||||||
@qtwebkit_skip
|
@qtwebkit_skip
|
||||||
Scenario: Scrolling (qtwebengine)
|
Scenario: Scrolling (qtwebengine)
|
||||||
When I open data/scroll/simple.html
|
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 run :scroll-px 10 20
|
||||||
And I wait until the scroll position changed to 10/20
|
And I wait until the scroll position changed to 10/20
|
||||||
Then the session should look like:
|
Then the session should look like:
|
||||||
|
|
|
||||||
|
|
@ -50,13 +50,13 @@ def fresh_instance(quteproc):
|
||||||
# on PyQt6.8 we disable that with the new API, otherwise restart the
|
# on PyQt6.8 we disable that with the new API, otherwise restart the
|
||||||
# browser to make it forget previous prompts.
|
# browser to make it forget previous prompts.
|
||||||
#
|
#
|
||||||
# Qt 6.10 Beta 4 accidentally persists some permissions;
|
# Starting with Qt 6.10, QtWebEngine unconditionally persists some permissions;
|
||||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-140194
|
# see https://bugreports.qt.io/browse/QTBUG-140194
|
||||||
if (
|
if (
|
||||||
qtutils.version_check("6.8", compiled=False)
|
qtutils.version_check("6.8", compiled=False)
|
||||||
and PYQT_WEBENGINE_VERSION
|
and PYQT_WEBENGINE_VERSION
|
||||||
and PYQT_WEBENGINE_VERSION < 0x60800
|
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.terminate()
|
||||||
quteproc.start()
|
quteproc.start()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,14 @@ def is_ignored_chromium_message(line):
|
||||||
"GetGpuDriverOverlayInfo: Failed to retrieve video device",
|
"GetGpuDriverOverlayInfo: Failed to retrieve video device",
|
||||||
# [1784:7100:1022/150434.202:ERROR:direct_composition_support.cc(1122)]
|
# [1784:7100:1022/150434.202:ERROR:direct_composition_support.cc(1122)]
|
||||||
"QueryInterface to IDCompositionDevice4 failed: No such interface supported (0x80004002)",
|
"QueryInterface to IDCompositionDevice4 failed: No such interface supported (0x80004002)",
|
||||||
|
|
||||||
|
# Qt 6.10 on Windows + GitHub Actions
|
||||||
|
# [3508:6056:1103/172403.602:ERROR:cache_util_win.cc(20)]
|
||||||
|
"Unable to move the cache: The system cannot find the file specified. (0x2)",
|
||||||
|
# [3508:5516:1103/172403.608:ERROR:disk_cache.cc(216)]
|
||||||
|
"Unable to create cache",
|
||||||
|
# [3508:5516:1103/172403.608:ERROR:gpu_disk_cache.cc(711)]
|
||||||
|
"Gpu Cache Creation failed: -2",
|
||||||
]
|
]
|
||||||
return any(testutils.pattern_match(pattern=pattern, value=message)
|
return any(testutils.pattern_match(pattern=pattern, value=message)
|
||||||
for pattern in ignored_messages)
|
for pattern in ignored_messages)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import pytest
|
||||||
from qutebrowser.qt.core import QProcess, QPoint
|
from qutebrowser.qt.core import QProcess, QPoint
|
||||||
|
|
||||||
from helpers import testutils
|
from helpers import testutils
|
||||||
|
from end2end.fixtures import quteprocess
|
||||||
from qutebrowser.utils import qtutils, utils, version
|
from qutebrowser.utils import qtutils, utils, version
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -251,6 +252,7 @@ def test_optimize(request, quteproc_new, capfd, level):
|
||||||
def test_version(request):
|
def test_version(request):
|
||||||
"""Test invocation with --version argument."""
|
"""Test invocation with --version argument."""
|
||||||
args = ['-m', 'qutebrowser', '--version'] + _base_args(request.config)
|
args = ['-m', 'qutebrowser', '--version'] + _base_args(request.config)
|
||||||
|
args.remove("--json-logging")
|
||||||
# can't use quteproc_new here because it's confused by
|
# can't use quteproc_new here because it's confused by
|
||||||
# early process termination
|
# early process termination
|
||||||
proc = QProcess()
|
proc = QProcess()
|
||||||
|
|
@ -611,6 +613,26 @@ def test_service_worker_workaround(
|
||||||
assert not service_worker_dir.exists()
|
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])
|
@pytest.mark.parametrize('store', [True, False])
|
||||||
def test_cookies_store(quteproc_new, request, short_tmpdir, store):
|
def test_cookies_store(quteproc_new, request, short_tmpdir, store):
|
||||||
# Start test process
|
# Start test process
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
import string
|
import string
|
||||||
import functools
|
import functools
|
||||||
import itertools
|
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -76,7 +75,7 @@ def test_match_benchmark(benchmark, tabbed_browser, qtbot, mode_manager, qapp,
|
||||||
|
|
||||||
@pytest.mark.parametrize('min_len', [0, 3])
|
@pytest.mark.parametrize('min_len', [0, 3])
|
||||||
@pytest.mark.parametrize('num_chars', [5, 9])
|
@pytest.mark.parametrize('num_chars', [5, 9])
|
||||||
@pytest.mark.parametrize('num_elements', itertools.chain(range(1, 26), [125]))
|
@pytest.mark.parametrize('num_elements', [*range(1, 26), 125])
|
||||||
def test_scattered_hints_count(min_len, num_chars, num_elements):
|
def test_scattered_hints_count(min_len, num_chars, num_elements):
|
||||||
"""Test scattered hints function.
|
"""Test scattered hints function.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import pytest
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
from qutebrowser import qutebrowser
|
from qutebrowser import qutebrowser
|
||||||
from qutebrowser.config import qtargs, configdata
|
from qutebrowser.config import qtargs, configdata
|
||||||
from qutebrowser.utils import usertypes, version
|
from qutebrowser.utils import usertypes, version, utils
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -52,6 +52,7 @@ def reduce_args(config_stub, version_patcher, monkeypatch):
|
||||||
config_stub.val.scrolling.bar = 'never'
|
config_stub.val.scrolling.bar = 'never'
|
||||||
config_stub.val.qt.chromium.experimental_web_platform_features = '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_accelerated_2d_canvas = 'never'
|
||||||
|
config_stub.val.qt.workarounds.disable_accessibility = 'never'
|
||||||
monkeypatch.setattr(qtargs.utils, 'is_mac', False)
|
monkeypatch.setattr(qtargs.utils, 'is_mac', False)
|
||||||
# Avoid WebRTC pipewire feature
|
# Avoid WebRTC pipewire feature
|
||||||
monkeypatch.setattr(qtargs.utils, 'is_linux', False)
|
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]]
|
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')
|
@pytest.mark.usefixtures('reduce_args')
|
||||||
class TestWebEngineArgs:
|
class TestWebEngineArgs:
|
||||||
|
|
||||||
|
|
@ -190,6 +199,40 @@ class TestWebEngineArgs:
|
||||||
args = qtargs.qt_args(parsed)
|
args = qtargs.qt_args(parsed)
|
||||||
assert ('--disable-accelerated-2d-canvas' in args) == has_arg
|
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', [
|
@pytest.mark.parametrize('flags, args', [
|
||||||
([], []),
|
([], []),
|
||||||
(['--debug-flag', 'chromium'], ['--enable-logging', '--v=1']),
|
(['--debug-flag', 'chromium'], ['--enable-logging', '--v=1']),
|
||||||
|
|
@ -471,6 +514,9 @@ class TestWebEngineArgs:
|
||||||
# Qt 6.9
|
# Qt 6.9
|
||||||
('6.9.0', "DocumentPictureInPictureAPI,PermissionElement"),
|
('6.9.0', "DocumentPictureInPictureAPI,PermissionElement"),
|
||||||
('6.9.1', "DocumentPictureInPictureAPI,PermissionElement"),
|
('6.9.1', "DocumentPictureInPictureAPI,PermissionElement"),
|
||||||
|
# Qt 6.10
|
||||||
|
('6.10.0', "DocumentPictureInPictureAPI,PermissionElement"),
|
||||||
|
('6.10.1', "DocumentPictureInPictureAPI"),
|
||||||
])
|
])
|
||||||
def test_disable_feature_workaround(
|
def test_disable_feature_workaround(
|
||||||
self, parser, version_patcher, qt_version, disabled
|
self, parser, version_patcher, qt_version, disabled
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,31 @@ Thread 0x00007fa135ac7700 (most recent call first):
|
||||||
File "", line 1 in testfunc
|
File "", line 1 in testfunc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
VALID_CRASH_TEXT_PY314 = """
|
||||||
|
Fatal Python error: Segmentation fault
|
||||||
|
_
|
||||||
|
Current thread 0x00000001fe53e140 [CrBrowserMain] (most recent call first):
|
||||||
|
File "qutebrowser/app.py", line 126 in qt_mainloop
|
||||||
|
File "qutebrowser/app.py", line 116 in run
|
||||||
|
File "qutebrowser/qutebrowser.py", line 234 in main
|
||||||
|
File "__main__.py", line 15 in <module>
|
||||||
|
_
|
||||||
|
Current thread's C stack trace (most recent call first):
|
||||||
|
Binary file "...", at _Py_DumpStack+0x48 [0x10227cc9c]
|
||||||
|
<truncated rest of calls>
|
||||||
|
"""
|
||||||
|
|
||||||
|
VALID_CRASH_TEXT_PY314_NO_PY = """
|
||||||
|
Fatal Python error: Segmentation fault
|
||||||
|
_
|
||||||
|
Current thread 0x00007f0dc805cbc0 [qutebrowser] (most recent call first):
|
||||||
|
<no Python frame>
|
||||||
|
_
|
||||||
|
Current thread's C stack trace (most recent call first):
|
||||||
|
Binary file "/lib64/libpython3.14.so.1.0", at _Py_DumpStack+0x4c [0x7f0dc7b2127b]
|
||||||
|
<truncated rest of calls>
|
||||||
|
"""
|
||||||
|
|
||||||
WINDOWS_CRASH_TEXT = r"""
|
WINDOWS_CRASH_TEXT = r"""
|
||||||
Windows fatal exception: access violation
|
Windows fatal exception: access violation
|
||||||
_
|
_
|
||||||
|
|
@ -45,13 +70,32 @@ Hello world!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('text, typ, func', [
|
@pytest.mark.parametrize(
|
||||||
(VALID_CRASH_TEXT, 'Segmentation fault', 'testfunc'),
|
"text, typ, func",
|
||||||
(VALID_CRASH_TEXT_THREAD, 'Segmentation fault', 'testfunc'),
|
[
|
||||||
(VALID_CRASH_TEXT_EMPTY, 'Aborted', ''),
|
pytest.param(VALID_CRASH_TEXT, "Segmentation fault", "testfunc", id="valid"),
|
||||||
(WINDOWS_CRASH_TEXT, 'Windows access violation', 'tabopen'),
|
pytest.param(
|
||||||
(INVALID_CRASH_TEXT, '', ''),
|
VALID_CRASH_TEXT_THREAD, "Segmentation fault", "testfunc", id="valid-thread"
|
||||||
])
|
),
|
||||||
|
pytest.param(
|
||||||
|
VALID_CRASH_TEXT_PY314,
|
||||||
|
"Segmentation fault",
|
||||||
|
"qt mainloop",
|
||||||
|
id="valid-py314",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
VALID_CRASH_TEXT_PY314_NO_PY,
|
||||||
|
"Segmentation fault",
|
||||||
|
"",
|
||||||
|
id="valid-py314-no-py",
|
||||||
|
),
|
||||||
|
pytest.param(VALID_CRASH_TEXT_EMPTY, "Aborted", "", id="valid-empty"),
|
||||||
|
pytest.param(
|
||||||
|
WINDOWS_CRASH_TEXT, "Windows access violation", "tabopen", id="windows"
|
||||||
|
),
|
||||||
|
pytest.param(INVALID_CRASH_TEXT, "", "", id="invalid"),
|
||||||
|
],
|
||||||
|
)
|
||||||
def test_parse_fatal_stacktrace(text, typ, func):
|
def test_parse_fatal_stacktrace(text, typ, func):
|
||||||
text = text.strip().replace('_', ' ')
|
text = text.strip().replace('_', ' ')
|
||||||
assert crashdialog.parse_fatal_stacktrace(text) == (typ, func)
|
assert crashdialog.parse_fatal_stacktrace(text) == (typ, func)
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,18 @@ from qutebrowser.misc import wmname
|
||||||
def test_load_libwayland_client():
|
def test_load_libwayland_client():
|
||||||
"""Test loading the Wayland client library, which might or might not exist."""
|
"""Test loading the Wayland client library, which might or might not exist."""
|
||||||
try:
|
try:
|
||||||
wmname._load_libwayland_client()
|
wmname._load_library("wayland-client")
|
||||||
except wmname.Error:
|
except wmname.Error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_load_libwayland_client_error(mocker: pytest_mock.MockerFixture):
|
def test_load_libwayland_client_error(mocker: pytest_mock.MockerFixture):
|
||||||
"""Test that an error in loading the Wayland client library raises an error."""
|
"""Test that an error in loading the Wayland client library raises an error."""
|
||||||
|
mocker.patch.object(ctypes.util, "find_library", return_value="libwayland-client.so.6")
|
||||||
mocker.patch("ctypes.CDLL", side_effect=OSError("Library not found"))
|
mocker.patch("ctypes.CDLL", side_effect=OSError("Library not found"))
|
||||||
|
|
||||||
with pytest.raises(wmname.Error, match="Failed to load libwayland-client"):
|
with pytest.raises(wmname.Error, match="Failed to load wayland-client"):
|
||||||
wmname._load_libwayland_client()
|
wmname._load_library("wayland-client")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -177,7 +178,7 @@ def test_wayland_real():
|
||||||
def test_load_xlib():
|
def test_load_xlib():
|
||||||
"""Test loading Xlib, which might or might not exist."""
|
"""Test loading Xlib, which might or might not exist."""
|
||||||
try:
|
try:
|
||||||
wmname._x11_load_lib()
|
wmname._load_library("X11")
|
||||||
except wmname.Error:
|
except wmname.Error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -187,7 +188,7 @@ def test_load_xlib_not_found(monkeypatch: pytest.MonkeyPatch):
|
||||||
monkeypatch.setattr(ctypes.util, "find_library", lambda x: None)
|
monkeypatch.setattr(ctypes.util, "find_library", lambda x: None)
|
||||||
|
|
||||||
with pytest.raises(wmname.Error, match="X11 library not found"):
|
with pytest.raises(wmname.Error, match="X11 library not found"):
|
||||||
wmname._x11_load_lib()
|
wmname._load_library("X11")
|
||||||
|
|
||||||
|
|
||||||
def test_load_xlib_error(mocker: pytest_mock.MockerFixture):
|
def test_load_xlib_error(mocker: pytest_mock.MockerFixture):
|
||||||
|
|
@ -198,7 +199,7 @@ def test_load_xlib_error(mocker: pytest_mock.MockerFixture):
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
wmname.Error, match="Failed to load X11 library: Failed to load library"
|
wmname.Error, match="Failed to load X11 library: Failed to load library"
|
||||||
):
|
):
|
||||||
wmname._x11_load_lib()
|
wmname._load_library("X11")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -289,7 +290,7 @@ def test_x11_get_wm_name(
|
||||||
qtbot.add_widget(w)
|
qtbot.add_widget(w)
|
||||||
w.setWindowTitle("Test Window")
|
w.setWindowTitle("Test Window")
|
||||||
|
|
||||||
xlib = wmname._x11_load_lib()
|
xlib = wmname._load_library("X11")
|
||||||
with wmname._x11_open_display(xlib) as display:
|
with wmname._x11_open_display(xlib) as display:
|
||||||
atoms = wmname._X11Atoms(
|
atoms = wmname._X11Atoms(
|
||||||
NET_SUPPORTING_WM_CHECK=-1,
|
NET_SUPPORTING_WM_CHECK=-1,
|
||||||
|
|
|
||||||
|
|
@ -1449,11 +1449,9 @@ def test_version_info(params, stubs, monkeypatch, config_stub):
|
||||||
monkeypatch.delattr(version, 'qtutils.qWebKitVersion', raising=False)
|
monkeypatch.delattr(version, 'qtutils.qWebKitVersion', raising=False)
|
||||||
if machinery.IS_QT6:
|
if machinery.IS_QT6:
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
QWebEngineProfile,
|
webenginesettings,
|
||||||
"defaultProfile",
|
"default_profile",
|
||||||
lambda: FakeExtensionProfile(
|
FakeExtensionProfile(FakeExtensionManager([FakeExtensionInfo("ext1")])),
|
||||||
FakeExtensionManager([FakeExtensionInfo("ext1")])
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
substitutions['webextensions'] = (
|
substitutions['webextensions'] = (
|
||||||
"\n"
|
"\n"
|
||||||
|
|
@ -1592,20 +1590,35 @@ class TestOpenGLInfo:
|
||||||
class TestWebEngineExtensions:
|
class TestWebEngineExtensions:
|
||||||
|
|
||||||
def test_qtwebkit(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
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(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()
|
assert not version._webengine_extensions()
|
||||||
|
|
||||||
def test_avoid_chromium_init(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
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(version.objects, "backend", usertypes.Backend.QtWebEngine)
|
||||||
monkeypatch.setattr(objects, "debug_flags", {"avoid-chromium-init"})
|
monkeypatch.setattr(objects, "debug_flags", {"avoid-chromium-init"})
|
||||||
monkeypatch.setattr(QWebEngineProfile, "defaultProfile", lambda: 1/0)
|
monkeypatch.setattr(webenginesettings, "default_qt_profile", lambda: 1 / 0)
|
||||||
assert not version._webengine_extensions()
|
assert version._webengine_extensions() == [
|
||||||
|
"WebExtensions: unknown (avoiding init)"
|
||||||
|
]
|
||||||
|
|
||||||
def test_no_extension_manager(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
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()
|
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(
|
@pytest.mark.parametrize(
|
||||||
"extensions, expected",
|
"extensions, expected",
|
||||||
[
|
[
|
||||||
|
|
@ -1666,11 +1679,9 @@ class TestWebEngineExtensions:
|
||||||
expected: list[str],
|
expected: list[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
QWebEngineProfile,
|
webenginesettings,
|
||||||
"defaultProfile",
|
"default_profile",
|
||||||
lambda: FakeExtensionProfile(
|
FakeExtensionProfile(FakeExtensionManager(extensions)),
|
||||||
FakeExtensionManager(extensions)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
assert version._webengine_extensions() == expected
|
assert version._webengine_extensions() == expected
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue