Compare commits
No commits in common. "main" and "v3.3.0" have entirely different histories.
|
|
@ -0,0 +1,19 @@
|
|||
[bumpversion]
|
||||
current_version = 3.3.0
|
||||
commit = True
|
||||
message = Release v{new_version}
|
||||
tag = True
|
||||
sign_tags = True
|
||||
tag_name = v{new_version}
|
||||
|
||||
[bumpversion:file:qutebrowser/__init__.py]
|
||||
parse = __version__ = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||
|
||||
[bumpversion:file:misc/org.qutebrowser.qutebrowser.appdata.xml]
|
||||
search = <!-- Add new releases here -->
|
||||
replace = <!-- Add new releases here -->
|
||||
<release version="{new_version}" date="{now:%Y-%m-%d}"/>
|
||||
|
||||
[bumpversion:file:doc/changelog.asciidoc]
|
||||
search = (unreleased)
|
||||
replace = ({now:%Y-%m-%d})
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
[tool.bumpversion]
|
||||
current_version = "3.6.3"
|
||||
commit = true
|
||||
message = "Release v{new_version}"
|
||||
tag = true
|
||||
sign_tags = true
|
||||
tag_name = "v{new_version}"
|
||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||
serialize = ["{major}.{minor}.{patch}"]
|
||||
allow_dirty = false
|
||||
|
||||
[[tool.bumpversion.files]]
|
||||
filename = "qutebrowser/__init__.py"
|
||||
search = "__version__ = \"{current_version}\""
|
||||
replace = "__version__ = \"{new_version}\""
|
||||
|
||||
[[tool.bumpversion.files]]
|
||||
filename = "misc/org.qutebrowser.qutebrowser.appdata.xml"
|
||||
search = "<!-- Add new releases here -->"
|
||||
replace = """<!-- Add new releases here -->
|
||||
<release version='{new_version}' date='{now:%Y-%m-%d}'/>"""
|
||||
|
||||
[[tool.bumpversion.files]]
|
||||
filename = "doc/changelog.asciidoc"
|
||||
search = "(unreleased)"
|
||||
replace = "({now:%Y-%m-%d})"
|
||||
|
|
@ -4,7 +4,6 @@ include =
|
|||
tests/*
|
||||
scripts/*
|
||||
branch = true
|
||||
patch = subprocess
|
||||
omit =
|
||||
qutebrowser/__main__.py
|
||||
*/__init__.py
|
||||
|
|
|
|||
5
.flake8
5
.flake8
|
|
@ -42,7 +42,6 @@ exclude = .*,__pycache__,resources.py
|
|||
# W503: like break before binary operator
|
||||
# W504: line break after binary operator
|
||||
# FI18: __future__ import "annotations" missing
|
||||
# FI58: __future__ import "annotations" present
|
||||
# PT004: fixture '{name}' does not return anything, add leading underscore
|
||||
# PT011: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception
|
||||
# PT012: pytest.raises() block should contain a single simple statement
|
||||
|
|
@ -55,11 +54,11 @@ ignore =
|
|||
D102,D103,D106,D107,D104,D105,D209,D211,D401,D402,D403,D412,D413,
|
||||
A003,
|
||||
W503, W504,
|
||||
FI18,FI58,
|
||||
FI18,
|
||||
PT004,
|
||||
PT011,
|
||||
PT012
|
||||
min-version = 3.9.0
|
||||
min-version = 3.8.0
|
||||
max-complexity = 12
|
||||
per-file-ignores =
|
||||
qutebrowser/api/hook.py : N801
|
||||
|
|
|
|||
|
|
@ -10,13 +10,15 @@ on:
|
|||
jobs:
|
||||
tests:
|
||||
if: "github.repository == 'qutebrowser/qutebrowser'"
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- testenv: bleeding
|
||||
image: "archlinux-webengine-unstable-qt6"
|
||||
- testenv: bleeding-qt5
|
||||
image: "archlinux-webengine-unstable"
|
||||
container:
|
||||
image: "qutebrowser/ci:${{ matrix.image }}"
|
||||
|
|
@ -31,13 +33,14 @@ jobs:
|
|||
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
||||
options: --privileged --tty
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up problem matchers
|
||||
run: "python scripts/dev/ci/problemmatchers.py py3 ${{ runner.temp }}"
|
||||
- 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 }}"
|
||||
if: "endsWith(matrix.image, '-qt6')"
|
||||
- name: Run tox
|
||||
run: dbus-run-session tox -e ${{ matrix.testenv }}
|
||||
- name: Gather info
|
||||
|
|
@ -48,7 +51,7 @@ jobs:
|
|||
shell: bash
|
||||
if: failure()
|
||||
- name: Upload screenshots
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
||||
path: |
|
||||
|
|
@ -58,7 +61,7 @@ jobs:
|
|||
irc:
|
||||
timeout-minutes: 2
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [tests]
|
||||
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
linters:
|
||||
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
||||
timeout-minutes: 10
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
@ -27,6 +27,7 @@ jobs:
|
|||
- testenv: vulture
|
||||
- testenv: misc
|
||||
- testenv: pyroma
|
||||
- testenv: check-manifest
|
||||
- testenv: eslint
|
||||
- testenv: shellcheck
|
||||
args: "-f gcc" # For problem matchers
|
||||
|
|
@ -34,30 +35,30 @@ jobs:
|
|||
- testenv: actionlint
|
||||
- testenv: package
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.mypy_cache
|
||||
.tox
|
||||
~/.cache/pip
|
||||
key: "${{ matrix.testenv }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('scripts/dev/pylint_checkers/qute_pylint/*.py') }}"
|
||||
- uses: actions/setup-python@v6
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x'
|
||||
node-version: '16.x'
|
||||
if: "matrix.testenv == 'eslint'"
|
||||
- name: Set up problem matchers
|
||||
run: "python scripts/dev/ci/problemmatchers.py ${{ matrix.testenv }} ${{ runner.temp }}"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
[[ ${{ matrix.testenv }} == eslint ]] && npm install -g 'eslint@<9.0.0'
|
||||
[[ ${{ matrix.testenv }} == docs ]] && sudo apt-get update && sudo apt-get install --no-install-recommends asciidoc libegl1
|
||||
[[ ${{ matrix.testenv }} == vulture || ${{ matrix.testenv }} == pylint ]] && sudo apt-get update && sudo apt-get install --no-install-recommends libegl1
|
||||
[[ ${{ matrix.testenv }} == docs ]] && sudo apt-get update && sudo apt-get install --no-install-recommends asciidoc libegl1-mesa
|
||||
[[ ${{ matrix.testenv }} == vulture || ${{ matrix.testenv }} == pylint ]] && sudo apt-get update && sudo apt-get install --no-install-recommends libegl1-mesa
|
||||
if [[ ${{ matrix.testenv }} == shellcheck ]]; then
|
||||
scversion="stable"
|
||||
bindir="$HOME/.local/bin"
|
||||
|
|
@ -85,15 +86,21 @@ jobs:
|
|||
tests-docker:
|
||||
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
||||
timeout-minutes: 45
|
||||
runs-on: ubuntu-22.04 # not 24.04 because sandboxing fails by default (#8424)
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- testenv: py
|
||||
- testenv: py-qt5
|
||||
image: archlinux-webkit
|
||||
- testenv: py-qt5
|
||||
image: archlinux-webengine
|
||||
- testenv: py
|
||||
- testenv: py-qt5
|
||||
image: archlinux-webengine-unstable
|
||||
- testenv: py
|
||||
image: archlinux-webengine-qt6
|
||||
- testenv: py
|
||||
image: archlinux-webengine-unstable-qt6
|
||||
container:
|
||||
image: "qutebrowser/ci:${{ matrix.image }}"
|
||||
env:
|
||||
|
|
@ -106,7 +113,7 @@ jobs:
|
|||
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
||||
options: --privileged --tty
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up problem matchers
|
||||
|
|
@ -121,7 +128,7 @@ jobs:
|
|||
shell: bash
|
||||
if: failure()
|
||||
- name: Upload screenshots
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
||||
path: |
|
||||
|
|
@ -137,10 +144,10 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
### PyQt 5.15.2 (Python 3.9)
|
||||
- testenv: py39-pyqt5152
|
||||
os: ubuntu-22.04
|
||||
python: "3.9"
|
||||
### PyQt 5.15.2 (Python 3.8)
|
||||
- testenv: py38-pyqt5152
|
||||
os: ubuntu-20.04
|
||||
python: "3.8"
|
||||
### PyQt 5.15 (Python 3.10, with coverage)
|
||||
# FIXME:qt6
|
||||
# - testenv: py310-pyqt515-cov
|
||||
|
|
@ -148,19 +155,19 @@ jobs:
|
|||
# python: "3.10"
|
||||
### PyQt 5.15 (Python 3.11)
|
||||
- testenv: py311-pyqt515
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-20.04
|
||||
python: "3.11"
|
||||
### PyQt 6.2 (Python 3.9)
|
||||
- testenv: py39-pyqt62
|
||||
os: ubuntu-22.04
|
||||
python: "3.9"
|
||||
### PyQt 6.3 (Python 3.9)
|
||||
- testenv: py39-pyqt63
|
||||
os: ubuntu-22.04
|
||||
python: "3.9"
|
||||
### PyQt 6.2 (Python 3.8)
|
||||
- testenv: py38-pyqt62
|
||||
os: ubuntu-20.04
|
||||
python: "3.8"
|
||||
### PyQt 6.3 (Python 3.8)
|
||||
- testenv: py38-pyqt63
|
||||
os: ubuntu-20.04
|
||||
python: "3.8"
|
||||
## PyQt 6.4 (Python 3.9)
|
||||
- testenv: py39-pyqt64
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-20.04
|
||||
python: "3.9"
|
||||
### PyQt 6.5 (Python 3.10)
|
||||
- testenv: py310-pyqt65
|
||||
|
|
@ -182,45 +189,31 @@ jobs:
|
|||
- testenv: py312-pyqt67
|
||||
os: ubuntu-22.04
|
||||
python: "3.12"
|
||||
### PyQt 6.8 (Python 3.13)
|
||||
- testenv: py313-pyqt68
|
||||
os: ubuntu-24.04
|
||||
python: "3.13"
|
||||
### PyQt 6.8 (Python 3.13)
|
||||
- testenv: py313-pyqt68
|
||||
os: ubuntu-24.04
|
||||
python: "3.13"
|
||||
### PyQt 6.9 (Python 3.14)
|
||||
- testenv: py314-pyqt69
|
||||
os: ubuntu-24.04
|
||||
python: "3.14"
|
||||
### PyQt 6.10 (Python 3.14)
|
||||
- testenv: py314-pyqt610
|
||||
os: ubuntu-24.04
|
||||
python: "3.14"
|
||||
### macOS Sonoma (M1 runner)
|
||||
- testenv: py314-pyqt610
|
||||
os: macos-14
|
||||
python: "3.14"
|
||||
### macOS Monterey
|
||||
- testenv: py312-pyqt67
|
||||
os: macos-12
|
||||
python: "3.12"
|
||||
args: "tests/unit" # Only run unit tests on macOS
|
||||
### macOS Sequoia (Intel runner)
|
||||
- testenv: py314-pyqt610
|
||||
os: macos-15-intel
|
||||
python: "3.14"
|
||||
### macOS Ventura
|
||||
- testenv: py312-pyqt67
|
||||
os: macos-13
|
||||
python: "3.12"
|
||||
args: "tests/unit" # Only run unit tests on macOS
|
||||
### macOS Sonoma (M1 runner)
|
||||
- testenv: py312-pyqt67
|
||||
os: macos-14
|
||||
python: "3.12"
|
||||
args: "tests/unit" # Only run unit tests on macOS
|
||||
### Windows
|
||||
- testenv: py314-pyqt610
|
||||
os: windows-2022
|
||||
python: "3.14"
|
||||
- testenv: py314-pyqt610
|
||||
os: windows-2025
|
||||
python: "3.14"
|
||||
- testenv: py312-pyqt67
|
||||
os: windows-2019
|
||||
python: "3.12"
|
||||
runs-on: "${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.mypy_cache
|
||||
|
|
@ -228,7 +221,7 @@ jobs:
|
|||
~/.cache/pip
|
||||
key: "${{ matrix.testenv }}-${{ matrix.os }}-${{ matrix.python }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}"
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "${{ matrix.python }}"
|
||||
- name: Set up problem matchers
|
||||
|
|
@ -236,7 +229,7 @@ jobs:
|
|||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends libyaml-dev libegl1 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 libjpeg-dev
|
||||
sudo apt-get install --no-install-recommends libyaml-dev libegl1-mesa libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0
|
||||
if: "startsWith(matrix.os, 'ubuntu-')"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
|
@ -258,7 +251,7 @@ jobs:
|
|||
if: "failure()"
|
||||
- name: Upload coverage
|
||||
if: "endsWith(matrix.testenv, '-cov')"
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: "${{ matrix.testenv }}"
|
||||
- name: Gather info
|
||||
|
|
@ -269,7 +262,7 @@ jobs:
|
|||
shell: bash
|
||||
if: failure()
|
||||
- name: Upload screenshots
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.testenv }}-${{ matrix.os }}"
|
||||
path: |
|
||||
|
|
@ -282,24 +275,24 @@ jobs:
|
|||
permissions:
|
||||
security-events: write
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: javascript, python
|
||||
queries: +security-extended
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
irc:
|
||||
timeout-minutes: 2
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [linters, tests, tests-docker, codeql]
|
||||
if: "always() && github.repository_owner == 'qutebrowser'"
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -8,16 +8,19 @@ on:
|
|||
jobs:
|
||||
docker:
|
||||
if: "github.repository == 'qutebrowser/qutebrowser'"
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image:
|
||||
- archlinux-webkit
|
||||
- archlinux-webengine
|
||||
- archlinux-webengine-unstable
|
||||
- archlinux-webengine-unstable-qt6
|
||||
- archlinux-webengine-qt6
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- run: pip install jinja2
|
||||
|
|
@ -39,7 +42,7 @@ jobs:
|
|||
irc:
|
||||
timeout-minutes: 2
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [docker]
|
||||
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -14,45 +14,50 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-15-intel
|
||||
- os: macos-12
|
||||
toxenv: build-release-qt5
|
||||
name: qt5-macos
|
||||
- os: windows-2019
|
||||
toxenv: build-release-qt5
|
||||
name: qt5-windows
|
||||
- os: macos-12
|
||||
args: --debug
|
||||
toxenv: build-release-qt5
|
||||
name: qt5-macos-debug
|
||||
- os: windows-2019
|
||||
args: --debug
|
||||
toxenv: build-release-qt5
|
||||
name: qt5-windows-debug
|
||||
- os: macos-12
|
||||
toxenv: build-release
|
||||
name: macos-intel
|
||||
- os: macos-14
|
||||
toxenv: build-release
|
||||
name: macos-apple-silicon
|
||||
- os: windows-latest
|
||||
- os: windows-2019
|
||||
toxenv: build-release
|
||||
name: windows
|
||||
- os: macos-15-intel
|
||||
- os: macos-12
|
||||
args: --debug
|
||||
toxenv: build-release
|
||||
name: macos-debug-intel
|
||||
- os: macos-14
|
||||
toxenv: build-release
|
||||
name: macos-debug-apple-silicon
|
||||
- os: windows-latest
|
||||
- os: windows-2019
|
||||
args: --debug
|
||||
toxenv: build-release
|
||||
name: windows-debug
|
||||
runs-on: "${{ matrix.os }}"
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- name: Install nsis
|
||||
if: "matrix.os == 'windows-latest'"
|
||||
run: |
|
||||
irm get.scoop.sh | iex
|
||||
scoop update
|
||||
scoop bucket add extras
|
||||
scoop install nsis
|
||||
Add-Content $env:GITHUB_PATH "C:\Users\runneradmin\scoop\shims"
|
||||
shell: pwsh
|
||||
python-version: "3.10"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
|
|
@ -72,7 +77,7 @@ jobs:
|
|||
echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||
shell: bash
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "qutebrowser-nightly-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.name }}"
|
||||
path: |
|
||||
|
|
@ -84,7 +89,7 @@ jobs:
|
|||
irc:
|
||||
timeout-minutes: 2
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [pyinstaller]
|
||||
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -18,20 +18,20 @@ jobs:
|
|||
timeout-minutes: 20
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v6
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.9'
|
||||
python-version: '3.8'
|
||||
- name: Recompile requirements
|
||||
run: "python3 scripts/dev/recompile_requirements.py ${{ github.event.input.environments }}"
|
||||
id: requirements
|
||||
- name: Install apt dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends libyaml-dev libegl1 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 asciidoc python3-venv xvfb
|
||||
sudo apt-get install --no-install-recommends libyaml-dev libegl1-mesa libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 asciidoc python3-venv xvfb
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
|
|
@ -41,7 +41,7 @@ jobs:
|
|||
- name: Run qutebrowser smoke test
|
||||
run: "xvfb-run .venv/bin/python3 -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ':later 500 quit'"
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
committer: qutebrowser bot <bot@qutebrowser.org>
|
||||
author: qutebrowser bot <bot@qutebrowser.org>
|
||||
|
|
|
|||
|
|
@ -12,33 +12,30 @@ on:
|
|||
- 'patch'
|
||||
- 'minor'
|
||||
- 'major'
|
||||
- 'reupload' # reupload last release
|
||||
# FIXME do we want a possibility to do prereleases here?
|
||||
python_version:
|
||||
description: 'Python version'
|
||||
required: true
|
||||
default: '3.14'
|
||||
default: '3.12'
|
||||
type: choice
|
||||
options:
|
||||
- '3.8'
|
||||
- '3.9'
|
||||
- '3.10'
|
||||
- '3.11'
|
||||
- '3.12'
|
||||
- '3.13'
|
||||
- '3.14'
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
version: ${{ steps.bump.outputs.version }}
|
||||
version_x: ${{ steps.bump.outputs.version_x }}
|
||||
release_id: ${{ inputs.release_type == 'reupload' && steps.find-release.outputs.result || steps.create-release.outputs.id }}
|
||||
release_id: ${{ steps.create-release.outputs.id }}
|
||||
permissions:
|
||||
contents: write # To push release commit/tag
|
||||
steps:
|
||||
- name: Find release branch
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@v7
|
||||
id: find-branch
|
||||
with:
|
||||
script: |
|
||||
|
|
@ -62,9 +59,9 @@ jobs:
|
|||
console.log(`sorted: ${sorted}`);
|
||||
return sorted.at(-1);
|
||||
result-encoding: string
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
# Doesn't really matter what we prepare the release with, but let's
|
||||
# use the same version for consistency.
|
||||
|
|
@ -78,7 +75,7 @@ jobs:
|
|||
git config --global user.name "qutebrowser bot"
|
||||
git config --global user.email "bot@qutebrowser.org"
|
||||
- name: Switch to release branch
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.find-branch.outputs.result }}
|
||||
- name: Import GPG Key
|
||||
|
|
@ -86,9 +83,9 @@ jobs:
|
|||
gpg --import <<< "${{ secrets.QUTEBROWSER_BOT_GPGKEY }}"
|
||||
- name: Bump version
|
||||
id: bump
|
||||
run: "tox -e update-version -- ${{ inputs.release_type }}"
|
||||
run: "tox -e update-version -- ${{ github.event.inputs.release_type }}"
|
||||
- name: Check milestone
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const milestones = await github.paginate(github.rest.issues.listMilestones, {
|
||||
|
|
@ -103,74 +100,49 @@ jobs:
|
|||
core.setFailed(`Found open milestone ${milestone.title} with ${milestone.open_issues} open and ${milestone.closed_issues} closed issues!`);
|
||||
}
|
||||
- name: Push release commit/tag
|
||||
if: ${{ inputs.release_type != 'reupload' }}
|
||||
run: |
|
||||
git push origin ${{ steps.find-branch.outputs.result }}
|
||||
git push origin v${{ steps.bump.outputs.version }}
|
||||
- name: Cherry-pick release commit
|
||||
if: ${{ inputs.release_type == 'patch' }}
|
||||
if: ${{ github.event.inputs.release_type == 'patch' }}
|
||||
run: |
|
||||
git fetch origin main
|
||||
git checkout main
|
||||
git cherry-pick -x v${{ steps.bump.outputs.version }}
|
||||
git push origin main
|
||||
git checkout v${{ steps.bump.outputs.version_x }}
|
||||
- name: Create release branch
|
||||
if: ${{ inputs.release_type == 'minor' || inputs.release_type == 'major' }}
|
||||
if: ${{ github.event.inputs.release_type != 'patch' }}
|
||||
run: |
|
||||
git checkout -b v${{ steps.bump.outputs.version_x }}
|
||||
git push --set-upstream origin v${{ steps.bump.outputs.version_x }}
|
||||
- name: Create GitHub draft release
|
||||
if: ${{ inputs.release_type != 'reupload' }}
|
||||
id: create-release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: v${{ steps.bump.outputs.version }}
|
||||
draft: true
|
||||
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:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-14-large # Intel
|
||||
- os: macos-14 # Apple Silicon
|
||||
- os: windows-2022
|
||||
- os: ubuntu-24.04
|
||||
- os: macos-12
|
||||
- os: macos-14
|
||||
- os: windows-2019
|
||||
- os: ubuntu-20.04
|
||||
runs-on: "${{ matrix.os }}"
|
||||
timeout-minutes: 45
|
||||
needs: [prepare]
|
||||
permissions:
|
||||
contents: write # To upload release artifacts
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: v${{ inputs.release_type == 'reupload' && needs.prepare.outputs.version_x || needs.prepare.outputs.version }}
|
||||
ref: v${{ needs.prepare.outputs.version }}
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
python-version: ${{ github.event.inputs.python_version }}
|
||||
- name: Import GPG Key
|
||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||
run: |
|
||||
|
|
@ -187,7 +159,7 @@ jobs:
|
|||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends libegl1 libxml2-utils docbook-xml xsltproc docbook-xsl
|
||||
sudo apt-get install --no-install-recommends libegl1-mesa libxml2-utils docbook-xml xsltproc docbook-xsl
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
|
|
@ -195,20 +167,20 @@ jobs:
|
|||
# FIXME consider switching to trusted publishers:
|
||||
# https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
|
||||
- name: Build and upload release
|
||||
run: "tox -e build-release -- --upload --no-confirm ${{ inputs.release_type == 'reupload' && '--reupload' || '' }}"
|
||||
run: "tox -e build-release -- --upload --no-confirm"
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.QUTEBROWSER_BOT_PYPI_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
finalize:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
timeout-minutes: 5
|
||||
needs: [prepare, release]
|
||||
permissions:
|
||||
contents: write # To change release
|
||||
steps:
|
||||
- name: Publish final release
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.repos.updateRelease({
|
||||
|
|
@ -221,7 +193,7 @@ jobs:
|
|||
irc:
|
||||
timeout-minutes: 2
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [prepare, release, finalize]
|
||||
if: "${{ always() }}"
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[mypy]
|
||||
python_version = 3.9
|
||||
python_version = 3.8
|
||||
|
||||
### --strict
|
||||
warn_unused_configs = True
|
||||
|
|
@ -20,7 +20,6 @@ strict_equality = True
|
|||
warn_unreachable = True
|
||||
disallow_any_unimported = True
|
||||
enable_error_code = ignore-without-code
|
||||
strict_bytes = True
|
||||
|
||||
### Output
|
||||
show_error_context = True
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ load-plugins=qute_pylint.config,
|
|||
pylint.extensions.dunder
|
||||
|
||||
persistent=n
|
||||
py-version=3.9
|
||||
py-version=3.8
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
enable=all
|
||||
|
|
@ -71,8 +71,7 @@ argument-rgx=[a-z_][a-z0-9_]{0,30}$
|
|||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
docstring-min-length=3
|
||||
no-docstring-rgx=(^_|^main$)
|
||||
class-const-naming-style=snake_case
|
||||
max-positional-arguments=7
|
||||
class-const-naming-style = snake_case
|
||||
|
||||
[FORMAT]
|
||||
# FIXME:v4 (lint) down to 88 again once we use black
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ image:doc/img/hints.png["screenshot 4",width=300,link="doc/img/hints.png"]
|
|||
Downloads
|
||||
---------
|
||||
|
||||
See the https://github.com/qutebrowser/qutebrowser/releases[GitHub releases
|
||||
See the https://github.com/qutebrowser/qutebrowser/releases[github releases
|
||||
page] for available downloads and the link:doc/install.asciidoc[INSTALL] file for
|
||||
detailed instructions on how to get qutebrowser running on various platforms.
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ Requirements
|
|||
|
||||
The following software and libraries are required to run qutebrowser:
|
||||
|
||||
* https://www.python.org/[Python] 3.9 or newer
|
||||
* https://www.python.org/[Python] 3.8 or newer
|
||||
* https://www.qt.io/[Qt], either 6.2.0 or newer, or 5.15.0 or newer, with the following modules:
|
||||
- QtCore / qtbase
|
||||
- QtQuick (part of qtbase or qtdeclarative in some distributions)
|
||||
|
|
@ -105,6 +105,10 @@ websites and using it for transmission of sensitive data._
|
|||
* https://palletsprojects.com/p/jinja/[jinja2]
|
||||
* https://github.com/yaml/pyyaml[PyYAML]
|
||||
|
||||
On Python 3.8, the following backport is also required:
|
||||
|
||||
* https://importlib-resources.readthedocs.io/[importlib_resources]
|
||||
|
||||
On macOS, the following libraries are also required:
|
||||
|
||||
* https://pyobjc.readthedocs.io/en/latest/[pyobjc-core and pyobjc-framework-Cocoa]
|
||||
|
|
|
|||
|
|
@ -15,279 +15,6 @@ breaking changes (such as renamed commands) can happen in minor releases.
|
|||
// `Fixed` for any bug fixes.
|
||||
// `Security` to invite users to upgrade in case of vulnerabilities.
|
||||
|
||||
[[v3.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 (2025-10-24)
|
||||
-------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- The `:version` info now shows additional information:
|
||||
* The X11 window manager / Wayland compositor name (mostly useful for
|
||||
bug/crash reports).
|
||||
* Loaded WebExtensions (partial support landed in QtWebEngine 6.10, no
|
||||
official qutebrowser support yet).
|
||||
- Support for hinting elements which are part of an (open) shadow DOM.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- The `qutedmenu` userscript now sorts history by the last access time.
|
||||
- Hardware accelerated 2D canvas is now enabled by default on Qt 6.8.2+,
|
||||
as graphic glitches with e.g. PDF.js and Google Sheets should be fixed
|
||||
nowadays. If you still run into issues, please report them and set
|
||||
`qt.workarounds.disable_accelerated_2d_canvas` to `always` to disable it
|
||||
again.
|
||||
- Changes to binary releases:
|
||||
* Windows and macOS releases are now built with Qt 6.10.0, which is based
|
||||
on Chromium 134.0.6998.208 with security patches up to 140.0.7339.207.
|
||||
* Windows and macOS releases are now built with Python 3.14.
|
||||
* Windows releases are now built on Windows Server 2022 (previously 2019),
|
||||
which might break compatibility with older Windows releases (untested).
|
||||
* If using `mkvenv.py` on Linux, note that Qt now requires glibc v2.34 (v2.28
|
||||
previously). This is available down to Ubuntu 22.04 LTS and Debian Bookworm
|
||||
(oldstable), so this should not affect most users of desktop distributions.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed crash if two new downloads start while a download prompt is already open
|
||||
(#8674).
|
||||
- Fixed exception when closing a qutebrowser window while a download prompt is
|
||||
still open.
|
||||
- Hopefully proper fix for some web pages jumping to the top when the statusbar
|
||||
is hidden (#8223).
|
||||
- Fix for the page header being shown on YouTube after the fullscreen
|
||||
notification was hidden (#8625).
|
||||
- Fix for videos losing keyboard focus when the fullscreen notification shows
|
||||
(#8174).
|
||||
- The workaround for microphone/camera permissions not being requested with
|
||||
QtWebEngine 6.9 on Google Meet, Zoom, or other pages using the new
|
||||
`<permission>` element now got extended to Qt 6.9.1+ as it's still not fixed
|
||||
upstream. (#8612)
|
||||
- The package version for Jinja 3.3+ is now correctly displayed in `:version`.
|
||||
- Fixed crash with Qt 6.10 (and possibly older Qt versions) when navigating
|
||||
from a `qute://` page to a web page, e.g. when searching on `qute://start`.
|
||||
- On Wayland with Qt <= 6.9, `EGL_PLATFORM=wayland` is now set by qutebrowser to
|
||||
get hardware rendering. Qt 6.10 includes an equivalent fix (#8637).
|
||||
- Added workaround for per-domain User-Agent header not being used on redirects
|
||||
(#8679).
|
||||
- Added site-specific quirk for gitlab.gnome.org agressively blocking old
|
||||
Chromium versions (and thus QtWebEngine) (#8509).
|
||||
- Using `:config-list-remove` with an invalid value for the respective option
|
||||
type now corrently displays an error instead of crashing.
|
||||
|
||||
[[v3.5.1]]
|
||||
v3.5.1 (2025-06-05)
|
||||
-------------------
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- QtWebKit (legacy) support got removed from CI and is now untested. If it
|
||||
breaks, it's not going to be fixed, and support will be removed over the next
|
||||
releases.
|
||||
- Qt 5 support is currently still tested, but is also planned to get removed
|
||||
over the next releases. Same goes for support for older Qt 6 versions (likely
|
||||
6.2/6.3/6.4 and perhaps 6.5, see https://github.com/qutebrowser/qutebrowser/issues/8464[#8464]).
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Windows/macOS releases now bundle Qt 6.9.1, including many graphics-related
|
||||
bugfixes, as well as security patches up to Chromium 136.0.7103.114.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- A bogus "wildcard call disconnects from destroyed signal" warning from Qt is
|
||||
now suppressed.
|
||||
- PDF.js now loads correctly on Windows installations with broken mimetype
|
||||
configurations.
|
||||
- A "Ignoring new child ..." debug log message which got spammy with Qt 6.9 is
|
||||
now removed.
|
||||
- A unknown crash (possibly related to using devtools) due to weird (Py)Qt
|
||||
behavior now has a workaround.
|
||||
- No "QtWebEngine version mismatch" warning is now logged anymore with newer Qt
|
||||
5.15 releases (but you should still stop using Qt 5).
|
||||
- The PDF.js version can now correctly be extracted/displayed with newer PDF.js
|
||||
versions.
|
||||
- The `qute-bitwarden`, `-lastpass` and `-pass` userscripts now properly avoid
|
||||
a `DeprecationWarning` from the upcoming 6.0 release of `tldextract`. The
|
||||
previous fix in v3.5.1 was insufficient.
|
||||
|
||||
[[v3.5.0]]
|
||||
v3.5.0 (2025-04-12)
|
||||
-------------------
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Windows/macOS releases are now built with Qt 6.9.0
|
||||
* Based on Chromium 130.0.6723.192
|
||||
* Security fixes up to Chromium 133.0.6943.141
|
||||
* Also fixes issues with opening links on macOS
|
||||
- The `content.headers.user_agent` setting now has a new
|
||||
`{upstream_browser_version_short}` template field, which is the
|
||||
upstream/Chromium version but shortened to only major version.
|
||||
- The default user agent now uses the shortened Chromium version and doesn't
|
||||
expose the `QtWebEngine/...` part anymore, thus making it equal to the
|
||||
corresponding Chromium user agent. This increases compatibilty due to various
|
||||
overzealous "security" products used by a variety of websites that block
|
||||
QtWebEngine, presumably as a bot (known issues existed with Whatsapp Web, UPS,
|
||||
Digitec Galaxus).
|
||||
- Changed features in userscripts:
|
||||
* `qute-bitwarden` now passes your password to the subprocess in an
|
||||
environment variable when unlocking your vault, instead of as a command
|
||||
line argument. (#7781)
|
||||
- New `-D no-system-pdfjs` debug flag to ignore system-wide PDF.js installations
|
||||
for testing.
|
||||
- Polyfill for missing `URL.parse` with PDF.js v5 and QtWebEngine < 6.9. Note
|
||||
this is a "best effort" fix and you should be using the "older browsers"
|
||||
("legacy") build of PDF.js instead.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- The `ua-slack` site-specific quirk, as things seem to work better nowadays
|
||||
without a quirk needed.
|
||||
- The `ua-whatsapp` site-specific quirk, as it's unneeded with the default UA
|
||||
change described above.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Crash when trying to use the `DocumentPictureInPicture` JS API, such as done
|
||||
by the new Google Workspaces Huddle feature. The API is unsupported by
|
||||
QtWebEngine and now correctly disabled on the JS side. (#8449)
|
||||
- Crash when a buggy notification presenter returns a duplicate ID (now an
|
||||
error is shown instead).
|
||||
- Crashes when running `:tab-move` or `:yank title` at startup, before a tab is
|
||||
available.
|
||||
- Crash with `input.insert_mode.auto_load`, when closing a new tab quickly after
|
||||
opening it, but before it was fully loaded. (#3895, #8400)
|
||||
- Workaround for microphone/camera permissions not being requested with
|
||||
QtWebEngine 6.9.0 on Google Meet, Zoom, or other pages using the new
|
||||
`<permission>` element. (#8539)
|
||||
- Resolved issues in userscripts:
|
||||
* `qute-bitwarden` will now prompt a re-login if its cached session has
|
||||
been invalidated since last used. (#8456)
|
||||
* `qute-bitwarden`, `-lastpass` and `-pass` now avoid a
|
||||
`DeprecationWarning` from the upcoming 6.0 release of `tldextract`
|
||||
|
||||
[[v3.4.0]]
|
||||
v3.4.0 (2024-12-14)
|
||||
-------------------
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- Support for Python 3.8 is dropped, and Python 3.9 is now required. (#8325)
|
||||
- Support for macOS 12 Monterey is now dropped, and binaries will be built on
|
||||
macOS 13 Ventura. (#8327)
|
||||
- When using the installer on Windows 10, build 1809 or newer is now required
|
||||
(previous versions required 1607 or newer, but that's not officialy supported by
|
||||
Qt upstream). (#8336)
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Windows/macOS binaries are now built with Qt 6.8.1. (#8242)
|
||||
- Based on Chromium 122.0.6261.171
|
||||
- With security patches up to 131.0.6778.70
|
||||
- Windows/macOS binaries are now using Python 3.13. (#8205)
|
||||
- The `.desktop` file now also declares qutebrowser as a valid viewer for
|
||||
`image/webp`. (#8340)
|
||||
- Updated mimetype information for getting a suitable extension when downloading
|
||||
a `data:` URL.
|
||||
- The `content.javascript.clipboard` setting now defaults to "ask", which on
|
||||
Qt 6.8+ will prompt the user to grant clipboard access. On older Qt versions,
|
||||
this is still equivalent to `"none"` and needs to be set manually. (#8348)
|
||||
- If a XHR request made via JS sets a custom `Accept-Language` header, it now
|
||||
correctly has precedence over the global `content.headers.accept_language`
|
||||
setting (but not per-domain overrides). This fixes subtle JS issues on
|
||||
websites that rely on the custom header being sent for those requests, and
|
||||
e.g. block the requests server-side otherwise. (#8370)
|
||||
- Our packaging scripts now prefer the "legacy"/"for older browsers" PDF.js
|
||||
build as their normal release only supports the latest Chromium version and
|
||||
might break in qutebrowser on updates. **Note to packagers:** If there's a
|
||||
PDF.js package in your distribution as an (optional) qutebrowser dependency,
|
||||
consider also switching to this variant (same code, built differently).
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Crash with recent Jinja/Markupsafe versions when viewing a finished userscript
|
||||
(or potentially editor) process via `:process`.
|
||||
- `scripts/open_url_in_instance.sh` now avoids `echo -n`, thus running
|
||||
correctly on POSIX sh. (#8409)
|
||||
- Added a workaround for a bogus QtWebEngine warning about missing spell
|
||||
checking dictionaries. (#8330)
|
||||
|
||||
|
||||
[[v3.3.1]]
|
||||
v3.3.1 (2024-10-12)
|
||||
-------------------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Updated the workaround for Google sign-in issues.
|
||||
|
||||
[[v3.3.0]]
|
||||
v3.3.0 (2024-10-12)
|
||||
-------------------
|
||||
|
|
@ -297,16 +24,16 @@ Added
|
|||
|
||||
- Added the `qt.workarounds.disable_hangouts_extension` setting,
|
||||
for disabling the Google Hangouts extension built into Chromium/QtWebEngine.
|
||||
- Failed end2end tests will now save screenshots of the browser window when
|
||||
run under xvfb (the default on linux). Screenshots will be under
|
||||
`$TEMP/pytest-current/pytest-screenshots/` or attached to the GitHub actions
|
||||
run as an artifact. (#7625)
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- Support for macOS 11 Big Sur is dropped. Binaries are now built on macOS 12
|
||||
Monterey and are unlikely to still run on older macOS versions.
|
||||
- Failed end2end tests will now save screenshots of the browser window when
|
||||
run under xvfb (the default on linux). Screenshots will be under
|
||||
`$TEMP/pytest-current/pytest-screenshots/` or attached to the GitHub actions
|
||||
run as an artifact. (#7625)
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
|
@ -318,8 +45,6 @@ Changed
|
|||
respected when yanking any URL (for example, through hints with `hint links
|
||||
yank`). The `{url:yank}` substitution has also been added as a version of
|
||||
`{url}` that respects ignored URL query parameters. (#7879)
|
||||
- Windows and macOS releases now bundle Qt 6.7.3, which includes security fixes
|
||||
up to Chromium 129.0.6668.58.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
|
@ -330,7 +55,6 @@ Fixed
|
|||
documentation in our settings docs now loads a live page again. (#8268)
|
||||
- A rare crash when on Qt 6, a renderer process terminates with an unknown
|
||||
termination reason.
|
||||
- Updated the workaround for Google sign-in issues.
|
||||
|
||||
[[v3.2.1]]
|
||||
v3.2.1 (2024-06-25)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ If you want to find something useful to do, check the
|
|||
https://github.com/qutebrowser/qutebrowser/issues[issue tracker]. Some
|
||||
pointers:
|
||||
|
||||
* https://github.com/qutebrowser/qutebrowser/contribute[Issues which should
|
||||
* https://github.com/qutebrowser/qutebrowser/labels/easy[Issues which should
|
||||
be easy to solve]
|
||||
* https://github.com/qutebrowser/qutebrowser/labels/component%3A%20docs[Documentation issues which require little/no coding]
|
||||
|
||||
|
|
@ -111,9 +111,9 @@ unittests and several linters/checkers.
|
|||
Currently, the following tox environments are available:
|
||||
|
||||
* Tests using https://www.pytest.org[pytest]:
|
||||
- `py39`, `py310`, ...: Run pytest for python 3.9/3.10/... with the system-wide PyQt.
|
||||
- `py39-pyqt515`, ..., `py39-pyqt65`: Run pytest with the given PyQt version (`py310-*` etc. also works).
|
||||
- `py39-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
|
||||
- `py38`, `py39`, ...: Run pytest for python 3.8/3.9/... with the system-wide PyQt.
|
||||
- `py38-pyqt515`, ..., `py38-pyqt65`: Run pytest with the given PyQt version (`py39-*` etc. also works).
|
||||
- `py38-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
|
||||
* `flake8`: Run various linting checks via https://pypi.python.org/pypi/flake8[flake8].
|
||||
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
|
||||
unused code portions.
|
||||
|
|
@ -121,6 +121,8 @@ Currently, the following tox environments are available:
|
|||
* `pyroma`: Check packaging practices with
|
||||
https://pypi.python.org/pypi/pyroma/[pyroma].
|
||||
* `eslint`: Run https://eslint.org/[ESLint] javascript checker.
|
||||
* `check-manifest`: Check MANIFEST.in completeness with
|
||||
https://github.com/mgedmin/check-manifest[check-manifest].
|
||||
* `mkvenv`: Bootstrap a virtualenv for testing.
|
||||
* `misc`: Run `scripts/misc_checks.py` to check for:
|
||||
- untracked git files
|
||||
|
|
@ -169,16 +171,16 @@ Examples:
|
|||
|
||||
----
|
||||
# run only pytest tests which failed in last run:
|
||||
tox -e py39 -- --lf
|
||||
tox -e py38 -- --lf
|
||||
|
||||
# run only the end2end feature tests:
|
||||
tox -e py39 -- tests/end2end/features
|
||||
tox -e py38 -- tests/end2end/features
|
||||
|
||||
# run everything with undo in the generated name, based on the scenario text
|
||||
tox -e py39 -- tests/end2end/features/test_tabs_bdd.py -k undo
|
||||
tox -e py38 -- tests/end2end/features/test_tabs_bdd.py -k undo
|
||||
|
||||
# run coverage test for specific file (updates htmlcov/index.html)
|
||||
tox -e py39-cov -- tests/unit/browser/test_webelem.py
|
||||
tox -e py38-cov -- tests/unit/browser/test_webelem.py
|
||||
----
|
||||
|
||||
Specifying the backend for tests
|
||||
|
|
@ -602,7 +604,6 @@ Info pages:
|
|||
- chrome://device-log/ (QtWebEngine >= 6.3)
|
||||
- chrome://gpu/
|
||||
- chrome://sandbox/ (Linux only)
|
||||
- chrome://qt/ (QtWebEngine >= 6.7)
|
||||
|
||||
Misc. / Debugging pages:
|
||||
|
||||
|
|
@ -613,7 +614,6 @@ Misc. / Debugging pages:
|
|||
- chrome://ukm/ (QtWebEngine >= 5.15.3)
|
||||
- chrome://user-actions/ (QtWebEngine >= 5.15.3)
|
||||
- chrome://webrtc-logs/ (QtWebEngine >= 5.15.3)
|
||||
- chrome://extensions/ (QtWebEngine >= 6.10)
|
||||
|
||||
Internals pages:
|
||||
|
||||
|
|
@ -789,12 +789,10 @@ New PyQt release
|
|||
qutebrowser release
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Make sure there are no unstaged or unpushed changes.
|
||||
* Make sure CI is reasonably green.
|
||||
* Make sure there are no unstaged changes and the tests are green.
|
||||
* Make sure all issues with the related milestone are closed.
|
||||
* Mark the https://github.com/qutebrowser/qutebrowser/milestones[milestone] as closed.
|
||||
* Consider updating the completions for `content.headers.user_agent` in `configdata.yml`
|
||||
and the Firefox UA in `qutebrowser/browser/webengine/webenginesettings.py`.
|
||||
* Consider updating the completions for `content.headers.user_agent` in `configdata.yml`.
|
||||
* Minor release: Consider updating some files from main:
|
||||
- `misc/requirements/` and `requirements.txt`
|
||||
- `scripts/`
|
||||
|
|
@ -804,8 +802,7 @@ qutebrowser release
|
|||
**Automatic release via GitHub Actions (starting with v3.0.0):**
|
||||
|
||||
* Double check Python version in `.github/workflows/release.yml`
|
||||
* Run the `release` workflow on the `main` branch, e.g. via `gh workflow run release -f release_type=minor` (`release_type` can be `major`, `minor` or `patch`; you can also override `python_version`)
|
||||
* Consider running `gh run watch` or `gh run view --web` to watch the progress
|
||||
* Run the `release` workflow on the `main` branch, e.g. via `gh workflow run release -f release_type=major` (`release_type` can be `major`, `minor` or `patch`; you can also override `python_version`)
|
||||
|
||||
**Manual release:**
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ Why Python?::
|
|||
point, I wasn't comfortable with C++ so that wasn't an alternative.
|
||||
|
||||
But isn't Python too slow for a browser?::
|
||||
https://www.infoworld.com/article/2303031/van-rossum-python-is-not-too-slow-2.html[It's generally less of a problem than one would expect.]
|
||||
https://www.infoworld.com/d/application-development/van-rossum-python-not-too-slow-188715[It's generally less of a problem than one would expect.]
|
||||
Most of the heavy lifting of qutebrowser is done by Qt and
|
||||
QtWebKit/QtWebEngine in C++, with the
|
||||
https://wiki.python.org/moin/GlobalInterpreterLock[GIL] released.
|
||||
|
|
@ -141,7 +141,7 @@ The comma prefix is used to make sure user-defined bindings don't conflict with
|
|||
the built-in ones.
|
||||
+
|
||||
Note that you might need an additional package (e.g.
|
||||
https://archlinux.org/packages/extra/any/yt-dlp/[yt-dlp] on
|
||||
https://www.archlinux.org/packages/community/any/youtube-dl/[youtube-dl] on
|
||||
Archlinux) to play web videos with mpv.
|
||||
+
|
||||
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]
|
||||
----
|
||||
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
|
||||
|
|
@ -187,7 +187,7 @@ similar to `c.` which is scoped to the given domain:
|
|||
|
||||
[source,python]
|
||||
----
|
||||
with config.pattern('*://example.com/*') as p:
|
||||
with config.pattern('*://example.com/') as p:
|
||||
p.content.images = False
|
||||
----
|
||||
|
||||
|
|
@ -416,8 +416,6 @@ Pre-built colorschemes
|
|||
- https://github.com/gicrisf/qute-city-lights[City Lights (matte dark)]
|
||||
- https://github.com/catppuccin/qutebrowser[Catppuccin]
|
||||
- https://github.com/iruzo/matrix-qutebrowser[Matrix]
|
||||
- https://github.com/harmtemolder/qutebrowser-solarized[Solarized]
|
||||
- https://github.com/Rehpotsirhc-z/qutebrowser-doom-one[Doom One]
|
||||
|
||||
Avoiding flake8 errors
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -454,7 +452,7 @@ Various emacs/conkeror-like keybinding configs exist:
|
|||
- https://gitlab.com/Kaligule/qutebrowser-emacs-config/blob/master/config.py[Kaligule]
|
||||
- https://web.archive.org/web/20210512185023/https://me0w.net/pit/1540882719[nm0i]
|
||||
- https://www.reddit.com/r/qutebrowser/comments/eh10i7/config_share_qute_with_emacs_keybindings/[jasonsun0310]
|
||||
- https://git.sr.ht/~willvaughn/dots/tree/main/item/.config/qutebrowser/qutemacs.py[willvaughn]
|
||||
- https://git.sr.ht/~willvaughn/dots/tree/mjolnir/item/.config/qutebrowser/qutemacs.py[willvaughn]
|
||||
|
||||
It's also mostly possible to get rid of modal keybindings by setting
|
||||
`input.insert_mode.auto_enter` to `false`, and `input.forward_unbound_keys` to
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Getting help
|
|||
You can get help in the IRC channel
|
||||
link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
|
||||
https://libera.chat/[Libera Chat]
|
||||
(https://web.libera.chat/#qutebrowser[webchat]),
|
||||
(https://web.libera.chat/#qutebrowser[webchat], https://matrix.to/#qutebrowser:libera.chat[via Matrix]),
|
||||
or by writing a message to the
|
||||
https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
|
||||
mailto:qutebrowser@lists.qutebrowser.org[].
|
||||
|
|
|
|||
|
|
@ -302,7 +302,6 @@
|
|||
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|
||||
|<<qt.highdpi,qt.highdpi>>|Turn on Qt HighDPI scaling.
|
||||
|<<qt.workarounds.disable_accelerated_2d_canvas,qt.workarounds.disable_accelerated_2d_canvas>>|Disable accelerated 2d canvas to avoid graphical glitches.
|
||||
|<<qt.workarounds.disable_accessibility,qt.workarounds.disable_accessibility>>|Disable accessibility to avoid crashes on Qt 6.10.1.
|
||||
|<<qt.workarounds.disable_hangouts_extension,qt.workarounds.disable_hangouts_extension>>|Disable the Hangouts extension.
|
||||
|<<qt.workarounds.locale,qt.workarounds.locale>>|Work around locale parsing issues in QtWebEngine 5.15.3.
|
||||
|<<qt.workarounds.remove_service_workers,qt.workarounds.remove_service_workers>>|Delete the QtWebEngine Service Worker directory on every start.
|
||||
|
|
@ -2276,22 +2275,21 @@ The following placeholders are defined:
|
|||
* `{upstream_browser_key}`: "Version" for QtWebKit, "Chrome" for
|
||||
QtWebEngine.
|
||||
* `{upstream_browser_version}`: The corresponding Safari/Chrome version.
|
||||
* `{upstream_browser_version_short}`: The corresponding Safari/Chrome
|
||||
version, but only with its major version.
|
||||
* `{qutebrowser_version}`: The currently running qutebrowser version.
|
||||
|
||||
The default value is equal to the default user agent of
|
||||
QtWebKit/QtWebEngine, but with the `QtWebEngine/...` part removed for
|
||||
increased compatibility.
|
||||
The default value is equal to the unchanged user agent of
|
||||
QtWebKit/QtWebEngine.
|
||||
|
||||
Note that the value read from JavaScript is always the global value.
|
||||
Note that the value read from JavaScript is always the global value. With
|
||||
QtWebEngine between 5.12 and 5.14 (inclusive), changing the value exposed
|
||||
to JavaScript requires a restart.
|
||||
|
||||
|
||||
This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
|
||||
|
||||
Type: <<types,FormatString>>
|
||||
|
||||
Default: +pass:[Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, like Gecko) {upstream_browser_key}/{upstream_browser_version_short} Safari/{webkit_version}]+
|
||||
Default: +pass:[Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, like Gecko) {qt_key}/{qt_version} {upstream_browser_key}/{upstream_browser_version} Safari/{webkit_version}]+
|
||||
|
||||
[[content.hyperlink_auditing]]
|
||||
=== content.hyperlink_auditing
|
||||
|
|
@ -2347,20 +2345,18 @@ Default: +pass:[false]+
|
|||
=== content.javascript.clipboard
|
||||
Allow JavaScript to read from or write to the clipboard.
|
||||
With QtWebEngine, writing the clipboard as response to a user interaction is always allowed.
|
||||
On Qt < 6.8, the `ask` setting is equivalent to `none` and permission needs to be granted manually via this setting.
|
||||
|
||||
This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
|
||||
|
||||
Type: <<types,JSClipboardPermission>>
|
||||
Type: <<types,String>>
|
||||
|
||||
Valid values:
|
||||
|
||||
* +none+: Disable access to clipboard.
|
||||
* +access+: Allow reading from and writing to the clipboard.
|
||||
* +access-paste+: Allow accessing the clipboard and pasting clipboard content.
|
||||
* +ask+: Prompt when requested (grants 'access-paste' permission).
|
||||
|
||||
Default: +pass:[ask]+
|
||||
Default: +pass:[none]+
|
||||
|
||||
[[content.javascript.enabled]]
|
||||
=== content.javascript.enabled
|
||||
|
|
@ -2769,9 +2765,10 @@ Type: <<types,FlagList>>
|
|||
|
||||
Valid values:
|
||||
|
||||
* +ua-whatsapp+
|
||||
* +ua-google+
|
||||
* +ua-slack+
|
||||
* +ua-googledocs+
|
||||
* +ua-gnome-gitlab+
|
||||
* +js-whatsapp-web+
|
||||
* +js-discord+
|
||||
* +js-string-replaceall+
|
||||
|
|
@ -3894,7 +3891,7 @@ Chromium has various sandboxing layers, which should be enabled for normal brows
|
|||
Open `chrome://sandbox` to see the current sandbox status.
|
||||
Changing this setting is only recommended if you know what you're doing, as it **disables one of Chromium's security layers**. To avoid sandboxing being accidentally disabled persistently, this setting can only be set via `config.py`, not via `:set`.
|
||||
See the Chromium documentation for more details:
|
||||
- https://chromium.googlesource.com/chromium/src/\+/HEAD/sandbox/linux/README.md[Linux] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] - https://chromium.googlesource.com/chromium/src/\+/HEAD/sandbox/mac/README.md[Mac] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
|
||||
- https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
|
||||
|
||||
This setting requires a restart.
|
||||
|
||||
|
|
@ -3992,29 +3989,11 @@ Type: <<types,String>>
|
|||
Valid values:
|
||||
|
||||
* +always+: Disable accelerated 2d canvas
|
||||
* +auto+: Disable on Qt versions with known issues, enable otherwise
|
||||
* +auto+: Disable on Qt6 < 6.6.0, enable otherwise
|
||||
* +never+: Enable accelerated 2d canvas
|
||||
|
||||
Default: +pass:[auto]+
|
||||
|
||||
[[qt.workarounds.disable_accessibility]]
|
||||
=== qt.workarounds.disable_accessibility
|
||||
Disable accessibility to avoid crashes on Qt 6.10.1.
|
||||
|
||||
This setting requires a restart.
|
||||
|
||||
This setting is only available with the QtWebEngine backend.
|
||||
|
||||
Type: <<types,String>>
|
||||
|
||||
Valid values:
|
||||
|
||||
* +always+: Disable renderer accessibility
|
||||
* +auto+: Disable on Qt versions with known issues, enable otherwise
|
||||
* +never+: Enable renderer accessibility
|
||||
|
||||
Default: +pass:[auto]+
|
||||
|
||||
[[qt.workarounds.disable_hangouts_extension]]
|
||||
=== qt.workarounds.disable_hangouts_extension
|
||||
Disable the Hangouts extension.
|
||||
|
|
@ -4865,7 +4844,6 @@ Lists with duplicate flags are invalid. Each item is checked against the valid v
|
|||
|FuzzyUrl|A URL which gets interpreted as search if needed.
|
||||
|IgnoreCase|Whether to search case insensitively.
|
||||
|Int|Base class for an integer setting.
|
||||
|JSClipboardPermission|Permission for page JS to access the system clipboard.
|
||||
|Key|A name of a key.
|
||||
|List|A list of values.
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ newer qutebrowser running with:
|
|||
Note you'll need some basic libraries to use the virtualenv-installed PyQt:
|
||||
|
||||
----
|
||||
# apt install --no-install-recommends git ca-certificates python3 python3-venv libgl1 libxkbcommon-x11-0 libegl1 libfontconfig1 libglib2.0-0 libdbus-1-3 libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-shape0 libnss3 libxcomposite1 libxdamage1 libxrender1 libxrandr2 libxtst6 libxi6 libasound2
|
||||
# apt install --no-install-recommends git ca-certificates python3 python3-venv libgl1 libxkbcommon-x11-0 libegl1-mesa libfontconfig1 libglib2.0-0 libdbus-1-3 libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-shape0 libnss3 libxcomposite1 libxdamage1 libxrender1 libxrandr2 libxtst6 libxi6 libasound2
|
||||
----
|
||||
|
||||
Additional hints
|
||||
|
|
@ -103,18 +103,12 @@ To be able to play videos with proprietary codecs with QtWebEngine, you will
|
|||
need to install an additional package from the RPM Fusion Free repository.
|
||||
For more information see https://rpmfusion.org/Configuration.
|
||||
|
||||
With Qt 6 (recommended):
|
||||
|
||||
-----
|
||||
# dnf install libavcodec-freeworld
|
||||
-----
|
||||
|
||||
With Qt 5:
|
||||
|
||||
-----
|
||||
# dnf install qt5-qtwebengine-freeworld
|
||||
-----
|
||||
|
||||
It's currently unknown what the Qt 6 equivalent of this is.
|
||||
|
||||
On Archlinux
|
||||
------------
|
||||
|
||||
|
|
@ -444,7 +438,7 @@ This installs all needed Python dependencies in a `.venv` subfolder
|
|||
This comes with an up-to-date Qt/PyQt including a pre-compiled QtWebEngine
|
||||
binary, but has a few caveats:
|
||||
|
||||
- Make sure your `python3` is Python 3.9 or newer, otherwise you'll get a "No
|
||||
- Make sure your `python3` is Python 3.8 or newer, otherwise you'll get a "No
|
||||
matching distribution found" error and/or qutebrowser will not run.
|
||||
- It only works on 64-bit x86 systems, with other architectures you'll get the
|
||||
same error.
|
||||
|
|
|
|||
|
|
@ -432,18 +432,32 @@ Function .onInit
|
|||
StrCpy $KeepReg 1
|
||||
|
||||
; OS version check
|
||||
; https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa#remarks
|
||||
; https://learn.microsoft.com/en-us/windows/release-health/release-information
|
||||
; https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information
|
||||
${If} ${AtLeastWin11}
|
||||
Goto _os_check_pass
|
||||
${ElseIf} ${IsNativeAMD64} ; Windows 10 has no x86_64 emulation on arm64
|
||||
${AndIf} ${AtLeastWin10}
|
||||
${AndIf} ${AtLeastBuild} 17763 ; Windows 10 1809 (also in error message below)
|
||||
Goto _os_check_pass
|
||||
${If} ${RunningX64}
|
||||
; https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa#remarks
|
||||
GetWinVer $R0 Major
|
||||
!if "${QT5}" == "True"
|
||||
IntCmpU $R0 6 0 _os_check_fail _os_check_pass
|
||||
GetWinVer $R1 Minor
|
||||
IntCmpU $R1 2 _os_check_pass _os_check_fail _os_check_pass
|
||||
!else
|
||||
IntCmpU $R0 10 0 _os_check_fail _os_check_pass
|
||||
GetWinVer $R1 Build
|
||||
${If} $R1 >= 22000 ; Windows 11 21H2
|
||||
Goto _os_check_pass
|
||||
${ElseIf} $R1 >= 14393 ; Windows 10 1607
|
||||
${AndIf} ${IsNativeAMD64} ; Windows 10 has no x86_64 emulation on arm64
|
||||
Goto _os_check_pass
|
||||
${EndIf}
|
||||
!endif
|
||||
${EndIf}
|
||||
MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\
|
||||
version of Windows 10 1809 or later."
|
||||
_os_check_fail:
|
||||
!if "${QT5}" == "True"
|
||||
MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\
|
||||
version of Windows 8 or later."
|
||||
!else
|
||||
MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\
|
||||
version of Windows 10 1607 or later."
|
||||
!endif
|
||||
Abort
|
||||
_os_check_pass:
|
||||
|
||||
|
|
|
|||
|
|
@ -131,6 +131,9 @@ ShowUninstDetails hide
|
|||
!define /ifndef DIST_DIR ".\..\..\dist\${PRODUCT_NAME}-${VERSION}"
|
||||
!endif
|
||||
|
||||
; If not defined, assume Qt6 (requires a more recent windows version)
|
||||
!define /ifndef QT5 "False"
|
||||
|
||||
; Pack the exe header with upx if UPX is defined.
|
||||
!ifdef UPX
|
||||
!packhdr "$%TEMP%\exehead.tmp" '"upx" "--ultra-brute" "$%TEMP%\exehead.tmp"'
|
||||
|
|
|
|||
|
|
@ -44,14 +44,6 @@
|
|||
</content_rating>
|
||||
<releases>
|
||||
<!-- Add new releases here -->
|
||||
<release version='3.6.3' date='2025-11-30'/>
|
||||
<release version='3.6.2' date='2025-11-27'/>
|
||||
<release version='3.6.1' date='2025-11-03'/>
|
||||
<release version='3.6.0' date='2025-10-24'/>
|
||||
<release version='3.5.1' date='2025-06-05'/>
|
||||
<release version='3.5.0' date='2025-04-12'/>
|
||||
<release version="3.4.0" date="2024-12-14"/>
|
||||
<release version="3.3.1" date="2024-10-12"/>
|
||||
<release version="3.3.0" date="2024-10-12"/>
|
||||
<release version="3.2.1" date="2024-06-25"/>
|
||||
<release version="3.2.0" date="2024-06-03"/>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ Categories=Network;WebBrowser;
|
|||
Exec=qutebrowser --untrusted-args %u
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/webp;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute;
|
||||
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute;
|
||||
Keywords=Browser
|
||||
Actions=new-window;preferences;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,66 +25,24 @@ INFO_PLIST_UPDATES = {
|
|||
"CFBundleURLName": "local file URL",
|
||||
"CFBundleURLSchemes": ["file"]
|
||||
}],
|
||||
'CFBundleDocumentTypes': [
|
||||
{
|
||||
"CFBundleTypeIconFile": "document.icns",
|
||||
"CFBundleTypeName": name,
|
||||
"CFBundleTypeRole": "Viewer",
|
||||
"LSItemContentTypes": [content_type],
|
||||
}
|
||||
for name, content_type in [
|
||||
("GIF image", "com.compuserve.gif"),
|
||||
("HTML document", "public.html"),
|
||||
("XHTML document", "public.xhtml"),
|
||||
("JavaScript script", "com.netscape.javascript-source"),
|
||||
("JPEG image", "public.jpeg"),
|
||||
("MHTML document", "org.ietf.mhtml"),
|
||||
("HTML5 Audio (Ogg)", "org.xiph.ogg-audio"),
|
||||
("HTML5 Video (Ogg)", "org.xiph.oggv"),
|
||||
("PNG image", "public.png"),
|
||||
("SVG document", "public.svg-image"),
|
||||
("Plain text document", "public.text"),
|
||||
("HTML5 Video (WebM)", "org.webmproject.webm"),
|
||||
("WebP image", "org.webmproject.webp"),
|
||||
("PDF Document", "com.adobe.pdf"),
|
||||
]
|
||||
],
|
||||
'UTImportedTypeDeclarations': [
|
||||
{
|
||||
"UTTypeConformsTo": ["public.data", "public.content"],
|
||||
"UTTypeDescription": "MIME HTML document",
|
||||
"UTTypeIconFile": "document.icns",
|
||||
"UTTypeIdentifier": "org.ietf.mhtml",
|
||||
"UTTypeReferenceURL": "https://www.ietf.org/rfc/rfc2557",
|
||||
"UTTypeTagSpecification": {
|
||||
"com.apple.ostype": "MHTM",
|
||||
"public.filename-extension": ["mht", "mhtml"],
|
||||
"public.mime-type": ["multipart/related", "application/x-mimearchive"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"UTTypeConformsTo": ["public.audio"],
|
||||
"UTTypeDescription": "Ogg Audio",
|
||||
"UTTypeIconFile": "document.icns",
|
||||
"UTTypeIdentifier": "org.xiph.ogg-audio",
|
||||
"UTTypeReferenceURL": "https://xiph.org/ogg/",
|
||||
"UTTypeTagSpecification": {
|
||||
"public.filename-extension": ["ogg", "oga"],
|
||||
"public.mime-type": ["audio/ogg"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"UTTypeConformsTo": ["public.movie"],
|
||||
"UTTypeDescription": "Ogg Video",
|
||||
"UTTypeIconFile": "document.icns",
|
||||
"UTTypeIdentifier": "org.xiph.ogv",
|
||||
"UTTypeReferenceURL": "https://xiph.org/ogg/",
|
||||
"UTTypeTagSpecification": {
|
||||
"public.filename-extension": ["ogm", "ogv"],
|
||||
"public.mime-type": ["video/ogg"],
|
||||
},
|
||||
},
|
||||
],
|
||||
'CFBundleDocumentTypes': [{
|
||||
"CFBundleTypeExtensions": ["html", "htm"],
|
||||
"CFBundleTypeMIMETypes": ["text/html"],
|
||||
"CFBundleTypeName": "HTML document",
|
||||
"CFBundleTypeOSTypes": ["HTML"],
|
||||
"CFBundleTypeRole": "Viewer",
|
||||
}, {
|
||||
"CFBundleTypeExtensions": ["xhtml"],
|
||||
"CFBundleTypeMIMETypes": ["text/xhtml"],
|
||||
"CFBundleTypeName": "XHTML document",
|
||||
"CFBundleTypeRole": "Viewer",
|
||||
}, {
|
||||
"CFBundleTypeExtensions": ["mhtml"],
|
||||
"CFBundleTypeMIMETypes": ["multipart/related", "application/x-mimearchive", "message/rfc822"],
|
||||
"CFBundleTypeName": "MHTML document",
|
||||
"CFBundleTypeRole": "Viewer",
|
||||
}],
|
||||
|
||||
# https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_macos
|
||||
#
|
||||
# Keys based on Google Chrome's .app, except Bluetooth keys which seem to
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
build==1.2.2
|
||||
check-manifest==0.49
|
||||
importlib_metadata==8.5.0
|
||||
packaging==24.1
|
||||
pyproject_hooks==1.2.0
|
||||
tomli==2.0.1
|
||||
zipp==3.20.2
|
||||
|
|
@ -0,0 +1 @@
|
|||
check-manifest
|
||||
|
|
@ -1,73 +1,54 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
annotated-types==0.7.0
|
||||
anyio==4.12.0
|
||||
autocommand==2.2.2
|
||||
backports.tarfile==1.2.0
|
||||
bracex==2.6
|
||||
build==1.3.0
|
||||
bump-my-version==1.2.5
|
||||
certifi==2025.11.12
|
||||
cffi==2.0.0
|
||||
charset-normalizer==3.4.4
|
||||
click==8.1.8
|
||||
cryptography==46.0.3
|
||||
docutils==0.22.3
|
||||
exceptiongroup==1.3.1
|
||||
build==1.2.2
|
||||
bump2version==1.0.1
|
||||
certifi==2024.8.30
|
||||
cffi==1.17.1
|
||||
charset-normalizer==3.3.2
|
||||
cryptography==43.0.1
|
||||
docutils==0.20.1
|
||||
github3.py==4.0.1
|
||||
h11==0.16.0
|
||||
httpcore==1.0.9
|
||||
httpx==0.28.1
|
||||
hunter==3.9.0
|
||||
id==1.5.0
|
||||
idna==3.11
|
||||
importlib_metadata==8.7.0
|
||||
importlib_resources==6.5.2
|
||||
hunter==3.7.0
|
||||
idna==3.10
|
||||
importlib_metadata==8.5.0
|
||||
importlib_resources==6.4.5
|
||||
inflect==7.3.1
|
||||
jaraco.classes==3.4.0
|
||||
jaraco.collections==5.1.0
|
||||
jaraco.context==6.0.1
|
||||
jaraco.functools==4.0.1
|
||||
jaraco.functools==4.1.0
|
||||
jaraco.text==3.12.1
|
||||
jeepney==0.9.0
|
||||
keyring==25.7.0
|
||||
jeepney==0.8.0
|
||||
keyring==25.4.1
|
||||
manhole==1.8.1
|
||||
markdown-it-py==3.0.0
|
||||
mdurl==0.1.2
|
||||
more-itertools==10.8.0
|
||||
nh3==0.3.2
|
||||
packaging==25.0
|
||||
platformdirs==4.4.0
|
||||
prompt_toolkit==3.0.52
|
||||
pycparser==2.23
|
||||
pydantic==2.12.5
|
||||
pydantic-settings==2.11.0
|
||||
pydantic_core==2.41.5
|
||||
Pygments==2.19.2
|
||||
PyJWT==2.10.1
|
||||
more-itertools==10.5.0
|
||||
nh3==0.2.18
|
||||
packaging==24.1
|
||||
pkginfo==1.10.0
|
||||
platformdirs==4.3.6
|
||||
pycparser==2.22
|
||||
Pygments==2.18.0
|
||||
PyJWT==2.9.0
|
||||
Pympler==1.1
|
||||
pyproject_hooks==1.2.0
|
||||
PyQt-builder==1.19.1
|
||||
PyQt-builder==1.16.4
|
||||
python-dateutil==2.9.0.post0
|
||||
python-dotenv==1.2.1
|
||||
questionary==2.1.1
|
||||
readme_renderer==44.0
|
||||
requests==2.32.5
|
||||
readme_renderer==43.0
|
||||
requests==2.32.3
|
||||
requests-toolbelt==1.0.0
|
||||
rfc3986==2.0.0
|
||||
rich==14.2.0
|
||||
rich-click==1.9.4
|
||||
rich==13.8.1
|
||||
SecretStorage==3.3.3
|
||||
sip==6.14.0
|
||||
six==1.17.0
|
||||
tomli==2.3.0
|
||||
tomlkit==0.13.3
|
||||
twine==6.2.0
|
||||
sip==6.8.6
|
||||
six==1.16.0
|
||||
tomli==2.0.1
|
||||
twine==5.1.1
|
||||
typeguard==4.3.0
|
||||
typing-inspection==0.4.2
|
||||
typing_extensions==4.15.0
|
||||
uritemplate==4.2.0
|
||||
# urllib3==2.6.2
|
||||
wcmatch==10.1
|
||||
wcwidth==0.2.14
|
||||
zipp==3.23.0
|
||||
typing_extensions==4.12.2
|
||||
uritemplate==4.1.1
|
||||
# urllib3==2.2.3
|
||||
zipp==3.20.2
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
hunter
|
||||
pympler
|
||||
github3.py
|
||||
bump-my-version
|
||||
bump2version
|
||||
requests
|
||||
pyqt-builder
|
||||
build
|
||||
|
|
@ -9,7 +9,6 @@ twine
|
|||
|
||||
# Included to override setuptools' vendored version that is being included in
|
||||
# the lock file by pip freeze.
|
||||
importlib_resources
|
||||
platformdirs
|
||||
|
||||
# Already included via test requirements
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
attrs==25.4.0
|
||||
flake8==7.3.0
|
||||
flake8-bugbear==24.12.12
|
||||
flake8-builtins==3.0.0
|
||||
flake8-comprehensions==3.17.0
|
||||
attrs==24.2.0
|
||||
flake8==7.1.1
|
||||
flake8-bugbear==24.8.19
|
||||
flake8-builtins==2.5.0
|
||||
flake8-comprehensions==3.15.0
|
||||
flake8-debugger==4.1.2
|
||||
flake8-deprecated==2.2.1
|
||||
flake8-docstrings==1.7.0
|
||||
flake8-future-import==0.4.7
|
||||
flake8-plugin-utils==1.3.3
|
||||
flake8-pytest-style==2.1.0
|
||||
flake8-pytest-style==2.0.0
|
||||
flake8-string-format==0.3.0
|
||||
flake8-tidy-imports==4.12.0
|
||||
flake8-tidy-imports==4.10.0
|
||||
flake8-tuple==0.4.1
|
||||
mccabe==0.7.0
|
||||
pep8-naming==0.15.1
|
||||
pycodestyle==2.14.0
|
||||
pep8-naming==0.14.1
|
||||
pycodestyle==2.12.1
|
||||
pydocstyle==6.3.0
|
||||
pyflakes==3.4.0
|
||||
six==1.17.0
|
||||
snowballstemmer==3.0.1
|
||||
pyflakes==3.2.0
|
||||
six==1.16.0
|
||||
snowballstemmer==2.2.0
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
chardet==5.2.0
|
||||
diff_cover==10.0.0
|
||||
Jinja2==3.1.6
|
||||
librt==0.7.3
|
||||
lxml==6.0.2
|
||||
MarkupSafe==3.0.3
|
||||
mypy==1.19.0
|
||||
mypy_extensions==1.1.0
|
||||
pathspec==0.12.1
|
||||
pluggy==1.6.0
|
||||
Pygments==2.19.2
|
||||
diff_cover==9.2.0
|
||||
importlib_resources==6.4.5
|
||||
Jinja2==3.1.4
|
||||
lxml==5.3.0
|
||||
MarkupSafe==2.1.5
|
||||
mypy==1.11.2
|
||||
mypy-extensions==1.0.0
|
||||
pluggy==1.5.0
|
||||
Pygments==2.18.0
|
||||
PyQt5-stubs==5.15.6.0
|
||||
tomli==2.3.0
|
||||
types-colorama==0.4.15.20250801
|
||||
types-docutils==0.22.3.20251115
|
||||
types-Pygments==2.19.0.20251121
|
||||
types-PyYAML==6.0.12.20250915
|
||||
typing_extensions==4.15.0
|
||||
tomli==2.0.1
|
||||
types-colorama==0.4.15.20240311
|
||||
types-docutils==0.21.0.20240907
|
||||
types-Pygments==2.18.0.20240506
|
||||
types-PyYAML==6.0.12.20240917
|
||||
types-setuptools==75.1.0.20240917
|
||||
typing_extensions==4.12.2
|
||||
zipp==3.20.2
|
||||
|
|
|
|||
|
|
@ -6,3 +6,6 @@ PyQt5-stubs
|
|||
types-PyYAML
|
||||
types-colorama
|
||||
types-Pygments
|
||||
|
||||
# So stubs are available even on newer Python versions
|
||||
importlib_resources
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
altgraph==0.17.5
|
||||
importlib_metadata==8.7.0
|
||||
packaging==25.0
|
||||
pyinstaller==6.17.0
|
||||
pyinstaller-hooks-contrib==2025.10
|
||||
zipp==3.23.0
|
||||
altgraph==0.17.4
|
||||
importlib_metadata==8.5.0
|
||||
packaging==24.1
|
||||
pyinstaller==6.10.0
|
||||
pyinstaller-hooks-contrib==2024.8
|
||||
zipp==3.20.2
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
astroid==3.3.11
|
||||
certifi==2025.11.12
|
||||
cffi==2.0.0
|
||||
charset-normalizer==3.4.4
|
||||
cryptography==46.0.3
|
||||
dill==0.4.0
|
||||
astroid==3.2.4
|
||||
certifi==2024.8.30
|
||||
cffi==1.17.1
|
||||
charset-normalizer==3.3.2
|
||||
cryptography==43.0.1
|
||||
dill==0.3.9
|
||||
github3.py==4.0.1
|
||||
idna==3.11
|
||||
importlib_metadata==8.7.0
|
||||
isort==6.1.0
|
||||
idna==3.10
|
||||
isort==5.13.2
|
||||
mccabe==0.7.0
|
||||
pefile==2024.8.26
|
||||
platformdirs==4.4.0
|
||||
pycparser==2.23
|
||||
PyJWT==2.10.1
|
||||
pylint==3.3.9
|
||||
platformdirs==4.3.6
|
||||
pycparser==2.22
|
||||
PyJWT==2.9.0
|
||||
pylint==3.2.7
|
||||
python-dateutil==2.9.0.post0
|
||||
./scripts/dev/pylint_checkers
|
||||
requests==2.32.5
|
||||
six==1.17.0
|
||||
tomli==2.3.0
|
||||
tomlkit==0.13.3
|
||||
typing_extensions==4.15.0
|
||||
uritemplate==4.2.0
|
||||
# urllib3==2.6.2
|
||||
zipp==3.23.0
|
||||
requests==2.32.3
|
||||
six==1.16.0
|
||||
tomli==2.0.1
|
||||
tomlkit==0.13.2
|
||||
typing_extensions==4.12.2
|
||||
uritemplate==4.1.1
|
||||
# urllib3==2.2.3
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ pefile
|
|||
|
||||
# fix qute-pylint location
|
||||
#@ replace: qute[_-]pylint.* ./scripts/dev/pylint_checkers
|
||||
#@ markers: typed-ast python_version<"3.8"
|
||||
|
||||
# Already included via test requirements
|
||||
#@ ignore: urllib3
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt5==5.15.2 # rq.filter: == 5.15.2
|
||||
PyQt5_sip==12.17.1
|
||||
PyQt5_sip==12.15.0
|
||||
PyQtWebEngine==5.15.2 # rq.filter: == 5.15.2
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt5==5.15.11 # rq.filter: < 5.16
|
||||
PyQt5-Qt5==5.15.18
|
||||
PyQt5_sip==12.17.1
|
||||
PyQt5-Qt5==5.15.15
|
||||
PyQt5_sip==12.15.0
|
||||
PyQtWebEngine==5.15.7 # rq.filter: < 5.16
|
||||
PyQtWebEngine-Qt5==5.15.18
|
||||
PyQtWebEngine-Qt5==5.15.15
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt5==5.15.11
|
||||
PyQt5-Qt5==5.15.18
|
||||
PyQt5_sip==12.17.1
|
||||
PyQt5-Qt5==5.15.15
|
||||
PyQt5_sip==12.15.0
|
||||
PyQtWebEngine==5.15.7
|
||||
PyQtWebEngine-Qt5==5.15.18
|
||||
PyQtWebEngine-Qt5==5.15.15
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt6==6.10.1
|
||||
PyQt6-Qt6==6.10.1
|
||||
PyQt6-WebEngine==6.10.0
|
||||
PyQt6-WebEngine-Qt6==6.10.1
|
||||
PyQt6_sip==13.10.2
|
||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
PyQt6 >= 6.10, < 6.11
|
||||
PyQt6-Qt6 >= 6.10, < 6.11
|
||||
PyQt6-WebEngine >= 6.10, < 6.11
|
||||
PyQt6-WebEngine-Qt6 >= 6.10, < 6.11
|
||||
|
||||
# WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2025-October/046347.html
|
||||
#@ add: --extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||
|
|
@ -4,4 +4,4 @@ PyQt6==6.2.3
|
|||
PyQt6-Qt6==6.2.4
|
||||
PyQt6-WebEngine==6.2.1
|
||||
PyQt6-WebEngine-Qt6==6.2.4
|
||||
PyQt6_sip==13.10.2
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ PyQt6==6.3.1
|
|||
PyQt6-Qt6==6.3.2
|
||||
PyQt6-WebEngine==6.3.1
|
||||
PyQt6-WebEngine-Qt6==6.3.2
|
||||
PyQt6_sip==13.10.2
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ PyQt6==6.4.2
|
|||
PyQt6-Qt6==6.4.3
|
||||
PyQt6-WebEngine==6.4.0
|
||||
PyQt6-WebEngine-Qt6==6.4.3
|
||||
PyQt6_sip==13.10.2
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ PyQt6==6.5.3
|
|||
PyQt6-Qt6==6.5.3
|
||||
PyQt6-WebEngine==6.5.0
|
||||
PyQt6-WebEngine-Qt6==6.5.3
|
||||
PyQt6_sip==13.10.2
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ PyQt6==6.6.1
|
|||
PyQt6-Qt6==6.6.3
|
||||
PyQt6-WebEngine==6.6.0
|
||||
PyQt6-WebEngine-Qt6==6.6.3
|
||||
PyQt6_sip==13.10.2
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ PyQt6-Qt6==6.7.3
|
|||
PyQt6-WebEngine==6.7.0
|
||||
PyQt6-WebEngine-Qt6==6.7.3
|
||||
PyQt6-WebEngineSubwheel-Qt6==6.7.3
|
||||
PyQt6_sip==13.10.2
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt6==6.8.1
|
||||
PyQt6-Qt6==6.8.2
|
||||
PyQt6-WebEngine==6.8.0
|
||||
PyQt6-WebEngine-Qt6==6.8.2
|
||||
PyQt6_sip==13.10.2
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
PyQt6 >= 6.8, < 6.9
|
||||
PyQt6-Qt6 >= 6.8, < 6.9
|
||||
PyQt6-WebEngine >= 6.8, < 6.9
|
||||
PyQt6-WebEngine-Qt6 >= 6.8, < 6.9
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt6==6.9.1
|
||||
PyQt6-Qt6==6.9.2
|
||||
PyQt6-WebEngine==6.9.0
|
||||
PyQt6-WebEngine-Qt6==6.9.2
|
||||
PyQt6_sip==13.10.2
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
PyQt6 >= 6.9, < 6.10
|
||||
PyQt6-Qt6 >= 6.9, < 6.10
|
||||
PyQt6-WebEngine >= 6.9, < 6.10
|
||||
PyQt6-WebEngine-Qt6 >= 6.9, < 6.10
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt6==6.10.1
|
||||
PyQt6-Qt6==6.10.1
|
||||
PyQt6-WebEngine==6.10.0
|
||||
PyQt6-WebEngine-Qt6==6.10.1
|
||||
PyQt6_sip==13.10.2
|
||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||
PyQt6==6.7.1
|
||||
PyQt6-Qt6==6.7.3
|
||||
PyQt6-WebEngine==6.7.0
|
||||
PyQt6-WebEngine-Qt6==6.7.3
|
||||
PyQt6-WebEngineSubwheel-Qt6==6.7.3
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -2,7 +2,3 @@ PyQt6
|
|||
PyQt6-Qt6
|
||||
PyQt6-WebEngine
|
||||
PyQt6-WebEngine-Qt6
|
||||
|
||||
# WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2025-October/046347.html
|
||||
#@ add: --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
|
||||
|
||||
PyQt6==6.10.1
|
||||
PyQt6-Qt6==6.10.1
|
||||
PyQt6-WebEngine==6.10.0
|
||||
PyQt6-WebEngine-Qt6==6.10.1
|
||||
PyQt6_sip==13.10.2
|
||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||
PyQt6==6.7.1
|
||||
PyQt6-Qt6==6.7.3
|
||||
PyQt6-WebEngine==6.7.0
|
||||
PyQt6-WebEngine-Qt6==6.7.3
|
||||
PyQt6-WebEngineSubwheel-Qt6==6.7.3
|
||||
PyQt6_sip==13.8.0
|
||||
|
|
|
|||
|
|
@ -2,7 +2,3 @@ PyQt6
|
|||
PyQt6-Qt6
|
||||
PyQt6-WebEngine
|
||||
PyQt6-WebEngine-Qt6
|
||||
|
||||
# WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2025-October/046347.html
|
||||
#@ add: --extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
build==1.3.0
|
||||
certifi==2025.11.12
|
||||
charset-normalizer==3.4.4
|
||||
check-manifest==0.51
|
||||
docutils==0.22.3
|
||||
idna==3.11
|
||||
importlib_metadata==8.7.0
|
||||
packaging==25.0
|
||||
Pygments==2.19.2
|
||||
build==1.2.2
|
||||
certifi==2024.8.30
|
||||
charset-normalizer==3.3.2
|
||||
docutils==0.20.1
|
||||
idna==3.10
|
||||
importlib_metadata==8.5.0
|
||||
packaging==24.1
|
||||
Pygments==2.18.0
|
||||
pyproject_hooks==1.2.0
|
||||
pyroma==5.0.1
|
||||
requests==2.32.5
|
||||
tomli==2.3.0
|
||||
trove-classifiers==2025.12.1.14
|
||||
urllib3==2.6.2
|
||||
zipp==3.23.0
|
||||
pyroma==4.2
|
||||
requests==2.32.3
|
||||
tomli==2.0.1
|
||||
trove-classifiers==2024.9.12
|
||||
urllib3==2.2.3
|
||||
zipp==3.20.2
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
pyroma
|
||||
check-manifest
|
||||
|
|
|
|||
|
|
@ -12,7 +12,12 @@ PyYAML
|
|||
#@ add: pyobjc-core ; sys_platform=="darwin"
|
||||
#@ add: pyobjc-framework-Cocoa ; sys_platform=="darwin"
|
||||
|
||||
## stdlib backports
|
||||
importlib_resources
|
||||
|
||||
## Optional dependencies
|
||||
Pygments # For :view-source --pygments or on QtWebKit
|
||||
colorama # Colored log output on Windows
|
||||
adblock # Improved adblocking
|
||||
|
||||
#@ markers: importlib_resources python_version=="3.8.*"
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
alabaster==0.7.16
|
||||
babel==2.17.0
|
||||
certifi==2025.11.12
|
||||
charset-normalizer==3.4.4
|
||||
docutils==0.21.2
|
||||
idna==3.11
|
||||
alabaster==0.7.13
|
||||
babel==2.16.0
|
||||
certifi==2024.8.30
|
||||
charset-normalizer==3.3.2
|
||||
docutils==0.20.1
|
||||
idna==3.10
|
||||
imagesize==1.4.1
|
||||
importlib_metadata==8.7.0
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.3
|
||||
packaging==25.0
|
||||
Pygments==2.19.2
|
||||
requests==2.32.5
|
||||
snowballstemmer==3.0.1
|
||||
Sphinx==7.4.7
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
sphinxcontrib-devhelp==2.0.0
|
||||
sphinxcontrib-htmlhelp==2.1.0
|
||||
importlib_metadata==8.5.0
|
||||
Jinja2==3.1.4
|
||||
MarkupSafe==2.1.5
|
||||
packaging==24.1
|
||||
Pygments==2.18.0
|
||||
pytz==2024.2
|
||||
requests==2.32.3
|
||||
snowballstemmer==2.2.0
|
||||
Sphinx==7.1.2
|
||||
sphinxcontrib-applehelp==1.0.4
|
||||
sphinxcontrib-devhelp==1.0.2
|
||||
sphinxcontrib-htmlhelp==2.0.1
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
sphinxcontrib-qthelp==2.0.0
|
||||
sphinxcontrib-serializinghtml==2.0.0
|
||||
tomli==2.3.0
|
||||
urllib3==2.6.2
|
||||
zipp==3.23.0
|
||||
sphinxcontrib-qthelp==1.0.3
|
||||
sphinxcontrib-serializinghtml==1.1.5
|
||||
urllib3==2.2.3
|
||||
zipp==3.20.2
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@
|
|||
# bzr+lp:beautifulsoup
|
||||
beautifulsoup4
|
||||
git+https://github.com/cherrypy/cheroot.git
|
||||
coverage[toml] @ git+https://github.com/nedbat/coveragepy.git
|
||||
git+https://github.com/nedbat/coveragepy.git#egg=coverage[toml]
|
||||
git+https://github.com/pallets/flask.git
|
||||
git+https://github.com/pallets/werkzeug.git # transitive dep, but needed to work
|
||||
git+https://github.com/HypothesisWorks/hypothesis.git#subdirectory=hypothesis-python
|
||||
git+https://github.com/pytest-dev/pytest.git
|
||||
git+https://github.com/pytest-dev/pytest-bdd.git
|
||||
gherkin-official<31.0.0 # https://github.com/cucumber/gherkin/issues/373
|
||||
git+https://github.com/ionelmc/pytest-benchmark.git
|
||||
git+https://github.com/pytest-dev/pytest-instafail.git
|
||||
git+https://github.com/pytest-dev/pytest-mock.git
|
||||
|
|
|
|||
|
|
@ -1,67 +1,66 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
attrs==25.4.0
|
||||
attrs==24.2.0
|
||||
autocommand==2.2.2
|
||||
backports.tarfile==1.2.0
|
||||
beautifulsoup4==4.14.3
|
||||
blinker==1.9.0
|
||||
certifi==2025.11.12
|
||||
charset-normalizer==3.4.4
|
||||
cheroot==11.1.2
|
||||
click==8.1.8
|
||||
coverage==7.10.7
|
||||
exceptiongroup==1.3.1
|
||||
execnet==2.1.2
|
||||
filelock==3.19.1
|
||||
Flask==3.1.2
|
||||
gherkin-official==29.0.0
|
||||
hunter==3.9.0
|
||||
hypothesis==6.141.1
|
||||
idna==3.11
|
||||
importlib_metadata==8.7.0
|
||||
importlib_resources==6.5.2
|
||||
beautifulsoup4==4.12.3
|
||||
blinker==1.8.2
|
||||
certifi==2024.8.30
|
||||
charset-normalizer==3.3.2
|
||||
cheroot==10.0.1
|
||||
click==8.1.7
|
||||
coverage==7.6.1
|
||||
exceptiongroup==1.2.2
|
||||
execnet==2.1.1
|
||||
filelock==3.16.1
|
||||
Flask==3.0.3
|
||||
hunter==3.7.0
|
||||
hypothesis==6.112.2
|
||||
idna==3.10
|
||||
importlib_metadata==8.5.0
|
||||
importlib_resources==6.4.5 ; python_version=="3.8.*"
|
||||
inflect==7.3.1
|
||||
iniconfig==2.1.0
|
||||
iniconfig==2.0.0
|
||||
itsdangerous==2.2.0
|
||||
jaraco.collections==5.1.0
|
||||
jaraco.context==6.0.1
|
||||
jaraco.functools==4.0.1
|
||||
jaraco.functools==4.1.0
|
||||
jaraco.text==3.12.1
|
||||
# Jinja2==3.1.6
|
||||
Mako==1.3.10
|
||||
# Jinja2==3.1.4
|
||||
Mako==1.3.5
|
||||
manhole==1.8.1
|
||||
# MarkupSafe==3.0.3
|
||||
more-itertools==10.8.0
|
||||
packaging==25.0
|
||||
# MarkupSafe==2.1.5
|
||||
more-itertools==10.5.0
|
||||
packaging==24.1
|
||||
parse==1.20.2
|
||||
parse_type==0.6.6
|
||||
pillow==11.3.0
|
||||
platformdirs==4.4.0
|
||||
pluggy==1.6.0
|
||||
parse_type==0.6.3
|
||||
platformdirs==4.3.6
|
||||
pillow==10.4.0
|
||||
pluggy==1.5.0
|
||||
py-cpuinfo==9.0.0
|
||||
Pygments==2.19.2
|
||||
pytest==8.4.2
|
||||
pytest-bdd==8.1.0
|
||||
pytest-benchmark==5.2.3
|
||||
pytest-cov==7.0.0
|
||||
Pygments==2.18.0
|
||||
pytest==8.3.3
|
||||
pytest-bdd==7.3.0
|
||||
pytest-benchmark==4.0.0
|
||||
pytest-cov==5.0.0
|
||||
pytest-instafail==0.5.0
|
||||
pytest-mock==3.15.1
|
||||
pytest-qt==4.5.0
|
||||
pytest-repeat==0.9.4
|
||||
pytest-rerunfailures==16.0.1
|
||||
pytest-xdist==3.8.0
|
||||
pytest-xvfb==3.1.1
|
||||
pytest-mock==3.14.0
|
||||
pytest-qt==4.4.0
|
||||
pytest-repeat==0.9.3
|
||||
pytest-rerunfailures==14.0
|
||||
pytest-xdist==3.6.1
|
||||
pytest-xvfb==3.0.0
|
||||
PyVirtualDisplay==3.0
|
||||
requests==2.32.5
|
||||
requests-file==3.0.1
|
||||
six==1.17.0
|
||||
requests==2.32.3
|
||||
requests-file==2.1.0
|
||||
six==1.16.0
|
||||
sortedcontainers==2.4.0
|
||||
soupsieve==2.8
|
||||
tldextract==5.3.0
|
||||
tomli==2.3.0
|
||||
soupsieve==2.6
|
||||
tldextract==5.1.2
|
||||
tomli==2.0.1
|
||||
typeguard==4.3.0
|
||||
typing_extensions==4.15.0
|
||||
urllib3==2.6.2
|
||||
vulture==2.14
|
||||
Werkzeug==3.1.4
|
||||
zipp==3.23.0
|
||||
typing_extensions==4.12.2
|
||||
urllib3==2.2.3
|
||||
vulture==2.12
|
||||
Werkzeug==3.0.4
|
||||
zipp==3.20.2
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ tldextract
|
|||
# Include them here even though we don't need them to make sure we at least
|
||||
# get an up to date version.
|
||||
importlib_resources
|
||||
#@ markers: importlib_resources python_version=="3.8.*"
|
||||
jaraco.context
|
||||
platformdirs
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
cachetools==6.2.3
|
||||
cachetools==5.5.0
|
||||
chardet==5.2.0
|
||||
colorama==0.4.6
|
||||
distlib==0.4.0
|
||||
filelock==3.19.1
|
||||
packaging==25.0
|
||||
pip==25.3
|
||||
platformdirs==4.4.0
|
||||
pluggy==1.6.0
|
||||
pyproject-api==1.9.1
|
||||
setuptools==80.9.0
|
||||
tomli==2.3.0
|
||||
tox==4.30.3 ; python_full_version!="3.14.0b1"
|
||||
typing_extensions==4.15.0
|
||||
virtualenv==20.35.4
|
||||
wheel==0.45.1
|
||||
tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1"
|
||||
distlib==0.3.8
|
||||
filelock==3.16.1
|
||||
packaging==24.1
|
||||
pip==24.2
|
||||
platformdirs==4.3.6
|
||||
pluggy==1.5.0
|
||||
pyproject-api==1.8.0
|
||||
setuptools==75.1.0
|
||||
tomli==2.0.1
|
||||
tox==4.20.0
|
||||
virtualenv==20.26.6
|
||||
wheel==0.44.0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,2 @@
|
|||
tox
|
||||
wheel
|
||||
|
||||
#@ markers: tox python_full_version!="3.14.0b1"
|
||||
#@ add: tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
tomli==2.3.0
|
||||
vulture==2.14
|
||||
tomli==2.0.1
|
||||
vulture==2.12
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
pathspec==0.12.1
|
||||
PyYAML==6.0.3
|
||||
yamllint==1.37.1
|
||||
PyYAML==6.0.2
|
||||
yamllint==1.35.1
|
||||
|
|
|
|||
|
|
@ -106,8 +106,6 @@ The following userscripts can be found on their own repositories.
|
|||
More powerfully manage single window sessions
|
||||
- [qutebrowser-url-mutator](https://codeberg.org/mister_monster/qutebrowser-url-mutator):
|
||||
automatically mutates input URLs based on configurable rules
|
||||
- [qute-translate-popup](https://github.com/JohnBardoe/qute-translate-popup):
|
||||
selected text translation, with a qute popup!
|
||||
|
||||
[Zotero]: https://www.zotero.org/
|
||||
[Pocket]: https://getpocket.com/
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ no_entries_found() {
|
|||
# expected to write the username of that entry to the $username variable and
|
||||
# the corresponding password to $password
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
# shellcheck disable=SC2317
|
||||
reset_backend() {
|
||||
init() { true ; }
|
||||
query_entries() { true ; }
|
||||
|
|
@ -199,8 +199,7 @@ choose_entry_zenity() {
|
|||
}
|
||||
|
||||
choose_entry_zenity_radio() {
|
||||
# shellcheck disable=SC2329
|
||||
zenity_helper() {
|
||||
zenity_helper() { # shellcheck disable=SC2317
|
||||
awk '{ print $0 ; print $0 }' \
|
||||
| zenity --list --radiolist \
|
||||
--title "qutebrowser password fill" \
|
||||
|
|
@ -280,7 +279,7 @@ pass_backend() {
|
|||
|
||||
# =======================================================
|
||||
# backend: secret
|
||||
# shellcheck disable=SC2329
|
||||
# shellcheck disable=SC2317
|
||||
secret_backend() {
|
||||
init() {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -96,8 +96,7 @@ def ask_password(password_prompt_invocation):
|
|||
raise Exception('Could not unlock vault')
|
||||
master_pass = process.stdout.strip()
|
||||
return subprocess.check_output(
|
||||
['bw', 'unlock', '--raw', '--passwordenv', 'BW_MASTERPASS'],
|
||||
env={**os.environ, 'BW_MASTERPASS': master_pass},
|
||||
['bw', 'unlock', '--raw', master_pass],
|
||||
text=True,
|
||||
).strip()
|
||||
|
||||
|
|
@ -133,7 +132,7 @@ def get_session_key(auto_lock, password_prompt_invocation):
|
|||
def pass_(domain, encoding, auto_lock, password_prompt_invocation):
|
||||
session_key = get_session_key(auto_lock, password_prompt_invocation)
|
||||
process = subprocess.run(
|
||||
['bw', 'list', 'items', '--nointeraction', '--session', session_key, '--url', domain],
|
||||
['bw', 'list', 'items', '--session', session_key, '--url', domain],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
|
@ -142,10 +141,6 @@ def pass_(domain, encoding, auto_lock, password_prompt_invocation):
|
|||
msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain, err)
|
||||
stderr(msg)
|
||||
|
||||
if "Vault is locked" in err:
|
||||
stderr("Bitwarden Vault got locked, trying again with clean session")
|
||||
return pass_(domain, encoding, 0, password_prompt_invocation)
|
||||
|
||||
if process.returncode:
|
||||
return '[]'
|
||||
|
||||
|
|
@ -157,7 +152,7 @@ def pass_(domain, encoding, auto_lock, password_prompt_invocation):
|
|||
def get_totp_code(selection_id, domain_name, encoding, auto_lock, password_prompt_invocation):
|
||||
session_key = get_session_key(auto_lock, password_prompt_invocation)
|
||||
process = subprocess.run(
|
||||
['bw', 'get', 'totp', '--nointeraction', '--session', session_key, selection_id],
|
||||
['bw', 'get', 'totp', '--session', session_key, selection_id],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
|
@ -167,10 +162,6 @@ def get_totp_code(selection_id, domain_name, encoding, auto_lock, password_promp
|
|||
msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain_name, err)
|
||||
stderr(msg)
|
||||
|
||||
if "Vault is locked" in err:
|
||||
stderr("Bitwarden Vault got locked, trying again with clean session")
|
||||
return get_totp_code(selection_id, domain_name, encoding, 0, password_prompt_invocation)
|
||||
|
||||
if process.returncode:
|
||||
return '[]'
|
||||
|
||||
|
|
@ -204,20 +195,12 @@ def main(arguments):
|
|||
# the registered domain name and finally: the IPv4 address if that's what
|
||||
# the URL represents
|
||||
candidates = []
|
||||
for target in filter(
|
||||
None,
|
||||
[
|
||||
extract_result.fqdn,
|
||||
(
|
||||
extract_result.top_domain_under_public_suffix
|
||||
if hasattr(extract_result, "top_domain_under_public_suffix")
|
||||
else extract_result.registered_domain
|
||||
),
|
||||
extract_result.subdomain + "." + extract_result.domain,
|
||||
extract_result.domain,
|
||||
extract_result.ipv4,
|
||||
],
|
||||
):
|
||||
for target in filter(None, [
|
||||
extract_result.fqdn,
|
||||
extract_result.registered_domain,
|
||||
extract_result.subdomain + '.' + extract_result.domain,
|
||||
extract_result.domain,
|
||||
extract_result.ipv4]):
|
||||
target_candidates = json.loads(
|
||||
pass_(
|
||||
target,
|
||||
|
|
|
|||
|
|
@ -117,20 +117,7 @@ def main(arguments):
|
|||
# the URL represents
|
||||
candidates = []
|
||||
seen_id = set()
|
||||
for target in filter(
|
||||
None,
|
||||
[
|
||||
extract_result.fqdn,
|
||||
(
|
||||
extract_result.top_domain_under_public_suffix
|
||||
if hasattr(extract_result, "top_domain_under_public_suffix")
|
||||
else extract_result.registered_domain
|
||||
),
|
||||
extract_result.subdomain + extract_result.domain,
|
||||
extract_result.domain,
|
||||
extract_result.ipv4,
|
||||
],
|
||||
):
|
||||
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.subdomain + extract_result.domain, extract_result.domain, extract_result.ipv4]):
|
||||
target_candidates, err = pass_(target, arguments.io_encoding)
|
||||
if err:
|
||||
stderr("LastPass CLI returned for {:s} - {:s}".format(target, err))
|
||||
|
|
|
|||
|
|
@ -243,20 +243,7 @@ def main(arguments):
|
|||
|
||||
netloc = urlparse(arguments.url).netloc
|
||||
|
||||
for target in filter(
|
||||
None,
|
||||
[
|
||||
extract_result.fqdn,
|
||||
(
|
||||
extract_result.top_domain_under_public_suffix
|
||||
if hasattr(extract_result, "top_domain_under_public_suffix")
|
||||
else extract_result.registered_domain
|
||||
),
|
||||
extract_result.ipv4,
|
||||
private_domain,
|
||||
netloc,
|
||||
],
|
||||
):
|
||||
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.ipv4, private_domain, netloc]):
|
||||
attempted_targets.append(target)
|
||||
target_candidates = find_pass_candidates(target, unfiltered=arguments.unfiltered)
|
||||
if not target_candidates:
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ create_menu() {
|
|||
done < "$QUTE_CONFIG_DIR"/bookmarks/urls
|
||||
|
||||
# Finally history
|
||||
printf -- '%s\n' "$(sqlite3 -separator ' ' "$QUTE_DATA_DIR/history.sqlite" 'select title, url from CompletionHistory ORDER BY last_atime DESC')"
|
||||
printf -- '%s\n' "$(sqlite3 -separator ' ' "$QUTE_DATA_DIR/history.sqlite" 'select title, url from CompletionHistory')"
|
||||
}
|
||||
|
||||
get_selection() {
|
||||
|
|
|
|||
|
|
@ -9,16 +9,18 @@
|
|||
# :spawn --userscript ripbang amazon maps
|
||||
#
|
||||
|
||||
import os, requests, sys
|
||||
import os, re, requests, sys
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
for argument in sys.argv[1:]:
|
||||
bang = '!' + argument
|
||||
r = requests.get('https://html.duckduckgo.com/html/',
|
||||
allow_redirects=False,
|
||||
r = requests.get('https://duckduckgo.com/',
|
||||
params={'q': bang + ' SEARCHTEXT'},
|
||||
headers={'user-agent': 'qutebrowser ripbang'})
|
||||
|
||||
searchengine = r.headers['location']
|
||||
searchengine = re.search("url=([^']+)", r.text).group(1)
|
||||
searchengine = urlparse(searchengine).query
|
||||
searchengine = parse_qs(searchengine)['uddg'][0]
|
||||
searchengine = searchengine.replace('SEARCHTEXT', '{}')
|
||||
|
||||
if os.getenv('QUTE_FIFO'):
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.check-manifest]
|
||||
ignore = [
|
||||
"qutebrowser/git-commit-id",
|
||||
"qutebrowser/html/doc",
|
||||
"qutebrowser/html/doc/*",
|
||||
"qutebrowser/html/doc/img/cheatsheet-*.png",
|
||||
"*/__pycache__",
|
||||
]
|
||||
27
pytest.ini
27
pytest.ini
|
|
@ -1,5 +1,4 @@
|
|||
[pytest]
|
||||
pythonpath = .
|
||||
log_level = NOTSET
|
||||
addopts = --strict-markers --strict-config --instafail --benchmark-columns=Min,Max,Median
|
||||
testpaths = tests
|
||||
|
|
@ -20,7 +19,6 @@ markers =
|
|||
not_frozen: Tests which can't be run if sys.frozen is True.
|
||||
not_flatpak: Tests which can't be run if running with Flatpak.
|
||||
no_xvfb: Tests which can't be run with Xvfb.
|
||||
no_offscreen: Tests which can't be run with the offscreen platform plugin.
|
||||
frozen: Tests which can only be run if sys.frozen is True.
|
||||
integration: Tests which test a bigger portion of code
|
||||
end2end: End to end tests which run qutebrowser as subprocess
|
||||
|
|
@ -43,8 +41,7 @@ markers =
|
|||
qt6_only: Tests which should only run with Qt 6
|
||||
qt5_xfail: Tests which fail with Qt 5
|
||||
qt6_xfail: Tests which fail with Qt 6
|
||||
qt69_ci_flaky: Tests which are flaky with Qt 6.9+ on CI
|
||||
qt69_ci_skip: Tests which should be skipped with Qt 6.9+ on CI
|
||||
qt68_beta4_skip: Fails on Qt 6.8 beta 4
|
||||
qt_log_level_fail = WARNING
|
||||
qt_log_ignore =
|
||||
# GitHub Actions
|
||||
|
|
@ -80,29 +77,13 @@ qt_log_ignore =
|
|||
# model, for example, when no completion function is available for the
|
||||
# current text pattern.
|
||||
QItemSelectionModel: Selecting when no model has been set will result in a no-op.
|
||||
^QSaveFile::commit: File \(.*[/\\]test_failing_flush0[/\\]foo\) is not open$
|
||||
^QSaveFile::commit: File \(.*/test_failing_flush0/foo\) is not open$
|
||||
^The following paths were searched for Qt WebEngine dictionaries:.*
|
||||
# Qt 6.9 with Xvfb
|
||||
^Backend texture is not a Vulkan texture\.$
|
||||
^Compositor returned null texture$
|
||||
# With offscreen platform plugin
|
||||
^This plugin does not support (raise\(\)|propagateSizeHints\(\)|createPlatformVulkanInstance|grabbing the keyboard)$
|
||||
^QRhiGles2: Failed to create (temporary )?context$
|
||||
^QVulkanInstance: Failed to initialize Vulkan$
|
||||
^Unable to detect GPU vendor\.$
|
||||
# Qt 5 on CI with WebKit
|
||||
^qglx_findConfig: Failed to finding matching FBConfig for QSurfaceFormat\(version 2\.0, options QFlags<QSurfaceFormat::FormatOption>\(\), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SingleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile QSurfaceFormat::NoProfile\)$
|
||||
# Qt 6.8+ debug build
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/8069#issuecomment-2017644465
|
||||
^QObject::connect: Connecting from COMPAT signal \(QWebEnginePage::featurePermissionRequest(ed|Canceled)\(QUrl,QWebEnginePage::Feature\)\)
|
||||
xfail_strict = true
|
||||
filterwarnings =
|
||||
error
|
||||
default:Test process .* failed to terminate!:UserWarning
|
||||
# https://github.com/cucumber/gherkin/commit/2f4830093149eae7ff7bd82f683b3d3bb7320d39
|
||||
# https://github.com/pytest-dev/pytest-bdd/issues/752
|
||||
ignore:'maxsplit' is passed as positional argument:DeprecationWarning:gherkin.gherkin_line
|
||||
# https://github.com/ionelmc/pytest-benchmark/issues/283
|
||||
ignore:FileType is deprecated\. Simply open files after parsing arguments\.:PendingDeprecationWarning:pytest_benchmark.plugin
|
||||
# Python 3.12: https://github.com/ionelmc/pytest-benchmark/issues/240 (fixed but not released)
|
||||
ignore:(datetime\.)?datetime\.utcnow\(\) is deprecated and scheduled for removal in a future version\. Use timezone-aware objects to represent datetimes in UTC. (datetime\.)?datetime\.now\(datetime\.UTC\)\.:DeprecationWarning:pytest_benchmark\.utils
|
||||
faulthandler_timeout = 90
|
||||
xvfb_colordepth = 24
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ _year = datetime.date.today().year
|
|||
|
||||
__author__ = "Florian Bruhin"
|
||||
__copyright__ = "Copyright 2013-{} Florian Bruhin (The Compiler)".format(_year)
|
||||
__license__ = "GPL-3.0-or-later"
|
||||
__license__ = "GPL"
|
||||
__maintainer__ = __author__
|
||||
__email__ = "mail@qutebrowser.org"
|
||||
__version__ = "3.6.3"
|
||||
__version__ = "3.3.0"
|
||||
__version_info__ = tuple(int(part) for part in __version__.split('.'))
|
||||
__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ Possible values:
|
|||
|
||||
|
||||
import inspect
|
||||
from typing import Any, Protocol, Optional, cast
|
||||
from collections.abc import Iterable, Callable
|
||||
from typing import Any, Callable, Iterable, Protocol, Optional, Dict, cast
|
||||
|
||||
from qutebrowser.utils import qtutils
|
||||
from qutebrowser.commands import command, cmdexc
|
||||
|
|
@ -102,7 +101,7 @@ class _CmdHandlerType(Protocol):
|
|||
Below, we cast the decorated function to _CmdHandlerType to make mypy aware of this.
|
||||
"""
|
||||
|
||||
qute_args: Optional[dict[str, 'command.ArgInfo']]
|
||||
qute_args: Optional[Dict[str, 'command.ArgInfo']]
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
||||
...
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
"""Hooks for extensions."""
|
||||
|
||||
import importlib
|
||||
from typing import Any
|
||||
from collections.abc import Callable
|
||||
from typing import Callable, Any
|
||||
|
||||
|
||||
from qutebrowser.extensions import loader
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ import tempfile
|
|||
import pathlib
|
||||
import datetime
|
||||
import argparse
|
||||
from typing import Optional
|
||||
from collections.abc import Iterable
|
||||
from typing import Iterable, Optional, List, Tuple
|
||||
|
||||
from qutebrowser.qt import machinery
|
||||
from qutebrowser.qt.widgets import QApplication, QWidget
|
||||
|
|
@ -331,7 +330,7 @@ def _open_special_pages(args):
|
|||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window='last-focused')
|
||||
|
||||
pages: list[tuple[str, bool, str]] = [
|
||||
pages: List[Tuple[str, bool, str]] = [
|
||||
# state, condition, URL
|
||||
('quickstart-done',
|
||||
True,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import pathlib
|
|||
import itertools
|
||||
import functools
|
||||
import dataclasses
|
||||
from typing import (cast, TYPE_CHECKING, Any, Optional, Union)
|
||||
from collections.abc import Iterable, Sequence, Callable
|
||||
from typing import (cast, TYPE_CHECKING, Any, Callable, Iterable, List, Optional,
|
||||
Sequence, Set, Type, Union, Tuple)
|
||||
|
||||
from qutebrowser.qt import machinery
|
||||
from qutebrowser.qt.core import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt,
|
||||
|
|
@ -29,7 +29,7 @@ if TYPE_CHECKING:
|
|||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.config import config, websettings
|
||||
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
|
||||
urlutils, message, jinja)
|
||||
urlutils, message, jinja, version)
|
||||
from qutebrowser.misc import miscwidgets, objects, sessions
|
||||
from qutebrowser.browser import eventfilter, inspector
|
||||
from qutebrowser.qt import sip
|
||||
|
|
@ -60,7 +60,7 @@ def create(win_id: int,
|
|||
mode_manager = modeman.instance(win_id)
|
||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||
from qutebrowser.browser.webengine import webenginetab
|
||||
tab_class: type[AbstractTab] = webenginetab.WebEngineTab
|
||||
tab_class: Type[AbstractTab] = webenginetab.WebEngineTab
|
||||
elif objects.backend == usertypes.Backend.QtWebKit:
|
||||
from qutebrowser.browser.webkit import webkittab
|
||||
tab_class = webkittab.WebKitTab
|
||||
|
|
@ -142,7 +142,7 @@ class AbstractAction:
|
|||
|
||||
"""Attribute ``action`` of AbstractTab for Qt WebActions."""
|
||||
|
||||
action_base: type[Union['QWebPage.WebAction', 'QWebEnginePage.WebAction']]
|
||||
action_base: Type[Union['QWebPage.WebAction', 'QWebEnginePage.WebAction']]
|
||||
|
||||
def __init__(self, tab: 'AbstractTab') -> None:
|
||||
self._widget = cast(_WidgetType, None)
|
||||
|
|
@ -639,7 +639,7 @@ class AbstractScroller(QObject):
|
|||
def pos_px(self) -> QPoint:
|
||||
raise NotImplementedError
|
||||
|
||||
def pos_perc(self) -> tuple[int, int]:
|
||||
def pos_perc(self) -> Tuple[int, int]:
|
||||
raise NotImplementedError
|
||||
|
||||
def to_perc(self, x: float = None, y: float = None) -> None:
|
||||
|
|
@ -765,10 +765,10 @@ class AbstractHistory:
|
|||
def _go_to_item(self, item: Any) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def back_items(self) -> list[Any]:
|
||||
def back_items(self) -> List[Any]:
|
||||
raise NotImplementedError
|
||||
|
||||
def forward_items(self) -> list[Any]:
|
||||
def forward_items(self) -> List[Any]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
|
@ -1018,7 +1018,7 @@ class AbstractTab(QWidget):
|
|||
# Note that we remember hosts here, without scheme/port:
|
||||
# QtWebEngine/Chromium also only remembers hostnames, and certificates are
|
||||
# for a given hostname anyways.
|
||||
_insecure_hosts: set[str] = set()
|
||||
_insecure_hosts: Set[str] = set()
|
||||
|
||||
# Sub-APIs initialized by subclasses
|
||||
history: AbstractHistory
|
||||
|
|
@ -1177,6 +1177,37 @@ class AbstractTab(QWidget):
|
|||
navigation.url.errorString()))
|
||||
navigation.accepted = False
|
||||
|
||||
# WORKAROUND for QtWebEngine >= 6.2 not allowing form requests from
|
||||
# qute:// to outside domains.
|
||||
needs_load_workarounds = (
|
||||
objects.backend == usertypes.Backend.QtWebEngine and
|
||||
version.qtwebengine_versions().webengine >= utils.VersionNumber(6, 2)
|
||||
)
|
||||
if (
|
||||
needs_load_workarounds and
|
||||
self.url() == QUrl("qute://start/") and
|
||||
navigation.navigation_type == navigation.Type.form_submitted and
|
||||
navigation.url.matches(
|
||||
QUrl(config.val.url.searchengines['DEFAULT']),
|
||||
urlutils.FormatOption.REMOVE_QUERY)
|
||||
):
|
||||
log.webview.debug(
|
||||
"Working around qute://start loading issue for "
|
||||
f"{navigation.url.toDisplayString()}")
|
||||
navigation.accepted = False
|
||||
self.load_url(navigation.url)
|
||||
|
||||
if (
|
||||
needs_load_workarounds and
|
||||
self.url() == QUrl("qute://bookmarks/") and
|
||||
navigation.navigation_type == navigation.Type.back_forward
|
||||
):
|
||||
log.webview.debug(
|
||||
"Working around qute://bookmarks loading issue for "
|
||||
f"{navigation.url.toDisplayString()}")
|
||||
navigation.accepted = False
|
||||
self.load_url(navigation.url)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def _on_load_finished(self, ok: bool) -> None:
|
||||
assert self._widget is not None
|
||||
|
|
|
|||
|
|
@ -2,15 +2,12 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# pylint: disable=too-many-positional-arguments
|
||||
|
||||
"""Command dispatcher for TabbedBrowser."""
|
||||
|
||||
import os.path
|
||||
import shlex
|
||||
import functools
|
||||
from typing import cast, Union, Optional
|
||||
from collections.abc import Callable
|
||||
from typing import cast, Callable, Dict, Union, Optional
|
||||
|
||||
from qutebrowser.qt.widgets import QApplication, QTabBar
|
||||
from qutebrowser.qt.core import Qt, QUrl, QEvent, QUrlQuery
|
||||
|
|
@ -71,10 +68,7 @@ class CommandDispatcher:
|
|||
|
||||
def _current_index(self):
|
||||
"""Convenience method to get the current widget index."""
|
||||
current_index = self._tabbed_browser.widget.currentIndex()
|
||||
if current_index == -1:
|
||||
raise cmdutils.CommandError("No WebView available yet!")
|
||||
return current_index
|
||||
return self._tabbed_browser.widget.currentIndex()
|
||||
|
||||
def _current_url(self):
|
||||
"""Convenience method to get the current url."""
|
||||
|
|
@ -642,7 +636,7 @@ class CommandDispatcher:
|
|||
widget = self._current_widget()
|
||||
url = self._current_url()
|
||||
|
||||
handlers: dict[str, Callable[..., QUrl]] = {
|
||||
handlers: Dict[str, Callable[..., QUrl]] = {
|
||||
'prev': functools.partial(navigate.prevnext, prev=True),
|
||||
'next': functools.partial(navigate.prevnext, prev=False),
|
||||
'up': navigate.path_up,
|
||||
|
|
@ -868,6 +862,10 @@ class CommandDispatcher:
|
|||
Args:
|
||||
count: How many tabs to switch back.
|
||||
"""
|
||||
if self._count() == 0:
|
||||
# Running :tab-prev after last tab was closed
|
||||
# See https://github.com/qutebrowser/qutebrowser/issues/1448
|
||||
return
|
||||
newidx = self._current_index() - count
|
||||
if newidx >= 0:
|
||||
self._set_current_index(newidx)
|
||||
|
|
@ -884,6 +882,10 @@ class CommandDispatcher:
|
|||
Args:
|
||||
count: How many tabs to switch forward.
|
||||
"""
|
||||
if self._count() == 0:
|
||||
# Running :tab-next after last tab was closed
|
||||
# See https://github.com/qutebrowser/qutebrowser/issues/1448
|
||||
return
|
||||
newidx = self._current_index() + count
|
||||
if newidx < self._count():
|
||||
self._set_current_index(newidx)
|
||||
|
|
@ -1134,7 +1136,8 @@ class CommandDispatcher:
|
|||
else:
|
||||
cmd = os.path.expanduser(cmd)
|
||||
proc = guiprocess.GUIProcess(what='command', verbose=verbose,
|
||||
output_messages=output_messages)
|
||||
output_messages=output_messages,
|
||||
parent=self._tabbed_browser)
|
||||
if detach:
|
||||
ok = proc.start_detached(cmd, args)
|
||||
if not ok:
|
||||
|
|
@ -1163,7 +1166,7 @@ class CommandDispatcher:
|
|||
if count is not None:
|
||||
env['QUTE_COUNT'] = str(count)
|
||||
|
||||
idx = self._tabbed_browser.widget.currentIndex()
|
||||
idx = self._current_index()
|
||||
if idx != -1:
|
||||
env['QUTE_TAB_INDEX'] = str(idx + 1)
|
||||
env['QUTE_TITLE'] = self._tabbed_browser.widget.page_title(idx)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ import functools
|
|||
import pathlib
|
||||
import tempfile
|
||||
import enum
|
||||
from typing import Any, IO, Optional, Union
|
||||
from collections.abc import MutableSequence
|
||||
from typing import Any, Dict, IO, List, MutableSequence, Optional, Union
|
||||
|
||||
from qutebrowser.qt.core import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
|
||||
QTimer, QAbstractListModel, QUrl)
|
||||
|
|
@ -188,22 +187,15 @@ def transform_path(path):
|
|||
"""
|
||||
if not utils.is_windows:
|
||||
return path
|
||||
|
||||
path = utils.expand_windows_drive(path)
|
||||
# Drive dependent working directories are not supported, e.g.
|
||||
# E:filename is invalid
|
||||
if re.search(r'^[A-Z]:[^\\]', path, re.IGNORECASE):
|
||||
return None
|
||||
|
||||
# Paths like COM1, ...
|
||||
# See https://github.com/qutebrowser/qutebrowser/issues/82
|
||||
if sys.version_info[:2] >= (3, 13):
|
||||
if os.path.isreserved(path): # pylint: disable=no-member
|
||||
return None
|
||||
else:
|
||||
if pathlib.Path(path).is_reserved(): # pylint: disable=else-if-used
|
||||
return None
|
||||
|
||||
if pathlib.Path(path).is_reserved():
|
||||
return None
|
||||
return path
|
||||
|
||||
|
||||
|
|
@ -455,7 +447,7 @@ class AbstractDownloadItem(QObject):
|
|||
UnsupportedAttribute, IO[bytes], None
|
||||
] = UnsupportedAttribute()
|
||||
self.raw_headers: Union[
|
||||
UnsupportedAttribute, dict[bytes, bytes]
|
||||
UnsupportedAttribute, Dict[bytes, bytes]
|
||||
] = UnsupportedAttribute()
|
||||
|
||||
self._filename: Optional[str] = None
|
||||
|
|
@ -907,7 +899,7 @@ class AbstractDownloadManager(QObject):
|
|||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.downloads: list[AbstractDownloadItem] = []
|
||||
self.downloads: List[AbstractDownloadItem] = []
|
||||
self._update_timer = usertypes.Timer(self, 'download-update')
|
||||
self._update_timer.timeout.connect(self._update_gui)
|
||||
self._update_timer.setInterval(_REFRESH_INTERVAL)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@
|
|||
"""The ListView to display downloads in."""
|
||||
|
||||
import functools
|
||||
from typing import Union
|
||||
from collections.abc import MutableSequence, Callable
|
||||
from typing import Callable, MutableSequence, Tuple, Union
|
||||
|
||||
from qutebrowser.qt.core import pyqtSlot, QSize, Qt
|
||||
from qutebrowser.qt.widgets import QListView, QSizePolicy, QMenu, QStyleFactory
|
||||
|
|
@ -18,8 +17,8 @@ from qutebrowser.utils import qtutils, utils
|
|||
|
||||
_ActionListType = MutableSequence[
|
||||
Union[
|
||||
tuple[None, None], # separator
|
||||
tuple[str, Callable[[], None]],
|
||||
Tuple[None, None], # separator
|
||||
Tuple[str, Callable[[], None]],
|
||||
]
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@
|
|||
|
||||
from qutebrowser.qt import machinery
|
||||
from qutebrowser.qt.core import QObject, QEvent, Qt, QTimer
|
||||
from qutebrowser.qt.gui import QKeyEvent
|
||||
from qutebrowser.qt.widgets import QWidget
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import log, message, usertypes, qtutils, version, utils
|
||||
from qutebrowser.keyinput import modeman, keyutils
|
||||
from qutebrowser.utils import log, message, usertypes, qtutils
|
||||
from qutebrowser.keyinput import modeman
|
||||
|
||||
|
||||
class ChildEventFilter(QObject):
|
||||
|
|
@ -38,8 +37,8 @@ class ChildEventFilter(QObject):
|
|||
if event.type() == QEvent.Type.ChildAdded:
|
||||
child = event.child()
|
||||
if not isinstance(child, QWidget):
|
||||
# Can e.g. happen when dragging text, or accessibility tree
|
||||
# nodes since Qt 6.9
|
||||
# Can e.g. happen when dragging text
|
||||
log.misc.debug(f"Ignoring new child {qtutils.qobj_repr(child)}")
|
||||
return False
|
||||
|
||||
log.misc.debug(
|
||||
|
|
@ -55,30 +54,21 @@ class ChildEventFilter(QObject):
|
|||
# - This is a child event filter on a tab (self._widget is not None)
|
||||
# - We find an old existing child which is a QQuickWidget and is
|
||||
# currently focused.
|
||||
# - We're using an affected QtWebEngine version
|
||||
# - We're using QtWebEngine >= 6.4 (older versions are not affected)
|
||||
children = [
|
||||
c for c in self._widget.findChildren(
|
||||
QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
|
||||
if c is not child and
|
||||
c.hasFocus() and
|
||||
c.metaObject() is not None and
|
||||
c.metaObject().className() == "QQuickWidget" # Qt 6.4+
|
||||
c.metaObject().className() == "QQuickWidget"
|
||||
]
|
||||
if children and version.qtwebengine_versions().webengine < utils.VersionNumber(6, 6, 3):
|
||||
if children:
|
||||
log.misc.debug("Focusing new child")
|
||||
child.setFocus()
|
||||
|
||||
child.installEventFilter(self._filter)
|
||||
elif event.type() == QEvent.Type.ChildRemoved:
|
||||
if isinstance(event, QKeyEvent):
|
||||
# WORKAROUND for unknown (Py)Qt bug
|
||||
info = keyutils.KeyInfo.from_event(event)
|
||||
log.misc.warning(
|
||||
f"ChildEventFilter: ignoring key event {info} "
|
||||
f"on {qtutils.qobj_repr(obj)}"
|
||||
)
|
||||
return False
|
||||
|
||||
child = event.child()
|
||||
log.misc.debug(
|
||||
f"{qtutils.qobj_repr(obj)}: removed child {qtutils.qobj_repr(child)}")
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ import functools
|
|||
import glob
|
||||
import textwrap
|
||||
import dataclasses
|
||||
from typing import cast, Optional
|
||||
from collections.abc import Sequence
|
||||
from typing import cast, List, Sequence, Tuple, Optional
|
||||
|
||||
from qutebrowser.qt.core import pyqtSignal, QObject, QUrl
|
||||
|
||||
|
|
@ -208,9 +207,9 @@ class MatchingScripts:
|
|||
"""All userscripts registered to run on a particular url."""
|
||||
|
||||
url: QUrl
|
||||
start: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
end: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
idle: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
start: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
end: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
idle: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
|
|
@ -218,8 +217,8 @@ class LoadResults:
|
|||
|
||||
"""The results of loading all Greasemonkey scripts."""
|
||||
|
||||
successful: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
errors: list[tuple[str, str]] = dataclasses.field(default_factory=list)
|
||||
successful: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||
errors: List[Tuple[str, str]] = dataclasses.field(default_factory=list)
|
||||
|
||||
def successful_str(self) -> str:
|
||||
"""Get a string with all successfully loaded scripts.
|
||||
|
|
@ -295,10 +294,10 @@ class GreasemonkeyManager(QObject):
|
|||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._run_start: list[GreasemonkeyScript] = []
|
||||
self._run_end: list[GreasemonkeyScript] = []
|
||||
self._run_idle: list[GreasemonkeyScript] = []
|
||||
self._in_progress_dls: list[downloads.AbstractDownloadItem] = []
|
||||
self._run_start: List[GreasemonkeyScript] = []
|
||||
self._run_end: List[GreasemonkeyScript] = []
|
||||
self._run_idle: List[GreasemonkeyScript] = []
|
||||
self._in_progress_dls: List[downloads.AbstractDownloadItem] = []
|
||||
|
||||
def load_scripts(self, *, force: bool = False) -> LoadResults:
|
||||
"""Re-read Greasemonkey scripts from disk.
|
||||
|
|
|
|||
|
|
@ -12,15 +12,8 @@ import html
|
|||
import enum
|
||||
import dataclasses
|
||||
from string import ascii_lowercase
|
||||
from typing import (TYPE_CHECKING, Optional)
|
||||
from collections.abc import (
|
||||
Iterable,
|
||||
Iterator,
|
||||
Mapping,
|
||||
MutableSequence,
|
||||
Sequence,
|
||||
Callable,
|
||||
)
|
||||
from typing import (TYPE_CHECKING, Callable, Dict, Iterable, Iterator, List, Mapping,
|
||||
MutableSequence, Optional, Sequence, Set)
|
||||
|
||||
from qutebrowser.qt.core import pyqtSignal, pyqtSlot, QObject, Qt, QUrl
|
||||
from qutebrowser.qt.widgets import QLabel
|
||||
|
|
@ -182,11 +175,11 @@ class HintContext:
|
|||
add_history: bool
|
||||
first: bool
|
||||
baseurl: QUrl
|
||||
args: list[str]
|
||||
args: List[str]
|
||||
group: str
|
||||
|
||||
all_labels: list[HintLabel] = dataclasses.field(default_factory=list)
|
||||
labels: dict[str, HintLabel] = dataclasses.field(default_factory=dict)
|
||||
all_labels: List[HintLabel] = dataclasses.field(default_factory=list)
|
||||
labels: Dict[str, HintLabel] = dataclasses.field(default_factory=dict)
|
||||
to_follow: Optional[str] = None
|
||||
first_run: bool = True
|
||||
filterstr: Optional[str] = None
|
||||
|
|
@ -1040,7 +1033,7 @@ class WordHinter:
|
|||
|
||||
def __init__(self) -> None:
|
||||
# will be initialized on first use.
|
||||
self.words: set[str] = set()
|
||||
self.words: Set[str] = set()
|
||||
self.dictionary = None
|
||||
|
||||
def ensure_initialized(self) -> None:
|
||||
|
|
@ -1150,7 +1143,7 @@ class WordHinter:
|
|||
"""
|
||||
self.ensure_initialized()
|
||||
hints = []
|
||||
used_hints: set[str] = set()
|
||||
used_hints: Set[str] = set()
|
||||
words = iter(self.words)
|
||||
for elem in elems:
|
||||
hint = self.new_hint_for(elem, used_hints, words)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ import os
|
|||
import time
|
||||
import contextlib
|
||||
import pathlib
|
||||
from typing import cast, Optional
|
||||
from collections.abc import Mapping, MutableSequence
|
||||
from typing import cast, Mapping, MutableSequence, Optional
|
||||
|
||||
from qutebrowser.qt import machinery
|
||||
from qutebrowser.qt.core import pyqtSlot, QUrl, QObject, pyqtSignal
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import re
|
||||
import posixpath
|
||||
from typing import Optional
|
||||
from typing import Optional, Set
|
||||
|
||||
from qutebrowser.qt.core import QUrl
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ def incdec(url, count, inc_or_dec):
|
|||
inc_or_dec: Either 'increment' or 'decrement'.
|
||||
"""
|
||||
urlutils.ensure_valid(url)
|
||||
segments: Optional[set[str]] = (
|
||||
segments: Optional[Set[str]] = (
|
||||
set(config.val.url.incdec_segments)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ class PACFetcher(QObject):
|
|||
pac_prefix = "pac+"
|
||||
|
||||
assert url.scheme().startswith(pac_prefix)
|
||||
url.setScheme(url.scheme().removeprefix(pac_prefix))
|
||||
url.setScheme(url.scheme()[len(pac_prefix):])
|
||||
|
||||
self._pac_url = url
|
||||
with qtlog.disable_qt_msghandler():
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
"""Handling of proxies."""
|
||||
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
|
||||
from qutebrowser.qt.core import QUrl, pyqtSlot
|
||||
from qutebrowser.qt.network import QNetworkProxy, QNetworkProxyFactory, QNetworkProxyQuery
|
||||
|
|
@ -71,7 +71,7 @@ class ProxyFactory(QNetworkProxyFactory):
|
|||
capabilities &= ~lookup_cap
|
||||
proxy.setCapabilities(capabilities)
|
||||
|
||||
def queryProxy(self, query: QNetworkProxyQuery = QNetworkProxyQuery()) -> list[QNetworkProxy]:
|
||||
def queryProxy(self, query: QNetworkProxyQuery = QNetworkProxyQuery()) -> List[QNetworkProxy]:
|
||||
"""Get the QNetworkProxies for a query.
|
||||
|
||||
Args:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ from qutebrowser.qt.core import QUrl, QUrlQuery
|
|||
|
||||
from qutebrowser.utils import resources, javascript, jinja, standarddir, log, urlutils
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.misc import objects
|
||||
|
||||
|
||||
_SYSTEM_PATHS = [
|
||||
|
|
@ -70,8 +69,19 @@ def generate_pdfjs_page(filename, url):
|
|||
return html
|
||||
|
||||
|
||||
def _get_polyfills() -> str:
|
||||
return resources.read_file("javascript/pdfjs_polyfills.js")
|
||||
def _generate_polyfills():
|
||||
return """
|
||||
if (typeof Promise.withResolvers === 'undefined') {
|
||||
Promise.withResolvers = function () {
|
||||
let resolve, reject
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
return { promise, resolve, reject }
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def _generate_pdfjs_script(filename):
|
||||
|
|
@ -111,7 +121,7 @@ def _generate_pdfjs_script(filename):
|
|||
});
|
||||
}
|
||||
});
|
||||
""").render(url=js_url, polyfills=_get_polyfills())
|
||||
""").render(url=js_url, polyfills=_generate_polyfills())
|
||||
|
||||
|
||||
def get_pdfjs_res_and_path(path):
|
||||
|
|
@ -128,12 +138,7 @@ def get_pdfjs_res_and_path(path):
|
|||
content = None
|
||||
file_path = None
|
||||
|
||||
if 'no-system-pdfjs' in objects.debug_flags:
|
||||
system_paths = []
|
||||
else:
|
||||
system_paths = _SYSTEM_PATHS[:]
|
||||
|
||||
system_paths += [
|
||||
system_paths = _SYSTEM_PATHS + [
|
||||
# fallback
|
||||
os.path.join(standarddir.data(), 'pdfjs'),
|
||||
# hardcoded fallback for --temp-basedir
|
||||
|
|
@ -163,7 +168,7 @@ def get_pdfjs_res_and_path(path):
|
|||
if path == "build/pdf.worker.mjs":
|
||||
content = b"\n".join(
|
||||
[
|
||||
_get_polyfills().encode("ascii"),
|
||||
_generate_polyfills().encode("ascii"),
|
||||
content,
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import os.path
|
|||
import shutil
|
||||
import functools
|
||||
import dataclasses
|
||||
from typing import IO, Optional
|
||||
from typing import Dict, IO, Optional
|
||||
|
||||
from qutebrowser.qt.core import pyqtSlot, pyqtSignal, QTimer, QUrl
|
||||
from qutebrowser.qt.widgets import QApplication
|
||||
|
|
@ -73,7 +73,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
|
|||
"""
|
||||
super().__init__(manager=manager, parent=manager)
|
||||
self.fileobj: Optional[IO[bytes]] = None
|
||||
self.raw_headers: dict[bytes, bytes] = {}
|
||||
self.raw_headers: Dict[bytes, bytes] = {}
|
||||
|
||||
self._autoclose = True
|
||||
self._retry_info = None
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ import textwrap
|
|||
import urllib
|
||||
import collections
|
||||
import secrets
|
||||
from typing import TypeVar, Optional, Union
|
||||
from collections.abc import Sequence, Callable
|
||||
from typing import TypeVar, Callable, Dict, List, Optional, Union, Sequence, Tuple
|
||||
|
||||
from qutebrowser.qt.core import QUrlQuery, QUrl
|
||||
|
||||
|
|
@ -36,7 +35,7 @@ pyeval_output = ":pyeval was never called"
|
|||
csrf_token: Optional[str] = None
|
||||
|
||||
|
||||
_HANDLERS: dict[str, "_HandlerCallable"] = {}
|
||||
_HANDLERS: Dict[str, "_HandlerCallable"] = {}
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
|
|
@ -78,7 +77,7 @@ class Redirect(Exception):
|
|||
|
||||
|
||||
# Return value: (mimetype, data) (encoded as utf-8 if a str is returned)
|
||||
_HandlerRet = tuple[str, Union[str, bytes]]
|
||||
_HandlerRet = Tuple[str, Union[str, bytes]]
|
||||
_HandlerCallable = Callable[[QUrl], _HandlerRet]
|
||||
_Handler = TypeVar('_Handler', bound=_HandlerCallable)
|
||||
|
||||
|
|
@ -106,7 +105,7 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
|
|||
return self._function(url)
|
||||
|
||||
|
||||
def data_for_url(url: QUrl) -> tuple[str, bytes]:
|
||||
def data_for_url(url: QUrl) -> Tuple[str, bytes]:
|
||||
"""Get the data to show for the given URL.
|
||||
|
||||
Args:
|
||||
|
|
@ -123,24 +122,25 @@ def data_for_url(url: QUrl) -> tuple[str, bytes]:
|
|||
|
||||
path = url.path()
|
||||
host = url.host()
|
||||
query = url.query()
|
||||
# A url like "qute:foo" is split as "scheme:path", not "scheme:host".
|
||||
log.misc.debug("url: {}, path: {}, host {}".format(
|
||||
url.toDisplayString(), path, host))
|
||||
if not path or not host:
|
||||
new_url = QUrl()
|
||||
new_url.setScheme('qute')
|
||||
# When path is absent, e.g. qute://help (with no trailing slash)
|
||||
if host:
|
||||
new_url.setHost(host)
|
||||
# When host is absent, e.g. qute:help
|
||||
else:
|
||||
new_url.setHost(path)
|
||||
|
||||
if not host:
|
||||
# Redirect qute:help -> qute://help/
|
||||
new_url = QUrl(url)
|
||||
new_url.setHost(path)
|
||||
new_url.setPath('/')
|
||||
if not new_url.host(): # Valid path but not valid host
|
||||
raise UrlInvalidError(f"Invalid host (from path): {path!r}")
|
||||
raise Redirect(new_url)
|
||||
|
||||
if not path:
|
||||
# Redirect qute://help -> qute://help/
|
||||
new_url = QUrl(url)
|
||||
new_url.setPath('/')
|
||||
raise Redirect(new_url)
|
||||
if query:
|
||||
new_url.setQuery(query)
|
||||
if new_url.host(): # path was a valid host
|
||||
raise Redirect(new_url)
|
||||
|
||||
try:
|
||||
handler = _HANDLERS[host]
|
||||
|
|
@ -180,7 +180,7 @@ def qute_bookmarks(_url: QUrl) -> _HandlerRet:
|
|||
@add_handler('tabs')
|
||||
def qute_tabs(_url: QUrl) -> _HandlerRet:
|
||||
"""Handler for qute://tabs. Display information about all open tabs."""
|
||||
tabs: dict[str, list[tuple[str, str]]] = collections.defaultdict(list)
|
||||
tabs: Dict[str, List[Tuple[str, str]]] = collections.defaultdict(list)
|
||||
for win_id, window in objreg.window_registry.items():
|
||||
if sip.isdeleted(window):
|
||||
continue
|
||||
|
|
@ -201,7 +201,7 @@ def qute_tabs(_url: QUrl) -> _HandlerRet:
|
|||
def history_data(
|
||||
start_time: float,
|
||||
offset: int = None
|
||||
) -> Sequence[dict[str, Union[str, int]]]:
|
||||
) -> Sequence[Dict[str, Union[str, int]]]:
|
||||
"""Return history data.
|
||||
|
||||
Arguments:
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@ import html
|
|||
import enum
|
||||
import netrc
|
||||
import tempfile
|
||||
from typing import Optional
|
||||
from collections.abc import Mapping, Iterable, Iterator, Callable
|
||||
from typing import Callable, Mapping, List, Optional, Iterable, Iterator
|
||||
|
||||
from qutebrowser.qt.core import QUrl, pyqtBoundSignal
|
||||
|
||||
from qutebrowser.config import config, configtypes
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import (usertypes, message, log, objreg, jinja, utils,
|
||||
qtutils, version, urlutils)
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
|
|
@ -26,15 +25,8 @@ class CallSuper(Exception):
|
|||
"""Raised when the caller should call the superclass instead."""
|
||||
|
||||
|
||||
def custom_headers(
|
||||
url: QUrl, *, fallback_accept_language: bool = True
|
||||
) -> list[tuple[bytes, bytes]]:
|
||||
"""Get the combined custom headers.
|
||||
|
||||
Arguments:
|
||||
fallback_accept_language: Whether to include the global (rather than
|
||||
per-domain override) accept language header as well.
|
||||
"""
|
||||
def custom_headers(url):
|
||||
"""Get the combined custom headers."""
|
||||
headers = {}
|
||||
|
||||
dnt_config = config.instance.get('content.headers.do_not_track', url=url)
|
||||
|
|
@ -48,17 +40,9 @@ def custom_headers(
|
|||
encoded_value = b"" if value is None else value.encode('ascii')
|
||||
headers[encoded_header] = encoded_value
|
||||
|
||||
# On QtWebEngine, we have fallback_accept_language set to False here for XHR
|
||||
# requests, so that we don't end up overriding headers that are set via the XHR API.
|
||||
#
|
||||
# The global Accept-Language header is set via
|
||||
# QWebEngineProfile::setHttpAcceptLanguage already anyways, so we only need
|
||||
# to take care of URL pattern overrides here.
|
||||
#
|
||||
# note: Once we drop QtWebKit, we could hardcode fallback_accept_language to False.
|
||||
accept_language = config.instance.get('content.headers.accept_language',
|
||||
url=url, fallback=fallback_accept_language)
|
||||
if accept_language is not None and not isinstance(accept_language, usertypes.Unset):
|
||||
url=url)
|
||||
if accept_language is not None:
|
||||
headers[b'Accept-Language'] = accept_language.encode('ascii')
|
||||
|
||||
return sorted(headers.items())
|
||||
|
|
@ -319,7 +303,6 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on,
|
|||
None otherwise.
|
||||
"""
|
||||
config_val = config.instance.get(option, url=url)
|
||||
opt = config.instance.get_opt(option)
|
||||
if config_val == 'ask':
|
||||
if url.isValid():
|
||||
urlstr = url.toString(QUrl.UrlFormattingOption.RemovePassword | QUrl.ComponentFormattingOption.FullyEncoded)
|
||||
|
|
@ -345,21 +328,12 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on,
|
|||
cancel_action=no_action, abort_on=abort_on,
|
||||
title='Permission request', text=text, url=urlstr,
|
||||
option=option)
|
||||
|
||||
if isinstance(opt.typ, configtypes.AsBool):
|
||||
config_val = opt.typ.to_bool(config_val)
|
||||
|
||||
if config_val is True:
|
||||
elif config_val:
|
||||
yes_action()
|
||||
return None
|
||||
elif config_val is False:
|
||||
else:
|
||||
no_action()
|
||||
return None
|
||||
else:
|
||||
raise AssertionError(
|
||||
f"Unsupported value for permission prompt setting ({option}), expected boolean or "
|
||||
f"'ask', got: {config_val} ({type(config_val)})"
|
||||
)
|
||||
|
||||
|
||||
def get_tab(win_id, target):
|
||||
|
|
@ -471,7 +445,7 @@ class FileSelectionMode(enum.Enum):
|
|||
folder = enum.auto()
|
||||
|
||||
|
||||
def choose_file(qb_mode: FileSelectionMode) -> list[str]:
|
||||
def choose_file(qb_mode: FileSelectionMode) -> List[str]:
|
||||
"""Select file(s)/folder for up-/downloading, using an external command.
|
||||
|
||||
Args:
|
||||
|
|
@ -511,10 +485,10 @@ def choose_file(qb_mode: FileSelectionMode) -> list[str]:
|
|||
|
||||
|
||||
def _execute_fileselect_command(
|
||||
command: list[str],
|
||||
command: List[str],
|
||||
qb_mode: FileSelectionMode,
|
||||
tmpfilename: Optional[str] = None
|
||||
) -> list[str]:
|
||||
) -> List[str]:
|
||||
"""Execute external command to choose file.
|
||||
|
||||
Args:
|
||||
|
|
@ -548,7 +522,7 @@ def _execute_fileselect_command(
|
|||
|
||||
def _validated_selected_files(
|
||||
qb_mode: FileSelectionMode,
|
||||
selected_files: list[str],
|
||||
selected_files: List[str],
|
||||
) -> Iterator[str]:
|
||||
"""Validates selected files if they are.
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import os.path
|
|||
import html
|
||||
import functools
|
||||
import collections
|
||||
from collections.abc import MutableMapping
|
||||
from typing import MutableMapping
|
||||
|
||||
from qutebrowser.qt.core import pyqtSignal, QUrl, QObject
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
"""Generic web element related code."""
|
||||
|
||||
from typing import Optional, TYPE_CHECKING, Union
|
||||
from collections.abc import Iterator
|
||||
from typing import Iterator, Optional, Set, TYPE_CHECKING, Union, Dict
|
||||
import collections.abc
|
||||
|
||||
from qutebrowser.qt import machinery
|
||||
|
|
@ -94,7 +93,7 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
|
|||
"""Get the geometry for this element."""
|
||||
raise NotImplementedError
|
||||
|
||||
def classes(self) -> set[str]:
|
||||
def classes(self) -> Set[str]:
|
||||
"""Get a set of classes assigned to this element."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
@ -337,7 +336,7 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
|
|||
log.webelem.debug("Sending fake click to {!r} at position {} with "
|
||||
"target {}".format(self, pos, click_target))
|
||||
|
||||
target_modifiers: dict[usertypes.ClickTarget, KeyboardModifierType] = {
|
||||
target_modifiers: Dict[usertypes.ClickTarget, KeyboardModifierType] = {
|
||||
usertypes.ClickTarget.normal: Qt.KeyboardModifier.NoModifier,
|
||||
usertypes.ClickTarget.window: Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.ShiftModifier,
|
||||
usertypes.ClickTarget.tab: Qt.KeyboardModifier.ControlModifier,
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ import copy
|
|||
import enum
|
||||
import dataclasses
|
||||
import collections
|
||||
from typing import (Any, Optional, Union)
|
||||
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
|
||||
from typing import (Any, Iterator, Mapping, MutableMapping, Optional, Set, Tuple, Union,
|
||||
Sequence, List)
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import usertypes, utils, log, version
|
||||
|
|
@ -212,7 +212,7 @@ class _Setting:
|
|||
return str(value)
|
||||
return str(self.mapping[value])
|
||||
|
||||
def chromium_tuple(self, value: Any) -> Optional[tuple[str, str]]:
|
||||
def chromium_tuple(self, value: Any) -> Optional[Tuple[str, str]]:
|
||||
"""Get the Chromium key and value, or None if no value should be set."""
|
||||
if self.mapping is not None and self.mapping[value] is None:
|
||||
return None
|
||||
|
|
@ -242,7 +242,7 @@ class _Definition:
|
|||
def __init__(
|
||||
self,
|
||||
*args: _Setting,
|
||||
mandatory: set[str],
|
||||
mandatory: Set[str],
|
||||
prefix: str,
|
||||
switch_names: Mapping[Optional[str], str] = None,
|
||||
) -> None:
|
||||
|
|
@ -255,7 +255,7 @@ class _Definition:
|
|||
else:
|
||||
self._switch_names = {None: _BLINK_SETTINGS}
|
||||
|
||||
def prefixed_settings(self) -> Iterator[tuple[str, _Setting]]:
|
||||
def prefixed_settings(self) -> Iterator[Tuple[str, _Setting]]:
|
||||
"""Get all "prepared" settings.
|
||||
|
||||
Yields tuples which contain the Chromium setting key (e.g. 'blink-settings' or
|
||||
|
|
@ -399,7 +399,7 @@ def settings(
|
|||
*,
|
||||
versions: version.WebEngineVersions,
|
||||
special_flags: Sequence[str],
|
||||
) -> Mapping[str, Sequence[tuple[str, str]]]:
|
||||
) -> Mapping[str, Sequence[Tuple[str, str]]]:
|
||||
"""Get necessary blink settings to configure dark mode for QtWebEngine.
|
||||
|
||||
Args:
|
||||
|
|
@ -413,12 +413,12 @@ def settings(
|
|||
variant = _variant(versions)
|
||||
log.init.debug(f"Darkmode variant: {variant.name}")
|
||||
|
||||
result: Mapping[str, list[tuple[str, str]]] = collections.defaultdict(list)
|
||||
result: Mapping[str, List[Tuple[str, str]]] = collections.defaultdict(list)
|
||||
|
||||
blink_settings_flag = f'--{_BLINK_SETTINGS}='
|
||||
for flag in special_flags:
|
||||
if flag.startswith(blink_settings_flag):
|
||||
for pair in flag.removeprefix(blink_settings_flag).split(','):
|
||||
for pair in flag[len(blink_settings_flag):].split(','):
|
||||
key, val = pair.split('=', maxsplit=1)
|
||||
result[_BLINK_SETTINGS].append((key, val))
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
|||
}
|
||||
new_types = {
|
||||
"WebSocket": interceptors.ResourceType.websocket, # added in Qt 6.4
|
||||
"Json": interceptors.ResourceType.json, # added in Qt 6.8
|
||||
}
|
||||
for qt_name, qb_value in new_types.items():
|
||||
qt_value = getattr(
|
||||
|
|
@ -188,9 +187,7 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
|||
if request.is_blocked:
|
||||
info.block(True)
|
||||
|
||||
for header, value in shared.custom_headers(
|
||||
url=url, fallback_accept_language=not is_xhr
|
||||
):
|
||||
for header, value in shared.custom_headers(url=url):
|
||||
if header.lower() == b'accept' and is_xhr:
|
||||
# https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader
|
||||
# says: "If no Accept header has been set using this, an Accept header
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ import dataclasses
|
|||
import itertools
|
||||
import functools
|
||||
import subprocess
|
||||
from typing import Any, Optional, TYPE_CHECKING
|
||||
from collections.abc import Iterator
|
||||
from typing import Any, List, Dict, Optional, Iterator, Type, TYPE_CHECKING
|
||||
|
||||
from qutebrowser.qt import machinery
|
||||
from qutebrowser.qt.core import (Qt, QObject, QVariant, QMetaType, QByteArray, pyqtSlot,
|
||||
|
|
@ -196,7 +195,7 @@ class NotificationBridgePresenter(QObject):
|
|||
def __init__(self, parent: QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self._active_notifications: dict[int, 'QWebEngineNotification'] = {}
|
||||
self._active_notifications: Dict[int, 'QWebEngineNotification'] = {}
|
||||
self._adapter: Optional[AbstractNotificationAdapter] = None
|
||||
|
||||
config.instance.changed.connect(self._init_adapter)
|
||||
|
|
@ -233,8 +232,8 @@ class NotificationBridgePresenter(QObject):
|
|||
def _get_adapter_candidates(
|
||||
self,
|
||||
setting: str,
|
||||
) -> list[type[AbstractNotificationAdapter]]:
|
||||
candidates: dict[str, list[type[AbstractNotificationAdapter]]] = {
|
||||
) -> List[Type[AbstractNotificationAdapter]]:
|
||||
candidates: Dict[str, List[Type[AbstractNotificationAdapter]]] = {
|
||||
"libnotify": [
|
||||
DBusNotificationAdapter,
|
||||
SystrayNotificationAdapter,
|
||||
|
|
@ -286,10 +285,7 @@ class NotificationBridgePresenter(QObject):
|
|||
|
||||
if replaces_id is None:
|
||||
if notification_id in self._active_notifications:
|
||||
message.error(f"Got duplicate notification id {notification_id} "
|
||||
f"from {self._adapter.NAME}")
|
||||
self._drop_adapter()
|
||||
return
|
||||
raise Error(f"Got duplicate id {notification_id}")
|
||||
|
||||
qt_notification.show()
|
||||
self._active_notifications[notification_id] = qt_notification
|
||||
|
|
@ -669,7 +665,7 @@ class _ServerCapabilities:
|
|||
kde_origin_name: bool
|
||||
|
||||
@classmethod
|
||||
def from_list(cls, capabilities: list[str]) -> "_ServerCapabilities":
|
||||
def from_list(cls, capabilities: List[str]) -> "_ServerCapabilities":
|
||||
return cls(
|
||||
actions='actions' in capabilities,
|
||||
body_markup='body-markup' in capabilities,
|
||||
|
|
@ -955,10 +951,10 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
|
|||
qtutils.extract_enum_val(QMetaType.Type.QStringList),
|
||||
)
|
||||
|
||||
def _get_hints_arg(self, *, origin_url: QUrl, icon: QImage) -> dict[str, Any]:
|
||||
def _get_hints_arg(self, *, origin_url: QUrl, icon: QImage) -> Dict[str, Any]:
|
||||
"""Get the hints argument for present()."""
|
||||
origin_url_str = origin_url.toDisplayString()
|
||||
hints: dict[str, Any] = {
|
||||
hints: Dict[str, Any] = {
|
||||
# Include the origin in case the user wants to do different things
|
||||
# with different origin's notifications.
|
||||
"x-qutebrowser-origin": origin_url_str,
|
||||
|
|
@ -988,7 +984,7 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
|
|||
title: str,
|
||||
body: str,
|
||||
actions: QDBusArgument,
|
||||
hints: dict[str, Any],
|
||||
hints: Dict[str, Any],
|
||||
timeout: int,
|
||||
) -> Any:
|
||||
"""Wrapper around DBus call to use keyword args."""
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@
|
|||
"""QtWebEngine specific part of the web element API."""
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Optional, Union)
|
||||
from collections.abc import Iterator, Callable
|
||||
TYPE_CHECKING, Any, Callable, Dict, Iterator, Optional, Set, Tuple, Union)
|
||||
|
||||
from qutebrowser.qt.core import QRect, QEventLoop
|
||||
from qutebrowser.qt.widgets import QApplication
|
||||
|
|
@ -25,11 +24,11 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
|
||||
_tab: "webenginetab.WebEngineTab"
|
||||
|
||||
def __init__(self, js_dict: dict[str, Any],
|
||||
def __init__(self, js_dict: Dict[str, Any],
|
||||
tab: 'webenginetab.WebEngineTab') -> None:
|
||||
super().__init__(tab)
|
||||
# Do some sanity checks on the data we get from JS
|
||||
js_dict_types: dict[str, Union[type, tuple[type, ...]]] = {
|
||||
js_dict_types: Dict[str, Union[type, Tuple[type, ...]]] = {
|
||||
'id': int,
|
||||
'text': str,
|
||||
'value': (str, int, float),
|
||||
|
|
@ -106,7 +105,7 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
log.stub()
|
||||
return QRect()
|
||||
|
||||
def classes(self) -> set[str]:
|
||||
def classes(self) -> Set[str]:
|
||||
"""Get a list of classes assigned to this element."""
|
||||
return set(self._js_dict['class_name'].split())
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Module attributes:
|
|||
import os
|
||||
import operator
|
||||
import pathlib
|
||||
from typing import cast, Any, Optional, Union, TYPE_CHECKING
|
||||
from typing import cast, Any, List, Optional, Tuple, Union, TYPE_CHECKING
|
||||
|
||||
from qutebrowser.qt import machinery
|
||||
from qutebrowser.qt.gui import QFont
|
||||
|
|
@ -26,7 +26,7 @@ from qutebrowser.config import config, websettings
|
|||
from qutebrowser.config.websettings import AttributeInfo as Attr
|
||||
from qutebrowser.misc import pakjoy
|
||||
from qutebrowser.utils import (standarddir, qtutils, message, log,
|
||||
urlmatch, usertypes, objreg, version, utils)
|
||||
urlmatch, usertypes, objreg, version)
|
||||
if TYPE_CHECKING:
|
||||
from qutebrowser.browser.webengine import interceptor
|
||||
|
||||
|
|
@ -216,10 +216,6 @@ class WebEngineSettings(websettings.AbstractSettings):
|
|||
QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: True,
|
||||
QWebEngineSettings.WebAttribute.JavascriptCanPaste: True,
|
||||
},
|
||||
'ask': {
|
||||
QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: False,
|
||||
QWebEngineSettings.WebAttribute.JavascriptCanPaste: False,
|
||||
},
|
||||
}
|
||||
|
||||
def set_unknown_url_scheme_policy(
|
||||
|
|
@ -285,7 +281,6 @@ class ProfileSetter:
|
|||
self._set_hardcoded_settings()
|
||||
self.set_persistent_cookie_policy()
|
||||
self.set_dictionary_language()
|
||||
self.disable_persistent_permissions_policy()
|
||||
|
||||
def _set_hardcoded_settings(self):
|
||||
"""Set up settings with a fixed value."""
|
||||
|
|
@ -348,23 +343,7 @@ class ProfileSetter:
|
|||
|
||||
log.config.debug("Found dicts: {}".format(filenames))
|
||||
self._profile.setSpellCheckLanguages(filenames)
|
||||
|
||||
should_enable = bool(filenames)
|
||||
if self._profile.isSpellCheckEnabled() != should_enable:
|
||||
# Only setting conditionally as a WORKAROUND for a bogus Qt error message:
|
||||
# https://bugreports.qt.io/browse/QTBUG-131969
|
||||
self._profile.setSpellCheckEnabled(should_enable)
|
||||
|
||||
def disable_persistent_permissions_policy(self):
|
||||
"""Disable webengine's permission persistence."""
|
||||
if machinery.IS_QT6: # for mypy
|
||||
try:
|
||||
# New in WebEngine 6.8.0
|
||||
self._profile.setPersistentPermissionsPolicy(
|
||||
QWebEngineProfile.PersistentPermissionsPolicy.AskEveryTime
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
self._profile.setSpellCheckEnabled(bool(filenames))
|
||||
|
||||
|
||||
def _update_settings(option):
|
||||
|
|
@ -378,12 +357,6 @@ def _update_settings(option):
|
|||
def _init_user_agent_str(ua):
|
||||
global parsed_user_agent
|
||||
parsed_user_agent = websettings.UserAgent.parse(ua)
|
||||
if parsed_user_agent.upstream_browser_version.endswith(".0.0.0"):
|
||||
# https://codereview.qt-project.org/c/qt/qtwebengine/+/616314
|
||||
# but we still want the full version available to users if they want it.
|
||||
qtwe_versions = version.qtwebengine_versions()
|
||||
assert qtwe_versions.chromium is not None
|
||||
parsed_user_agent.upstream_browser_version = qtwe_versions.chromium
|
||||
|
||||
|
||||
def init_user_agent():
|
||||
|
|
@ -417,70 +390,16 @@ def _init_profile(profile: QWebEngineProfile) -> None:
|
|||
lambda url: profile.clearVisitedLinks([url]))
|
||||
|
||||
_global_settings.init_settings()
|
||||
_maybe_disable_hangouts_extension(profile)
|
||||
|
||||
|
||||
def _maybe_disable_hangouts_extension(profile: QWebEngineProfile) -> None:
|
||||
"""Disable the Hangouts extension for Qt 6.10+."""
|
||||
if not config.val.qt.workarounds.disable_hangouts_extension:
|
||||
return
|
||||
|
||||
if machinery.IS_QT6: # mypy
|
||||
try:
|
||||
ext_manager = profile.extensionManager()
|
||||
except AttributeError:
|
||||
return # added in QtWebEngine 6.10
|
||||
|
||||
qtwe_versions = version.qtwebengine_versions(avoid_init=True)
|
||||
if (
|
||||
qtwe_versions.webengine == utils.VersionNumber(6, 10, 1)
|
||||
and profile.isOffTheRecord()
|
||||
):
|
||||
# WORKAROUND for https://github.com/qutebrowser/qutebrowser/issues/8785
|
||||
log.misc.warning(
|
||||
"Not disabling Hangouts extension on private profile to avoid "
|
||||
"QtWebEngine crash with Qt 6.10.1")
|
||||
return
|
||||
|
||||
assert ext_manager is not None # mypy
|
||||
for info in ext_manager.extensions():
|
||||
if info.id() == pakjoy.HANGOUTS_EXT_ID:
|
||||
log.misc.debug(f"Disabling extension: {info.name()}")
|
||||
# setExtensionEnabled(info, False) seems to segfault
|
||||
ext_manager.unloadExtension(info)
|
||||
|
||||
|
||||
def _clear_webengine_permissions_json():
|
||||
"""Remove QtWebEngine's persistent permissions file, if present.
|
||||
|
||||
We have our own permissions feature and don't integrate with their one.
|
||||
This only needs to be called when you are on Qt6.8 but PyQt<6.8, since if
|
||||
we have access to the `setPersistentPermissionsPolicy()` we will use that
|
||||
to disable the Qt feature.
|
||||
This needs to be called before we call `setPersistentStoragePath()`
|
||||
because Qt will load the file during that.
|
||||
"""
|
||||
permissions_file = pathlib.Path(standarddir.data()) / "webengine" / "permissions.json"
|
||||
try:
|
||||
permissions_file.unlink(missing_ok=True)
|
||||
except OSError as err:
|
||||
log.init.warning(
|
||||
f"Error while cleaning up webengine permissions file: {err}"
|
||||
)
|
||||
|
||||
|
||||
def default_qt_profile() -> QWebEngineProfile:
|
||||
"""Get the default profile from Qt."""
|
||||
if machinery.IS_QT6:
|
||||
return QWebEngineProfile("Default")
|
||||
else:
|
||||
return QWebEngineProfile.defaultProfile()
|
||||
|
||||
|
||||
def _init_default_profile():
|
||||
"""Init the default QWebEngineProfile."""
|
||||
global default_profile
|
||||
default_profile = default_qt_profile()
|
||||
|
||||
if machinery.IS_QT6:
|
||||
default_profile = QWebEngineProfile("Default")
|
||||
else:
|
||||
default_profile = QWebEngineProfile.defaultProfile()
|
||||
assert not default_profile.isOffTheRecord()
|
||||
|
||||
assert parsed_user_agent is None # avoid earlier profile initialization
|
||||
|
|
@ -488,25 +407,13 @@ def _init_default_profile():
|
|||
|
||||
init_user_agent()
|
||||
ua_version = version.qtwebengine_versions()
|
||||
|
||||
logger = log.init.warning
|
||||
if machinery.IS_QT5:
|
||||
# With Qt 5.15, we can't quite be sure about which QtWebEngine patch version
|
||||
# we're getting, as ELF parsing might be broken and there's no other way.
|
||||
# For most of the code, we don't really care about the patch version though.
|
||||
assert (
|
||||
non_ua_version.webengine.strip_patch() == ua_version.webengine.strip_patch()
|
||||
), (non_ua_version, ua_version)
|
||||
logger = log.init.debug
|
||||
|
||||
if ua_version.webengine != non_ua_version.webengine:
|
||||
logger(
|
||||
log.init.warning(
|
||||
"QtWebEngine version mismatch - unexpected behavior might occur, "
|
||||
"please open a bug about this.\n"
|
||||
f" Early version: {non_ua_version}\n"
|
||||
f" Real version: {ua_version}")
|
||||
|
||||
_clear_webengine_permissions_json()
|
||||
default_profile.setCachePath(
|
||||
os.path.join(standarddir.cache(), 'webengine'))
|
||||
default_profile.setPersistentStoragePath(
|
||||
|
|
@ -539,23 +446,13 @@ def _init_site_specific_quirks():
|
|||
# default_ua = ("Mozilla/5.0 ({os_info}) "
|
||||
# "AppleWebKit/{webkit_version} (KHTML, like Gecko) "
|
||||
# "{qt_key}/{qt_version} "
|
||||
# "{upstream_browser_key}/{upstream_browser_version_short} "
|
||||
# "{upstream_browser_key}/{upstream_browser_version} "
|
||||
# "Safari/{webkit_version}")
|
||||
firefox_ua = "Mozilla/5.0 ({os_info}; rv:145.0) Gecko/20100101 Firefox/145.0"
|
||||
|
||||
# Needed for gitlab.gnome.org which blocks old Chromium versions outright,
|
||||
# except when QtWebEngine/... is in the UA.
|
||||
#
|
||||
# We could further modify the UA to just "qutebrowser" or something so we don't get
|
||||
# Anubis at all, but it looks like their Anubis triggers to more than just
|
||||
# Mozilla/5.0 (also AppleWebKit/... and Chromium/... possibly?), so at that point
|
||||
# I'm not sure if we can strip down the UA so much without breaking
|
||||
# something in GitLab as well.
|
||||
not_mozilla_ua = (
|
||||
"Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, like Gecko) "
|
||||
"{qt_key}/{qt_version} {upstream_browser_key}/{upstream_browser_version_short} "
|
||||
"Safari/{webkit_version}"
|
||||
)
|
||||
no_qtwe_ua = ("Mozilla/5.0 ({os_info}) "
|
||||
"AppleWebKit/{webkit_version} (KHTML, like Gecko) "
|
||||
"{upstream_browser_key}/{upstream_browser_version} "
|
||||
"Safari/{webkit_version}")
|
||||
firefox_ua = "Mozilla/5.0 ({os_info}; rv:90.0) Gecko/20100101 Firefox/90.0"
|
||||
|
||||
def maybe_newer_chrome_ua(at_least_version):
|
||||
"""Return a new UA if our current chrome version isn't at least at_least_version."""
|
||||
|
|
@ -570,14 +467,23 @@ def _init_site_specific_quirks():
|
|||
"Safari/537.36"
|
||||
)
|
||||
|
||||
utils.unused(maybe_newer_chrome_ua)
|
||||
|
||||
user_agents = [
|
||||
# Needed to avoid a ""WhatsApp works with Google Chrome 36+" error
|
||||
# page which doesn't allow to use WhatsApp Web at all. Also see the
|
||||
# additional JS quirk: qutebrowser/javascript/quirks/whatsapp_web.user.js
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/4445
|
||||
("ua-whatsapp", 'https://web.whatsapp.com/', no_qtwe_ua),
|
||||
|
||||
# Needed to avoid a "you're using a browser [...] that doesn't allow us
|
||||
# to keep your account secure" error.
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/5182
|
||||
("ua-google", "https://accounts.google.com/*", firefox_ua),
|
||||
("ua-gnome-gitlab", "https://gitlab.gnome.org/*", not_mozilla_ua),
|
||||
("ua-google", 'https://accounts.google.com/*', firefox_ua),
|
||||
|
||||
# Needed because Slack adds an error which prevents using it relatively
|
||||
# aggressively, despite things actually working fine.
|
||||
# October 2023: Slack claims they only support 112+. On #7951 at least
|
||||
# one user claims it still works fine on 108 based Qt versions.
|
||||
("ua-slack", 'https://*.slack.com/*', maybe_newer_chrome_ua(112)),
|
||||
]
|
||||
|
||||
for name, pattern, ua in user_agents:
|
||||
|
|
@ -603,7 +509,7 @@ def _init_default_settings():
|
|||
- Make sure the devtools always get images/JS permissions.
|
||||
- On Qt 6, make sure files in the data path can load external resources.
|
||||
"""
|
||||
devtools_settings: list[tuple[str, Any]] = [
|
||||
devtools_settings: List[Tuple[str, Any]] = [
|
||||
('content.javascript.enabled', True),
|
||||
('content.images', True),
|
||||
('content.cookies.accept', 'all'),
|
||||
|
|
@ -616,7 +522,7 @@ def _init_default_settings():
|
|||
hide_userconfig=True)
|
||||
|
||||
if machinery.IS_QT6:
|
||||
userscripts_settings: list[tuple[str, Any]] = [
|
||||
userscripts_settings: List[Tuple[str, Any]] = [
|
||||
("content.local_content_can_access_remote_urls", True),
|
||||
("content.local_content_can_access_file_urls", False),
|
||||
]
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue